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 std::string
& 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 std::string
& name
)
79 : MockFunction(name
) {}
80 void GetQuotaLimitHeuristics(
81 QuotaLimitHeuristics
* heuristics
) const override
{
82 heuristics
->push_back(
83 new TimedLimit(k2PerMinute
, new Mapper(), kGenericName
));
87 ~TimedLimitMockFunction() override
{}
90 class FrozenMockFunction
: public MockFunction
{
92 explicit FrozenMockFunction(const std::string
& name
) : MockFunction(name
) {}
93 void GetQuotaLimitHeuristics(
94 QuotaLimitHeuristics
* heuristics
) const override
{
95 heuristics
->push_back(
96 new TimedLimit(kFrozenConfig
, new Mapper(), kGenericName
));
100 ~FrozenMockFunction() override
{}
104 class QuotaServiceTest
: public testing::Test
{
111 ui_thread_(BrowserThread::UI
, &loop_
) {}
112 void SetUp() override
{ service_
.reset(new QuotaService()); }
113 void TearDown() override
{
114 loop_
.RunUntilIdle();
119 std::string extension_a_
;
120 std::string extension_b_
;
121 std::string extension_c_
;
122 scoped_ptr
<QuotaService
> service_
;
123 base::MessageLoop loop_
;
124 content::TestBrowserThread ui_thread_
;
127 class QuotaLimitHeuristicTest
: public testing::Test
{
129 static void DoMoreThan2PerMinuteFor5Minutes(const TimeTicks
& start_time
,
130 QuotaLimitHeuristic
* lim
,
132 int an_unexhausted_minute
) {
133 for (int i
= 0; i
< 5; i
++) {
134 // Perform one operation in each minute.
136 EXPECT_TRUE(lim
->Apply(b
, start_time
+ TimeDelta::FromSeconds(10 + m
)));
137 EXPECT_TRUE(b
->has_tokens());
139 if (i
== an_unexhausted_minute
)
140 continue; // Don't exhaust all tokens this minute.
142 EXPECT_TRUE(lim
->Apply(b
, start_time
+ TimeDelta::FromSeconds(15 + m
)));
143 EXPECT_FALSE(b
->has_tokens());
145 // These are OK because we haven't exhausted all buckets.
146 EXPECT_TRUE(lim
->Apply(b
, start_time
+ TimeDelta::FromSeconds(20 + m
)));
147 EXPECT_FALSE(b
->has_tokens());
148 EXPECT_TRUE(lim
->Apply(b
, start_time
+ TimeDelta::FromSeconds(50 + m
)));
149 EXPECT_FALSE(b
->has_tokens());
154 TEST_F(QuotaLimitHeuristicTest
, Timed
) {
155 TimedLimit
lim(k2PerMinute
, new MockMapper(), kGenericName
);
158 b
.Reset(k2PerMinute
, kStartTime
);
159 EXPECT_TRUE(lim
.Apply(&b
, kStartTime
));
160 EXPECT_TRUE(b
.has_tokens());
161 EXPECT_TRUE(lim
.Apply(&b
, kStartTime
+ TimeDelta::FromSeconds(30)));
162 EXPECT_FALSE(b
.has_tokens());
163 EXPECT_FALSE(lim
.Apply(&b
, k1MinuteAfterStart
));
165 b
.Reset(k2PerMinute
, kStartTime
);
166 EXPECT_TRUE(lim
.Apply(&b
, k1MinuteAfterStart
- TimeDelta::FromSeconds(1)));
167 EXPECT_TRUE(lim
.Apply(&b
, k1MinuteAfterStart
));
168 EXPECT_TRUE(lim
.Apply(&b
, k1MinuteAfterStart
+ TimeDelta::FromSeconds(1)));
169 EXPECT_TRUE(lim
.Apply(&b
, k1MinuteAfterStart
+ TimeDelta::FromSeconds(2)));
170 EXPECT_FALSE(lim
.Apply(&b
, k1MinuteAfterStart
+ TimeDelta::FromSeconds(3)));
173 TEST_F(QuotaServiceTest
, NoHeuristic
) {
174 scoped_refptr
<MockFunction
> f(new MockFunction("foo"));
175 base::ListValue args
;
176 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &args
, kStartTime
));
179 TEST_F(QuotaServiceTest
, FrozenHeuristic
) {
180 scoped_refptr
<MockFunction
> f(new FrozenMockFunction("foo"));
181 base::ListValue args
;
182 args
.Append(new base::FundamentalValue(1));
183 EXPECT_NE("", service_
->Assess(extension_a_
, f
.get(), &args
, kStartTime
));
186 TEST_F(QuotaServiceTest
, SingleHeuristic
) {
187 scoped_refptr
<MockFunction
> f(new TimedLimitMockFunction("foo"));
188 base::ListValue args
;
189 args
.Append(new base::FundamentalValue(1));
190 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &args
, kStartTime
));
192 service_
->Assess(extension_a_
,
195 kStartTime
+ TimeDelta::FromSeconds(10)));
197 service_
->Assess(extension_a_
,
200 kStartTime
+ TimeDelta::FromSeconds(15)));
202 base::ListValue args2
;
203 args2
.Append(new base::FundamentalValue(1));
204 args2
.Append(new base::FundamentalValue(2));
205 EXPECT_EQ("", service_
->Assess(extension_b_
, f
.get(), &args2
, kStartTime
));
207 service_
->Assess(extension_b_
,
210 kStartTime
+ TimeDelta::FromSeconds(10)));
212 TimeDelta peace
= TimeDelta::FromMinutes(30);
214 service_
->Assess(extension_b_
, f
.get(), &args
, kStartTime
+ peace
));
216 service_
->Assess(extension_b_
,
219 kStartTime
+ peace
+ TimeDelta::FromSeconds(10)));
221 service_
->Assess(extension_b_
,
224 kStartTime
+ peace
+ TimeDelta::FromSeconds(15)));
226 // Test that items are independent.
227 base::ListValue args3
;
228 args3
.Append(new base::FundamentalValue(3));
229 EXPECT_EQ("", service_
->Assess(extension_c_
, f
.get(), &args
, kStartTime
));
231 service_
->Assess(extension_c_
,
234 kStartTime
+ TimeDelta::FromSeconds(10)));
236 service_
->Assess(extension_c_
,
239 kStartTime
+ TimeDelta::FromSeconds(15)));
241 service_
->Assess(extension_c_
,
244 kStartTime
+ TimeDelta::FromSeconds(20)));
246 service_
->Assess(extension_c_
,
249 kStartTime
+ TimeDelta::FromSeconds(25)));
251 service_
->Assess(extension_c_
,
254 kStartTime
+ TimeDelta::FromSeconds(30)));
257 TEST_F(QuotaServiceTest
, MultipleFunctionsDontInterfere
) {
258 scoped_refptr
<MockFunction
> f(new TimedLimitMockFunction("foo"));
259 scoped_refptr
<MockFunction
> g(new TimedLimitMockFunction("bar"));
261 base::ListValue args_f
;
262 base::ListValue args_g
;
263 args_f
.Append(new base::FundamentalValue(1));
264 args_g
.Append(new base::FundamentalValue(2));
266 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &args_f
, kStartTime
));
267 EXPECT_EQ("", service_
->Assess(extension_a_
, g
.get(), &args_g
, kStartTime
));
269 service_
->Assess(extension_a_
,
272 kStartTime
+ TimeDelta::FromSeconds(10)));
274 service_
->Assess(extension_a_
,
277 kStartTime
+ TimeDelta::FromSeconds(10)));
279 service_
->Assess(extension_a_
,
282 kStartTime
+ TimeDelta::FromSeconds(15)));
284 service_
->Assess(extension_a_
,
287 kStartTime
+ TimeDelta::FromSeconds(15)));
290 TEST_F(QuotaServiceTest
, ViolatorsWillBeForgiven
) {
291 scoped_refptr
<MockFunction
> f(new TimedLimitMockFunction("foo"));
293 arg
.Append(new base::FundamentalValue(1));
294 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &arg
, kStartTime
));
296 service_
->Assess(extension_a_
,
299 kStartTime
+ TimeDelta::FromSeconds(10)));
301 service_
->Assess(extension_a_
,
304 kStartTime
+ TimeDelta::FromSeconds(15)));
306 // Waiting a while will give the extension access to the function again.
307 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &arg
,
308 kStartTime
+ TimeDelta::FromDays(1)));
310 // And lose it again soon after.
311 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &arg
,
312 kStartTime
+ TimeDelta::FromDays(1) +
313 TimeDelta::FromSeconds(10)));
314 EXPECT_NE("", service_
->Assess(extension_a_
, f
.get(), &arg
,
315 kStartTime
+ TimeDelta::FromDays(1) +
316 TimeDelta::FromSeconds(15)));
318 // Going further over quota should continue to fail within this time period,
319 // but still all restored later.
320 EXPECT_NE("", service_
->Assess(extension_a_
, f
.get(), &arg
,
321 kStartTime
+ TimeDelta::FromDays(1) +
322 TimeDelta::FromSeconds(20)));
323 EXPECT_NE("", service_
->Assess(extension_a_
, f
.get(), &arg
,
324 kStartTime
+ TimeDelta::FromDays(1) +
325 TimeDelta::FromSeconds(25)));
328 EXPECT_EQ("", service_
->Assess(extension_a_
, f
.get(), &arg
,
329 kStartTime
+ TimeDelta::FromDays(2)));
332 } // namespace extensions