Make default apps cache multiprofile friendly
[chromium-blink-merge.git] / chrome / browser / extensions / extensions_quota_service_unittest.cc
bloba0438ff3887d9f92199df43b820e187e255a4b6d
1 // Copyright (c) 2012 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/message_loop/message_loop.h"
6 #include "base/process/process.h"
7 #include "base/stl_util.h"
8 #include "base/strings/string_util.h"
9 #include "chrome/browser/extensions/extension_function.h"
10 #include "chrome/browser/extensions/extensions_quota_service.h"
11 #include "content/public/test/test_browser_thread.h"
12 #include "testing/gtest/include/gtest/gtest.h"
14 using base::TimeDelta;
15 using base::TimeTicks;
16 using content::BrowserThread;
18 typedef QuotaLimitHeuristic::Bucket Bucket;
19 typedef QuotaLimitHeuristic::Config Config;
20 typedef QuotaLimitHeuristic::BucketList BucketList;
21 typedef ExtensionsQuotaService::TimedLimit TimedLimit;
22 typedef ExtensionsQuotaService::SustainedLimit SustainedLimit;
24 namespace {
26 const char kGenericName[] = "name";
27 const Config kFrozenConfig = { 0, TimeDelta::FromDays(0) };
28 const Config k2PerMinute = { 2, TimeDelta::FromMinutes(1) };
29 const Config k20PerHour = { 20, TimeDelta::FromHours(1) };
30 const TimeTicks kStartTime = TimeTicks();
31 const TimeTicks k1MinuteAfterStart = kStartTime + TimeDelta::FromMinutes(1);
33 class Mapper : public QuotaLimitHeuristic::BucketMapper {
34 public:
35 Mapper() {}
36 virtual ~Mapper() { STLDeleteValues(&buckets_); }
37 virtual void GetBucketsForArgs(const base::ListValue* args,
38 BucketList* buckets) OVERRIDE {
39 for (size_t i = 0; i < args->GetSize(); i++) {
40 int id;
41 ASSERT_TRUE(args->GetInteger(i, &id));
42 if (buckets_.find(id) == buckets_.end())
43 buckets_[id] = new Bucket();
44 buckets->push_back(buckets_[id]);
47 private:
48 typedef std::map<int, Bucket*> BucketMap;
49 BucketMap buckets_;
50 DISALLOW_COPY_AND_ASSIGN(Mapper);
53 class MockMapper : public QuotaLimitHeuristic::BucketMapper {
54 public:
55 virtual void GetBucketsForArgs(const base::ListValue* args,
56 BucketList* buckets) OVERRIDE {
60 class MockFunction : public ExtensionFunction {
61 public:
62 explicit MockFunction(const std::string& name) { set_name(name); }
64 virtual void SetArgs(const base::ListValue* args) OVERRIDE {}
65 virtual const std::string GetError() OVERRIDE { return std::string(); }
66 virtual void SetError(const std::string& error) OVERRIDE {}
67 virtual void Run() OVERRIDE {}
68 virtual void Destruct() const OVERRIDE { delete this; }
69 virtual bool RunImpl() OVERRIDE { return true; }
70 virtual void SendResponse(bool) OVERRIDE { }
72 protected:
73 virtual ~MockFunction() {}
76 class TimedLimitMockFunction : public MockFunction {
77 public:
78 explicit TimedLimitMockFunction(const std::string& name)
79 : MockFunction(name) {}
80 virtual void GetQuotaLimitHeuristics(
81 QuotaLimitHeuristics* heuristics) const OVERRIDE {
82 heuristics->push_back(
83 new TimedLimit(k2PerMinute, new Mapper(), kGenericName));
86 private:
87 virtual ~TimedLimitMockFunction() {}
90 class ChainedLimitsMockFunction : public MockFunction {
91 public:
92 explicit ChainedLimitsMockFunction(const std::string& name)
93 : MockFunction(name) {}
94 virtual void GetQuotaLimitHeuristics(
95 QuotaLimitHeuristics* heuristics) const OVERRIDE {
96 // No more than 2 per minute sustained over 5 minutes.
97 heuristics->push_back(new SustainedLimit(
98 TimeDelta::FromMinutes(5), k2PerMinute, new Mapper(), kGenericName));
99 // No more than 20 per hour.
100 heuristics->push_back(
101 new TimedLimit(k20PerHour, new Mapper(), kGenericName));
104 private:
105 virtual ~ChainedLimitsMockFunction() {}
108 class FrozenMockFunction : public MockFunction {
109 public:
110 explicit FrozenMockFunction(const std::string& name) : MockFunction(name) {}
111 virtual void GetQuotaLimitHeuristics(
112 QuotaLimitHeuristics* heuristics) const OVERRIDE {
113 heuristics->push_back(
114 new TimedLimit(kFrozenConfig, new Mapper(), kGenericName));
117 private:
118 virtual ~FrozenMockFunction() {}
120 } // namespace
122 class ExtensionsQuotaServiceTest : public testing::Test {
123 public:
124 ExtensionsQuotaServiceTest()
125 : extension_a_("a"),
126 extension_b_("b"),
127 extension_c_("c"),
128 loop_(),
129 ui_thread_(BrowserThread::UI, &loop_) {
131 virtual void SetUp() {
132 service_.reset(new ExtensionsQuotaService());
134 virtual void TearDown() {
135 loop_.RunUntilIdle();
136 service_.reset();
138 protected:
139 std::string extension_a_;
140 std::string extension_b_;
141 std::string extension_c_;
142 scoped_ptr<ExtensionsQuotaService> service_;
143 base::MessageLoop loop_;
144 content::TestBrowserThread ui_thread_;
147 class QuotaLimitHeuristicTest : public testing::Test {
148 public:
149 static void DoMoreThan2PerMinuteFor5Minutes(const TimeTicks& start_time,
150 QuotaLimitHeuristic* lim,
151 Bucket* b,
152 int an_unexhausted_minute) {
153 for (int i = 0; i < 5; i++) {
154 // Perform one operation in each minute.
155 int m = i * 60;
156 EXPECT_TRUE(lim->Apply(b, start_time + TimeDelta::FromSeconds(10 + m)));
157 EXPECT_TRUE(b->has_tokens());
159 if (i == an_unexhausted_minute)
160 continue; // Don't exhaust all tokens this minute.
162 EXPECT_TRUE(lim->Apply(b, start_time + TimeDelta::FromSeconds(15 + m)));
163 EXPECT_FALSE(b->has_tokens());
165 // These are OK because we haven't exhausted all buckets.
166 EXPECT_TRUE(lim->Apply(b, start_time + TimeDelta::FromSeconds(20 + m)));
167 EXPECT_FALSE(b->has_tokens());
168 EXPECT_TRUE(lim->Apply(b, start_time + TimeDelta::FromSeconds(50 + m)));
169 EXPECT_FALSE(b->has_tokens());
174 TEST_F(QuotaLimitHeuristicTest, Timed) {
175 TimedLimit lim(k2PerMinute, new MockMapper(), kGenericName);
176 Bucket b;
178 b.Reset(k2PerMinute, kStartTime);
179 EXPECT_TRUE(lim.Apply(&b, kStartTime));
180 EXPECT_TRUE(b.has_tokens());
181 EXPECT_TRUE(lim.Apply(&b, kStartTime + TimeDelta::FromSeconds(30)));
182 EXPECT_FALSE(b.has_tokens());
183 EXPECT_FALSE(lim.Apply(&b, k1MinuteAfterStart));
185 b.Reset(k2PerMinute, kStartTime);
186 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart - TimeDelta::FromSeconds(1)));
187 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart));
188 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart + TimeDelta::FromSeconds(1)));
189 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart + TimeDelta::FromSeconds(2)));
190 EXPECT_FALSE(lim.Apply(&b, k1MinuteAfterStart + TimeDelta::FromSeconds(3)));
193 TEST_F(QuotaLimitHeuristicTest, Sustained) {
194 SustainedLimit lim(
195 TimeDelta::FromMinutes(5), k2PerMinute, new MockMapper(), kGenericName);
196 Bucket bucket;
198 bucket.Reset(k2PerMinute, kStartTime);
199 DoMoreThan2PerMinuteFor5Minutes(kStartTime, &lim, &bucket, -1);
200 // This straw breaks the camel's back.
201 EXPECT_FALSE(lim.Apply(&bucket, kStartTime + TimeDelta::FromMinutes(6)));
203 // The heuristic resets itself on a safe request.
204 EXPECT_TRUE(lim.Apply(&bucket, kStartTime + TimeDelta::FromDays(1)));
206 // Do the same as above except don't exhaust final bucket.
207 bucket.Reset(k2PerMinute, kStartTime);
208 DoMoreThan2PerMinuteFor5Minutes(kStartTime, &lim, &bucket, -1);
209 EXPECT_TRUE(lim.Apply(&bucket, kStartTime + TimeDelta::FromMinutes(7)));
211 // Do the same as above except don't exhaust the 3rd (w.l.o.g) bucket.
212 bucket.Reset(k2PerMinute, kStartTime);
213 DoMoreThan2PerMinuteFor5Minutes(kStartTime, &lim, &bucket, 3);
214 // If the 3rd bucket were exhausted, this would fail (see first test).
215 EXPECT_TRUE(lim.Apply(&bucket, kStartTime + TimeDelta::FromMinutes(6)));
218 TEST_F(ExtensionsQuotaServiceTest, NoHeuristic) {
219 scoped_refptr<MockFunction> f(new MockFunction("foo"));
220 base::ListValue args;
221 EXPECT_EQ("", service_->Assess(extension_a_, f.get(), &args, kStartTime));
224 TEST_F(ExtensionsQuotaServiceTest, FrozenHeuristic) {
225 scoped_refptr<MockFunction> f(new FrozenMockFunction("foo"));
226 base::ListValue args;
227 args.Append(new base::FundamentalValue(1));
228 EXPECT_NE("", service_->Assess(extension_a_, f.get(), &args, kStartTime));
231 TEST_F(ExtensionsQuotaServiceTest, SingleHeuristic) {
232 scoped_refptr<MockFunction> f(new TimedLimitMockFunction("foo"));
233 base::ListValue args;
234 args.Append(new base::FundamentalValue(1));
235 EXPECT_EQ("", service_->Assess(extension_a_, f.get(), &args, kStartTime));
236 EXPECT_EQ("",
237 service_->Assess(extension_a_,
238 f.get(),
239 &args,
240 kStartTime + TimeDelta::FromSeconds(10)));
241 EXPECT_NE("",
242 service_->Assess(extension_a_,
243 f.get(),
244 &args,
245 kStartTime + TimeDelta::FromSeconds(15)));
247 base::ListValue args2;
248 args2.Append(new base::FundamentalValue(1));
249 args2.Append(new base::FundamentalValue(2));
250 EXPECT_EQ("", service_->Assess(extension_b_, f.get(), &args2, kStartTime));
251 EXPECT_EQ("",
252 service_->Assess(extension_b_,
253 f.get(),
254 &args2,
255 kStartTime + TimeDelta::FromSeconds(10)));
257 TimeDelta peace = TimeDelta::FromMinutes(30);
258 EXPECT_EQ("",
259 service_->Assess(extension_b_, f.get(), &args, kStartTime + peace));
260 EXPECT_EQ("",
261 service_->Assess(extension_b_,
262 f.get(),
263 &args,
264 kStartTime + peace + TimeDelta::FromSeconds(10)));
265 EXPECT_NE("",
266 service_->Assess(extension_b_,
267 f.get(),
268 &args2,
269 kStartTime + peace + TimeDelta::FromSeconds(15)));
271 // Test that items are independent.
272 base::ListValue args3;
273 args3.Append(new base::FundamentalValue(3));
274 EXPECT_EQ("", service_->Assess(extension_c_, f.get(), &args, kStartTime));
275 EXPECT_EQ("",
276 service_->Assess(extension_c_,
277 f.get(),
278 &args3,
279 kStartTime + TimeDelta::FromSeconds(10)));
280 EXPECT_EQ("",
281 service_->Assess(extension_c_,
282 f.get(),
283 &args,
284 kStartTime + TimeDelta::FromSeconds(15)));
285 EXPECT_EQ("",
286 service_->Assess(extension_c_,
287 f.get(),
288 &args3,
289 kStartTime + TimeDelta::FromSeconds(20)));
290 EXPECT_NE("",
291 service_->Assess(extension_c_,
292 f.get(),
293 &args,
294 kStartTime + TimeDelta::FromSeconds(25)));
295 EXPECT_NE("",
296 service_->Assess(extension_c_,
297 f.get(),
298 &args3,
299 kStartTime + TimeDelta::FromSeconds(30)));
302 TEST_F(ExtensionsQuotaServiceTest, ChainedHeuristics) {
303 scoped_refptr<MockFunction> f(new ChainedLimitsMockFunction("foo"));
304 base::ListValue args;
305 args.Append(new base::FundamentalValue(1));
307 // First, test that the low limit can be avoided but the higher one is hit.
308 // One event per minute for 20 minutes comes in under the sustained limit,
309 // but is equal to the timed limit.
310 for (int i = 0; i < 20; i++) {
311 EXPECT_EQ(
313 service_->Assess(extension_a_,
314 f.get(),
315 &args,
316 kStartTime + TimeDelta::FromSeconds(10 + i * 60)));
319 // This will bring us to 21 events in an hour, which is a violation.
320 EXPECT_NE("",
321 service_->Assess(extension_a_,
322 f.get(),
323 &args,
324 kStartTime + TimeDelta::FromMinutes(30)));
326 // Now, check that we can still hit the lower limit.
327 for (int i = 0; i < 5; i++) {
328 EXPECT_EQ(
330 service_->Assess(extension_b_,
331 f.get(),
332 &args,
333 kStartTime + TimeDelta::FromSeconds(10 + i * 60)));
334 EXPECT_EQ(
336 service_->Assess(extension_b_,
337 f.get(),
338 &args,
339 kStartTime + TimeDelta::FromSeconds(15 + i * 60)));
340 EXPECT_EQ(
342 service_->Assess(extension_b_,
343 f.get(),
344 &args,
345 kStartTime + TimeDelta::FromSeconds(20 + i * 60)));
348 EXPECT_NE("",
349 service_->Assess(extension_b_,
350 f.get(),
351 &args,
352 kStartTime + TimeDelta::FromMinutes(6)));
355 TEST_F(ExtensionsQuotaServiceTest, MultipleFunctionsDontInterfere) {
356 scoped_refptr<MockFunction> f(new TimedLimitMockFunction("foo"));
357 scoped_refptr<MockFunction> g(new TimedLimitMockFunction("bar"));
359 base::ListValue args_f;
360 base::ListValue args_g;
361 args_f.Append(new base::FundamentalValue(1));
362 args_g.Append(new base::FundamentalValue(2));
364 EXPECT_EQ("", service_->Assess(extension_a_, f.get(), &args_f, kStartTime));
365 EXPECT_EQ("", service_->Assess(extension_a_, g.get(), &args_g, kStartTime));
366 EXPECT_EQ("",
367 service_->Assess(extension_a_,
368 f.get(),
369 &args_f,
370 kStartTime + TimeDelta::FromSeconds(10)));
371 EXPECT_EQ("",
372 service_->Assess(extension_a_,
373 g.get(),
374 &args_g,
375 kStartTime + TimeDelta::FromSeconds(10)));
376 EXPECT_NE("",
377 service_->Assess(extension_a_,
378 f.get(),
379 &args_f,
380 kStartTime + TimeDelta::FromSeconds(15)));
381 EXPECT_NE("",
382 service_->Assess(extension_a_,
383 g.get(),
384 &args_g,
385 kStartTime + TimeDelta::FromSeconds(15)));
388 TEST_F(ExtensionsQuotaServiceTest, ViolatorsWillBeViolators) {
389 scoped_refptr<MockFunction> f(new TimedLimitMockFunction("foo"));
390 scoped_refptr<MockFunction> g(new TimedLimitMockFunction("bar"));
391 base::ListValue arg;
392 arg.Append(new base::FundamentalValue(1));
393 EXPECT_EQ("", service_->Assess(extension_a_, f.get(), &arg, kStartTime));
394 EXPECT_EQ("",
395 service_->Assess(extension_a_,
396 f.get(),
397 &arg,
398 kStartTime + TimeDelta::FromSeconds(10)));
399 EXPECT_NE("",
400 service_->Assess(extension_a_,
401 f.get(),
402 &arg,
403 kStartTime + TimeDelta::FromSeconds(15)));
405 // We don't allow this extension to use quota limited functions even if they
406 // wait a while.
407 EXPECT_NE(
409 service_->Assess(
410 extension_a_, f.get(), &arg, kStartTime + TimeDelta::FromDays(1)));
411 EXPECT_NE(
413 service_->Assess(
414 extension_a_, g.get(), &arg, kStartTime + TimeDelta::FromDays(1)));