Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / apps / drive / drive_app_provider_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 "chrome/browser/apps/drive/drive_app_provider.h"
6
7 #include "base/logging.h"
8 #include "base/macros.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/path_service.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/timer/timer.h"
13 #include "chrome/browser/apps/drive/drive_app_mapping.h"
14 #include "chrome/browser/apps/drive/drive_service_bridge.h"
15 #include "chrome/browser/drive/drive_app_registry.h"
16 #include "chrome/browser/drive/fake_drive_service.h"
17 #include "chrome/browser/extensions/crx_installer.h"
18 #include "chrome/browser/extensions/extension_browsertest.h"
19 #include "chrome/browser/extensions/install_tracker.h"
20 #include "chrome/common/chrome_paths.h"
21 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
22 #include "chrome/common/web_application_info.h"
23 #include "content/public/test/test_utils.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/extension_system.h"
26
27 using extensions::AppLaunchInfo;
28 using extensions::Extension;
29 using extensions::ExtensionRegistry;
30
31 namespace {
32
33 const char kDriveAppId[] = "drive_app_id";
34 const char kDriveAppName[] = "Fake Drive App";
35 const char kLaunchUrl[] = "http://example.com/drive";
36
37 // App id of hosted_app.crx.
38 const char kChromeAppId[] = "kbmnembihfiondgfjekmnmcbddelicoi";
39
40 // Stub drive service bridge.
41 class TestDriveServiceBridge : public DriveServiceBridge {
42  public:
43   explicit TestDriveServiceBridge(drive::DriveAppRegistry* registry)
44       : registry_(registry) {}
45   virtual ~TestDriveServiceBridge() {}
46
47   virtual drive::DriveAppRegistry* GetAppRegistry() OVERRIDE {
48     return registry_;
49   }
50
51  private:
52   drive::DriveAppRegistry* registry_;
53
54   DISALLOW_COPY_AND_ASSIGN(TestDriveServiceBridge);
55 };
56
57 }  // namespace
58
59 class DriveAppProviderTest : public ExtensionBrowserTest,
60                              public extensions::InstallObserver {
61  public:
62   DriveAppProviderTest() {}
63   virtual ~DriveAppProviderTest() {}
64
65   // ExtensionBrowserTest:
66   virtual void SetUpOnMainThread() OVERRIDE {
67     ExtensionBrowserTest::SetUpOnMainThread();
68
69     fake_drive_service_.reset(new drive::FakeDriveService);
70     fake_drive_service_->LoadAppListForDriveApi("drive/applist_empty.json");
71     apps_registry_.reset(
72         new drive::DriveAppRegistry(fake_drive_service_.get()));
73
74     provider_.reset(new DriveAppProvider(profile()));
75     provider_->SetDriveServiceBridgeForTest(
76         make_scoped_ptr(new TestDriveServiceBridge(apps_registry_.get()))
77             .PassAs<DriveServiceBridge>());
78   }
79
80   virtual void TearDownOnMainThread() OVERRIDE {
81     provider_.reset();
82     apps_registry_.reset();
83     fake_drive_service_.reset();
84
85     ExtensionBrowserTest::TearDownOnMainThread();
86   }
87
88   const Extension* InstallChromeApp(int expected_change) {
89     base::FilePath test_data_path;
90     if (!PathService::Get(chrome::DIR_TEST_DATA, &test_data_path)) {
91       ADD_FAILURE();
92       return NULL;
93     }
94     test_data_path =
95         test_data_path.AppendASCII("extensions").AppendASCII("hosted_app.crx");
96     const Extension* extension =
97         InstallExtension(test_data_path, expected_change);
98     return extension;
99   }
100
101   void RefreshDriveAppRegistry() {
102     apps_registry_->Update();
103     content::RunAllPendingInMessageLoop();
104   }
105
106   void WaitForPendingDriveAppConverters() {
107     DCHECK(!runner_);
108
109     if (provider_->pending_converters_.empty())
110       return;
111
112     runner_ = new content::MessageLoopRunner;
113
114     pending_drive_app_converter_check_timer_.Start(
115         FROM_HERE,
116         base::TimeDelta::FromMilliseconds(50),
117         base::Bind(&DriveAppProviderTest::OnPendingDriveAppConverterCheckTimer,
118                    base::Unretained(this)));
119
120     runner_->Run();
121
122     pending_drive_app_converter_check_timer_.Stop();
123     runner_ = NULL;
124   }
125
126   void InstallUserUrlApp(const std::string& url) {
127     DCHECK(!runner_);
128     runner_ = new content::MessageLoopRunner;
129
130     WebApplicationInfo web_app;
131     web_app.title = base::ASCIIToUTF16("User installed Url app");
132     web_app.app_url = GURL(url);
133
134     scoped_refptr<extensions::CrxInstaller> crx_installer =
135         extensions::CrxInstaller::CreateSilent(
136             extensions::ExtensionSystem::Get(profile())->extension_service());
137     crx_installer->set_creation_flags(Extension::FROM_BOOKMARK);
138     extensions::InstallTracker::Get(profile())->AddObserver(this);
139     crx_installer->InstallWebApp(web_app);
140
141     runner_->Run();
142     runner_ = NULL;
143     extensions::InstallTracker::Get(profile())->RemoveObserver(this);
144
145     content::RunAllPendingInMessageLoop();
146   }
147
148   bool HasPendingConverters() const {
149     return !provider_->pending_converters_.empty();
150   }
151
152   drive::FakeDriveService* fake_drive_service() {
153     return fake_drive_service_.get();
154   }
155   DriveAppProvider* provider() { return provider_.get(); }
156   DriveAppMapping* mapping() { return provider_->mapping_.get(); }
157
158  private:
159   void OnPendingDriveAppConverterCheckTimer() {
160     if (!HasPendingConverters())
161       runner_->Quit();
162   }
163
164   // extensions::InstallObserver
165   virtual void OnFinishCrxInstall(const std::string& extension_id,
166                                   bool success) OVERRIDE {
167     runner_->Quit();
168   }
169
170   scoped_ptr<drive::FakeDriveService> fake_drive_service_;
171   scoped_ptr<drive::DriveAppRegistry> apps_registry_;
172   scoped_ptr<DriveAppProvider> provider_;
173
174   base::RepeatingTimer<DriveAppProviderTest>
175       pending_drive_app_converter_check_timer_;
176   scoped_refptr<content::MessageLoopRunner> runner_;
177
178   DISALLOW_COPY_AND_ASSIGN(DriveAppProviderTest);
179 };
180
181 // A Drive app maps to an existing Chrome app that has a matching id.
182 // Uninstalling the chrome app would also disconnect the drive app.
183 IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, ExistingChromeApp) {
184   // Prepare an existing chrome app.
185   const Extension* chrome_app = InstallChromeApp(1);
186   ASSERT_TRUE(chrome_app);
187
188   // Prepare a Drive app that matches the chrome app id.
189   fake_drive_service()->AddApp(
190       kDriveAppId, kDriveAppName, chrome_app->id(), kLaunchUrl);
191   RefreshDriveAppRegistry();
192   EXPECT_FALSE(HasPendingConverters());
193
194   // The Drive app should use the matching chrome app.
195   EXPECT_EQ(chrome_app->id(), mapping()->GetChromeApp(kDriveAppId));
196   EXPECT_FALSE(mapping()->IsChromeAppGenerated(chrome_app->id()));
197
198   // Unintalling chrome app should disconnect the Drive app on server.
199   EXPECT_TRUE(fake_drive_service()->HasApp(kDriveAppId));
200   UninstallExtension(chrome_app->id());
201   EXPECT_FALSE(fake_drive_service()->HasApp(kDriveAppId));
202 }
203
204 // A Drive app creates an URL app when no matching Chrome app presents.
205 IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, CreateUrlApp) {
206   // Prepare a Drive app with no underlying chrome app.
207   fake_drive_service()->AddApp(kDriveAppId, kDriveAppName, "", kLaunchUrl);
208   RefreshDriveAppRegistry();
209   WaitForPendingDriveAppConverters();
210
211   // An Url app should be created.
212   const Extension* chrome_app =
213       ExtensionRegistry::Get(profile())->GetExtensionById(
214           mapping()->GetChromeApp(kDriveAppId), ExtensionRegistry::EVERYTHING);
215   ASSERT_TRUE(chrome_app);
216   EXPECT_EQ(kDriveAppName, chrome_app->name());
217   EXPECT_TRUE(chrome_app->is_hosted_app());
218   EXPECT_TRUE(chrome_app->from_bookmark());
219   EXPECT_EQ(GURL(kLaunchUrl), AppLaunchInfo::GetLaunchWebURL(chrome_app));
220
221   EXPECT_EQ(chrome_app->id(), mapping()->GetChromeApp(kDriveAppId));
222   EXPECT_TRUE(mapping()->IsChromeAppGenerated(chrome_app->id()));
223
224   // Unintalling the chrome app should disconnect the Drive app on server.
225   EXPECT_TRUE(fake_drive_service()->HasApp(kDriveAppId));
226   UninstallExtension(chrome_app->id());
227   EXPECT_FALSE(fake_drive_service()->HasApp(kDriveAppId));
228 }
229
230 // A matching Chrome app replaces the created URL app.
231 IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, MatchingChromeAppInstalled) {
232   // Prepare a Drive app that matches the not-yet-installed kChromeAppId.
233   fake_drive_service()->AddApp(
234       kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
235   RefreshDriveAppRegistry();
236   WaitForPendingDriveAppConverters();
237
238   // An Url app should be created.
239   const Extension* url_app =
240       ExtensionRegistry::Get(profile())->GetExtensionById(
241           mapping()->GetChromeApp(kDriveAppId), ExtensionRegistry::EVERYTHING);
242   EXPECT_TRUE(url_app->is_hosted_app());
243   EXPECT_TRUE(url_app->from_bookmark());
244
245   const std::string url_app_id = url_app->id();
246   EXPECT_NE(kChromeAppId, url_app_id);
247   EXPECT_EQ(url_app_id, mapping()->GetChromeApp(kDriveAppId));
248   EXPECT_TRUE(mapping()->IsChromeAppGenerated(url_app_id));
249
250   // Installs a chrome app with matching id.
251   InstallChromeApp(0);
252
253   // The Drive app should be mapped to chrome app.
254   EXPECT_EQ(kChromeAppId, mapping()->GetChromeApp(kDriveAppId));
255   EXPECT_FALSE(mapping()->IsChromeAppGenerated(kChromeAppId));
256
257   // Url app should be auto uninstalled.
258   EXPECT_FALSE(ExtensionRegistry::Get(profile())->GetExtensionById(
259       url_app_id, ExtensionRegistry::EVERYTHING));
260 }
261
262 // Tests that the corresponding URL app is uninstalled when a Drive app is
263 // disconnected.
264 IN_PROC_BROWSER_TEST_F(DriveAppProviderTest,
265                        DisconnectDriveAppUninstallUrlApp) {
266   // Prepare a Drive app that matches the not-yet-installed kChromeAppId.
267   fake_drive_service()->AddApp(
268       kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
269   RefreshDriveAppRegistry();
270   WaitForPendingDriveAppConverters();
271
272   // Url app is created.
273   const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
274   EXPECT_TRUE(ExtensionRegistry::Get(profile())->GetExtensionById(
275       url_app_id, ExtensionRegistry::EVERYTHING));
276
277   fake_drive_service()->RemoveAppByProductId(kChromeAppId);
278   RefreshDriveAppRegistry();
279
280   // Url app is auto uninstalled.
281   EXPECT_FALSE(ExtensionRegistry::Get(profile())->GetExtensionById(
282       url_app_id, ExtensionRegistry::EVERYTHING));
283 }
284
285 // Tests that the matching Chrome app is preserved when a Drive app is
286 // disconnected.
287 IN_PROC_BROWSER_TEST_F(DriveAppProviderTest,
288                        DisconnectDriveAppPreserveChromeApp) {
289   // Prepare an existing chrome app.
290   const Extension* chrome_app = InstallChromeApp(1);
291   ASSERT_TRUE(chrome_app);
292
293   // Prepare a Drive app that matches the chrome app id.
294   fake_drive_service()->AddApp(
295       kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
296   RefreshDriveAppRegistry();
297   EXPECT_FALSE(HasPendingConverters());
298
299   fake_drive_service()->RemoveAppByProductId(kChromeAppId);
300   RefreshDriveAppRegistry();
301
302   // Chrome app is still present after the Drive app is disconnected.
303   EXPECT_TRUE(ExtensionRegistry::Get(profile())->GetExtensionById(
304       kChromeAppId, ExtensionRegistry::EVERYTHING));
305 }
306
307 // The "generated" flag of an app should stay across Drive app conversion.
308 IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, KeepGeneratedFlagBetweenUpdates) {
309   // Prepare a Drive app with no underlying chrome app.
310   fake_drive_service()->AddApp(
311       kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
312   RefreshDriveAppRegistry();
313   WaitForPendingDriveAppConverters();
314
315   const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
316   EXPECT_TRUE(mapping()->IsChromeAppGenerated(url_app_id));
317
318   // Change name to trigger an update.
319   const char kChangedName[] = "Changed name";
320   fake_drive_service()->RemoveAppByProductId(kChromeAppId);
321   fake_drive_service()->AddApp(
322       kDriveAppId, kChangedName, kChromeAppId, kLaunchUrl);
323   RefreshDriveAppRegistry();
324   WaitForPendingDriveAppConverters();
325
326   // It should still map to the same url app id and tagged as generated.
327   EXPECT_EQ(url_app_id, mapping()->GetChromeApp(kDriveAppId));
328   EXPECT_TRUE(mapping()->IsChromeAppGenerated(url_app_id));
329 }
330
331 // A new URL app replaces the existing one and keeps existing// position when a
332 // Drive app changes its name or URL.
333 IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, DriveAppChanged) {
334   // Prepare a Drive app with no underlying chrome app.
335   fake_drive_service()->AddApp(
336       kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
337   RefreshDriveAppRegistry();
338   WaitForPendingDriveAppConverters();
339
340   // An Url app should be created.
341   const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
342   const Extension* url_app =
343       ExtensionRegistry::Get(profile())
344           ->GetExtensionById(url_app_id, ExtensionRegistry::EVERYTHING);
345   ASSERT_TRUE(url_app);
346   EXPECT_EQ(kDriveAppName, url_app->name());
347   EXPECT_TRUE(url_app->is_hosted_app());
348   EXPECT_TRUE(url_app->from_bookmark());
349   EXPECT_EQ(GURL(kLaunchUrl), AppLaunchInfo::GetLaunchWebURL(url_app));
350   EXPECT_TRUE(mapping()->IsChromeAppGenerated(url_app_id));
351
352   // Register the Drive app with a different name and URL.
353   const char kAnotherName[] = "Another drive app name";
354   const char kAnotherLaunchUrl[] = "http://example.com/another_end_point";
355   fake_drive_service()->RemoveAppByProductId(kChromeAppId);
356   fake_drive_service()->AddApp(
357       kDriveAppId, kAnotherName, kChromeAppId, kAnotherLaunchUrl);
358   RefreshDriveAppRegistry();
359   WaitForPendingDriveAppConverters();
360
361   // Old URL app should be auto uninstalled.
362   url_app = ExtensionRegistry::Get(profile())
363                 ->GetExtensionById(url_app_id, ExtensionRegistry::EVERYTHING);
364   EXPECT_FALSE(url_app);
365
366   // New URL app should be used.
367   const std::string new_url_app_id = mapping()->GetChromeApp(kDriveAppId);
368   EXPECT_NE(new_url_app_id, url_app_id);
369   EXPECT_TRUE(mapping()->IsChromeAppGenerated(new_url_app_id));
370
371   const Extension* new_url_app =
372       ExtensionRegistry::Get(profile())
373           ->GetExtensionById(new_url_app_id, ExtensionRegistry::EVERYTHING);
374   ASSERT_TRUE(new_url_app);
375   EXPECT_EQ(kAnotherName, new_url_app->name());
376   EXPECT_TRUE(new_url_app->is_hosted_app());
377   EXPECT_TRUE(new_url_app->from_bookmark());
378   EXPECT_EQ(GURL(kAnotherLaunchUrl),
379             AppLaunchInfo::GetLaunchWebURL(new_url_app));
380 }
381
382 // An existing URL app is not changed when underlying drive app data (name and
383 // URL) is not changed.
384 IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, NoChange) {
385   // Prepare one Drive app.
386   fake_drive_service()->AddApp(
387       kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
388   RefreshDriveAppRegistry();
389   WaitForPendingDriveAppConverters();
390
391   const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
392   const Extension* url_app =
393       ExtensionRegistry::Get(profile())
394           ->GetExtensionById(url_app_id, ExtensionRegistry::EVERYTHING);
395
396   // Refresh with no actual change.
397   RefreshDriveAppRegistry();
398   EXPECT_FALSE(HasPendingConverters());
399
400   // Url app should remain unchanged.
401   const std::string new_url_app_id = mapping()->GetChromeApp(kDriveAppId);
402   EXPECT_EQ(new_url_app_id, url_app_id);
403
404   const Extension* new_url_app =
405       ExtensionRegistry::Get(profile())
406           ->GetExtensionById(new_url_app_id, ExtensionRegistry::EVERYTHING);
407   EXPECT_EQ(url_app, new_url_app);
408 }
409
410 // User installed url app before Drive app conversion should not be tagged
411 // as generated and not auto uninstalled.
412 IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, UserInstalledBeforeDriveApp) {
413   InstallUserUrlApp(kLaunchUrl);
414
415   fake_drive_service()->AddApp(
416       kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
417   RefreshDriveAppRegistry();
418   WaitForPendingDriveAppConverters();
419
420   const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
421   EXPECT_FALSE(mapping()->IsChromeAppGenerated(url_app_id));
422
423   fake_drive_service()->RemoveAppByProductId(kChromeAppId);
424   RefreshDriveAppRegistry();
425
426   // Url app is still present after the Drive app is disconnected.
427   EXPECT_TRUE(ExtensionRegistry::Get(profile())->GetExtensionById(
428       url_app_id, ExtensionRegistry::EVERYTHING));
429 }
430
431 // Similar to UserInstalledBeforeDriveApp but test the case where user
432 // installation happens after Drive app conversion.
433 IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, UserInstalledAfterDriveApp) {
434   fake_drive_service()->AddApp(
435       kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
436   RefreshDriveAppRegistry();
437   WaitForPendingDriveAppConverters();
438
439   // Drive app converted and tagged as generated.
440   const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
441   EXPECT_TRUE(mapping()->IsChromeAppGenerated(url_app_id));
442
443   // User installation resets the generated flag.
444   InstallUserUrlApp(kLaunchUrl);
445   EXPECT_FALSE(mapping()->IsChromeAppGenerated(url_app_id));
446
447   fake_drive_service()->RemoveAppByProductId(kChromeAppId);
448   RefreshDriveAppRegistry();
449
450   // Url app is still present after the Drive app is disconnected.
451   EXPECT_TRUE(ExtensionRegistry::Get(profile())->GetExtensionById(
452       url_app_id, ExtensionRegistry::EVERYTHING));
453 }