1 // Copyright 2018 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/web_applications/components/pending_app_manager.h"
6 
7 #include <algorithm>
8 #include <sstream>
9 #include <vector>
10 
11 #include "base/callback_helpers.h"
12 #include "base/run_loop.h"
13 #include "base/test/bind.h"
14 #include "base/test/task_environment.h"
15 #include "chrome/browser/web_applications/components/app_registrar.h"
16 #include "chrome/browser/web_applications/components/web_app_constants.h"
17 #include "chrome/browser/web_applications/test/test_pending_app_manager.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 namespace web_app {
21 
22 class PendingAppManagerTest : public testing::Test {
23  public:
PendingAppManagerTest()24   PendingAppManagerTest() : pending_app_manager_(&registrar_) {}
25 
26  protected:
Sync(std::vector<GURL> urls)27   void Sync(std::vector<GURL> urls) {
28     pending_app_manager_.ResetCounts();
29 
30     std::vector<ExternalInstallOptions> install_options_list;
31     for (const auto& url : urls) {
32       install_options_list.emplace_back(
33           url, DisplayMode::kStandalone,
34           ExternalInstallSource::kInternalDefault);
35     }
36 
37     base::RunLoop run_loop;
38     pending_app_manager_.SynchronizeInstalledApps(
39         std::move(install_options_list),
40         ExternalInstallSource::kInternalDefault,
41         base::BindLambdaForTesting(
42             [&run_loop, urls](std::map<GURL, InstallResultCode> install_results,
43                               std::map<GURL, bool> uninstall_results) {
44               run_loop.Quit();
45             }));
46     // Wait for SynchronizeInstalledApps to finish.
47     run_loop.Run();
48   }
49 
Expect(int deduped_install_count,int deduped_uninstall_count,std::vector<GURL> installed_app_urls)50   void Expect(int deduped_install_count,
51               int deduped_uninstall_count,
52               std::vector<GURL> installed_app_urls) {
53     EXPECT_EQ(deduped_install_count,
54               pending_app_manager_.deduped_install_count());
55     EXPECT_EQ(deduped_uninstall_count,
56               pending_app_manager_.deduped_uninstall_count());
57 
58     std::map<AppId, GURL> apps = registrar_.GetExternallyInstalledApps(
59         ExternalInstallSource::kInternalDefault);
60     std::vector<GURL> urls;
61     for (auto it : apps)
62       urls.push_back(it.second);
63 
64     std::sort(urls.begin(), urls.end());
65     EXPECT_EQ(installed_app_urls, urls);
66   }
67 
68   base::test::TaskEnvironment task_environment_;
69   TestAppRegistrar registrar_;
70   TestPendingAppManager pending_app_manager_;
71 };
72 
73 // Test that destroying PendingAppManager during a synchronize call that
74 // installs an app doesn't crash.
75 // Regression test for https://crbug.com/962808
TEST_F(PendingAppManagerTest,DestroyDuringInstallInSynchronize)76 TEST_F(PendingAppManagerTest, DestroyDuringInstallInSynchronize) {
77   TestAppRegistrar registrar;
78   auto pending_app_manager =
79       std::make_unique<TestPendingAppManager>(&registrar);
80 
81   std::vector<ExternalInstallOptions> install_options_list;
82   install_options_list.emplace_back(GURL("https://foo.example"),
83                                     DisplayMode::kStandalone,
84                                     ExternalInstallSource::kInternalDefault);
85   install_options_list.emplace_back(GURL("https://bar.example"),
86                                     DisplayMode::kStandalone,
87                                     ExternalInstallSource::kInternalDefault);
88 
89   pending_app_manager->SynchronizeInstalledApps(
90       std::move(install_options_list), ExternalInstallSource::kInternalDefault,
91       // PendingAppManager gives no guarantees about whether its pending
92       // callbacks will be run or not when it gets destroyed.
93       base::DoNothing());
94   pending_app_manager.reset();
95   base::RunLoop().RunUntilIdle();
96 }
97 
98 // Test that destroying PendingAppManager during a synchronize call that
99 // uninstalls an app doesn't crash.
100 // Regression test for https://crbug.com/962808
TEST_F(PendingAppManagerTest,DestroyDuringUninstallInSynchronize)101 TEST_F(PendingAppManagerTest, DestroyDuringUninstallInSynchronize) {
102   TestAppRegistrar registrar;
103   auto pending_app_manager =
104       std::make_unique<TestPendingAppManager>(&registrar);
105 
106   // Install an app that will be uninstalled next.
107   {
108     std::vector<ExternalInstallOptions> install_options_list;
109     install_options_list.emplace_back(GURL("https://foo.example"),
110                                       DisplayMode::kStandalone,
111                                       ExternalInstallSource::kInternalDefault);
112     base::RunLoop run_loop;
113     pending_app_manager->SynchronizeInstalledApps(
114         std::move(install_options_list),
115         ExternalInstallSource::kInternalDefault,
116         base::BindLambdaForTesting(
117             [&](std::map<GURL, InstallResultCode> install_results,
118                 std::map<GURL, bool> uninstall_results) { run_loop.Quit(); }));
119     run_loop.Run();
120   }
121 
122   pending_app_manager->SynchronizeInstalledApps(
123       std::vector<ExternalInstallOptions>(),
124       ExternalInstallSource::kInternalDefault,
125       // PendingAppManager gives no guarantees about whether its pending
126       // callbacks will be run or not when it gets destroyed.
127       base::DoNothing());
128   pending_app_manager.reset();
129   base::RunLoop().RunUntilIdle();
130 }
131 
TEST_F(PendingAppManagerTest,SynchronizeInstalledApps)132 TEST_F(PendingAppManagerTest, SynchronizeInstalledApps) {
133   GURL a("https://a.example.com/");
134   GURL b("https://b.example.com/");
135   GURL c("https://c.example.com/");
136   GURL d("https://d.example.com/");
137   GURL e("https://e.example.com/");
138 
139   Sync(std::vector<GURL>{a, b, d});
140   Expect(3, 0, std::vector<GURL>{a, b, d});
141 
142   Sync(std::vector<GURL>{b, e});
143   Expect(1, 2, std::vector<GURL>{b, e});
144 
145   Sync(std::vector<GURL>{e});
146   Expect(0, 1, std::vector<GURL>{e});
147 
148   Sync(std::vector<GURL>{c});
149   Expect(1, 1, std::vector<GURL>{c});
150 
151   Sync(std::vector<GURL>{e, a, d});
152   Expect(3, 1, std::vector<GURL>{a, d, e});
153 
154   Sync(std::vector<GURL>{c, a, b, d, e});
155   Expect(2, 0, std::vector<GURL>{a, b, c, d, e});
156 
157   Sync(std::vector<GURL>{});
158   Expect(0, 5, std::vector<GURL>{});
159 
160   // The remaining code tests duplicate inputs.
161 
162   Sync(std::vector<GURL>{b, a, b, c});
163   Expect(3, 0, std::vector<GURL>{a, b, c});
164 
165   Sync(std::vector<GURL>{e, a, e, e, e, a});
166   Expect(1, 2, std::vector<GURL>{a, e});
167 
168   Sync(std::vector<GURL>{b, c, d});
169   Expect(3, 2, std::vector<GURL>{b, c, d});
170 
171   Sync(std::vector<GURL>{a, a, a, a, a, a});
172   Expect(1, 3, std::vector<GURL>{a});
173 
174   Sync(std::vector<GURL>{});
175   Expect(0, 1, std::vector<GURL>{});
176 }
177 
178 }  // namespace web_app
179