| Index: third_party/cacheinvalidation/src/google/cacheinvalidation/impl/throttle_test.cc
|
| diff --git a/third_party/cacheinvalidation/src/google/cacheinvalidation/impl/throttle_test.cc b/third_party/cacheinvalidation/src/google/cacheinvalidation/impl/throttle_test.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2df0de00378761d819c8ed65c8225cf0bf028c1a
|
| --- /dev/null
|
| +++ b/third_party/cacheinvalidation/src/google/cacheinvalidation/impl/throttle_test.cc
|
| @@ -0,0 +1,191 @@
|
| +// Copyright 2012 Google Inc.
|
| +//
|
| +// Licensed under the Apache License, Version 2.0 (the "License");
|
| +// you may not use this file except in compliance with the License.
|
| +// You may obtain a copy of the License at
|
| +//
|
| +// http://www.apache.org/licenses/LICENSE-2.0
|
| +//
|
| +// Unless required by applicable law or agreed to in writing, software
|
| +// distributed under the License is distributed on an "AS IS" BASIS,
|
| +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +// See the License for the specific language governing permissions and
|
| +// limitations under the License.
|
| +
|
| +// Tests the throttle.
|
| +
|
| +#include "google/cacheinvalidation/deps/googletest.h"
|
| +#include "google/cacheinvalidation/impl/proto-helpers.h"
|
| +#include "google/cacheinvalidation/impl/throttle.h"
|
| +#include "google/cacheinvalidation/test/deterministic-scheduler.h"
|
| +#include "google/cacheinvalidation/test/test-logger.h"
|
| +
|
| +namespace invalidation {
|
| +
|
| +class ThrottleTest : public testing::Test {
|
| + public:
|
| + ThrottleTest() : call_count_(0) {}
|
| +
|
| + virtual ~ThrottleTest() {}
|
| +
|
| + // Increments the call count.
|
| + void IncrementCounter() {
|
| + ++call_count_;
|
| + }
|
| +
|
| + // Increments the call count and checks state to ensure that rate limits are
|
| + // being observed.
|
| + void IncrementAndCheckRateLimits() {
|
| + // Increment the call count.
|
| + ++call_count_;
|
| + // Check that we haven't been called within the last one second.
|
| + Time now = scheduler_->GetCurrentTime();
|
| + ASSERT_TRUE((now - last_call_time_) >= TimeDelta::FromSeconds(1));
|
| + // Update the last time we were called to now.
|
| + last_call_time_ = now;
|
| + // Check that enough time has passed to allow the number of calls we've
|
| + // received.
|
| + Time min_time = start_time_ + TimeDelta::FromMinutes(
|
| + (call_count_ - 1) / kMessagesPerMinute);
|
| + ASSERT_TRUE(min_time <= now);
|
| + }
|
| +
|
| + void SetUp() {
|
| + logger_.reset(new TestLogger());
|
| + scheduler_.reset(new DeterministicScheduler(logger_.get()));
|
| + start_time_ = scheduler_->GetCurrentTime();
|
| + call_count_ = 0;
|
| + last_call_time_ = Time() - TimeDelta::FromHours(1);
|
| + ProtoHelpers::InitRateLimitP(1000, kMessagesPerSecond, rate_limits_.Add());
|
| + ProtoHelpers::InitRateLimitP(60 * 1000, kMessagesPerMinute,
|
| + rate_limits_.Add());
|
| + }
|
| +
|
| + int call_count_;
|
| + Time start_time_;
|
| + Time last_call_time_;
|
| + scoped_ptr<DeterministicScheduler> scheduler_;
|
| + scoped_ptr<Logger> logger_;
|
| + RepeatedPtrField<RateLimitP> rate_limits_;
|
| +
|
| + static const int kMessagesPerSecond;
|
| + static const int kMessagesPerMinute;
|
| +};
|
| +
|
| +const int ThrottleTest::kMessagesPerSecond = 1;
|
| +const int ThrottleTest::kMessagesPerMinute = 6;
|
| +
|
| +/* Make a throttler similar to what we expect the Ticl to use and check that it
|
| + * behaves as expected when called at a number of specific times. More
|
| + * specifically:
|
| + *
|
| + * 1. Check that the first call to Fire() triggers a call immediately.
|
| + * 2. Subsequent calls within the next one second don't trigger any calls.
|
| + * 3. After one second, one (and only one) buffered call is triggered.
|
| + * 4. If we Fire() slowly, each will trigger an immediate call until we reach
|
| + * the per-minute rate limit.
|
| + * 5. However, after a minute, another call i.
|
| + */
|
| +TEST_F(ThrottleTest, ThrottlingScripted) {
|
| + scheduler_->StartScheduler();
|
| + Closure* listener =
|
| + NewPermanentCallback(this, &ThrottleTest::IncrementCounter);
|
| +
|
| + scoped_ptr<Throttle> throttle(
|
| + new Throttle(rate_limits_, scheduler_.get(), listener));
|
| +
|
| + // The first time we fire(), it should call right away.
|
| + throttle->Fire();
|
| + scheduler_->PassTime(TimeDelta());
|
| + ASSERT_EQ(1, call_count_);
|
| +
|
| + // However, if we now fire() a bunch more times within one second, there
|
| + // should be no more calls to the listener ...
|
| + TimeDelta short_interval = TimeDelta::FromMilliseconds(80);
|
| + int fire_count = 10;
|
| + ASSERT_TRUE(short_interval * fire_count < TimeDelta::FromSeconds(1));
|
| + for (int i = 0; i < fire_count; ++i) {
|
| + scheduler_->PassTime(short_interval);
|
| + throttle->Fire();
|
| + ASSERT_EQ(1, call_count_);
|
| + }
|
| +
|
| + // Time since first event is now fireCount * intervalBetweenFires, i.e., 800.
|
| +
|
| + // ... until the short throttle interval passes, at which time it should be
|
| + // called once more.
|
| + scheduler_->PassTime(
|
| + start_time_ + TimeDelta::FromSeconds(1) - scheduler_->GetCurrentTime());
|
| +
|
| + ASSERT_EQ(2, call_count_);
|
| +
|
| + // However, the prior fire() calls don't get queued up, so no more calls to
|
| + // the listener will occur unless we fire() again.
|
| + scheduler_->PassTime(TimeDelta::FromSeconds(2));
|
| + ASSERT_EQ(2, call_count_);
|
| +
|
| + // At this point, we've fired twice within a few seconds. We can fire
|
| + // (kMessagesPerMinute - 2) more times within a minute until we get
|
| + // throttled.
|
| + TimeDelta long_interval = TimeDelta::FromSeconds(3);
|
| + for (int i = 0; i < kMessagesPerMinute - 2; ++i) {
|
| + throttle->Fire();
|
| + ASSERT_EQ(3 + i, call_count_);
|
| + scheduler_->PassTime(long_interval);
|
| + ASSERT_EQ(3 + i, call_count_);
|
| + }
|
| +
|
| + // Now we've sent kMessagesPerMinute times. If we fire again, nothing should
|
| + // happen.
|
| + throttle->Fire();
|
| + scheduler_->PassTime(TimeDelta());
|
| + ASSERT_EQ(kMessagesPerMinute, call_count_);
|
| +
|
| + // Now if we fire slowly, we still shouldn't make calls, since we'd violate
|
| + // the larger rate limit interval.
|
| + int fire_attempts =
|
| + ((start_time_ + TimeDelta::FromMinutes(1) - scheduler_->GetCurrentTime())
|
| + / long_interval) - 1;
|
| + // This value should be 20.
|
| + for (int i = 0; i < fire_attempts; ++i) {
|
| + scheduler_->PassTime(long_interval);
|
| + throttle->Fire();
|
| + ASSERT_EQ(kMessagesPerMinute, call_count_);
|
| + }
|
| +
|
| + Time time_to_send_again = start_time_ + TimeDelta::FromMinutes(1);
|
| + scheduler_->PassTime(time_to_send_again - scheduler_->GetCurrentTime());
|
| +
|
| + ASSERT_EQ(kMessagesPerMinute + 1, call_count_);
|
| +}
|
| +
|
| +/* Test that if we keep calling fire() every millisecond, we never violate the
|
| + * rate limits, and the expected number of total events is allowed through.
|
| + */
|
| +TEST_F(ThrottleTest, ThrottlingStorm) {
|
| + scheduler_->StartScheduler();
|
| + Closure* listener =
|
| + NewPermanentCallback(this, &ThrottleTest::IncrementAndCheckRateLimits);
|
| +
|
| + // Throttler allowing one call per second and six per minute.
|
| + scoped_ptr<Throttle> throttle(
|
| + new Throttle(rate_limits_, scheduler_.get(), listener));
|
| +
|
| + // For five minutes, call Fire() every ten milliseconds, and make sure the
|
| + // rate limits are respected.
|
| + TimeDelta fine_interval = TimeDelta::FromMilliseconds(10);
|
| + int duration_minutes = 5;
|
| + TimeDelta duration = TimeDelta::FromMinutes(duration_minutes);
|
| + int num_iterations = duration / fine_interval;
|
| + for (int i = 0; i < num_iterations; ++i) {
|
| + throttle->Fire();
|
| + scheduler_->PassTime(fine_interval);
|
| + }
|
| +
|
| + // Expect kMessagesPerMinute to be sent per minute for duration_minutes, plus
|
| + // one extra because we end on the precise boundary at which the next message
|
| + // is allowed to be sent.
|
| + ASSERT_EQ((kMessagesPerMinute * duration_minutes) + 1, call_count_);
|
| +}
|
| +
|
| +} // namespace invalidation
|
|
|