Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / apps / ephemeral_app_launcher_browsertest.cc
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.
4
5 #include "base/message_loop/message_loop_proxy.h"
6 #include "chrome/browser/apps/ephemeral_app_launcher.h"
7 #include "chrome/browser/apps/ephemeral_app_service.h"
8 #include "chrome/browser/extensions/extension_install_checker.h"
9 #include "chrome/browser/extensions/extension_service.h"
10 #include "chrome/browser/extensions/install_tracker.h"
11 #include "chrome/browser/extensions/test_blacklist.h"
12 #include "chrome/browser/extensions/webstore_installer_test.h"
13 #include "chrome/browser/ui/browser_finder.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/test/test_utils.h"
18 #include "extensions/browser/extension_prefs.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/browser/extension_system.h"
21 #include "extensions/browser/extension_util.h"
22 #include "extensions/browser/management_policy.h"
23 #include "extensions/browser/process_manager.h"
24 #include "extensions/common/switches.h"
25 #include "extensions/test/extension_test_message_listener.h"
26
27 using extensions::Extension;
28 using extensions::ExtensionPrefs;
29 using extensions::ExtensionRegistry;
30 using extensions::ExtensionSystem;
31 using extensions::InstallTracker;
32 namespace webstore_install = extensions::webstore_install;
33
34 namespace {
35
36 const char kWebstoreDomain[] = "cws.com";
37 const char kAppDomain[] = "app.com";
38 const char kNonAppDomain[] = "nonapp.com";
39 const char kTestDataPath[] = "extensions/platform_apps/ephemeral_launcher";
40
41 const char kExtensionId[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeid";
42 const char kExtensionTestPath[] = "extension";
43 const char kLegacyAppId[] = "lnbochkobjfnhbnbljgfgokadhmbahcn";
44 const char kLegacyAppTestPath[] = "legacy_app";
45 const char kNonExistentId[] = "baaaaaaaaaaaaaaaaaaaaaaaaaaaadid";
46 const char kDefaultAppId[] = "kbiancnbopdghkfedjhfdoegjadfjeal";
47 const char kDefaultAppCrxFilename[] = "app.crx";
48 const char kDefaultAppTestPath[] = "app";
49 const char kAppWithPermissionsId[] = "mbfcnecjknjpipkfkoangpfnhhlpamki";
50 const char kAppWithPermissionsFilename[] = "app_with_permissions.crx";
51 const char kHostedAppId[] = "haaaaaaaaaaaaaaaaaaaaaaaaaaappid";
52 const char kHostedAppLaunchUrl[] = "http://foo.bar.com";
53
54 class ExtensionInstallCheckerMock : public extensions::ExtensionInstallChecker {
55  public:
56   ExtensionInstallCheckerMock(Profile* profile,
57                               const std::string& requirements_error)
58       : extensions::ExtensionInstallChecker(profile),
59         requirements_error_(requirements_error) {}
60
61   ~ExtensionInstallCheckerMock() override {}
62
63  private:
64   void CheckRequirements() override {
65     // Simulate an asynchronous operation.
66     base::MessageLoopProxy::current()->PostTask(
67         FROM_HERE,
68         base::Bind(&ExtensionInstallCheckerMock::RequirementsErrorCheckDone,
69                    base::Unretained(this),
70                    current_sequence_number()));
71   }
72
73   void RequirementsErrorCheckDone(int sequence_number) {
74     std::vector<std::string> errors;
75     errors.push_back(requirements_error_);
76     OnRequirementsCheckDone(sequence_number, errors);
77   }
78
79   std::string requirements_error_;
80 };
81
82 class EphemeralAppLauncherForTest : public EphemeralAppLauncher {
83  public:
84   EphemeralAppLauncherForTest(const std::string& id,
85                               Profile* profile,
86                               const LaunchCallback& callback)
87       : EphemeralAppLauncher(id, profile, NULL, callback),
88         install_initiated_(false),
89         install_prompt_created_(false) {}
90
91   EphemeralAppLauncherForTest(const std::string& id, Profile* profile)
92       : EphemeralAppLauncher(id, profile, NULL, LaunchCallback()),
93         install_initiated_(false),
94         install_prompt_created_(false) {}
95
96   bool install_initiated() const { return install_initiated_; }
97   bool install_prompt_created() const { return install_prompt_created_; }
98
99   void set_requirements_error(const std::string& error) {
100     requirements_check_error_ = error;
101   }
102
103  private:
104   // Override necessary functions for testing.
105
106   scoped_ptr<extensions::ExtensionInstallChecker> CreateInstallChecker()
107       override {
108     if (requirements_check_error_.empty()) {
109       return EphemeralAppLauncher::CreateInstallChecker();
110     } else {
111       return scoped_ptr<extensions::ExtensionInstallChecker>(
112           new ExtensionInstallCheckerMock(profile(),
113                                           requirements_check_error_));
114     }
115   }
116
117   scoped_ptr<ExtensionInstallPrompt> CreateInstallUI() override {
118     install_prompt_created_ = true;
119     return EphemeralAppLauncher::CreateInstallUI();
120   }
121
122   scoped_ptr<extensions::WebstoreInstaller::Approval> CreateApproval()
123       const override {
124     install_initiated_ = true;
125     return EphemeralAppLauncher::CreateApproval();
126   }
127
128  private:
129   ~EphemeralAppLauncherForTest() override {}
130   friend class base::RefCountedThreadSafe<EphemeralAppLauncherForTest>;
131
132   mutable bool install_initiated_;
133   std::string requirements_check_error_;
134   bool install_prompt_created_;
135 };
136
137 class LaunchObserver {
138  public:
139   LaunchObserver()
140       : done_(false),
141         waiting_(false),
142         result_(webstore_install::OTHER_ERROR) {}
143
144   webstore_install::Result result() const { return result_; }
145   const std::string& error() const { return error_; }
146
147   void OnLaunchCallback(webstore_install::Result result,
148                         const std::string& error) {
149     result_ = result;
150     error_ = error;
151     done_ = true;
152     if (waiting_) {
153       waiting_ = false;
154       base::MessageLoopForUI::current()->Quit();
155     }
156   }
157
158   void Wait() {
159     if (done_)
160       return;
161
162     waiting_ = true;
163     content::RunMessageLoop();
164   }
165
166  private:
167   bool done_;
168   bool waiting_;
169   webstore_install::Result result_;
170   std::string error_;
171 };
172
173 class ManagementPolicyMock : public extensions::ManagementPolicy::Provider {
174  public:
175   ManagementPolicyMock() {}
176
177   std::string GetDebugPolicyProviderName() const override {
178     return "ManagementPolicyMock";
179   }
180
181   bool UserMayLoad(const Extension* extension,
182                    base::string16* error) const override {
183     return false;
184   }
185 };
186
187 }  // namespace
188
189 class EphemeralAppLauncherTest : public WebstoreInstallerTest {
190  public:
191   EphemeralAppLauncherTest()
192       : WebstoreInstallerTest(kWebstoreDomain,
193                               kTestDataPath,
194                               kDefaultAppCrxFilename,
195                               kAppDomain,
196                               kNonAppDomain) {}
197
198   void SetUpCommandLine(base::CommandLine* command_line) override {
199     WebstoreInstallerTest::SetUpCommandLine(command_line);
200
201     // Make event pages get suspended immediately.
202     extensions::ProcessManager::SetEventPageIdleTimeForTesting(1);
203     extensions::ProcessManager::SetEventPageSuspendingTimeForTesting(1);
204
205     // Enable ephemeral apps flag.
206     command_line->AppendSwitch(switches::kEnableEphemeralApps);
207   }
208
209   void SetUpOnMainThread() override {
210     WebstoreInstallerTest::SetUpOnMainThread();
211
212     // Disable ephemeral apps immediately after they stop running in tests.
213     EphemeralAppService::Get(profile())->set_disable_delay_for_test(0);
214   }
215
216   base::FilePath GetTestPath(const char* test_name) {
217     return test_data_dir_.AppendASCII("platform_apps/ephemeral_launcher")
218         .AppendASCII(test_name);
219   }
220
221   const Extension* GetInstalledExtension(const std::string& id) {
222     return ExtensionRegistry::Get(profile())
223         ->GetExtensionById(id, ExtensionRegistry::EVERYTHING);
224   }
225
226   void SetCrxFilename(const std::string& filename) {
227     GURL crx_url = GenerateTestServerUrl(kWebstoreDomain, filename);
228     base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
229         switches::kAppsGalleryUpdateURL, crx_url.spec());
230   }
231
232   void StartLauncherAndCheckResult(EphemeralAppLauncherForTest* launcher,
233                                    webstore_install::Result expected_result,
234                                    bool expect_install_initiated) {
235     ExtensionTestMessageListener launched_listener("launched", false);
236     LaunchObserver launch_observer;
237
238     launcher->launch_callback_ = base::Bind(&LaunchObserver::OnLaunchCallback,
239                                             base::Unretained(&launch_observer));
240     launcher->Start();
241     launch_observer.Wait();
242
243     // Verify the launch result.
244     EXPECT_EQ(expected_result, launch_observer.result());
245     EXPECT_EQ(expect_install_initiated, launcher->install_initiated());
246
247     // Verify that the app was actually launched if the launcher succeeded.
248     if (launch_observer.result() == webstore_install::SUCCESS)
249       EXPECT_TRUE(launched_listener.WaitUntilSatisfied());
250     else
251       EXPECT_FALSE(launched_listener.was_satisfied());
252
253     // Check the reference count to ensure the launcher instance will not be
254     // leaked.
255     EXPECT_TRUE(launcher->HasOneRef());
256   }
257
258   void RunLaunchTest(const std::string& id,
259                      webstore_install::Result expected_result,
260                      bool expect_install_initiated) {
261     InstallTracker* tracker = InstallTracker::Get(profile());
262     ASSERT_TRUE(tracker);
263     bool was_install_active = !!tracker->GetActiveInstall(id);
264
265     scoped_refptr<EphemeralAppLauncherForTest> launcher(
266         new EphemeralAppLauncherForTest(id, profile()));
267     StartLauncherAndCheckResult(
268         launcher.get(), expected_result, expect_install_initiated);
269
270     // Verify that the install was deregistered from the InstallTracker.
271     EXPECT_EQ(was_install_active, !!tracker->GetActiveInstall(id));
272   }
273
274   void ValidateAppInstalledEphemerally(const std::string& id) {
275     EXPECT_TRUE(GetInstalledExtension(id));
276     EXPECT_TRUE(extensions::util::IsEphemeralApp(id, profile()));
277   }
278
279   const Extension* InstallAndDisableApp(
280       const char* test_path,
281       Extension::DisableReason disable_reason) {
282     const Extension* app = InstallExtension(GetTestPath(test_path), 1);
283     EXPECT_TRUE(app);
284     if (!app)
285       return NULL;
286
287     ExtensionService* service =
288         ExtensionSystem::Get(profile())->extension_service();
289     service->DisableExtension(app->id(), disable_reason);
290
291     if (disable_reason == Extension::DISABLE_PERMISSIONS_INCREASE) {
292       // When an extension is disabled due to a permissions increase, this
293       // flag needs to be set too, for some reason.
294       ExtensionPrefs::Get(profile())
295           ->SetDidExtensionEscalatePermissions(app, true);
296     }
297
298     EXPECT_TRUE(
299         ExtensionRegistry::Get(profile())->disabled_extensions().Contains(
300             app->id()));
301     return app;
302   }
303 };
304
305 class EphemeralAppLauncherTestDisabled : public EphemeralAppLauncherTest {
306  public:
307   void SetUpCommandLine(base::CommandLine* command_line) override {
308     // Skip EphemeralAppLauncherTest as it enables the feature.
309     WebstoreInstallerTest::SetUpCommandLine(command_line);
310   }
311 };
312
313 // Verifies that an ephemeral app will not be installed and launched if the
314 // feature is disabled.
315 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTestDisabled, FeatureDisabled) {
316   RunLaunchTest(
317       kDefaultAppCrxFilename, webstore_install::LAUNCH_FEATURE_DISABLED, false);
318   EXPECT_FALSE(GetInstalledExtension(kDefaultAppId));
319 }
320
321 // Verifies that an app with no permission warnings will be installed
322 // ephemerally and launched without prompting the user.
323 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest,
324                        LaunchAppWithNoPermissionWarnings) {
325   content::WindowedNotificationObserver unloaded_signal(
326       extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
327       content::Source<Profile>(profile()));
328
329   scoped_refptr<EphemeralAppLauncherForTest> launcher(
330       new EphemeralAppLauncherForTest(kDefaultAppId, profile()));
331   StartLauncherAndCheckResult(launcher.get(), webstore_install::SUCCESS, true);
332   ValidateAppInstalledEphemerally(kDefaultAppId);
333
334   // Apps with no permission warnings should not result in a prompt.
335   EXPECT_FALSE(launcher->install_prompt_created());
336
337   // Ephemeral apps are unloaded after they stop running.
338   unloaded_signal.Wait();
339
340   // After an app has been installed ephemerally, it can be launched again
341   // without installing from the web store.
342   RunLaunchTest(kDefaultAppId, webstore_install::SUCCESS, false);
343 }
344
345 // Verifies that an app with permission warnings will be installed
346 // ephemerally and launched if accepted by the user.
347 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest,
348                        LaunchAppWithPermissionsWarnings) {
349   SetCrxFilename(kAppWithPermissionsFilename);
350   AutoAcceptInstall();
351
352   scoped_refptr<EphemeralAppLauncherForTest> launcher(
353       new EphemeralAppLauncherForTest(kAppWithPermissionsId, profile()));
354   StartLauncherAndCheckResult(launcher.get(), webstore_install::SUCCESS, true);
355   ValidateAppInstalledEphemerally(kAppWithPermissionsId);
356   EXPECT_TRUE(launcher->install_prompt_created());
357 }
358
359 // Verifies that an app with permission warnings will not be installed
360 // ephemerally if cancelled by the user.
361 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest,
362                        CancelInstallAppWithPermissionWarnings) {
363   SetCrxFilename(kAppWithPermissionsFilename);
364   AutoCancelInstall();
365
366   scoped_refptr<EphemeralAppLauncherForTest> launcher(
367       new EphemeralAppLauncherForTest(kAppWithPermissionsId, profile()));
368   StartLauncherAndCheckResult(
369       launcher.get(), webstore_install::USER_CANCELLED, false);
370   EXPECT_FALSE(GetInstalledExtension(kAppWithPermissionsId));
371   EXPECT_TRUE(launcher->install_prompt_created());
372 }
373
374 // Verifies that an extension will not be installed ephemerally.
375 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, InstallExtension) {
376   RunLaunchTest(
377       kExtensionId, webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE, false);
378   EXPECT_FALSE(GetInstalledExtension(kExtensionId));
379 }
380
381 // Verifies that an already installed extension will not be launched.
382 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchExtension) {
383   const Extension* extension =
384       InstallExtension(GetTestPath(kExtensionTestPath), 1);
385   ASSERT_TRUE(extension);
386   RunLaunchTest(extension->id(),
387                 webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE,
388                 false);
389 }
390
391 // Verifies that a legacy packaged app will not be installed ephemerally.
392 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, InstallLegacyApp) {
393   RunLaunchTest(
394       kLegacyAppId, webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE, false);
395   EXPECT_FALSE(GetInstalledExtension(kLegacyAppId));
396 }
397
398 // Verifies that a legacy packaged app that is already installed can be
399 // launched.
400 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchLegacyApp) {
401   const Extension* extension =
402       InstallExtension(GetTestPath(kLegacyAppTestPath), 1);
403   ASSERT_TRUE(extension);
404   RunLaunchTest(extension->id(), webstore_install::SUCCESS, false);
405 }
406
407 // Verifies that a hosted app is not installed. Launch succeeds because we
408 // navigate to its launch url.
409 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchHostedApp) {
410   LaunchObserver launch_observer;
411
412   scoped_refptr<EphemeralAppLauncherForTest> launcher(
413       new EphemeralAppLauncherForTest(
414           kHostedAppId,
415           profile(),
416           base::Bind(&LaunchObserver::OnLaunchCallback,
417                      base::Unretained(&launch_observer))));
418   launcher->Start();
419   launch_observer.Wait();
420
421   EXPECT_EQ(webstore_install::SUCCESS, launch_observer.result());
422   EXPECT_FALSE(launcher->install_initiated());
423   EXPECT_FALSE(GetInstalledExtension(kHostedAppId));
424
425   // Verify that a navigation to the launch url was attempted.
426   Browser* browser =
427       FindBrowserWithProfile(profile(), chrome::GetActiveDesktop());
428   ASSERT_TRUE(browser);
429   content::WebContents* web_contents =
430       browser->tab_strip_model()->GetActiveWebContents();
431   ASSERT_TRUE(web_contents);
432   EXPECT_EQ(GURL(kHostedAppLaunchUrl), web_contents->GetVisibleURL());
433 }
434
435 // Verifies that the EphemeralAppLauncher handles non-existent extension ids.
436 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, NonExistentExtensionId) {
437   RunLaunchTest(
438       kNonExistentId, webstore_install::WEBSTORE_REQUEST_ERROR, false);
439   EXPECT_FALSE(GetInstalledExtension(kNonExistentId));
440 }
441
442 // Verifies that an app blocked by management policy is not installed
443 // ephemerally.
444 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlockedByPolicy) {
445   // Register a provider that blocks the installation of all apps.
446   ManagementPolicyMock policy;
447   ExtensionSystem::Get(profile())->management_policy()->RegisterProvider(
448       &policy);
449
450   RunLaunchTest(kDefaultAppId, webstore_install::BLOCKED_BY_POLICY, false);
451   EXPECT_FALSE(GetInstalledExtension(kDefaultAppId));
452 }
453
454 // Verifies that an app blacklisted for malware is not installed ephemerally.
455 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlacklistedForMalware) {
456   // Mock a BLACKLISTED_MALWARE return status.
457   extensions::TestBlacklist blacklist_tester(
458       extensions::Blacklist::Get(profile()));
459   blacklist_tester.SetBlacklistState(
460       kDefaultAppId, extensions::BLACKLISTED_MALWARE, false);
461
462   RunLaunchTest(kDefaultAppId, webstore_install::BLACKLISTED, false);
463   EXPECT_FALSE(GetInstalledExtension(kDefaultAppId));
464 }
465
466 // Verifies that an app with unknown blacklist status is installed ephemerally
467 // and launched.
468 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlacklistStateUnknown) {
469   // Mock a BLACKLISTED_MALWARE return status.
470   extensions::TestBlacklist blacklist_tester(
471       extensions::Blacklist::Get(profile()));
472   blacklist_tester.SetBlacklistState(
473       kDefaultAppId, extensions::BLACKLISTED_UNKNOWN, false);
474
475   RunLaunchTest(kDefaultAppId, webstore_install::SUCCESS, true);
476   ValidateAppInstalledEphemerally(kDefaultAppId);
477 }
478
479 // Verifies that an app with unsupported requirements is not installed
480 // ephemerally.
481 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, UnsupportedRequirements) {
482   scoped_refptr<EphemeralAppLauncherForTest> launcher(
483       new EphemeralAppLauncherForTest(kDefaultAppId, profile()));
484   launcher->set_requirements_error("App has unsupported requirements");
485
486   StartLauncherAndCheckResult(
487       launcher.get(), webstore_install::REQUIREMENT_VIOLATIONS, false);
488   EXPECT_FALSE(GetInstalledExtension(kDefaultAppId));
489 }
490
491 // Verifies that an app disabled due to permissions increase can be enabled
492 // and launched.
493 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, EnableAndLaunchApp) {
494   const Extension* app = InstallAndDisableApp(
495       kDefaultAppTestPath, Extension::DISABLE_PERMISSIONS_INCREASE);
496   ASSERT_TRUE(app);
497
498   AutoAcceptInstall();
499   RunLaunchTest(app->id(), webstore_install::SUCCESS, false);
500 }
501
502 // Verifies that if the user cancels the enable flow, the app will not be
503 // enabled and launched.
504 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, EnableCancelled) {
505   const Extension* app = InstallAndDisableApp(
506       kDefaultAppTestPath, Extension::DISABLE_PERMISSIONS_INCREASE);
507   ASSERT_TRUE(app);
508
509   AutoCancelInstall();
510   RunLaunchTest(app->id(), webstore_install::USER_CANCELLED, false);
511 }
512
513 // Verifies that an installed app that had been blocked by policy cannot be
514 // launched.
515 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchAppBlockedByPolicy) {
516   const Extension* app = InstallExtension(GetTestPath(kDefaultAppTestPath), 1);
517   ASSERT_TRUE(app);
518
519   // Simulate blocking of the app after it has been installed.
520   ManagementPolicyMock policy;
521   ExtensionSystem::Get(profile())->management_policy()->RegisterProvider(
522       &policy);
523   ExtensionSystem::Get(profile())->extension_service()->CheckManagementPolicy();
524
525   RunLaunchTest(app->id(), webstore_install::BLOCKED_BY_POLICY, false);
526 }
527
528 // Verifies that an installed blacklisted app cannot be launched.
529 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchBlacklistedApp) {
530   const Extension* app = InstallExtension(GetTestPath(kDefaultAppTestPath), 1);
531   ASSERT_TRUE(app);
532
533   ExtensionService* service =
534       ExtensionSystem::Get(profile())->extension_service();
535   service->BlacklistExtensionForTest(app->id());
536   ASSERT_TRUE(
537       ExtensionRegistry::Get(profile())->blacklisted_extensions().Contains(
538           app->id()));
539
540   RunLaunchTest(app->id(), webstore_install::BLACKLISTED, false);
541 }
542
543 // Verifies that an installed app with unsupported requirements cannot be
544 // launched.
545 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest,
546                        LaunchAppWithUnsupportedRequirements) {
547   const Extension* app = InstallAndDisableApp(
548       kDefaultAppTestPath, Extension::DISABLE_UNSUPPORTED_REQUIREMENT);
549   ASSERT_TRUE(app);
550
551   RunLaunchTest(app->id(), webstore_install::REQUIREMENT_VIOLATIONS, false);
552 }
553
554 // Verifies that a launch will fail if the app is currently being installed.
555 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, InstallInProgress) {
556   extensions::ActiveInstallData install_data(kDefaultAppId);
557   InstallTracker::Get(profile())->AddActiveInstall(install_data);
558
559   RunLaunchTest(kDefaultAppId, webstore_install::INSTALL_IN_PROGRESS, false);
560 }
561
562 // Verifies that a launch will fail if a duplicate launch is in progress.
563 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, DuplicateLaunchInProgress) {
564   extensions::ActiveInstallData install_data(kDefaultAppId);
565   install_data.is_ephemeral = true;
566   InstallTracker::Get(profile())->AddActiveInstall(install_data);
567
568   RunLaunchTest(kDefaultAppId, webstore_install::LAUNCH_IN_PROGRESS, false);
569 }