blob: 73a50db4a3398e52759e3b9bff40eff847e68831 [file] [log] [blame]
Darin Petkovf1e85e42010-06-10 15:59:53 -07001// Copyright (c) 2010 The Chromium OS 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.
4
5#include <sys/file.h>
6
Darin Petkovf1e85e42010-06-10 15:59:53 -07007#include <base/file_util.h>
8#include <base/logging.h>
Chris Masonee10b5482013-02-14 12:15:35 -08009#include <base/posix/eintr_wrapper.h>
Darin Petkovf1e85e42010-06-10 15:59:53 -070010#include <base/string_util.h>
11#include <gmock/gmock.h>
12#include <gtest/gtest.h>
13
14#include "counter.h"
Ken Mixterccd84c02010-08-16 19:57:13 -070015#include "counter_mock.h" // For TaggedCounterMock.
Ken Mixter4c5daa42010-08-26 18:35:06 -070016#include "metrics_library_mock.h"
Darin Petkovf1e85e42010-06-10 15:59:53 -070017
18using ::testing::_;
19using ::testing::MockFunction;
20using ::testing::StrictMock;
21
22namespace chromeos_metrics {
23
24static const char kTestRecordFile[] = "record-file";
25static const char kDoesNotExistFile[] = "/does/not/exist";
26
27class RecordTest : public testing::Test {
28 protected:
29 virtual void SetUp() {
30 EXPECT_EQ(0, record_.tag());
31 EXPECT_EQ(0, record_.count());
32 }
33
34 // The record under test.
35 TaggedCounter::Record record_;
36};
37
38class TaggedCounterTest : public testing::Test {
39 protected:
40 virtual void SetUp() {
Ken Mixter4c5daa42010-08-26 18:35:06 -070041 EXPECT_TRUE(counter_.filename_.empty());
Darin Petkovf1e85e42010-06-10 15:59:53 -070042 EXPECT_TRUE(NULL == counter_.reporter_);
43 EXPECT_EQ(NULL, counter_.reporter_handle_);
44 EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_);
45
46 counter_.Init(kTestRecordFile, &Reporter, this);
47 EXPECT_TRUE(AssertNoOrEmptyRecordFile());
48 EXPECT_EQ(kTestRecordFile, counter_.filename_);
49 EXPECT_TRUE(Reporter == counter_.reporter_);
50 EXPECT_EQ(this, counter_.reporter_handle_);
51 EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_);
52
53 // The test fixture object will be used by the log message handler.
54 test_ = this;
55 logging::SetLogMessageHandler(HandleLogMessages);
56 }
57
58 virtual void TearDown() {
59 logging::SetLogMessageHandler(NULL);
60 test_ = NULL;
61 file_util::Delete(FilePath(kTestRecordFile), false);
62 }
63
64 // Asserts that the record file contains the specified contents.
65 testing::AssertionResult AssertRecord(const char* expr_tag,
66 const char* expr_count,
Darin Petkovcd8c3172010-06-24 10:13:54 -070067 int32 expected_tag,
68 int32 expected_count) {
Darin Petkovf1e85e42010-06-10 15:59:53 -070069 int fd = HANDLE_EINTR(open(kTestRecordFile, O_RDONLY));
70 if (fd < 0) {
71 testing::Message msg;
72 msg << "Unable to open " << kTestRecordFile;
73 return testing::AssertionFailure(msg);
74 }
75
76 TaggedCounter::Record record;
77 if (!file_util::ReadFromFD(fd, reinterpret_cast<char*>(&record),
78 sizeof(record))) {
79 testing::Message msg;
80 msg << "Unable to read " << sizeof(record) << " bytes from "
81 << kTestRecordFile;
82 HANDLE_EINTR(close(fd));
83 return testing::AssertionFailure(msg);
84 }
85
86 if (record.tag() != expected_tag || record.count() != expected_count) {
87 testing::Message msg;
88 msg << "actual record (" << record.tag() << ", " << record.count()
89 << ") expected (" << expected_tag << ", " << expected_count << ")";
90 HANDLE_EINTR(close(fd));
91 return testing::AssertionFailure(msg);
92 }
93
94 HANDLE_EINTR(close(fd));
95 return testing::AssertionSuccess();
96 }
97
98 // Returns true if the persistent record file does not exist or is
99 // empty, false otherwise.
100 bool AssertNoOrEmptyRecordFile() {
101 FilePath record_file(counter_.filename_);
102 int64 record_file_size;
103 return !file_util::PathExists(record_file) ||
104 (file_util::GetFileSize(record_file, &record_file_size) &&
105 record_file_size == 0);
106 }
107
108 // Adds a reporter call expectation that the specified tag/count
109 // callback will be generated.
Darin Petkovcd8c3172010-06-24 10:13:54 -0700110 void ExpectReporterCall(int32 tag, int32 count) {
Darin Petkovf1e85e42010-06-10 15:59:53 -0700111 EXPECT_CALL(reporter_, Call(_, tag, count))
112 .Times(1)
113 .RetiresOnSaturation();
114 }
115
116 // The reporter callback forwards the call to the reporter mock so
117 // that we can set call expectations.
Darin Petkovcd8c3172010-06-24 10:13:54 -0700118 static void Reporter(void* handle, int32 tag, int32 count) {
Darin Petkovf1e85e42010-06-10 15:59:53 -0700119 TaggedCounterTest* test = static_cast<TaggedCounterTest*>(handle);
120 ASSERT_FALSE(NULL == test);
121 test->reporter_.Call(handle, tag, count);
122 }
123
124 // Collects log messages in the |log_| member string so that they
125 // can be analyzed for errors and expected behavior.
Chris Masone817016a2011-05-12 14:14:48 -0700126 static bool HandleLogMessages(int severity,
127 const char* file,
128 int line,
129 size_t message_start,
130 const std::string& str) {
Darin Petkovf1e85e42010-06-10 15:59:53 -0700131 test_->log_.append(str);
132 test_->log_.append("\n");
133
134 // Returning true would mute the log.
135 return false;
136 }
137
138 // Returns true if the counter log contains |pattern|, false otherwise.
Darin Petkovcd8c3172010-06-24 10:13:54 -0700139 bool LogContains(const std::string& pattern) const {
Darin Petkovf1e85e42010-06-10 15:59:53 -0700140 return log_.find(pattern) != std::string::npos;
141 }
142
143 // The TaggedCounter object under test.
144 TaggedCounter counter_;
145
146 // The accumulated counter log.
147 std::string log_;
148
149 // Reporter mock to set callback expectations on.
Darin Petkovcd8c3172010-06-24 10:13:54 -0700150 StrictMock<MockFunction<void(void* handle,
151 int32 tag, int32 count)> > reporter_;
Darin Petkovf1e85e42010-06-10 15:59:53 -0700152
153 // Pointer to the current test fixture.
154 static TaggedCounterTest* test_;
155};
156
157// static
158TaggedCounterTest* TaggedCounterTest::test_ = NULL;
159
160TEST_F(RecordTest, Init) {
161 record_.Init(/* tag */ 5, /* count */ -1);
162 EXPECT_EQ(5, record_.tag());
163 EXPECT_EQ(0, record_.count());
164
165 record_.Init(/* tag */ -2, /* count */ 10);
166 EXPECT_EQ(-2, record_.tag());
167 EXPECT_EQ(10, record_.count());
168}
169
170TEST_F(RecordTest, Add) {
171 record_.Add(/* count */ -1);
172 EXPECT_EQ(0, record_.count());
173
174 record_.Add(/* count */ 5);
175 EXPECT_EQ(5, record_.count());
176
177 record_.Add(/* count */ 10);
178 EXPECT_EQ(15, record_.count());
179
180 record_.Add(/* count */ -2);
181 EXPECT_EQ(15, record_.count());
182
Darin Petkovcd8c3172010-06-24 10:13:54 -0700183 record_.Add(/* count */ kint32max);
184 EXPECT_EQ(kint32max, record_.count());
Darin Petkovf1e85e42010-06-10 15:59:53 -0700185
186 record_.Add(/* count */ 1);
Darin Petkovcd8c3172010-06-24 10:13:54 -0700187 EXPECT_EQ(kint32max, record_.count());
Darin Petkovf1e85e42010-06-10 15:59:53 -0700188}
189
190TEST_F(TaggedCounterTest, BadFileLocation) {
191 // Checks that the counter doesn't die badly if the file can't be
192 // created.
193 counter_.Init(kDoesNotExistFile,
194 /* reporter */ NULL, /* reporter_handle */ NULL);
195 counter_.Update(/* tag */ 10, /* count */ 20);
196 EXPECT_TRUE(LogContains("Unable to open the persistent counter file: "
197 "No such file or directory"));
198 EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_);
199 file_util::Delete(FilePath(kDoesNotExistFile), false);
200}
201
202TEST_F(TaggedCounterTest, Flush) {
203 counter_.Flush();
204 EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_);
205
206 counter_.Update(/* tag */ 40, /* count */ 60);
207 ExpectReporterCall(/* tag */ 40, /* count */ 60);
208 counter_.Flush();
209 EXPECT_TRUE(AssertNoOrEmptyRecordFile());
210 EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_);
211
212 counter_.Update(/* tag */ 41, /* count */ 70);
213 counter_.record_.Init(/* tag */ 0, /* count */ 0);
214 counter_.record_state_ = TaggedCounter::kRecordInvalid;
215 ExpectReporterCall(/* tag */ 41, /* count */ 70);
216 counter_.Flush();
217 EXPECT_TRUE(AssertNoOrEmptyRecordFile());
218 EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_);
219}
220
221TEST_F(TaggedCounterTest, InitFromFile) {
222 counter_.Update(/* tag */ 30, /* count */ 50);
223 EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 30, /* seconds */ 50);
224 EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
225
226 counter_.Init(kTestRecordFile, &Reporter, this);
227 counter_.Update(/* tag */ 30, /* count */ 40);
228 EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 30, /* seconds */ 90);
229 EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
230
231 counter_.Init(kTestRecordFile, &Reporter, this);
232 ExpectReporterCall(/* tag */ 30, /* count */ 90);
233 counter_.Update(/* tag */ 31, /* count */ 60);
234 EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 31, /* seconds */ 60);
235 EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
236
237 ExpectReporterCall(/* tag */ 31, /* count */ 60);
238 counter_.Init(kTestRecordFile, &Reporter, this);
239 counter_.Update(/* tag */ 32, /* count */ 0);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700240 EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 32, /* seconds */ 0);
241 EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700242}
243
244TEST_F(TaggedCounterTest, Update) {
245 counter_.Update(/* tag */ 20, /* count */ 30);
246 EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 20, /* seconds */ 30);
247 EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
248
249 counter_.Update(/* tag */ 20, /* count */ 40);
250 EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 20, /* seconds */ 70);
251 EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
252
253 ExpectReporterCall(/* tag */ 20, /* count */ 70);
254 counter_.Update(/* tag */ 21, /* count */ 15);
255 EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 21, /* seconds */ 15);
256 EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
257
258 ExpectReporterCall(/* tag */ 21, /* count */ 15);
259 counter_.Update(/* tag */ 22, /* count */ 0);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700260 EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 22, /* seconds */ 0);
261 EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700262}
263
Ken Mixter4c5daa42010-08-26 18:35:06 -0700264static const char kTestFilename[] = "test_filename";
265static const char kTestHistogram[] = "test_histogram";
266const int kHistogramMin = 15;
267const int kHistogramMax = 1024;
268const int kHistogramBuckets = 23;
269
270class TaggedCounterReporterTest : public testing::Test {
271 protected:
272 virtual void SetUp() {
273 tagged_counter_ = new StrictMock<TaggedCounterMock>();
274 reporter_.tagged_counter_.reset(tagged_counter_);
275 metrics_lib_.reset(new StrictMock<MetricsLibraryMock>);
276 reporter_.SetMetricsLibraryInterface(metrics_lib_.get());
277 ASSERT_TRUE(metrics_lib_.get() == reporter_.metrics_lib_);
278 }
279 virtual void TearDown() {
280 reporter_.SetMetricsLibraryInterface(NULL);
281 }
282
283 void DoInit();
284 StrictMock<TaggedCounterMock>* tagged_counter_;
285 TaggedCounterReporter reporter_;
286 scoped_ptr<MetricsLibraryMock> metrics_lib_;
287};
288
289void TaggedCounterReporterTest::DoInit() {
290 EXPECT_CALL(*tagged_counter_,
291 Init(kTestFilename,
292 TaggedCounterReporter::Report,
293 &reporter_))
294 .Times(1)
295 .RetiresOnSaturation();
296 reporter_.Init(kTestFilename,
297 kTestHistogram,
298 kHistogramMin,
299 kHistogramMax,
300 kHistogramBuckets);
301 EXPECT_EQ(kTestHistogram, reporter_.histogram_name_);
302 EXPECT_EQ(kHistogramBuckets, reporter_.buckets_);
303 EXPECT_EQ(kHistogramMax, reporter_.max_);
304 EXPECT_EQ(kHistogramMin, reporter_.min_);
305}
306
307TEST_F(TaggedCounterReporterTest, Init) {
308 DoInit();
309}
310
311TEST_F(TaggedCounterReporterTest, Update) {
312 DoInit();
313 EXPECT_CALL(*tagged_counter_, Update(1, 2))
314 .Times(1)
315 .RetiresOnSaturation();
316 reporter_.Update(1, 2);
317}
318
319TEST_F(TaggedCounterReporterTest, Flush) {
320 DoInit();
321 EXPECT_CALL(*tagged_counter_, Flush())
322 .Times(1)
323 .RetiresOnSaturation();
324 reporter_.Flush();
325}
326
327TEST_F(TaggedCounterReporterTest, Report) {
328 DoInit();
329 EXPECT_CALL(*metrics_lib_, SendToUMA(kTestHistogram,
330 301,
331 kHistogramMin,
332 kHistogramMax,
333 kHistogramBuckets))
334 .Times(1)
335 .RetiresOnSaturation();
336 reporter_.Report(&reporter_, 127, 301);
337}
338
Ken Mixterccd84c02010-08-16 19:57:13 -0700339class FrequencyCounterTest : public testing::Test {
340 protected:
341 virtual void SetUp() {
Ken Mixter4c5daa42010-08-26 18:35:06 -0700342 tagged_counter_ = NULL;
Ken Mixterccd84c02010-08-16 19:57:13 -0700343 }
344
345 void CheckInit(int32 cycle_duration);
346 void CheckCycleNumber(int32 cycle_duration);
347
348 FrequencyCounter frequency_counter_;
349 StrictMock<TaggedCounterMock>* tagged_counter_;
350
351 TaggedCounter::Reporter reporter_;
352};
353
354void FrequencyCounterTest::CheckInit(int32 cycle_duration) {
Ken Mixter4c5daa42010-08-26 18:35:06 -0700355 tagged_counter_ = new StrictMock<TaggedCounterMock>;
356 frequency_counter_.Init(tagged_counter_, cycle_duration);
Ken Mixterccd84c02010-08-16 19:57:13 -0700357 EXPECT_EQ(cycle_duration, frequency_counter_.cycle_duration_);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700358 EXPECT_EQ(tagged_counter_, frequency_counter_.tagged_counter_.get());
Ken Mixterccd84c02010-08-16 19:57:13 -0700359}
360
361TEST_F(FrequencyCounterTest, Init) {
362 CheckInit(100);
363}
364
365void FrequencyCounterTest::CheckCycleNumber(int32 cycle_duration) {
366 CheckInit(cycle_duration);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700367 EXPECT_EQ(150, frequency_counter_.GetCycleNumber(
368 cycle_duration * 150));
369 EXPECT_EQ(150, frequency_counter_.GetCycleNumber(
370 cycle_duration * 150 + cycle_duration - 1));
371 EXPECT_EQ(151, frequency_counter_.GetCycleNumber(
372 cycle_duration * 151 + 1));
Ken Mixterccd84c02010-08-16 19:57:13 -0700373 EXPECT_EQ(0, frequency_counter_.GetCycleNumber(0));
374}
375
376
377TEST_F(FrequencyCounterTest, GetCycleNumberForWeek) {
378 CheckCycleNumber(kSecondsPerWeek);
379}
380
381TEST_F(FrequencyCounterTest, GetCycleNumberForDay) {
382 CheckCycleNumber(kSecondsPerDay);
383}
384
385TEST_F(FrequencyCounterTest, UpdateInternal) {
386 CheckInit(kSecondsPerWeek);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700387 EXPECT_CALL(*tagged_counter_, Update(150, 2))
388 .Times(1)
389 .RetiresOnSaturation();
Ken Mixterccd84c02010-08-16 19:57:13 -0700390 frequency_counter_.UpdateInternal(2, kSecondsPerWeek * 150);
391}
392
Darin Petkovf1e85e42010-06-10 15:59:53 -0700393} // namespace chromeos_metrics
394
395int main(int argc, char** argv) {
396 testing::InitGoogleTest(&argc, argv);
397 return RUN_ALL_TESTS();
398}