1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/command_line.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "base/metrics/field_trial.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/test/test_simple_task_runner.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/extensions/extension_service_test_base.h"
12 #include "chrome/browser/extensions/test_extension_service.h"
13 #include "chrome/browser/search/hotword_audio_history_handler.h"
14 #include "chrome/browser/search/hotword_service.h"
15 #include "chrome/browser/search/hotword_service_factory.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/common/extensions/extension_constants.h"
18 #include "chrome/common/pref_names.h"
19 #include "chrome/test/base/testing_profile.h"
20 #include "components/history/core/browser/web_history_service.h"
21 #include "content/public/test/test_browser_thread_bundle.h"
22 #include "extensions/browser/extension_system.h"
23 #include "extensions/common/extension.h"
24 #include "extensions/common/extension_builder.h"
25 #include "extensions/common/manifest.h"
26 #include "extensions/common/one_shot_event.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 #if defined(OS_CHROMEOS)
30 #include "chromeos/audio/cras_audio_handler.h"
35 class MockAudioHistoryHandler
: public HotwordAudioHistoryHandler
{
37 MockAudioHistoryHandler(
38 content::BrowserContext
* context
,
39 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
,
40 history::WebHistoryService
* web_history
)
41 : HotwordAudioHistoryHandler(context
, task_runner
),
42 get_audio_history_calls_(0),
43 web_history_(web_history
) {
45 ~MockAudioHistoryHandler() override
{}
47 void GetAudioHistoryEnabled(
48 const HotwordAudioHistoryCallback
& callback
) override
{
49 get_audio_history_calls_
++;
50 callback
.Run(true, true);
53 history::WebHistoryService
* GetWebHistory() override
{
54 return web_history_
.get();
57 int GetAudioHistoryCalls() {
58 return get_audio_history_calls_
;
62 int get_audio_history_calls_
;
63 scoped_ptr
<history::WebHistoryService
> web_history_
;
66 class MockHotwordService
: public HotwordService
{
68 explicit MockHotwordService(Profile
* profile
)
69 : HotwordService(profile
),
73 bool UninstallHotwordExtension(ExtensionService
* extension_service
) override
{
75 return HotwordService::UninstallHotwordExtension(extension_service
);
78 void InstallHotwordExtensionFromWebstore(int num_tries
) override
{
79 scoped_ptr
<base::DictionaryValue
> manifest
=
80 extensions::DictionaryBuilder()
81 .Set("name", "Hotword Test Extension")
82 .Set("version", "1.0")
83 .Set("manifest_version", 2)
85 scoped_refptr
<extensions::Extension
> extension
=
86 extensions::ExtensionBuilder().SetManifest(manifest
.Pass())
87 .AddFlags(extensions::Extension::FROM_WEBSTORE
88 | extensions::Extension::WAS_INSTALLED_BY_DEFAULT
)
90 .SetLocation(extensions::Manifest::EXTERNAL_COMPONENT
)
92 ASSERT_TRUE(extension
.get());
93 service_
->OnExtensionInstalled(extension
.get(), syncer::StringOrdinal());
97 int uninstall_count() { return uninstall_count_
; }
99 void SetExtensionService(ExtensionService
* service
) { service_
= service
; }
100 void SetExtensionId(const std::string
& extension_id
) {
101 extension_id_
= extension_id
;
104 ExtensionService
* extension_service() { return service_
; }
107 ExtensionService
* service_
;
108 int uninstall_count_
;
109 std::string extension_id_
;
112 KeyedService
* BuildMockHotwordService(content::BrowserContext
* context
) {
113 return new MockHotwordService(static_cast<Profile
*>(context
));
118 class HotwordServiceTest
:
119 public extensions::ExtensionServiceTestBase
,
120 public ::testing::WithParamInterface
<const char*> {
122 HotwordServiceTest() : field_trial_list_(NULL
) {}
123 virtual ~HotwordServiceTest() {}
125 void SetApplicationLocale(Profile
* profile
, const std::string
& new_locale
) {
126 #if defined(OS_CHROMEOS)
127 // On ChromeOS locale is per-profile.
128 profile
->GetPrefs()->SetString(prefs::kApplicationLocale
, new_locale
);
130 g_browser_process
->SetApplicationLocale(new_locale
);
134 void SetUp() override
{
135 extension_id_
= GetParam();
136 #if defined(OS_CHROMEOS)
137 // Tests on chromeos need to have the handler initialized.
138 chromeos::CrasAudioHandler::InitializeForTesting();
141 extensions::ExtensionServiceTestBase::SetUp();
144 void TearDown() override
{
145 #if defined(OS_CHROMEOS)
146 DCHECK(chromeos::CrasAudioHandler::IsInitialized());
147 chromeos::CrasAudioHandler::Shutdown();
151 base::FieldTrialList field_trial_list_
;
152 std::string extension_id_
;
155 INSTANTIATE_TEST_CASE_P(HotwordServiceTests
,
158 extension_misc::kHotwordSharedModuleId
));
160 TEST_P(HotwordServiceTest
, IsHotwordAllowedDisabledFieldTrial
) {
161 TestingProfile::Builder profile_builder
;
162 scoped_ptr
<TestingProfile
> profile
= profile_builder
.Build();
164 // Check that the service exists so that a NULL service be ruled out in
166 HotwordService
* hotword_service
=
167 HotwordServiceFactory::GetForProfile(profile
.get());
168 EXPECT_TRUE(hotword_service
!= NULL
);
170 // When the field trial is empty, it should be allowed.
171 std::string group
= base::FieldTrialList::FindFullName(
172 hotword_internal::kHotwordFieldTrialName
);
173 EXPECT_TRUE(group
.empty());
174 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
176 // When the field trial is 'Disabled', it should not be allowed.
177 ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
178 hotword_internal::kHotwordFieldTrialName
,
179 hotword_internal::kHotwordFieldTrialDisabledGroupName
));
180 group
= base::FieldTrialList::FindFullName(
181 hotword_internal::kHotwordFieldTrialName
);
182 EXPECT_TRUE(group
== hotword_internal::kHotwordFieldTrialDisabledGroupName
);
183 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
185 // Set a valid locale with invalid field trial to be sure it is
187 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "en");
188 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
190 // Test that incognito returns false as well.
191 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(
192 profile
->GetOffTheRecordProfile()));
195 TEST_P(HotwordServiceTest
, IsHotwordAllowedInvalidFieldTrial
) {
196 TestingProfile::Builder profile_builder
;
197 scoped_ptr
<TestingProfile
> profile
= profile_builder
.Build();
199 // Check that the service exists so that a NULL service be ruled out in
201 HotwordService
* hotword_service
=
202 HotwordServiceFactory::GetForProfile(profile
.get());
203 EXPECT_TRUE(hotword_service
!= NULL
);
205 // When the field trial is set, but not 'Disabled', it should be allowed.
206 ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
207 hotword_internal::kHotwordFieldTrialName
, "foo"));
208 std::string group
= base::FieldTrialList::FindFullName(
209 hotword_internal::kHotwordFieldTrialName
);
210 EXPECT_TRUE(group
== "foo");
211 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
213 // Test that incognito returns false as well.
214 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(
215 profile
->GetOffTheRecordProfile()));
218 TEST_P(HotwordServiceTest
, IsHotwordAllowedLocale
) {
219 TestingProfile::Builder profile_builder
;
220 scoped_ptr
<TestingProfile
> profile
= profile_builder
.Build();
222 // Check that the service exists so that a NULL service be ruled out in
224 HotwordService
* hotword_service
=
225 HotwordServiceFactory::GetForProfile(profile
.get());
226 EXPECT_TRUE(hotword_service
!= NULL
);
228 // Set the language to an invalid one.
229 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "non-valid");
230 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
232 // Now with valid locales it should be fine.
233 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "en");
234 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
235 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "en-US");
236 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
237 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "en_us");
238 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
239 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "de_DE");
240 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
241 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "fr_fr");
242 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
244 // Test that incognito even with a valid locale and valid field trial
245 // still returns false.
246 Profile
* otr_profile
= profile
->GetOffTheRecordProfile();
247 SetApplicationLocale(otr_profile
, "en");
248 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(otr_profile
));
251 TEST_P(HotwordServiceTest
, ShouldReinstallExtension
) {
252 InitializeEmptyExtensionService();
254 HotwordServiceFactory
* hotword_service_factory
=
255 HotwordServiceFactory::GetInstance();
257 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
258 hotword_service_factory
->SetTestingFactoryAndUse(
259 profile(), BuildMockHotwordService
));
260 ASSERT_TRUE(hotword_service
!= NULL
);
261 hotword_service
->SetExtensionId(extension_id_
);
263 // If no locale has been set, no reason to uninstall.
264 EXPECT_FALSE(hotword_service
->ShouldReinstallHotwordExtension());
266 SetApplicationLocale(profile(), "en");
267 hotword_service
->SetPreviousLanguagePref();
269 // Now a locale is set, but it hasn't changed.
270 EXPECT_FALSE(hotword_service
->ShouldReinstallHotwordExtension());
272 SetApplicationLocale(profile(), "fr_fr");
274 // Now it's a different locale so it should uninstall.
275 EXPECT_TRUE(hotword_service
->ShouldReinstallHotwordExtension());
278 TEST_P(HotwordServiceTest
, PreviousLanguageSetOnInstall
) {
279 InitializeEmptyExtensionService();
282 HotwordServiceFactory
* hotword_service_factory
=
283 HotwordServiceFactory::GetInstance();
285 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
286 hotword_service_factory
->SetTestingFactoryAndUse(
287 profile(), BuildMockHotwordService
));
288 ASSERT_TRUE(hotword_service
!= NULL
);
289 hotword_service
->SetExtensionService(service());
290 hotword_service
->SetExtensionId(extension_id_
);
292 // If no locale has been set, no reason to uninstall.
293 EXPECT_FALSE(hotword_service
->ShouldReinstallHotwordExtension());
295 SetApplicationLocale(profile(), "test_locale");
297 hotword_service
->InstallHotwordExtensionFromWebstore(1);
298 base::MessageLoop::current()->RunUntilIdle();
300 EXPECT_EQ("test_locale",
301 profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage
));
304 TEST_P(HotwordServiceTest
, UninstallReinstallTriggeredCorrectly
) {
305 InitializeEmptyExtensionService();
308 HotwordServiceFactory
* hotword_service_factory
=
309 HotwordServiceFactory::GetInstance();
311 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
312 hotword_service_factory
->SetTestingFactoryAndUse(
313 profile(), BuildMockHotwordService
));
314 ASSERT_TRUE(hotword_service
!= NULL
);
315 hotword_service
->SetExtensionService(service());
316 hotword_service
->SetExtensionId(extension_id_
);
318 // Initialize the locale to "en".
319 SetApplicationLocale(profile(), "en");
321 // The previous locale should not be set. No reason to uninstall.
322 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
324 // Do an initial installation.
325 hotword_service
->InstallHotwordExtensionFromWebstore(1);
326 base::MessageLoop::current()->RunUntilIdle();
328 profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage
));
330 if (extension_id_
== extension_misc::kHotwordSharedModuleId
) {
331 // Shared module is installed and enabled.
332 EXPECT_EQ(0U, registry()->disabled_extensions().size());
333 EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id_
));
335 // Verify the extension is installed but disabled.
336 EXPECT_EQ(1U, registry()->disabled_extensions().size());
337 EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id_
));
340 // The previous locale should be set but should match the current
341 // locale. No reason to uninstall.
342 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
344 // Switch the locale to a valid but different one.
345 SetApplicationLocale(profile(), "fr_fr");
346 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile()));
348 // Different but valid locale so expect uninstall.
349 EXPECT_TRUE(hotword_service
->MaybeReinstallHotwordExtension());
350 EXPECT_EQ(1, hotword_service
->uninstall_count());
352 profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage
));
354 if (extension_id_
== extension_misc::kHotwordSharedModuleId
) {
355 // Shared module is installed and enabled.
356 EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id_
));
358 // Verify the extension is installed. It's still disabled.
359 EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id_
));
362 // Switch the locale to an invalid one.
363 SetApplicationLocale(profile(), "invalid");
364 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(profile()));
365 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
367 profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage
));
369 // If the locale is set back to the last valid one, then an uninstall-install
370 // shouldn't be needed.
371 SetApplicationLocale(profile(), "fr_fr");
372 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile()));
373 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
374 EXPECT_EQ(1, hotword_service
->uninstall_count()); // no change
377 TEST_P(HotwordServiceTest
, DisableAlwaysOnOnLanguageChange
) {
378 // Bypass test for old hotwording.
379 if (extension_id_
!= extension_misc::kHotwordSharedModuleId
)
383 base::CommandLine::ForCurrentProcess()->AppendSwitch(
384 switches::kEnableExperimentalHotwordHardware
);
386 InitializeEmptyExtensionService();
390 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
, true);
392 HotwordServiceFactory
* hotword_service_factory
=
393 HotwordServiceFactory::GetInstance();
395 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
396 hotword_service_factory
->SetTestingFactoryAndUse(
397 profile(), BuildMockHotwordService
));
398 ASSERT_TRUE(hotword_service
!= NULL
);
399 hotword_service
->SetExtensionService(service());
400 hotword_service
->SetExtensionId(extension_id_
);
402 // Initialize the locale to "en".
403 SetApplicationLocale(profile(), "en");
405 // The previous locale should not be set. No reason to uninstall.
406 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
407 EXPECT_TRUE(hotword_service
->IsAlwaysOnEnabled());
409 // Do an initial installation.
410 hotword_service
->InstallHotwordExtensionFromWebstore(1);
411 base::MessageLoop::current()->RunUntilIdle();
413 // The previous locale should be set but should match the current
414 // locale. No reason to uninstall.
415 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
416 EXPECT_TRUE(hotword_service
->IsAlwaysOnEnabled());
418 // Switch the locale to a valid but different one.
419 SetApplicationLocale(profile(), "fr_fr");
420 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile()));
422 // Different but valid locale so expect uninstall.
423 EXPECT_TRUE(hotword_service
->MaybeReinstallHotwordExtension());
424 EXPECT_FALSE(hotword_service
->IsAlwaysOnEnabled());
426 // Re-enable always-on.
427 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
, true);
429 // Switch the locale to an invalid one.
430 SetApplicationLocale(profile(), "invalid");
431 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(profile()));
432 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
433 EXPECT_TRUE(hotword_service
->IsAlwaysOnEnabled());
435 // If the locale is set back to the last valid one, then an uninstall-install
436 // shouldn't be needed.
437 SetApplicationLocale(profile(), "fr_fr");
438 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile()));
439 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
440 EXPECT_TRUE(hotword_service
->IsAlwaysOnEnabled());
443 TEST_P(HotwordServiceTest
, IsAlwaysOnEnabled
) {
444 // Bypass test for old hotwording.
445 if (extension_id_
!= extension_misc::kHotwordSharedModuleId
)
448 InitializeEmptyExtensionService();
450 HotwordServiceFactory
* hotword_service_factory
=
451 HotwordServiceFactory::GetInstance();
453 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
454 hotword_service_factory
->SetTestingFactoryAndUse(
455 profile(), BuildMockHotwordService
));
456 ASSERT_TRUE(hotword_service
!= NULL
);
457 hotword_service
->SetExtensionService(service());
458 hotword_service
->SetExtensionId(extension_id_
);
460 // No hardware available. Should never be true.
461 EXPECT_FALSE(hotword_service
->IsAlwaysOnEnabled());
463 // Enable always-on, still not available.
464 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
, true);
465 EXPECT_FALSE(hotword_service
->IsAlwaysOnEnabled());
467 // Enable regular hotwording, still not available.
468 profile()->GetPrefs()->SetBoolean(prefs::kHotwordSearchEnabled
, true);
469 EXPECT_FALSE(hotword_service
->IsAlwaysOnEnabled());
471 // Bypass hardware check.
472 base::CommandLine::ForCurrentProcess()->AppendSwitch(
473 switches::kEnableExperimentalHotwordHardware
);
474 EXPECT_TRUE(hotword_service
->IsAlwaysOnEnabled());
477 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
,
479 EXPECT_FALSE(hotword_service
->IsAlwaysOnEnabled());
482 TEST_P(HotwordServiceTest
, IsSometimesOnEnabled
) {
483 InitializeEmptyExtensionService();
485 HotwordServiceFactory
* hotword_service_factory
=
486 HotwordServiceFactory::GetInstance();
488 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
489 hotword_service_factory
->SetTestingFactoryAndUse(
490 profile(), BuildMockHotwordService
));
491 ASSERT_TRUE(hotword_service
!= NULL
);
492 hotword_service
->SetExtensionService(service());
493 hotword_service
->SetExtensionId(extension_id_
);
496 EXPECT_FALSE(hotword_service
->IsSometimesOnEnabled());
499 profile()->GetPrefs()->SetBoolean(prefs::kHotwordSearchEnabled
, true);
500 EXPECT_TRUE(hotword_service
->IsSometimesOnEnabled());
502 // Turn on always-on pref. Should have no effect.
503 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
, true);
504 EXPECT_TRUE(hotword_service
->IsSometimesOnEnabled());
507 profile()->GetPrefs()->SetBoolean(prefs::kHotwordSearchEnabled
, false);
508 EXPECT_FALSE(hotword_service
->IsSometimesOnEnabled());
510 // Bypass rest of test for old hotwording.
511 if (extension_id_
!= extension_misc::kHotwordSharedModuleId
)
514 // Bypass hardware check.
515 base::CommandLine::ForCurrentProcess()->AppendSwitch(
516 switches::kEnableExperimentalHotwordHardware
);
518 // With hardware, only always-on is allowed.
519 EXPECT_FALSE(hotword_service
->IsSometimesOnEnabled());
522 profile()->GetPrefs()->SetBoolean(prefs::kHotwordSearchEnabled
, true);
523 EXPECT_FALSE(hotword_service
->IsSometimesOnEnabled());
525 // Disable always-on.
526 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
,
528 EXPECT_FALSE(hotword_service
->IsSometimesOnEnabled());
531 TEST_P(HotwordServiceTest
, AudioHistorySyncOccurs
) {
532 InitializeEmptyExtensionService();
535 HotwordServiceFactory
* hotword_service_factory
=
536 HotwordServiceFactory::GetInstance();
538 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
539 hotword_service_factory
->SetTestingFactoryAndUse(
540 profile(), BuildMockHotwordService
));
541 ASSERT_TRUE(hotword_service
!= NULL
);
542 hotword_service
->SetExtensionService(service());
543 hotword_service
->SetExtensionId(extension_id_
);
545 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
546 new base::TestSimpleTaskRunner());
547 MockAudioHistoryHandler
* audio_history_handler
=
548 new MockAudioHistoryHandler(profile(), test_task_runner
, nullptr);
549 hotword_service
->SetAudioHistoryHandler(audio_history_handler
);
550 EXPECT_EQ(1, audio_history_handler
->GetAudioHistoryCalls());
551 // We expect the next check for audio history to be in the queue.
552 EXPECT_EQ(base::TimeDelta::FromDays(1),
553 test_task_runner
->NextPendingTaskDelay());
554 EXPECT_TRUE(test_task_runner
->HasPendingTask());
555 test_task_runner
->RunPendingTasks();
556 EXPECT_EQ(2, audio_history_handler
->GetAudioHistoryCalls());
557 EXPECT_TRUE(test_task_runner
->HasPendingTask());
558 test_task_runner
->ClearPendingTasks();