#include "stdafx.h"
#include
#include
#include
namespace {
class IStopWatch {
public:
virtual void Start() = 0;
virtual double SecondsFromStart() = 0;
};
class StopWatch: public IStopWatch {
private:
time_t start_;
public:
StopWatch(bool start) {
if (start) {
Start();
}
}
virtual void Start() {
time(&start_);
}
virtual double SecondsFromStart() {
time_t cur;
time(&cur);
return difftime(cur, start_);
}
};
class HitCounter {
IStopWatch* stopWatch_;
std::vector counts_;
int lastSecond_;
int curHit_;
public:
HitCounter(IStopWatch* stopWatch, int intervals): stopWatch_(
stopWatch), counts_(intervals) {
stopWatch_->Start();
lastSecond_ = 0;
curHit_ = 0;
}
void Hit() {
int curSecond = (int)stopWatch_->SecondsFromStart();
ExpireHits(curSecond);
++counts_[curSecond % counts_.size()];
++curHit_;
lastSecond_ = curSecond;
}
void ExpireHits(int curSecond) {
if (curSecond >= lastSecond_ + (int)counts_.size()) {
std::fill_n(counts_.begin(), counts_.size(), 0);
curHit_ = 0;
}
else {
for (int i = lastSecond_ + 1; i <= curSecond; ++i) {
int j = i % counts_.size();
curHit_ -= counts_[j];
counts_[j] = 0;
}
}
}
int TotalHit() {
int curSecond = (int)stopWatch_->SecondsFromStart();
ExpireHits(curSecond);
return curHit_;
}
};
class FakeStopWatch : public IStopWatch {
double toReturn_;
public:
FakeStopWatch() : toReturn_(0) {
}
void SetToReturn(double toReturn) {
toReturn_ = toReturn;
}
virtual void Start(){}
virtual double SecondsFromStart() {
return toReturn_;
}
};
TEST(HitCounter, Simple) {
FakeStopWatch stopWatch;
HitCounter counter(&stopWatch, 5);
for (int i = 0; i < 6; ++i)counter.Hit();
ASSERT_EQ(6, counter.TotalHit());
stopWatch.SetToReturn(3);
ASSERT_EQ(6, counter.TotalHit());
counter.Hit();
ASSERT_EQ(7, counter.TotalHit());
stopWatch.SetToReturn(5);
ASSERT_EQ(1, counter.TotalHit());
}
TEST(HitCounter, Increasing) {
FakeStopWatch stopWatch;
HitCounter counter(&stopWatch, 5);
for (int i = 0; i < 10; ++i) {
stopWatch.SetToReturn(i);
for (int j = 0; j <= i; ++j) {
counter.Hit();
}
if (i < 5) {
ASSERT_EQ((i + 1) * (i + 2) / 2, counter.TotalHit());
}
else {
ASSERT_EQ(((i + 1) * (i + 2) - (i - 4)*(i - 3)) / 2, counter
.TotalHit());
}
}
}
}