Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(44)

Side by Side Diff: chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate_unittest.cc

Issue 1903713002: Revert of Rate limit programmatic update checks for extensions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 <deque>
6 #include <memory>
7 #include <set>
8 #include <utility>
9
10 #include "base/callback.h"
11 #include "base/files/file_path.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/test/simple_test_tick_clock.h"
15 #include "chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/extension_service_test_with_install.h"
18 #include "chrome/browser/extensions/test_extension_system.h"
19 #include "chrome/browser/extensions/update_install_gate.h"
20 #include "chrome/browser/extensions/updater/extension_updater.h"
21 #include "extensions/browser/event_router.h"
22 #include "extensions/browser/event_router_factory.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/updater/extension_downloader.h"
26 #include "extensions/browser/updater/extension_downloader_test_delegate.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 namespace extensions {
30
31 namespace {
32
33 // A fake EventRouter that lets us pretend an extension has a listener
34 // registered for named events.
35 class TestEventRouter : public EventRouter {
36 public:
37 explicit TestEventRouter(content::BrowserContext* context)
38 : EventRouter(context, ExtensionPrefs::Get(context)) {}
39 ~TestEventRouter() override {}
40
41 // An entry in our fake event registry.
42 using Entry = std::pair<std::string, std::string>;
43
44 bool ExtensionHasEventListener(const std::string& extension_id,
45 const std::string& event_name) override {
46 return fake_registry_.find(Entry(extension_id, event_name)) !=
47 fake_registry_.end();
48 }
49
50 // Pretend that |extension_id| is listening for |event_name|.
51 void AddFakeListener(const std::string& extension_id,
52 const std::string& event_name) {
53 fake_registry_.insert(Entry(extension_id, event_name));
54 }
55
56 private:
57 std::set<Entry> fake_registry_;
58
59 DISALLOW_COPY_AND_ASSIGN(TestEventRouter);
60 };
61
62 std::unique_ptr<KeyedService> TestEventRouterFactoryFunction(
63 content::BrowserContext* context) {
64 return base::WrapUnique(new TestEventRouter(context));
65 }
66
67 // This class lets us intercept extension update checks and respond as if
68 // either no update was found, or one was (and it was downloaded).
69 class DownloaderTestDelegate : public ExtensionDownloaderTestDelegate {
70 public:
71 DownloaderTestDelegate() {}
72
73 // On the next update check for extension |id|, we'll respond that no update
74 // is available.
75 void AddNoUpdateResponse(const std::string& id) {
76 no_updates_.insert(id);
77 if (updates_.find(id) != updates_.end())
78 updates_.erase(id);
79 }
80
81 // On the next update check for extension |id|, pretend that an update to
82 // version |version| has been downloaded to |path|.
83 void AddUpdateResponse(const std::string& id,
84 const base::FilePath& path,
85 const std::string& version) {
86 if (no_updates_.find(id) != no_updates_.end())
87 no_updates_.erase(id);
88 DownloadFinishedArgs args;
89 args.path = path;
90 args.version = version;
91 updates_[id] = std::move(args);
92 }
93
94 void StartUpdateCheck(
95 ExtensionDownloader* downloader,
96 ExtensionDownloaderDelegate* delegate,
97 std::unique_ptr<ManifestFetchData> fetch_data) override {
98 // Instead of immediately firing callbacks to the delegate in matching
99 // cases below, we instead post a task since the delegate typically isn't
100 // expecting a synchronous reply (the real code has to go do at least one
101 // network request before getting a response, so this is is a reasonable
102 // expectation by delegates).
103 for (const std::string& id : fetch_data->extension_ids()) {
104 auto no_update = no_updates_.find(id);
105 if (no_update != no_updates_.end()) {
106 no_updates_.erase(no_update);
107 base::MessageLoop::current()->task_runner()->PostTask(
108 FROM_HERE,
109 base::Bind(&ExtensionDownloaderDelegate::OnExtensionDownloadFailed,
110 base::Unretained(delegate), id,
111 ExtensionDownloaderDelegate::NO_UPDATE_AVAILABLE,
112 ExtensionDownloaderDelegate::PingResult(),
113 fetch_data->request_ids()));
114 continue;
115 }
116 auto update = updates_.find(id);
117 if (update != updates_.end()) {
118 CRXFileInfo info(id, update->second.path, "" /* no hash */);
119 std::string version = update->second.version;
120 updates_.erase(update);
121 base::MessageLoop::current()->task_runner()->PostTask(
122 FROM_HERE,
123 base::Bind(
124 &ExtensionDownloaderDelegate::OnExtensionDownloadFinished,
125 base::Unretained(delegate), info,
126 false /* file_ownership_passed */, GURL(), version,
127 ExtensionDownloaderDelegate::PingResult(),
128 fetch_data->request_ids(),
129 ExtensionDownloaderDelegate::InstallCallback()));
130 continue;
131 }
132 ADD_FAILURE() << "Unexpected extension id " << id;
133 }
134 }
135
136 private:
137 // Simple holder for the data passed in AddUpdateResponse calls.
138 struct DownloadFinishedArgs {
139 base::FilePath path;
140 std::string version;
141 };
142
143 // These keep track of what response we should give for update checks, keyed
144 // by extension id. A given extension id should only appear in one or the
145 // other.
146 std::set<std::string> no_updates_;
147 std::map<std::string, DownloadFinishedArgs> updates_;
148
149 DISALLOW_COPY_AND_ASSIGN(DownloaderTestDelegate);
150 };
151
152 // Helper to let test code wait for and return an update check result.
153 class UpdateCheckResultCatcher {
154 public:
155 UpdateCheckResultCatcher() {}
156
157 void OnResult(const RuntimeAPIDelegate::UpdateCheckResult& result) {
158 EXPECT_EQ(nullptr, result_.get());
159 result_.reset(new RuntimeAPIDelegate::UpdateCheckResult(
160 result.success, result.response, result.version));
161 if (run_loop_)
162 run_loop_->Quit();
163 }
164
165 std::unique_ptr<RuntimeAPIDelegate::UpdateCheckResult> WaitForResult() {
166 if (!result_) {
167 run_loop_.reset(new base::RunLoop);
168 run_loop_->Run();
169 }
170 return std::move(result_);
171 }
172
173 private:
174 std::unique_ptr<RuntimeAPIDelegate::UpdateCheckResult> result_;
175 std::unique_ptr<base::RunLoop> run_loop_;
176
177 DISALLOW_COPY_AND_ASSIGN(UpdateCheckResultCatcher);
178 };
179
180 class ChromeRuntimeAPIDelegateTest : public ExtensionServiceTestWithInstall {
181 public:
182 ChromeRuntimeAPIDelegateTest() {}
183
184 void SetUp() override {
185 ExtensionServiceTestWithInstall::SetUp();
186 ExtensionDownloader::set_test_delegate(&downloader_test_delegate_);
187 ChromeRuntimeAPIDelegate::set_tick_clock_for_tests(&clock_);
188
189 InitializeExtensionServiceWithUpdater();
190 runtime_delegate_.reset(new ChromeRuntimeAPIDelegate(browser_context()));
191 service()->updater()->SetExtensionCacheForTesting(nullptr);
192 EventRouterFactory::GetInstance()->SetTestingFactory(
193 browser_context(), &TestEventRouterFactoryFunction);
194
195 // Setup the ExtensionService so that extension updates won't complete
196 // installation until the extension is idle.
197 update_install_gate_.reset(new UpdateInstallGate(service()));
198 service()->RegisterInstallGate(ExtensionPrefs::DELAY_REASON_WAIT_FOR_IDLE,
199 update_install_gate_.get());
200 static_cast<TestExtensionSystem*>(ExtensionSystem::Get(browser_context()))
201 ->SetReady();
202 }
203
204 void TearDown() override {
205 ExtensionDownloader::set_test_delegate(nullptr);
206 ChromeRuntimeAPIDelegate::set_tick_clock_for_tests(nullptr);
207 ExtensionServiceTestWithInstall::TearDown();
208 }
209
210 // Uses runtime_delegate_ to run an update check for |id|, expecting
211 // |expected_response| and (if an update was available) |expected_version|.
212 // The |expected_response| should be one of 'throttled', 'no_update', or
213 // 'update_available'.
214 void DoUpdateCheck(const std::string& id,
215 const std::string& expected_response,
216 const std::string& expected_version) {
217 UpdateCheckResultCatcher catcher;
218 EXPECT_TRUE(runtime_delegate_->CheckForUpdates(
219 id, base::Bind(&UpdateCheckResultCatcher::OnResult,
220 base::Unretained(&catcher))));
221 std::unique_ptr<RuntimeAPIDelegate::UpdateCheckResult> result =
222 catcher.WaitForResult();
223 ASSERT_NE(nullptr, result.get());
224 EXPECT_TRUE(result->success);
225 EXPECT_EQ(expected_response, result->response);
226 EXPECT_EQ(expected_version, result->version);
227 }
228
229 protected:
230 // A clock we pass to the code used for throttling, so that we can manually
231 // increment time to test various throttling scenarios.
232 base::SimpleTestTickClock clock_;
233
234 // Used for intercepting update check requests and possibly returning fake
235 // download results.
236 DownloaderTestDelegate downloader_test_delegate_;
237
238 // The object whose behavior we're testing.
239 std::unique_ptr<RuntimeAPIDelegate> runtime_delegate_;
240
241 // For preventing extensions from being updated immediately.
242 std::unique_ptr<UpdateInstallGate> update_install_gate_;
243
244 private:
245 DISALLOW_COPY_AND_ASSIGN(ChromeRuntimeAPIDelegateTest);
246 };
247
248 TEST_F(ChromeRuntimeAPIDelegateTest, RequestUpdateCheck) {
249 base::FilePath v1_path = data_dir().AppendASCII("autoupdate/v1.crx");
250 base::FilePath v2_path = data_dir().AppendASCII("autoupdate/v2.crx");
251
252 // Start by installing version 1.
253 scoped_refptr<const Extension> v1(InstallCRX(v1_path, INSTALL_NEW));
254 std::string id = v1->id();
255
256 // Make it look like our test extension listens for the
257 // runtime.onUpdateAvailable event, so that it won't be updated immediately
258 // when the ExtensionUpdater hands the new version to the ExtensionService.
259 TestEventRouter* event_router =
260 static_cast<TestEventRouter*>(EventRouter::Get(browser_context()));
261 event_router->AddFakeListener(id, "runtime.onUpdateAvailable");
262
263 // Run an update check that should get a "no_update" response.
264 downloader_test_delegate_.AddNoUpdateResponse(id);
265 DoUpdateCheck(id, "no_update", "");
266
267 // Check again after a short delay - we should be throttled because
268 // not enough time has passed.
269 clock_.Advance(base::TimeDelta::FromMinutes(15));
270 downloader_test_delegate_.AddNoUpdateResponse(id);
271 DoUpdateCheck(id, "throttled", "");
272
273 // Now simulate checking a few times at a 6 hour interval - none of these
274 // should be throttled.
275 for (int i = 0; i < 5; i++) {
276 clock_.Advance(base::TimeDelta::FromHours(6));
277 downloader_test_delegate_.AddNoUpdateResponse(id);
278 DoUpdateCheck(id, "no_update", "");
279 }
280
281 // Run an update check that should get an "update_available" response. This
282 // actually causes the new version to be downloaded/unpacked, but the install
283 // will not complete until we reload the extension.
284 clock_.Advance(base::TimeDelta::FromDays(1));
285 downloader_test_delegate_.AddUpdateResponse(id, v2_path, "2.0");
286 DoUpdateCheck(id, "update_available", "2.0");
287
288 // Call again after short delay - it should be throttled instead of getting
289 // another "update_available" response.
290 clock_.Advance(base::TimeDelta::FromMinutes(30));
291 downloader_test_delegate_.AddUpdateResponse(id, v2_path, "2.0");
292 DoUpdateCheck(id, "throttled", "");
293
294 // Reload the extension, causing the delayed update to v2 to happen, then do
295 // another update check - we should get a no_update instead of throttled.
296 service()->ReloadExtension(id);
297 const Extension* current =
298 ExtensionRegistry::Get(browser_context())->GetInstalledExtension(id);
299 ASSERT_NE(nullptr, current);
300 EXPECT_EQ("2.0", current->VersionString());
301 clock_.Advance(base::TimeDelta::FromSeconds(10));
302 downloader_test_delegate_.AddNoUpdateResponse(id);
303 DoUpdateCheck(id, "no_update", "");
304
305 // Check again after short delay; we should be throttled.
306 clock_.Advance(base::TimeDelta::FromMinutes(5));
307 DoUpdateCheck(id, "throttled", "");
308
309 // Call again after a longer delay, we should should be unthrottled.
310 clock_.Advance(base::TimeDelta::FromHours(8));
311 downloader_test_delegate_.AddNoUpdateResponse(id);
312 DoUpdateCheck(id, "no_update", "");
313 }
314
315 } // namespace
316
317 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc ('k') | chrome/browser/extensions/extension_service.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698