blob: cc95cc4d20df2dd7488b6959ced37d346a8e4010 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/contextual_search/touch_to_search_permissions_mediator.h"
#import <UIKit/UIKit.h>
#include <memory>
#include "base/command_line.h"
#import "base/mac/scoped_nsobject.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#import "base/test/ios/wait_util.h"
#include "base/time/time.h"
#include "components/prefs/pref_service.h"
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_data.h"
#include "components/search_engines/template_url_service.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/chrome_switches.h"
#include "ios/chrome/browser/pref_names.h"
#include "ios/chrome/browser/search_engines/template_url_service_factory.h"
#include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
#include "ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.h"
#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
#include "ios/chrome/browser/sync/sync_setup_service_mock.h"
#import "ios/chrome/browser/ui/contextual_search/touch_to_search_permissions_mediator+testing.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "net/base/network_change_notifier.h"
#import "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h"
using testing::Return;
@interface TestTouchToSearchPermissionsAudience
: NSObject<TouchToSearchPermissionsChangeAudience>
@property BOOL updated;
@end
@implementation TestTouchToSearchPermissionsAudience
@synthesize updated = _updated;
- (void)touchToSearchPermissionsUpdated {
self.updated = YES;
}
@end
namespace {
std::unique_ptr<KeyedService> CreateSyncSetupService(
web::BrowserState* context) {
ios::ChromeBrowserState* chrome_browser_state =
ios::ChromeBrowserState::FromBrowserState(context);
syncer::SyncService* sync_service =
IOSChromeProfileSyncServiceFactory::GetForBrowserState(
chrome_browser_state);
return base::MakeUnique<SyncSetupServiceMock>(
sync_service, chrome_browser_state->GetPrefs());
}
// Override NetworkChangeNotifier to simulate connection type changes for tests.
class TestNetworkChangeNotifier : public net::NetworkChangeNotifier {
public:
TestNetworkChangeNotifier()
: net::NetworkChangeNotifier(),
connection_type_to_return_(
net::NetworkChangeNotifier::CONNECTION_UNKNOWN) {}
// Simulates a change of the connection type to |type|. This will notify any
// objects that are NetworkChangeNotifiers.
void SimulateNetworkConnectionChange(
net::NetworkChangeNotifier::ConnectionType type) {
connection_type_to_return_ = type;
net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
base::RunLoop().RunUntilIdle();
}
private:
ConnectionType GetCurrentConnectionType() const override {
return connection_type_to_return_;
}
// The currently simulated network connection type. If this is set to
// CONNECTION_NONE, then NetworkChangeNotifier::IsOffline will return true.
net::NetworkChangeNotifier::ConnectionType connection_type_to_return_;
DISALLOW_COPY_AND_ASSIGN(TestNetworkChangeNotifier);
};
class TouchToSearchPermissionsMediatorTest : public PlatformTest {
protected:
TouchToSearchPermissionsMediatorTest() {
TestChromeBrowserState::Builder browserStateBuilder;
browserStateBuilder.AddTestingFactory(
SyncSetupServiceFactory::GetInstance(), &CreateSyncSetupService);
browserStateBuilder.AddTestingFactory(
ios::TemplateURLServiceFactory::GetInstance(),
ios::TemplateURLServiceFactory::GetDefaultFactory());
browser_state_ = browserStateBuilder.Build();
template_url_service_ =
ios::TemplateURLServiceFactory::GetForBrowserState(BrowserState());
template_url_service_->Load();
tts_permissions_.reset([[TouchToSearchPermissionsMediator alloc]
initWithBrowserState:BrowserState()]);
}
ios::ChromeBrowserState* BrowserState() { return browser_state_.get(); }
web::TestWebThreadBundle thread_bundle_;
std::unique_ptr<TestChromeBrowserState> browser_state_;
base::scoped_nsobject<TouchToSearchPermissionsMediator> tts_permissions_;
TemplateURLService* template_url_service_;
};
TEST_F(TouchToSearchPermissionsMediatorTest, PrefStates) {
// Expect empty browser state to have undecided pref value.
EXPECT_EQ(TouchToSearch::UNDECIDED, [tts_permissions_ preferenceState]);
const struct {
const std::string pref_value;
TouchToSearch::TouchToSearchPreferenceState state;
} tests[] = {
{"false", TouchToSearch::DISABLED},
{"true", TouchToSearch::ENABLED},
{"", TouchToSearch::UNDECIDED},
};
for (const auto& test : tests) {
// set state, test value
BrowserState()->GetPrefs()->SetString(prefs::kContextualSearchEnabled,
test.pref_value);
EXPECT_EQ(test.state, [tts_permissions_ preferenceState]);
// set state from value
[tts_permissions_ setPreferenceState:test.state];
std::string prefValue =
BrowserState()->GetPrefs()->GetString(prefs::kContextualSearchEnabled);
EXPECT_EQ(test.pref_value, prefValue);
}
// For non-debug builds, also test cases that would DCHECK.
#if !DCHECK_IS_ON()
// Check that an unexpected pref value returns DISABLED.
BrowserState()->GetPrefs()->SetString(prefs::kContextualSearchEnabled,
"somethingElse");
EXPECT_EQ(TouchToSearch::DISABLED, [tts_permissions_ preferenceState]);
#endif
}
TEST_F(TouchToSearchPermissionsMediatorTest, TapContext) {
const GURL secure_url("https://www.some-website.com/");
const GURL insecure_url("http://www.some-website.com/");
const struct {
TouchToSearch::TouchToSearchPreferenceState state;
const GURL* url;
BOOL expected_permission;
} tests[] = {
// DISABLED should always deny extraction.
// ENABLED should permit it.
// UNDECIDED should permit it only on http pages.
{TouchToSearch::DISABLED, &secure_url, NO},
{TouchToSearch::DISABLED, &insecure_url, NO},
{TouchToSearch::ENABLED, &secure_url, YES},
{TouchToSearch::ENABLED, &insecure_url, YES},
{TouchToSearch::UNDECIDED, &secure_url, NO},
{TouchToSearch::UNDECIDED, &insecure_url, YES},
};
for (const auto& test : tests) {
[tts_permissions_ setPreferenceState:test.state];
EXPECT_EQ(test.expected_permission,
[tts_permissions_ canExtractTapContextForURL:*test.url]);
}
}
TEST_F(TouchToSearchPermissionsMediatorTest, URLSending) {
// Wrap the tests of -canSendPageURLs states in contexts of the different
// pref states; DISABLED should mean that the expected result is false
// regardless of other tests.
const struct {
TouchToSearch::TouchToSearchPreferenceState prefState;
BOOL performs_check;
} state_contexts[] = {
{TouchToSearch::DISABLED, NO},
{TouchToSearch::UNDECIDED, YES},
{TouchToSearch::ENABLED, YES},
};
const struct {
bool sync_enabled;
BOOL expected_permission;
} tests[] = {
{false, NO}, {true, YES},
};
for (const auto& context : state_contexts) {
[tts_permissions_ setPreferenceState:context.prefState];
for (const auto& test : tests) {
SyncSetupServiceMock* syncSetupService =
static_cast<SyncSetupServiceMock*>(
SyncSetupServiceFactory::GetForBrowserState(BrowserState()));
if (context.performs_check) {
EXPECT_CALL(*syncSetupService, IsSyncEnabled())
.WillOnce(Return(test.sync_enabled));
}
EXPECT_EQ(test.expected_permission && context.performs_check,
[tts_permissions_ canSendPageURLs]);
}
}
}
TEST_F(TouchToSearchPermissionsMediatorTest, SearchPreloading) {
TestNetworkChangeNotifier network_change_notifier;
const struct {
TouchToSearch::TouchToSearchPreferenceState prefState;
BOOL expectation_mask;
} state_contexts[] = {
{TouchToSearch::DISABLED, NO},
{TouchToSearch::UNDECIDED, YES},
{TouchToSearch::ENABLED, YES},
};
const struct {
bool prefetch_enabled;
bool wifi_prefetch_only;
net::NetworkChangeNotifier::ConnectionType network_connection_type;
BOOL expected_permission;
} tests[] = {
// clang-format off
// If prefectching is off, network preloading should never be allowed.
{false, false, net::NetworkChangeNotifier::CONNECTION_WIFI, NO},
{false, false, net::NetworkChangeNotifier::CONNECTION_3G, NO},
{false, true, net::NetworkChangeNotifier::CONNECTION_WIFI, NO},
{false, true, net::NetworkChangeNotifier::CONNECTION_3G, NO},
// If prefetching is on and not wifi-only, preloading should always be OK.
{true, false, net::NetworkChangeNotifier::CONNECTION_WIFI, YES},
{true, false, net::NetworkChangeNotifier::CONNECTION_3G, YES},
// Wifi only prefetching should not allow prefetch over cellular.
{true, true, net::NetworkChangeNotifier::CONNECTION_WIFI, YES},
{true, true, net::NetworkChangeNotifier::CONNECTION_3G, NO},
// clang-format on
};
for (const auto& context : state_contexts) {
[tts_permissions_ setPreferenceState:context.prefState];
for (const auto& test : tests) {
BrowserState()->GetPrefs()->SetBoolean(prefs::kNetworkPredictionEnabled,
test.prefetch_enabled);
BrowserState()->GetPrefs()->SetBoolean(prefs::kNetworkPredictionWifiOnly,
test.wifi_prefetch_only);
network_change_notifier.SimulateNetworkConnectionChange(
test.network_connection_type);
EXPECT_EQ(test.expected_permission && context.expectation_mask,
[tts_permissions_ canPreloadSearchResults]);
}
}
}
TEST_F(TouchToSearchPermissionsMediatorTest,
AreQueriesDisallowedWithoutTemplateService) {
TestChromeBrowserState::Builder browserStateBuilder;
std::unique_ptr<TestChromeBrowserState> browser_state(
browserStateBuilder.Build());
base::scoped_nsobject<TouchToSearchPermissionsMediator> tts_permissions(
[[TouchToSearchPermissionsMediator alloc]
initWithBrowserState:browser_state.get()]);
EXPECT_FALSE([tts_permissions areContextualSearchQueriesSupported]);
}
TEST_F(TouchToSearchPermissionsMediatorTest, AreQueriesAllowed) {
// The initial default search engine should support contextual search.
EXPECT_TRUE([tts_permissions_ areContextualSearchQueriesSupported]);
// Make a user-defined search engine and set it as the default.
TemplateURLData data;
data.SetShortName(base::ASCIIToUTF16("cs-not-supported"));
TemplateURL* newUrl = template_url_service_->Add(
std::unique_ptr<TemplateURL>(new TemplateURL(data)));
// |template_url_service_| will take ownership of newUrl
ASSERT_TRUE(newUrl);
template_url_service_->SetUserSelectedDefaultSearchProvider(newUrl);
// The newly-selected search engine doesn't support contextual search.
EXPECT_FALSE([tts_permissions_ areContextualSearchQueriesSupported]);
}
TEST_F(TouchToSearchPermissionsMediatorTest, CanEnable) {
base::scoped_nsobject<MockTouchToSearchPermissionsMediator> permissions(
[[MockTouchToSearchPermissionsMediator alloc]
initWithBrowserState:BrowserState()]);
const struct {
BOOL enabled_on_device;
TouchToSearch::TouchToSearchPreferenceState prefState;
BOOL voiceover_enabled;
BOOL queries_allowed;
BOOL expect_available;
} tests[] = {
// clang-format off
// Enabled cases:
// Enabled on command line, voiceover off, queries supported,
// preference state not disabled.
{YES, TouchToSearch::ENABLED, NO, YES, YES},
{YES, TouchToSearch::UNDECIDED, NO, YES, YES},
// Disabled cases: All others.
{NO, TouchToSearch::DISABLED, NO, NO, NO},
{NO, TouchToSearch::DISABLED, NO, YES, NO},
{NO, TouchToSearch::DISABLED, YES, NO, NO},
{NO, TouchToSearch::DISABLED, YES, YES, NO},
{NO, TouchToSearch::UNDECIDED, NO, NO, NO},
{NO, TouchToSearch::UNDECIDED, NO, YES, NO},
{NO, TouchToSearch::UNDECIDED, YES, NO, NO},
{NO, TouchToSearch::UNDECIDED, YES, YES, NO},
{NO, TouchToSearch::ENABLED, NO, NO, NO},
{NO, TouchToSearch::ENABLED, NO, YES, NO},
{NO, TouchToSearch::ENABLED, YES, NO, NO},
{NO, TouchToSearch::ENABLED, YES, YES, NO},
{YES, TouchToSearch::DISABLED, NO, NO, NO},
{YES, TouchToSearch::DISABLED, NO, YES, NO},
{YES, TouchToSearch::DISABLED, YES, NO, NO},
{YES, TouchToSearch::DISABLED, YES, YES, NO},
{YES, TouchToSearch::UNDECIDED, NO, NO, NO},
{YES, TouchToSearch::UNDECIDED, YES, NO, NO},
{YES, TouchToSearch::UNDECIDED, YES, YES, NO},
{YES, TouchToSearch::ENABLED, NO, NO, NO},
{YES, TouchToSearch::ENABLED, YES, NO, NO},
{YES, TouchToSearch::ENABLED, YES, YES, NO},
// clang-format on
};
for (const auto& test : tests) {
[[permissions class]
setIsTouchToSearchAvailableOnDevice:test.enabled_on_device];
permissions.get().preferenceState = test.prefState;
permissions.get().isVoiceOverEnabled = test.voiceover_enabled;
permissions.get().areContextualSearchQueriesSupported =
test.queries_allowed;
EXPECT_EQ(test.expect_available, [permissions canEnable]);
}
}
TEST_F(TouchToSearchPermissionsMediatorTest, AudienceNotifications) {
base::scoped_nsobject<id> audience(
[[TestTouchToSearchPermissionsAudience alloc] init]);
[tts_permissions_ setAudience:audience];
base::TimeDelta delay = base::TimeDelta::FromMilliseconds(50);
[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationWillEnterForegroundNotification
object:[UIApplication sharedApplication]];
// Audience call is asynchronous, so expect nothing yet.
EXPECT_FALSE([audience updated]);
// Wait for async call.
base::test::ios::WaitUntilCondition(
^bool(void) {
return [audience updated];
},
false, delay);
EXPECT_TRUE([audience updated]);
// Reset |audience|.
[audience setUpdated:NO];
[[NSNotificationCenter defaultCenter]
postNotificationName:UIAccessibilityVoiceOverStatusChanged
object:nil];
// Audience call is asynchronous, so expect nothing yet.
EXPECT_FALSE([audience updated]);
// Wait for async call.
base::test::ios::WaitUntilCondition(^bool(void) {
return [audience updated];
});
EXPECT_TRUE([audience updated]);
// Reset |audience|.
[audience setUpdated:NO];
BrowserState()->GetPrefs()->SetString(prefs::kContextualSearchEnabled,
"true");
// Audience call is asynchronous, so expect nothing yet.
EXPECT_FALSE([audience updated]);
// Wait for async call.
base::test::ios::WaitUntilCondition(^bool(void) {
return [audience updated];
});
EXPECT_TRUE([audience updated]);
// Reset |audience|.
[audience setUpdated:NO];
// Make a user-defined search engine and set it as the default. This should
// trigger a prefs update that will in turn trigger an audience notification.
TemplateURLData data;
data.SetShortName(base::ASCIIToUTF16("cs-not-supported"));
TemplateURL* newUrl = template_url_service_->Add(
std::unique_ptr<TemplateURL>(new TemplateURL(data)));
// |template_url_service_| will take ownership of newUrl
ASSERT_TRUE(newUrl);
template_url_service_->SetUserSelectedDefaultSearchProvider(newUrl);
// Audience call is asynchronous, so expect nothing yet.
EXPECT_FALSE([audience updated]);
// Wait for async call.
base::test::ios::WaitUntilCondition(
^bool(void) {
return [audience updated];
},
false, delay);
EXPECT_TRUE([audience updated]);
// Reset |audience|.
[audience setUpdated:NO];
// Once set to nil, audience shouldn't be notified.
[tts_permissions_ setAudience:nil];
[[NSNotificationCenter defaultCenter]
postNotificationName:UIAccessibilityVoiceOverStatusChanged
object:nil];
BrowserState()->GetPrefs()->SetString(prefs::kContextualSearchEnabled,
"true");
// Audience call is asynchronous, so expect nothing yet.
EXPECT_FALSE([audience updated]);
// Wait long enough for async call not to occur.
base::test::ios::SpinRunLoopWithMaxDelay(delay);
EXPECT_FALSE([audience updated]);
// Reset |audience|.
[audience setUpdated:NO];
base::scoped_nsobject<id> audience2(
[[TestTouchToSearchPermissionsAudience alloc] init]);
// If the permissions object is destroyed, queued notifications should still
// be sent.
[tts_permissions_ setAudience:audience2];
[[NSNotificationCenter defaultCenter]
postNotificationName:UIAccessibilityVoiceOverStatusChanged
object:nil];
tts_permissions_.reset();
base::test::ios::WaitUntilCondition(
^bool(void) {
return [audience2 updated];
},
false, delay);
EXPECT_TRUE([audience2 updated]);
}
TEST_F(TouchToSearchPermissionsMediatorTest, AudiencePrefsSynchronous) {
id audience = [OCMockObject
niceMockForProtocol:@protocol(TouchToSearchPermissionsChangeAudience)];
[tts_permissions_ setAudience:audience];
// Test that setting preferences through the same permissions object triggers
// audience methods.
[[audience expect]
touchToSearchDidChangePreferenceState:TouchToSearch::ENABLED];
[tts_permissions_ setPreferenceState:TouchToSearch::ENABLED];
EXPECT_OCMOCK_VERIFY(audience);
[tts_permissions_ setPreferenceState:TouchToSearch::DISABLED];
// Test that setting preferences through another permissions object triggers
// audience methods.
base::scoped_nsobject<TouchToSearchPermissionsMediator> other_permissions(
[[TouchToSearchPermissionsMediator alloc]
initWithBrowserState:BrowserState()]);
[[audience expect]
touchToSearchDidChangePreferenceState:TouchToSearch::ENABLED];
[other_permissions setPreferenceState:TouchToSearch::ENABLED];
EXPECT_OCMOCK_VERIFY(audience);
[tts_permissions_ setPreferenceState:TouchToSearch::DISABLED];
// Test that setting preferences through the prefs system triggers audience
// methods.
[[audience expect]
touchToSearchDidChangePreferenceState:TouchToSearch::ENABLED];
BrowserState()->GetPrefs()->SetString(prefs::kContextualSearchEnabled,
"true");
EXPECT_OCMOCK_VERIFY(audience);
}
TEST_F(TouchToSearchPermissionsMediatorTest, OTR) {
ios::ChromeBrowserState* otr_state =
BrowserState()->GetOffTheRecordChromeBrowserState();
base::scoped_nsobject<TouchToSearchPermissionsMediator> permissions([
[TouchToSearchPermissionsMediator alloc] initWithBrowserState:otr_state]);
EXPECT_FALSE([permissions canEnable]);
EXPECT_FALSE([permissions canSendPageURLs]);
EXPECT_FALSE([permissions canEnable]);
EXPECT_FALSE([permissions canPreloadSearchResults]);
EXPECT_EQ(TouchToSearch::DISABLED, [permissions preferenceState]);
otr_state->GetPrefs()->SetString(prefs::kContextualSearchEnabled, "true");
EXPECT_EQ(TouchToSearch::DISABLED, [permissions preferenceState]);
EXPECT_FALSE([permissions canEnable]);
EXPECT_EQ(nil, [permissions audience]);
id audience = [OCMockObject
niceMockForProtocol:@protocol(TouchToSearchPermissionsChangeAudience)];
[permissions setAudience:audience];
EXPECT_EQ(nil, [permissions audience]);
}
TEST_F(TouchToSearchPermissionsMediatorTest, AudienceRemovedNotifications) {
@autoreleasepool {
base::scoped_nsobject<id> audience(
[[TestTouchToSearchPermissionsAudience alloc] init]);
[tts_permissions_ setAudience:audience];
EXPECT_TRUE([tts_permissions_ observing]);
audience.reset();
}
// Permissions shouldn't be observing after notifying a nil audience.
[[NSNotificationCenter defaultCenter]
postNotificationName:UIAccessibilityVoiceOverStatusChanged
object:nil];
EXPECT_FALSE([tts_permissions_ observing]);
// Permissions shouldn't observe while still observing.
base::scoped_nsobject<id> audience(
[[TestTouchToSearchPermissionsAudience alloc] init]);
[tts_permissions_ setAudience:audience];
audience.reset();
audience.reset([[TestTouchToSearchPermissionsAudience alloc] init]);
[tts_permissions_ setAudience:audience];
}
#pragma mark - Unit tests for availability class method.
TEST(TouchToSearchPermissionsAvailabilityTest, CommandLinePermissions) {
const struct {
bool set_disable;
bool set_enable;
bool expect_available;
} tests[] = {
{true, false, false},
{true, true, false},
{false, false, false},
{false, true, true},
};
for (const auto& test : tests) {
// Reset all flags.
base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
if (test.set_disable) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableContextualSearch);
}
if (test.set_enable) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableContextualSearch);
}
EXPECT_EQ(
[TouchToSearchPermissionsMediator isTouchToSearchAvailableOnDevice],
test.expect_available);
}
}
TEST(TouchToSearchPermissionsAvailabilityTest, FieldTrial) {
// Field trial support is not currently supported, so it is expected
// that under all field trial configs, the feature will remain disabled.
// If field trial support is added back in, this test should be updated.
const struct {
const std::string trial_group_name;
bool expect_available;
} tests[] = {
{"Enabled", false},
{"Disabled", false},
{"Control", false},
{"Spadoinkle", false},
};
// Reset all flags.
base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
for (const auto& test : tests) {
base::FieldTrialList field_trial_list(nullptr);
if (!test.trial_group_name.empty()) {
base::FieldTrialList::CreateFieldTrial("ContextualSearchIOS",
test.trial_group_name);
}
EXPECT_EQ(
[TouchToSearchPermissionsMediator isTouchToSearchAvailableOnDevice],
test.expect_available);
}
}
#pragma mark - Unit tests for mock class
TEST(MockTouchToSearchPermissionsTest, Mocking) {
base::scoped_nsobject<MockTouchToSearchPermissionsMediator>
scoped_permissions([[MockTouchToSearchPermissionsMediator alloc]
initWithBrowserState:nullptr]);
MockTouchToSearchPermissionsMediator* permissions = scoped_permissions;
const GURL test_urls[] = {
GURL("https://www.some-website.com/"),
GURL("http://www.some-website.com/"), GURL(""),
};
// Default expectations from mocked methods.
EXPECT_TRUE([[permissions class] isTouchToSearchAvailableOnDevice]);
EXPECT_TRUE([permissions canSendPageURLs]);
EXPECT_TRUE([permissions canPreloadSearchResults]);
EXPECT_FALSE([permissions isVoiceOverEnabled]);
EXPECT_TRUE([permissions areContextualSearchQueriesSupported]);
for (const auto& url : test_urls)
EXPECT_TRUE([permissions canExtractTapContextForURL:url]);
// Set non-default values:
for (const BOOL value : {NO, YES, NO, YES}) {
[[permissions class] setIsTouchToSearchAvailableOnDevice:value];
EXPECT_EQ(value, [[permissions class] isTouchToSearchAvailableOnDevice]);
permissions.canSendPageURLs = value;
EXPECT_EQ(value, [permissions canSendPageURLs]);
permissions.canPreloadSearchResults = value;
EXPECT_EQ(value, [permissions canPreloadSearchResults]);
permissions.isVoiceOverEnabled = value;
EXPECT_EQ(value, [permissions isVoiceOverEnabled]);
permissions.areContextualSearchQueriesSupported = value;
EXPECT_EQ(value, [permissions areContextualSearchQueriesSupported]);
permissions.canExtractTapContextForAllURLs = value;
for (const auto& url : test_urls) {
EXPECT_EQ(value, [permissions canExtractTapContextForURL:url]);
}
}
const TouchToSearch::TouchToSearchPreferenceState states[] = {
TouchToSearch::DISABLED, TouchToSearch::UNDECIDED, TouchToSearch::ENABLED,
};
for (const auto& state : states) {
permissions.preferenceState = state;
EXPECT_EQ(state, permissions.preferenceState);
}
}
} // namespace