1 // Copyright (c) 2011 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.
4
5 // Test of Histogram class
6
7 #include "base/metrics/histogram.h"
8 #include "base/scoped_ptr.h"
9 #include "base/time.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11
12 namespace base {
13 namespace {
14
15 class HistogramTest : public testing::Test {
16 };
17
18 // Check for basic syntax and use.
TEST(HistogramTest,StartupShutdownTest)19 TEST(HistogramTest, StartupShutdownTest) {
20 // Try basic construction
21 Histogram* histogram(Histogram::FactoryGet(
22 "TestHistogram", 1, 1000, 10, Histogram::kNoFlags));
23 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), histogram);
24 Histogram* histogram1(Histogram::FactoryGet(
25 "Test1Histogram", 1, 1000, 10, Histogram::kNoFlags));
26 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), histogram1);
27 EXPECT_NE(histogram, histogram1);
28
29
30 Histogram* linear_histogram(LinearHistogram::FactoryGet(
31 "TestLinearHistogram", 1, 1000, 10, Histogram::kNoFlags));
32 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), linear_histogram);
33 Histogram* linear_histogram1(LinearHistogram::FactoryGet(
34 "Test1LinearHistogram", 1, 1000, 10, Histogram::kNoFlags));
35 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), linear_histogram1);
36 EXPECT_NE(linear_histogram, linear_histogram1);
37
38 std::vector<int> custom_ranges;
39 custom_ranges.push_back(1);
40 custom_ranges.push_back(5);
41 custom_ranges.push_back(10);
42 custom_ranges.push_back(20);
43 custom_ranges.push_back(30);
44 Histogram* custom_histogram(CustomHistogram::FactoryGet(
45 "TestCustomHistogram", custom_ranges, Histogram::kNoFlags));
46 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), custom_histogram);
47 Histogram* custom_histogram1(CustomHistogram::FactoryGet(
48 "Test1CustomHistogram", custom_ranges, Histogram::kNoFlags));
49 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), custom_histogram1);
50
51 // Use standard macros (but with fixed samples)
52 HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1));
53 HISTOGRAM_COUNTS("Test3Histogram", 30);
54
55 DHISTOGRAM_TIMES("Test4Histogram", TimeDelta::FromDays(1));
56 DHISTOGRAM_COUNTS("Test5Histogram", 30);
57
58 HISTOGRAM_ENUMERATION("Test6Histogram", 129, 130);
59
60 // Try to construct samples.
61 Histogram::SampleSet sample1;
62 Histogram::SampleSet sample2;
63
64 // Use copy constructor of SampleSet
65 sample1 = sample2;
66 Histogram::SampleSet sample3(sample1);
67
68 // Finally test a statistics recorder, without really using it.
69 StatisticsRecorder recorder;
70 }
71
72 // Repeat with a recorder present to register with.
TEST(HistogramTest,RecordedStartupTest)73 TEST(HistogramTest, RecordedStartupTest) {
74 // Test a statistics recorder, by letting histograms register.
75 StatisticsRecorder recorder; // This initializes the global state.
76
77 StatisticsRecorder::Histograms histograms;
78 EXPECT_EQ(0U, histograms.size());
79 StatisticsRecorder::GetHistograms(&histograms); // Load up lists
80 EXPECT_EQ(0U, histograms.size());
81
82 // Try basic construction
83 Histogram* histogram(Histogram::FactoryGet(
84 "TestHistogram", 1, 1000, 10, Histogram::kNoFlags));
85 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), histogram);
86 histograms.clear();
87 StatisticsRecorder::GetHistograms(&histograms); // Load up lists
88 EXPECT_EQ(1U, histograms.size());
89 Histogram* histogram1(Histogram::FactoryGet(
90 "Test1Histogram", 1, 1000, 10, Histogram::kNoFlags));
91 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), histogram1);
92 histograms.clear();
93 StatisticsRecorder::GetHistograms(&histograms); // Load up lists
94 EXPECT_EQ(2U, histograms.size());
95
96 Histogram* linear_histogram(LinearHistogram::FactoryGet(
97 "TestLinearHistogram", 1, 1000, 10, Histogram::kNoFlags));
98 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), linear_histogram);
99 histograms.clear();
100 StatisticsRecorder::GetHistograms(&histograms); // Load up lists
101 EXPECT_EQ(3U, histograms.size());
102
103 Histogram* linear_histogram1(LinearHistogram::FactoryGet(
104 "Test1LinearHistogram", 1, 1000, 10, Histogram::kNoFlags));
105 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), linear_histogram1);
106 histograms.clear();
107 StatisticsRecorder::GetHistograms(&histograms); // Load up lists
108 EXPECT_EQ(4U, histograms.size());
109
110 std::vector<int> custom_ranges;
111 custom_ranges.push_back(1);
112 custom_ranges.push_back(5);
113 custom_ranges.push_back(10);
114 custom_ranges.push_back(20);
115 custom_ranges.push_back(30);
116 Histogram* custom_histogram(CustomHistogram::FactoryGet(
117 "TestCustomHistogram", custom_ranges, Histogram::kNoFlags));
118 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), custom_histogram);
119 Histogram* custom_histogram1(CustomHistogram::FactoryGet(
120 "TestCustomHistogram", custom_ranges, Histogram::kNoFlags));
121 EXPECT_NE(reinterpret_cast<Histogram*>(NULL), custom_histogram1);
122
123 histograms.clear();
124 StatisticsRecorder::GetHistograms(&histograms); // Load up lists
125 EXPECT_EQ(5U, histograms.size());
126
127 // Use standard macros (but with fixed samples)
128 HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1));
129 HISTOGRAM_COUNTS("Test3Histogram", 30);
130 histograms.clear();
131 StatisticsRecorder::GetHistograms(&histograms); // Load up lists
132 EXPECT_EQ(7U, histograms.size());
133
134 HISTOGRAM_ENUMERATION("TestEnumerationHistogram", 20, 200);
135 histograms.clear();
136 StatisticsRecorder::GetHistograms(&histograms); // Load up lists
137 EXPECT_EQ(8U, histograms.size());
138
139 DHISTOGRAM_TIMES("Test4Histogram", TimeDelta::FromDays(1));
140 DHISTOGRAM_COUNTS("Test5Histogram", 30);
141 histograms.clear();
142 StatisticsRecorder::GetHistograms(&histograms); // Load up lists
143 #ifndef NDEBUG
144 EXPECT_EQ(10U, histograms.size());
145 #else
146 EXPECT_EQ(8U, histograms.size());
147 #endif
148 }
149
TEST(HistogramTest,RangeTest)150 TEST(HistogramTest, RangeTest) {
151 StatisticsRecorder recorder;
152 StatisticsRecorder::Histograms histograms;
153
154 recorder.GetHistograms(&histograms);
155 EXPECT_EQ(0U, histograms.size());
156
157 Histogram* histogram(Histogram::FactoryGet(
158 "Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file.
159 // Check that we got a nice exponential when there was enough rooom.
160 EXPECT_EQ(0, histogram->ranges(0));
161 int power_of_2 = 1;
162 for (int i = 1; i < 8; i++) {
163 EXPECT_EQ(power_of_2, histogram->ranges(i));
164 power_of_2 *= 2;
165 }
166 EXPECT_EQ(INT_MAX, histogram->ranges(8));
167
168 Histogram* short_histogram(Histogram::FactoryGet(
169 "Histogram Shortened", 1, 7, 8, Histogram::kNoFlags));
170 // Check that when the number of buckets is short, we get a linear histogram
171 // for lack of space to do otherwise.
172 for (int i = 0; i < 8; i++)
173 EXPECT_EQ(i, short_histogram->ranges(i));
174 EXPECT_EQ(INT_MAX, short_histogram->ranges(8));
175
176 Histogram* linear_histogram(LinearHistogram::FactoryGet(
177 "Linear", 1, 7, 8, Histogram::kNoFlags));
178 // We also get a nice linear set of bucket ranges when we ask for it
179 for (int i = 0; i < 8; i++)
180 EXPECT_EQ(i, linear_histogram->ranges(i));
181 EXPECT_EQ(INT_MAX, linear_histogram->ranges(8));
182
183 Histogram* linear_broad_histogram(LinearHistogram::FactoryGet(
184 "Linear widened", 2, 14, 8, Histogram::kNoFlags));
185 // ...but when the list has more space, then the ranges naturally spread out.
186 for (int i = 0; i < 8; i++)
187 EXPECT_EQ(2 * i, linear_broad_histogram->ranges(i));
188 EXPECT_EQ(INT_MAX, linear_broad_histogram->ranges(8));
189
190 Histogram* transitioning_histogram(Histogram::FactoryGet(
191 "LinearAndExponential", 1, 32, 15, Histogram::kNoFlags));
192 // When space is a little tight, we transition from linear to exponential.
193 EXPECT_EQ(0, transitioning_histogram->ranges(0));
194 EXPECT_EQ(1, transitioning_histogram->ranges(1));
195 EXPECT_EQ(2, transitioning_histogram->ranges(2));
196 EXPECT_EQ(3, transitioning_histogram->ranges(3));
197 EXPECT_EQ(4, transitioning_histogram->ranges(4));
198 EXPECT_EQ(5, transitioning_histogram->ranges(5));
199 EXPECT_EQ(6, transitioning_histogram->ranges(6));
200 EXPECT_EQ(7, transitioning_histogram->ranges(7));
201 EXPECT_EQ(9, transitioning_histogram->ranges(8));
202 EXPECT_EQ(11, transitioning_histogram->ranges(9));
203 EXPECT_EQ(14, transitioning_histogram->ranges(10));
204 EXPECT_EQ(17, transitioning_histogram->ranges(11));
205 EXPECT_EQ(21, transitioning_histogram->ranges(12));
206 EXPECT_EQ(26, transitioning_histogram->ranges(13));
207 EXPECT_EQ(32, transitioning_histogram->ranges(14));
208 EXPECT_EQ(INT_MAX, transitioning_histogram->ranges(15));
209
210 std::vector<int> custom_ranges;
211 custom_ranges.push_back(0);
212 custom_ranges.push_back(9);
213 custom_ranges.push_back(10);
214 custom_ranges.push_back(11);
215 custom_ranges.push_back(300);
216 Histogram* test_custom_histogram(CustomHistogram::FactoryGet(
217 "TestCustomRangeHistogram", custom_ranges, Histogram::kNoFlags));
218
219 EXPECT_EQ(custom_ranges[0], test_custom_histogram->ranges(0));
220 EXPECT_EQ(custom_ranges[1], test_custom_histogram->ranges(1));
221 EXPECT_EQ(custom_ranges[2], test_custom_histogram->ranges(2));
222 EXPECT_EQ(custom_ranges[3], test_custom_histogram->ranges(3));
223 EXPECT_EQ(custom_ranges[4], test_custom_histogram->ranges(4));
224
225 recorder.GetHistograms(&histograms);
226 EXPECT_EQ(6U, histograms.size());
227 }
228
TEST(HistogramTest,CustomRangeTest)229 TEST(HistogramTest, CustomRangeTest) {
230 StatisticsRecorder recorder;
231 StatisticsRecorder::Histograms histograms;
232
233 // Check that missing leading zero is handled by an auto-insertion.
234 std::vector<int> custom_ranges;
235 // Don't include a zero.
236 custom_ranges.push_back(9);
237 custom_ranges.push_back(10);
238 custom_ranges.push_back(11);
239 Histogram* test_custom_histogram(CustomHistogram::FactoryGet(
240 "TestCustomRangeHistogram", custom_ranges, Histogram::kNoFlags));
241
242 EXPECT_EQ(0, test_custom_histogram->ranges(0)); // Auto added
243 EXPECT_EQ(custom_ranges[0], test_custom_histogram->ranges(1));
244 EXPECT_EQ(custom_ranges[1], test_custom_histogram->ranges(2));
245 EXPECT_EQ(custom_ranges[2], test_custom_histogram->ranges(3));
246
247 // Check that unsorted data with dups is handled gracefully.
248 const int kSmall = 7;
249 const int kMid = 8;
250 const int kBig = 9;
251 custom_ranges.clear();
252 custom_ranges.push_back(kBig);
253 custom_ranges.push_back(kMid);
254 custom_ranges.push_back(kSmall);
255 custom_ranges.push_back(kSmall);
256 custom_ranges.push_back(kMid);
257 custom_ranges.push_back(0); // Push an explicit zero.
258 custom_ranges.push_back(kBig);
259
260 Histogram* unsorted_histogram(CustomHistogram::FactoryGet(
261 "TestCustomUnsortedDupedHistogram", custom_ranges, Histogram::kNoFlags));
262 EXPECT_EQ(0, unsorted_histogram->ranges(0));
263 EXPECT_EQ(kSmall, unsorted_histogram->ranges(1));
264 EXPECT_EQ(kMid, unsorted_histogram->ranges(2));
265 EXPECT_EQ(kBig, unsorted_histogram->ranges(3));
266 }
267
268
269 // Make sure histogram handles out-of-bounds data gracefully.
TEST(HistogramTest,BoundsTest)270 TEST(HistogramTest, BoundsTest) {
271 const size_t kBucketCount = 50;
272 Histogram* histogram(Histogram::FactoryGet(
273 "Bounded", 10, 100, kBucketCount, Histogram::kNoFlags));
274
275 // Put two samples "out of bounds" above and below.
276 histogram->Add(5);
277 histogram->Add(-50);
278
279 histogram->Add(100);
280 histogram->Add(10000);
281
282 // Verify they landed in the underflow, and overflow buckets.
283 Histogram::SampleSet sample;
284 histogram->SnapshotSample(&sample);
285 EXPECT_EQ(2, sample.counts(0));
286 EXPECT_EQ(0, sample.counts(1));
287 size_t array_size = histogram->bucket_count();
288 EXPECT_EQ(kBucketCount, array_size);
289 EXPECT_EQ(0, sample.counts(array_size - 2));
290 EXPECT_EQ(2, sample.counts(array_size - 1));
291 }
292
293 // Check to be sure samples land as expected is "correct" buckets.
TEST(HistogramTest,BucketPlacementTest)294 TEST(HistogramTest, BucketPlacementTest) {
295 Histogram* histogram(Histogram::FactoryGet(
296 "Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file.
297
298 // Check that we got a nice exponential since there was enough rooom.
299 EXPECT_EQ(0, histogram->ranges(0));
300 int power_of_2 = 1;
301 for (int i = 1; i < 8; i++) {
302 EXPECT_EQ(power_of_2, histogram->ranges(i));
303 power_of_2 *= 2;
304 }
305 EXPECT_EQ(INT_MAX, histogram->ranges(8));
306
307 // Add i+1 samples to the i'th bucket.
308 histogram->Add(0);
309 power_of_2 = 1;
310 for (int i = 1; i < 8; i++) {
311 for (int j = 0; j <= i; j++)
312 histogram->Add(power_of_2);
313 power_of_2 *= 2;
314 }
315 // Leave overflow bucket empty.
316
317 // Check to see that the bucket counts reflect our additions.
318 Histogram::SampleSet sample;
319 histogram->SnapshotSample(&sample);
320 EXPECT_EQ(INT_MAX, histogram->ranges(8));
321 for (int i = 0; i < 8; i++)
322 EXPECT_EQ(i + 1, sample.counts(i));
323 }
324
325 } // namespace
326
327 //------------------------------------------------------------------------------
328 // We can't be an an anonymous namespace while being friends, so we pop back
329 // out to the base namespace here. We need to be friends to corrupt the
330 // internals of the histogram and/or sampleset.
TEST(HistogramTest,CorruptSampleCounts)331 TEST(HistogramTest, CorruptSampleCounts) {
332 Histogram* histogram(Histogram::FactoryGet(
333 "Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file.
334
335 EXPECT_EQ(0, histogram->sample_.redundant_count());
336 histogram->Add(20); // Add some samples.
337 histogram->Add(40);
338 EXPECT_EQ(2, histogram->sample_.redundant_count());
339
340 Histogram::SampleSet snapshot;
341 histogram->SnapshotSample(&snapshot);
342 EXPECT_EQ(Histogram::NO_INCONSISTENCIES, 0);
343 EXPECT_EQ(0, histogram->FindCorruption(snapshot)); // No default corruption.
344 EXPECT_EQ(2, snapshot.redundant_count());
345
346 snapshot.counts_[3] += 100; // Sample count won't match redundant count.
347 EXPECT_EQ(Histogram::COUNT_LOW_ERROR, histogram->FindCorruption(snapshot));
348 snapshot.counts_[2] -= 200;
349 EXPECT_EQ(Histogram::COUNT_HIGH_ERROR, histogram->FindCorruption(snapshot));
350
351 // But we can't spot a corruption if it is compensated for.
352 snapshot.counts_[1] += 100;
353 EXPECT_EQ(0, histogram->FindCorruption(snapshot));
354 }
355
TEST(HistogramTest,CorruptBucketBounds)356 TEST(HistogramTest, CorruptBucketBounds) {
357 Histogram* histogram(Histogram::FactoryGet(
358 "Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file.
359
360 Histogram::SampleSet snapshot;
361 histogram->SnapshotSample(&snapshot);
362 EXPECT_EQ(Histogram::NO_INCONSISTENCIES, 0);
363 EXPECT_EQ(0, histogram->FindCorruption(snapshot)); // No default corruption.
364
365 std::swap(histogram->ranges_[1], histogram->ranges_[2]);
366 EXPECT_EQ(Histogram::BUCKET_ORDER_ERROR | Histogram::RANGE_CHECKSUM_ERROR,
367 histogram->FindCorruption(snapshot));
368
369 std::swap(histogram->ranges_[1], histogram->ranges_[2]);
370 EXPECT_EQ(0, histogram->FindCorruption(snapshot));
371
372 ++histogram->ranges_[3];
373 EXPECT_EQ(Histogram::RANGE_CHECKSUM_ERROR,
374 histogram->FindCorruption(snapshot));
375
376 // Show that two simple changes don't offset each other
377 --histogram->ranges_[4];
378 EXPECT_EQ(Histogram::RANGE_CHECKSUM_ERROR,
379 histogram->FindCorruption(snapshot));
380
381 // Repair histogram so that destructor won't DCHECK().
382 --histogram->ranges_[3];
383 ++histogram->ranges_[4];
384 }
385
386 // Table was generated similarly to sample code for CRC-32 given on:
387 // http://www.w3.org/TR/PNG/#D-CRCAppendix.
TEST(HistogramTest,Crc32TableTest)388 TEST(HistogramTest, Crc32TableTest) {
389 for (int i = 0; i < 256; ++i) {
390 uint32 checksum = i;
391 for (int j = 0; j < 8; ++j) {
392 const uint32 kReversedPolynomial = 0xedb88320L;
393 if (checksum & 1)
394 checksum = kReversedPolynomial ^ (checksum >> 1);
395 else
396 checksum >>= 1;
397 }
398 EXPECT_EQ(Histogram::kCrcTable[i], checksum);
399 }
400 }
401
402 } // namespace base
403