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. 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. 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 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 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. 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. 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. 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 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. 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