1 // Copyright 2013 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 "content/public/test/test_browser_thread.h"
10 #include "extensions/browser/extension_function.h"
11 #include "extensions/browser/quota_service.h"
12 #include "testing/gtest/include/gtest/gtest.h"
14 using base::TimeDelta
;
15 using base::TimeTicks
;
16 using content::BrowserThread
;
18 namespace extensions
{
20 typedef QuotaLimitHeuristic::Bucket Bucket
;
21 typedef QuotaLimitHeuristic::Config Config
;
22 typedef QuotaLimitHeuristic::BucketList BucketList
;
23 typedef QuotaService::TimedLimit TimedLimit
;
27 const char kGenericName
[] = "name";
28 const Config kFrozenConfig
= {0, TimeDelta::FromDays(0)};
29 const Config k2PerMinute
= {2, TimeDelta::FromMinutes(1)};
30 const Config k20PerHour
= {20, TimeDelta::FromHours(1)};
31 const TimeTicks kStartTime
= TimeTicks();
32 const TimeTicks k1MinuteAfterStart
= kStartTime
+ TimeDelta::FromMinutes(1);
34 class Mapper
: public QuotaLimitHeuristic::BucketMapper
{
37 ~Mapper() override
{ STLDeleteValues(&buckets_
); }
38 void GetBucketsForArgs(const base::ListValue
* args
,
39 BucketList
* buckets
) override
{
40 for (size_t i
= 0; i
< args
->GetSize(); i
++) {
42 ASSERT_TRUE(args
->GetInteger(i
, &id
));
43 if (buckets_
.find(id
) == buckets_
.end())
44 buckets_
[id
] = new Bucket();
45 buckets
->push_back(buckets_
[id
]);
50 typedef std::map
<int, Bucket
*> BucketMap
;
52 DISALLOW_COPY_AND_ASSIGN(Mapper
);
55 class MockMapper
: public QuotaLimitHeuristic::BucketMapper
{
57 void GetBucketsForArgs(const base::ListValue
* args
,
58 BucketList
* buckets
) override
{}
61 class MockFunction
: public ExtensionFunction
{
63 explicit MockFunction(const char* name
) { set_name(name
); }
65 void SetArgs(const base::ListValue
* args
) override
{}
66 std::string
GetError() const override
{ return std::string(); }
67 void SetError(const std::string
& error
) override
{}
68 void Destruct() const override
{ delete this; }
69 ResponseAction
Run() override
{ return RespondLater(); }
70 void SendResponse(bool) override
{}
73 ~MockFunction() override
{}
76 class TimedLimitMockFunction
: public MockFunction
{
78 explicit TimedLimitMockFunction(const char* name
) : MockFunction(name
) {}
79 void GetQuotaLimitHeuristics(
80 QuotaLimitHeuristics
* heuristics
) const override
{
81 heuristics
->push_back(
82 new TimedLimit(k2PerMinute
, new Mapper(), kGenericName
));
86 ~TimedLimitMockFunction() override
{}
89 class FrozenMockFunction
: public MockFunction
{
91 explicit FrozenMockFunction(const char* name
) : MockFunction(name
) {}
92 void GetQuotaLimitHeuristics(
93 QuotaLimitHeuristics
* heuristics
) const override
{
94 heuristics
->push_back(
95 new TimedLimit(kFrozenConfig
, new Mapper(), kGenericName
));
99 ~FrozenMockFunction() override
{}
103 class QuotaServiceTest
: public testing::Test
{
110 ui_thread_(BrowserThread::UI
, &loop_
) {}
111 void SetUp() override
{ service_
.reset(new QuotaService()); }
112 void TearDown() override
{
113 loop_
.RunUntilIdle();
118 std::string extension_a_
;
119 std::string extension_b_
;
120 std::string extension_c_
;
121 scoped_ptr
<QuotaService
> service_
;
122 base::MessageLoop loop_
;
123 content::TestBrowserThread ui_thread_
;
126 class QuotaLimitHeuristicTest
: public testing::Test
{
128 static void DoMoreThan2PerMinuteFor5Minutes(const TimeTicks
& start_time
,
129 QuotaLimitHeuristic
* lim
,
131 int an_unexhausted_minute
) {
132 for (int i
= 0; i
< 5; i
++) {
133 // Perform one operation in each minute.
135 EXPECT_TRUE(lim
->Apply(b
, start_time
+ TimeDelta::FromSeconds(10 + m
)));
136 EXPECT_TRUE(b
->has_tokens());
138 if (i
== an_unexhausted_minute
)
139 continue; // Don't exhaust all tokens this minute.
141 EXPECT_TRUE(lim
->Apply(b
, start_time
+ TimeDelta::FromSeconds(15 + m
)));
142 EXPECT_FALSE(b
->has_tokens());
144 // These are OK because we haven't exhausted all buckets.
145 EXPECT_TRUE(lim
->Apply(b
, start_time
+ TimeDelta::FromSeconds(20 + m
)));
146 EXPECT_FALSE(b
->has_tokens());
147 EXPECT_TRUE(lim
->Apply(b
, start_time
+ TimeDelta::FromSeconds(50 + m
)));
148 EXPECT_FALSE(b
->has_tokens());
153 TEST_F(QuotaLimitHeuristicTest
, Timed
) {
154 TimedLimit
lim(k2PerMinute
, new MockMapper(), kGenericName
);
157 b
.Reset(k2PerMinute
, kStartTime
);
158 EXPECT_TRUE(lim
.Apply(&b
, kStartTime
));
159 EXPECT_TRUE(b
.has_tokens());
160 EXPECT_TRUE(lim
.Apply(&b
, kStartTime
+ TimeDelta::FromSeconds(30)));
161 EXPECT_FALSE(b
.has_tokens());
162 EXPECT_FALSE(lim
.Apply(&b
, k1MinuteAfterStart
));
164 b
.Reset(k2PerMinute
, kStartTime
);
165 EXPECT_TRUE(lim
.Apply(&b
, k1MinuteAfterStart
- TimeDelta::FromSeconds(1)));
166 EXPECT_TRUE(lim
.Apply(&b
, k1MinuteAfterStart
));
167 EXPECT_TRUE(lim
.Apply(&b
, k1MinuteAfterStart
+ TimeDelta::FromSeconds(1)));
168 EXPECT_TRUE(lim
.Apply(&b
, k1MinuteAfterStart
+ TimeDelta::FromSeconds(2)));
169 EXPECT_FALSE(lim
.Apply(&b
, k1MinuteAfterStart
+ TimeDelta::FromSeconds(3)));
172 TEST_F(QuotaServiceTest
, NoHeuristic
) {
173 scoped_refptr
<MockFunction
> f(new MockFunction("foo"));
174 base::ListValue args
;
175 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &args
, kStartTime
));
178 TEST_F(QuotaServiceTest
, FrozenHeuristic
) {
179 scoped_refptr
<MockFunction
> f(new FrozenMockFunction("foo"));
180 base::ListValue args
;
181 args
.Append(new base::FundamentalValue(1));
182 EXPECT_NE("", service_
->Assess(extension_a_
, f
.get(), &args
, kStartTime
));
185 TEST_F(QuotaServiceTest
, SingleHeuristic
) {
186 scoped_refptr
<MockFunction
> f(new TimedLimitMockFunction("foo"));
187 base::ListValue args
;
188 args
.Append(new base::FundamentalValue(1));
189 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &args
, kStartTime
));
191 service_
->Assess(extension_a_
,
194 kStartTime
+ TimeDelta::FromSeconds(10)));
196 service_
->Assess(extension_a_
,
199 kStartTime
+ TimeDelta::FromSeconds(15)));
201 base::ListValue args2
;
202 args2
.Append(new base::FundamentalValue(1));
203 args2
.Append(new base::FundamentalValue(2));
204 EXPECT_EQ("", service_
->Assess(extension_b_
, f
.get(), &args2
, kStartTime
));
206 service_
->Assess(extension_b_
,
209 kStartTime
+ TimeDelta::FromSeconds(10)));
211 TimeDelta peace
= TimeDelta::FromMinutes(30);
213 service_
->Assess(extension_b_
, f
.get(), &args
, kStartTime
+ peace
));
215 service_
->Assess(extension_b_
,
218 kStartTime
+ peace
+ TimeDelta::FromSeconds(10)));
220 service_
->Assess(extension_b_
,
223 kStartTime
+ peace
+ TimeDelta::FromSeconds(15)));
225 // Test that items are independent.
226 base::ListValue args3
;
227 args3
.Append(new base::FundamentalValue(3));
228 EXPECT_EQ("", service_
->Assess(extension_c_
, f
.get(), &args
, kStartTime
));
230 service_
->Assess(extension_c_
,
233 kStartTime
+ TimeDelta::FromSeconds(10)));
235 service_
->Assess(extension_c_
,
238 kStartTime
+ TimeDelta::FromSeconds(15)));
240 service_
->Assess(extension_c_
,
243 kStartTime
+ TimeDelta::FromSeconds(20)));
245 service_
->Assess(extension_c_
,
248 kStartTime
+ TimeDelta::FromSeconds(25)));
250 service_
->Assess(extension_c_
,
253 kStartTime
+ TimeDelta::FromSeconds(30)));
256 TEST_F(QuotaServiceTest
, MultipleFunctionsDontInterfere
) {
257 scoped_refptr
<MockFunction
> f(new TimedLimitMockFunction("foo"));
258 scoped_refptr
<MockFunction
> g(new TimedLimitMockFunction("bar"));
260 base::ListValue args_f
;
261 base::ListValue args_g
;
262 args_f
.Append(new base::FundamentalValue(1));
263 args_g
.Append(new base::FundamentalValue(2));
265 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &args_f
, kStartTime
));
266 EXPECT_EQ("", service_
->Assess(extension_a_
, g
.get(), &args_g
, kStartTime
));
268 service_
->Assess(extension_a_
,
271 kStartTime
+ TimeDelta::FromSeconds(10)));
273 service_
->Assess(extension_a_
,
276 kStartTime
+ TimeDelta::FromSeconds(10)));
278 service_
->Assess(extension_a_
,
281 kStartTime
+ TimeDelta::FromSeconds(15)));
283 service_
->Assess(extension_a_
,
286 kStartTime
+ TimeDelta::FromSeconds(15)));
289 TEST_F(QuotaServiceTest
, ViolatorsWillBeForgiven
) {
290 scoped_refptr
<MockFunction
> f(new TimedLimitMockFunction("foo"));
292 arg
.Append(new base::FundamentalValue(1));
293 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &arg
, kStartTime
));
295 service_
->Assess(extension_a_
,
298 kStartTime
+ TimeDelta::FromSeconds(10)));
300 service_
->Assess(extension_a_
,
303 kStartTime
+ TimeDelta::FromSeconds(15)));
305 // Waiting a while will give the extension access to the function again.
306 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &arg
,
307 kStartTime
+ TimeDelta::FromDays(1)));
309 // And lose it again soon after.
310 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &arg
,
311 kStartTime
+ TimeDelta::FromDays(1) +
312 TimeDelta::FromSeconds(10)));
313 EXPECT_NE("", service_
->Assess(extension_a_
, f
.get(), &arg
,
314 kStartTime
+ TimeDelta::FromDays(1) +
315 TimeDelta::FromSeconds(15)));
317 // Going further over quota should continue to fail within this time period,
318 // but still all restored later.
319 EXPECT_NE("", service_
->Assess(extension_a_
, f
.get(), &arg
,
320 kStartTime
+ TimeDelta::FromDays(1) +
321 TimeDelta::FromSeconds(20)));
322 EXPECT_NE("", service_
->Assess(extension_a_
, f
.get(), &arg
,
323 kStartTime
+ TimeDelta::FromDays(1) +
324 TimeDelta::FromSeconds(25)));
327 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &arg
,
328 kStartTime
+ TimeDelta::FromDays(2)));
331 } // namespace extensions