| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/message_loop/message_loop.h" | 5 #include "base/message_loop/message_loop.h" |
| 6 #include "base/process/process.h" | 6 #include "base/process/process.h" |
| 7 #include "base/stl_util.h" | 7 #include "base/stl_util.h" |
| 8 #include "base/strings/string_util.h" | 8 #include "base/strings/string_util.h" |
| 9 #include "content/public/test/test_browser_thread.h" | 9 #include "content/public/test/test_browser_thread.h" |
| 10 #include "extensions/browser/extension_function.h" | 10 #include "extensions/browser/extension_function.h" |
| 11 #include "extensions/browser/quota_service.h" | 11 #include "extensions/browser/quota_service.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 | 13 |
| 14 using base::TimeDelta; | 14 using base::TimeDelta; |
| 15 using base::TimeTicks; | 15 using base::TimeTicks; |
| 16 using content::BrowserThread; | 16 using content::BrowserThread; |
| 17 | 17 |
| 18 namespace extensions { | 18 namespace extensions { |
| 19 | 19 |
| 20 typedef QuotaLimitHeuristic::Bucket Bucket; | 20 typedef QuotaLimitHeuristic::Bucket Bucket; |
| 21 typedef QuotaLimitHeuristic::Config Config; | 21 typedef QuotaLimitHeuristic::Config Config; |
| 22 typedef QuotaLimitHeuristic::BucketList BucketList; | 22 typedef QuotaLimitHeuristic::BucketList BucketList; |
| 23 typedef QuotaService::TimedLimit TimedLimit; | 23 typedef QuotaService::TimedLimit TimedLimit; |
| 24 typedef QuotaService::SustainedLimit SustainedLimit; | |
| 25 | 24 |
| 26 namespace { | 25 namespace { |
| 27 | 26 |
| 28 const char kGenericName[] = "name"; | 27 const char kGenericName[] = "name"; |
| 29 const Config kFrozenConfig = {0, TimeDelta::FromDays(0)}; | 28 const Config kFrozenConfig = {0, TimeDelta::FromDays(0)}; |
| 30 const Config k2PerMinute = {2, TimeDelta::FromMinutes(1)}; | 29 const Config k2PerMinute = {2, TimeDelta::FromMinutes(1)}; |
| 31 const Config k20PerHour = {20, TimeDelta::FromHours(1)}; | 30 const Config k20PerHour = {20, TimeDelta::FromHours(1)}; |
| 32 const TimeTicks kStartTime = TimeTicks(); | 31 const TimeTicks kStartTime = TimeTicks(); |
| 33 const TimeTicks k1MinuteAfterStart = kStartTime + TimeDelta::FromMinutes(1); | 32 const TimeTicks k1MinuteAfterStart = kStartTime + TimeDelta::FromMinutes(1); |
| 34 | 33 |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 81 void GetQuotaLimitHeuristics( | 80 void GetQuotaLimitHeuristics( |
| 82 QuotaLimitHeuristics* heuristics) const override { | 81 QuotaLimitHeuristics* heuristics) const override { |
| 83 heuristics->push_back( | 82 heuristics->push_back( |
| 84 new TimedLimit(k2PerMinute, new Mapper(), kGenericName)); | 83 new TimedLimit(k2PerMinute, new Mapper(), kGenericName)); |
| 85 } | 84 } |
| 86 | 85 |
| 87 private: | 86 private: |
| 88 ~TimedLimitMockFunction() override {} | 87 ~TimedLimitMockFunction() override {} |
| 89 }; | 88 }; |
| 90 | 89 |
| 91 class ChainedLimitsMockFunction : public MockFunction { | |
| 92 public: | |
| 93 explicit ChainedLimitsMockFunction(const std::string& name) | |
| 94 : MockFunction(name) {} | |
| 95 void GetQuotaLimitHeuristics( | |
| 96 QuotaLimitHeuristics* heuristics) const override { | |
| 97 // No more than 2 per minute sustained over 5 minutes. | |
| 98 heuristics->push_back(new SustainedLimit( | |
| 99 TimeDelta::FromMinutes(5), k2PerMinute, new Mapper(), kGenericName)); | |
| 100 // No more than 20 per hour. | |
| 101 heuristics->push_back( | |
| 102 new TimedLimit(k20PerHour, new Mapper(), kGenericName)); | |
| 103 } | |
| 104 | |
| 105 private: | |
| 106 ~ChainedLimitsMockFunction() override {} | |
| 107 }; | |
| 108 | |
| 109 class FrozenMockFunction : public MockFunction { | 90 class FrozenMockFunction : public MockFunction { |
| 110 public: | 91 public: |
| 111 explicit FrozenMockFunction(const std::string& name) : MockFunction(name) {} | 92 explicit FrozenMockFunction(const std::string& name) : MockFunction(name) {} |
| 112 void GetQuotaLimitHeuristics( | 93 void GetQuotaLimitHeuristics( |
| 113 QuotaLimitHeuristics* heuristics) const override { | 94 QuotaLimitHeuristics* heuristics) const override { |
| 114 heuristics->push_back( | 95 heuristics->push_back( |
| 115 new TimedLimit(kFrozenConfig, new Mapper(), kGenericName)); | 96 new TimedLimit(kFrozenConfig, new Mapper(), kGenericName)); |
| 116 } | 97 } |
| 117 | 98 |
| 118 private: | 99 private: |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 182 EXPECT_FALSE(lim.Apply(&b, k1MinuteAfterStart)); | 163 EXPECT_FALSE(lim.Apply(&b, k1MinuteAfterStart)); |
| 183 | 164 |
| 184 b.Reset(k2PerMinute, kStartTime); | 165 b.Reset(k2PerMinute, kStartTime); |
| 185 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart - TimeDelta::FromSeconds(1))); | 166 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart - TimeDelta::FromSeconds(1))); |
| 186 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart)); | 167 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart)); |
| 187 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart + TimeDelta::FromSeconds(1))); | 168 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart + TimeDelta::FromSeconds(1))); |
| 188 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart + TimeDelta::FromSeconds(2))); | 169 EXPECT_TRUE(lim.Apply(&b, k1MinuteAfterStart + TimeDelta::FromSeconds(2))); |
| 189 EXPECT_FALSE(lim.Apply(&b, k1MinuteAfterStart + TimeDelta::FromSeconds(3))); | 170 EXPECT_FALSE(lim.Apply(&b, k1MinuteAfterStart + TimeDelta::FromSeconds(3))); |
| 190 } | 171 } |
| 191 | 172 |
| 192 TEST_F(QuotaLimitHeuristicTest, Sustained) { | |
| 193 SustainedLimit lim( | |
| 194 TimeDelta::FromMinutes(5), k2PerMinute, new MockMapper(), kGenericName); | |
| 195 Bucket bucket; | |
| 196 | |
| 197 bucket.Reset(k2PerMinute, kStartTime); | |
| 198 DoMoreThan2PerMinuteFor5Minutes(kStartTime, &lim, &bucket, -1); | |
| 199 // This straw breaks the camel's back. | |
| 200 EXPECT_FALSE(lim.Apply(&bucket, kStartTime + TimeDelta::FromMinutes(6))); | |
| 201 | |
| 202 // The heuristic resets itself on a safe request. | |
| 203 EXPECT_TRUE(lim.Apply(&bucket, kStartTime + TimeDelta::FromDays(1))); | |
| 204 | |
| 205 // Do the same as above except don't exhaust final bucket. | |
| 206 bucket.Reset(k2PerMinute, kStartTime); | |
| 207 DoMoreThan2PerMinuteFor5Minutes(kStartTime, &lim, &bucket, -1); | |
| 208 EXPECT_TRUE(lim.Apply(&bucket, kStartTime + TimeDelta::FromMinutes(7))); | |
| 209 | |
| 210 // Do the same as above except don't exhaust the 3rd (w.l.o.g) bucket. | |
| 211 bucket.Reset(k2PerMinute, kStartTime); | |
| 212 DoMoreThan2PerMinuteFor5Minutes(kStartTime, &lim, &bucket, 3); | |
| 213 // If the 3rd bucket were exhausted, this would fail (see first test). | |
| 214 EXPECT_TRUE(lim.Apply(&bucket, kStartTime + TimeDelta::FromMinutes(6))); | |
| 215 } | |
| 216 | |
| 217 TEST_F(QuotaServiceTest, NoHeuristic) { | 173 TEST_F(QuotaServiceTest, NoHeuristic) { |
| 218 scoped_refptr<MockFunction> f(new MockFunction("foo")); | 174 scoped_refptr<MockFunction> f(new MockFunction("foo")); |
| 219 base::ListValue args; | 175 base::ListValue args; |
| 220 EXPECT_EQ("", service_->Assess(extension_a_, f.get(), &args, kStartTime)); | 176 EXPECT_EQ("", service_->Assess(extension_a_, f.get(), &args, kStartTime)); |
| 221 } | 177 } |
| 222 | 178 |
| 223 TEST_F(QuotaServiceTest, FrozenHeuristic) { | 179 TEST_F(QuotaServiceTest, FrozenHeuristic) { |
| 224 scoped_refptr<MockFunction> f(new FrozenMockFunction("foo")); | 180 scoped_refptr<MockFunction> f(new FrozenMockFunction("foo")); |
| 225 base::ListValue args; | 181 base::ListValue args; |
| 226 args.Append(new base::FundamentalValue(1)); | 182 args.Append(new base::FundamentalValue(1)); |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 291 f.get(), | 247 f.get(), |
| 292 &args, | 248 &args, |
| 293 kStartTime + TimeDelta::FromSeconds(25))); | 249 kStartTime + TimeDelta::FromSeconds(25))); |
| 294 EXPECT_NE("", | 250 EXPECT_NE("", |
| 295 service_->Assess(extension_c_, | 251 service_->Assess(extension_c_, |
| 296 f.get(), | 252 f.get(), |
| 297 &args3, | 253 &args3, |
| 298 kStartTime + TimeDelta::FromSeconds(30))); | 254 kStartTime + TimeDelta::FromSeconds(30))); |
| 299 } | 255 } |
| 300 | 256 |
| 301 TEST_F(QuotaServiceTest, ChainedHeuristics) { | |
| 302 scoped_refptr<MockFunction> f(new ChainedLimitsMockFunction("foo")); | |
| 303 base::ListValue args; | |
| 304 args.Append(new base::FundamentalValue(1)); | |
| 305 | |
| 306 // First, test that the low limit can be avoided but the higher one is hit. | |
| 307 // One event per minute for 20 minutes comes in under the sustained limit, | |
| 308 // but is equal to the timed limit. | |
| 309 for (int i = 0; i < 20; i++) { | |
| 310 EXPECT_EQ( | |
| 311 "", | |
| 312 service_->Assess(extension_a_, | |
| 313 f.get(), | |
| 314 &args, | |
| 315 kStartTime + TimeDelta::FromSeconds(10 + i * 60))); | |
| 316 } | |
| 317 | |
| 318 // This will bring us to 21 events in an hour, which is a violation. | |
| 319 EXPECT_NE("", | |
| 320 service_->Assess(extension_a_, | |
| 321 f.get(), | |
| 322 &args, | |
| 323 kStartTime + TimeDelta::FromMinutes(30))); | |
| 324 | |
| 325 // Now, check that we can still hit the lower limit. | |
| 326 for (int i = 0; i < 5; i++) { | |
| 327 EXPECT_EQ( | |
| 328 "", | |
| 329 service_->Assess(extension_b_, | |
| 330 f.get(), | |
| 331 &args, | |
| 332 kStartTime + TimeDelta::FromSeconds(10 + i * 60))); | |
| 333 EXPECT_EQ( | |
| 334 "", | |
| 335 service_->Assess(extension_b_, | |
| 336 f.get(), | |
| 337 &args, | |
| 338 kStartTime + TimeDelta::FromSeconds(15 + i * 60))); | |
| 339 EXPECT_EQ( | |
| 340 "", | |
| 341 service_->Assess(extension_b_, | |
| 342 f.get(), | |
| 343 &args, | |
| 344 kStartTime + TimeDelta::FromSeconds(20 + i * 60))); | |
| 345 } | |
| 346 | |
| 347 EXPECT_NE("", | |
| 348 service_->Assess(extension_b_, | |
| 349 f.get(), | |
| 350 &args, | |
| 351 kStartTime + TimeDelta::FromMinutes(6))); | |
| 352 } | |
| 353 | |
| 354 TEST_F(QuotaServiceTest, MultipleFunctionsDontInterfere) { | 257 TEST_F(QuotaServiceTest, MultipleFunctionsDontInterfere) { |
| 355 scoped_refptr<MockFunction> f(new TimedLimitMockFunction("foo")); | 258 scoped_refptr<MockFunction> f(new TimedLimitMockFunction("foo")); |
| 356 scoped_refptr<MockFunction> g(new TimedLimitMockFunction("bar")); | 259 scoped_refptr<MockFunction> g(new TimedLimitMockFunction("bar")); |
| 357 | 260 |
| 358 base::ListValue args_f; | 261 base::ListValue args_f; |
| 359 base::ListValue args_g; | 262 base::ListValue args_g; |
| 360 args_f.Append(new base::FundamentalValue(1)); | 263 args_f.Append(new base::FundamentalValue(1)); |
| 361 args_g.Append(new base::FundamentalValue(2)); | 264 args_g.Append(new base::FundamentalValue(2)); |
| 362 | 265 |
| 363 EXPECT_EQ("", service_->Assess(extension_a_, f.get(), &args_f, kStartTime)); | 266 EXPECT_EQ("", service_->Assess(extension_a_, f.get(), &args_f, kStartTime)); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 407 "", | 310 "", |
| 408 service_->Assess( | 311 service_->Assess( |
| 409 extension_a_, f.get(), &arg, kStartTime + TimeDelta::FromDays(1))); | 312 extension_a_, f.get(), &arg, kStartTime + TimeDelta::FromDays(1))); |
| 410 EXPECT_NE( | 313 EXPECT_NE( |
| 411 "", | 314 "", |
| 412 service_->Assess( | 315 service_->Assess( |
| 413 extension_a_, g.get(), &arg, kStartTime + TimeDelta::FromDays(1))); | 316 extension_a_, g.get(), &arg, kStartTime + TimeDelta::FromDays(1))); |
| 414 } | 317 } |
| 415 | 318 |
| 416 } // namespace extensions | 319 } // namespace extensions |
| OLD | NEW |