histogram_unittest.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
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
12namespace base {
13namespace {
14
15class HistogramTest : public testing::Test {
16};
17
18// Check for basic syntax and use.
19TEST(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.
73TEST(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
150TEST(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
229TEST(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.
270TEST(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.
294TEST(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.
331TEST(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
356TEST(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.
388TEST(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