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 scoped_ptr
<KeyedService
> BuildMockHotwordService(
113 content::BrowserContext
* context
) {
114 return make_scoped_ptr(
115 new MockHotwordService(static_cast<Profile
*>(context
)));
120 class HotwordServiceTest
:
121 public extensions::ExtensionServiceTestBase
,
122 public ::testing::WithParamInterface
<const char*> {
124 HotwordServiceTest() {}
125 virtual ~HotwordServiceTest() {}
127 void SetApplicationLocale(Profile
* profile
, const std::string
& new_locale
) {
128 #if defined(OS_CHROMEOS)
129 // On ChromeOS locale is per-profile.
130 profile
->GetPrefs()->SetString(prefs::kApplicationLocale
, new_locale
);
132 g_browser_process
->SetApplicationLocale(new_locale
);
136 void SetUp() override
{
137 extension_id_
= GetParam();
138 #if defined(OS_CHROMEOS)
139 // Tests on chromeos need to have the handler initialized.
140 chromeos::CrasAudioHandler::InitializeForTesting();
143 extensions::ExtensionServiceTestBase::SetUp();
146 void TearDown() override
{
147 #if defined(OS_CHROMEOS)
148 DCHECK(chromeos::CrasAudioHandler::IsInitialized());
149 chromeos::CrasAudioHandler::Shutdown();
153 std::string extension_id_
;
156 INSTANTIATE_TEST_CASE_P(HotwordServiceTests
,
159 extension_misc::kHotwordSharedModuleId
));
161 TEST_P(HotwordServiceTest
, IsHotwordAllowedLocale
) {
162 #if defined(ENABLE_HOTWORDING)
163 TestingProfile::Builder profile_builder
;
164 scoped_ptr
<TestingProfile
> profile
= profile_builder
.Build();
166 // Check that the service exists so that a NULL service be ruled out in
168 HotwordService
* hotword_service
=
169 HotwordServiceFactory::GetForProfile(profile
.get());
170 EXPECT_TRUE(hotword_service
!= NULL
);
172 // Set the language to an invalid one.
173 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "non-valid");
174 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
176 // Now with valid locales it should be fine.
177 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "en");
178 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
179 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "en-US");
180 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
181 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "en_us");
182 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
183 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "de_DE");
184 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
185 SetApplicationLocale(static_cast<Profile
*>(profile
.get()), "fr_fr");
186 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile
.get()));
188 // Test that incognito even with a valid locale and valid field trial
189 // still returns false.
190 Profile
* otr_profile
= profile
->GetOffTheRecordProfile();
191 SetApplicationLocale(otr_profile
, "en");
192 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(otr_profile
));
193 #endif // defined(ENABLE_HOTWORDING)
196 TEST_P(HotwordServiceTest
, ShouldReinstallExtension
) {
197 InitializeEmptyExtensionService();
199 HotwordServiceFactory
* hotword_service_factory
=
200 HotwordServiceFactory::GetInstance();
202 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
203 hotword_service_factory
->SetTestingFactoryAndUse(
204 profile(), BuildMockHotwordService
));
205 ASSERT_TRUE(hotword_service
!= NULL
);
206 hotword_service
->SetExtensionId(extension_id_
);
208 // If no locale has been set, no reason to uninstall.
209 EXPECT_FALSE(hotword_service
->ShouldReinstallHotwordExtension());
211 SetApplicationLocale(profile(), "en");
212 hotword_service
->SetPreviousLanguagePref();
214 // Now a locale is set, but it hasn't changed.
215 EXPECT_FALSE(hotword_service
->ShouldReinstallHotwordExtension());
217 SetApplicationLocale(profile(), "fr_fr");
219 // Now it's a different locale so it should uninstall.
220 EXPECT_TRUE(hotword_service
->ShouldReinstallHotwordExtension());
223 TEST_P(HotwordServiceTest
, PreviousLanguageSetOnInstall
) {
224 InitializeEmptyExtensionService();
227 HotwordServiceFactory
* hotword_service_factory
=
228 HotwordServiceFactory::GetInstance();
230 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
231 hotword_service_factory
->SetTestingFactoryAndUse(
232 profile(), BuildMockHotwordService
));
233 ASSERT_TRUE(hotword_service
!= NULL
);
234 hotword_service
->SetExtensionService(service());
235 hotword_service
->SetExtensionId(extension_id_
);
237 // If no locale has been set, no reason to uninstall.
238 EXPECT_FALSE(hotword_service
->ShouldReinstallHotwordExtension());
240 SetApplicationLocale(profile(), "test_locale");
242 hotword_service
->InstallHotwordExtensionFromWebstore(1);
243 base::MessageLoop::current()->RunUntilIdle();
245 EXPECT_EQ("test_locale",
246 profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage
));
249 TEST_P(HotwordServiceTest
, UninstallReinstallTriggeredCorrectly
) {
250 #if defined(ENABLE_HOTWORDING)
251 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
->SetExtensionService(service());
262 hotword_service
->SetExtensionId(extension_id_
);
264 // Initialize the locale to "en".
265 SetApplicationLocale(profile(), "en");
267 // The previous locale should not be set. No reason to uninstall.
268 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
270 // Do an initial installation.
271 hotword_service
->InstallHotwordExtensionFromWebstore(1);
272 base::MessageLoop::current()->RunUntilIdle();
274 profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage
));
276 if (extension_id_
== extension_misc::kHotwordSharedModuleId
) {
277 // Shared module is installed and enabled.
278 EXPECT_EQ(0U, registry()->disabled_extensions().size());
279 EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id_
));
281 // Verify the extension is installed but disabled.
282 EXPECT_EQ(1U, registry()->disabled_extensions().size());
283 EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id_
));
286 // The previous locale should be set but should match the current
287 // locale. No reason to uninstall.
288 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
290 // Switch the locale to a valid but different one.
291 SetApplicationLocale(profile(), "fr_fr");
292 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile()));
294 // Different but valid locale so expect uninstall.
295 EXPECT_TRUE(hotword_service
->MaybeReinstallHotwordExtension());
296 EXPECT_EQ(1, hotword_service
->uninstall_count());
298 profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage
));
300 if (extension_id_
== extension_misc::kHotwordSharedModuleId
) {
301 // Shared module is installed and enabled.
302 EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id_
));
304 // Verify the extension is installed. It's still disabled.
305 EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id_
));
308 // Switch the locale to an invalid one.
309 SetApplicationLocale(profile(), "invalid");
310 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(profile()));
311 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
313 profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage
));
315 // If the locale is set back to the last valid one, then an uninstall-install
316 // shouldn't be needed.
317 SetApplicationLocale(profile(), "fr_fr");
318 EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile()));
319 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
320 EXPECT_EQ(1, hotword_service
->uninstall_count()); // no change
321 #endif // defined(ENABLE_HOTWORDING)
324 TEST_P(HotwordServiceTest
, DisableAlwaysOnOnLanguageChange
) {
325 // Bypass test for old hotwording.
326 if (extension_id_
!= extension_misc::kHotwordSharedModuleId
)
330 base::CommandLine::ForCurrentProcess()->AppendSwitch(
331 switches::kEnableExperimentalHotwordHardware
);
333 InitializeEmptyExtensionService();
337 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
, true);
339 HotwordServiceFactory
* hotword_service_factory
=
340 HotwordServiceFactory::GetInstance();
342 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
343 hotword_service_factory
->SetTestingFactoryAndUse(
344 profile(), BuildMockHotwordService
));
345 ASSERT_TRUE(hotword_service
!= NULL
);
346 hotword_service
->SetExtensionService(service());
347 hotword_service
->SetExtensionId(extension_id_
);
349 // Initialize the locale to "en_us".
350 SetApplicationLocale(profile(), "en_us");
352 // The previous locale should not be set. No reason to uninstall.
353 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
354 EXPECT_TRUE(hotword_service
->IsAlwaysOnEnabled());
356 // Do an initial installation.
357 hotword_service
->InstallHotwordExtensionFromWebstore(1);
358 base::MessageLoop::current()->RunUntilIdle();
360 // The previous locale should be set but should match the current
361 // locale. No reason to uninstall.
362 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
363 EXPECT_TRUE(hotword_service
->IsAlwaysOnEnabled());
365 // TODO(kcarattini): Uncomment this sectione once we launch always-on
366 // in more languages.
367 // // Switch the locale to a valid but different one.
368 // SetApplicationLocale(profile(), "fr_fr");
369 // EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile()));
371 // // Different but valid locale so expect uninstall.
372 // EXPECT_TRUE(hotword_service->MaybeReinstallHotwordExtension());
373 // EXPECT_FALSE(hotword_service->IsAlwaysOnEnabled());
375 // Re-enable always-on.
376 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
, true);
378 // Switch the locale to an invalid one.
379 SetApplicationLocale(profile(), "invalid");
380 EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(profile()));
381 EXPECT_FALSE(hotword_service
->MaybeReinstallHotwordExtension());
382 EXPECT_TRUE(hotword_service
->IsAlwaysOnEnabled());
384 // TODO(kcarattini): Uncomment this sectione once we launch always-on
385 // in more languages.
386 // // If the locale is set back to the last valid one, then an
387 // // uninstall-install shouldn't be needed.
388 // SetApplicationLocale(profile(), "fr_fr");
389 // EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile()));
390 // EXPECT_FALSE(hotword_service->MaybeReinstallHotwordExtension());
391 // EXPECT_TRUE(hotword_service->IsAlwaysOnEnabled());
394 TEST_P(HotwordServiceTest
, IsAlwaysOnEnabled
) {
395 // Bypass test for old hotwording.
396 if (extension_id_
!= extension_misc::kHotwordSharedModuleId
)
399 InitializeEmptyExtensionService();
401 HotwordServiceFactory
* hotword_service_factory
=
402 HotwordServiceFactory::GetInstance();
404 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
405 hotword_service_factory
->SetTestingFactoryAndUse(
406 profile(), BuildMockHotwordService
));
407 ASSERT_TRUE(hotword_service
!= NULL
);
408 hotword_service
->SetExtensionService(service());
409 hotword_service
->SetExtensionId(extension_id_
);
411 // No hardware available. Should never be true.
412 EXPECT_FALSE(hotword_service
->IsAlwaysOnEnabled());
414 // Enable always-on, still not available.
415 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
, true);
416 EXPECT_FALSE(hotword_service
->IsAlwaysOnEnabled());
418 // Enable regular hotwording, still not available.
419 profile()->GetPrefs()->SetBoolean(prefs::kHotwordSearchEnabled
, true);
420 EXPECT_FALSE(hotword_service
->IsAlwaysOnEnabled());
422 // Bypass hardware check.
423 base::CommandLine::ForCurrentProcess()->AppendSwitch(
424 switches::kEnableExperimentalHotwordHardware
);
425 EXPECT_TRUE(hotword_service
->IsAlwaysOnEnabled());
428 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
,
430 EXPECT_FALSE(hotword_service
->IsAlwaysOnEnabled());
433 TEST_P(HotwordServiceTest
, IsSometimesOnEnabled
) {
434 InitializeEmptyExtensionService();
436 HotwordServiceFactory
* hotword_service_factory
=
437 HotwordServiceFactory::GetInstance();
439 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
440 hotword_service_factory
->SetTestingFactoryAndUse(
441 profile(), BuildMockHotwordService
));
442 ASSERT_TRUE(hotword_service
!= NULL
);
443 hotword_service
->SetExtensionService(service());
444 hotword_service
->SetExtensionId(extension_id_
);
447 EXPECT_FALSE(hotword_service
->IsSometimesOnEnabled());
450 profile()->GetPrefs()->SetBoolean(prefs::kHotwordSearchEnabled
, true);
451 EXPECT_TRUE(hotword_service
->IsSometimesOnEnabled());
453 // Turn on always-on pref. Should have no effect.
454 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
, true);
455 EXPECT_TRUE(hotword_service
->IsSometimesOnEnabled());
458 profile()->GetPrefs()->SetBoolean(prefs::kHotwordSearchEnabled
, false);
459 EXPECT_FALSE(hotword_service
->IsSometimesOnEnabled());
461 // Bypass rest of test for old hotwording.
462 if (extension_id_
!= extension_misc::kHotwordSharedModuleId
)
465 // Bypass hardware check.
466 base::CommandLine::ForCurrentProcess()->AppendSwitch(
467 switches::kEnableExperimentalHotwordHardware
);
469 // With hardware, only always-on is allowed.
470 EXPECT_FALSE(hotword_service
->IsSometimesOnEnabled());
473 profile()->GetPrefs()->SetBoolean(prefs::kHotwordSearchEnabled
, true);
474 EXPECT_FALSE(hotword_service
->IsSometimesOnEnabled());
476 // Disable always-on.
477 profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
,
479 EXPECT_FALSE(hotword_service
->IsSometimesOnEnabled());
482 TEST_P(HotwordServiceTest
, AudioHistorySyncOccurs
) {
483 InitializeEmptyExtensionService();
486 HotwordServiceFactory
* hotword_service_factory
=
487 HotwordServiceFactory::GetInstance();
489 MockHotwordService
* hotword_service
= static_cast<MockHotwordService
*>(
490 hotword_service_factory
->SetTestingFactoryAndUse(
491 profile(), BuildMockHotwordService
));
492 ASSERT_TRUE(hotword_service
!= NULL
);
493 hotword_service
->SetExtensionService(service());
494 hotword_service
->SetExtensionId(extension_id_
);
496 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
497 new base::TestSimpleTaskRunner());
498 MockAudioHistoryHandler
* audio_history_handler
=
499 new MockAudioHistoryHandler(profile(), test_task_runner
, nullptr);
500 hotword_service
->SetAudioHistoryHandler(audio_history_handler
);
501 EXPECT_EQ(1, audio_history_handler
->GetAudioHistoryCalls());
502 // We expect the next check for audio history to be in the queue.
503 EXPECT_EQ(base::TimeDelta::FromDays(1),
504 test_task_runner
->NextPendingTaskDelay());
505 EXPECT_TRUE(test_task_runner
->HasPendingTask());
506 test_task_runner
->RunPendingTasks();
507 EXPECT_EQ(2, audio_history_handler
->GetAudioHistoryCalls());
508 EXPECT_TRUE(test_task_runner
->HasPendingTask());
509 test_task_runner
->ClearPendingTasks();