| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/test/metrics/histogram_tester.h" |
| |
| #include <stddef.h> |
| |
| #include "base/metrics/histogram.h" |
| #include "base/metrics/histogram_samples.h" |
| #include "base/metrics/metrics_hashes.h" |
| #include "base/metrics/sample_map.h" |
| #include "base/metrics/sparse_histogram.h" |
| #include "base/metrics/statistics_recorder.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace base { |
| |
| HistogramTester::HistogramTester() { |
| // Record any histogram data that exists when the object is created so it can |
| // be subtracted later. |
| for (const auto* const histogram : StatisticsRecorder::GetHistograms()) { |
| histograms_snapshot_[histogram->histogram_name()] = |
| histogram->SnapshotSamples(); |
| } |
| } |
| |
| HistogramTester::~HistogramTester() = default; |
| |
| void HistogramTester::ExpectUniqueSample( |
| StringPiece name, |
| HistogramBase::Sample sample, |
| HistogramBase::Count expected_bucket_count, |
| const Location& location) const { |
| HistogramBase* histogram = StatisticsRecorder::FindHistogram(name); |
| if (histogram) { |
| int actual_bucket_count; |
| int actual_total_count; |
| GetBucketCountForSamples(*histogram, sample, &actual_bucket_count, |
| &actual_total_count); |
| |
| EXPECT_TRUE(expected_bucket_count == actual_bucket_count && |
| expected_bucket_count == actual_total_count) |
| << "Histogram \"" << name << "\" did not meet its expectations.\n" |
| << "Bucket " << sample << " should contain " << expected_bucket_count |
| << " samples and contained " << actual_bucket_count << " samples.\n" |
| << "The total count of samples in the histogram should be " |
| << expected_bucket_count << " and was " << actual_total_count << ".\n" |
| << SnapshotToString(*histogram) << "\n" |
| << "(expected at " << location.ToString() << ")"; |
| } else { |
| // No histogram means there were zero samples. |
| EXPECT_EQ(0, expected_bucket_count) |
| << "Zero samples found for Histogram \"" << name << "\".\n" |
| << "(expected at " << location.ToString() << ")"; |
| return; |
| } |
| } |
| |
| void HistogramTester::ExpectUniqueTimeSample( |
| StringPiece name, |
| TimeDelta sample, |
| HistogramBase::Count expected_bucket_count, |
| const Location& location) const { |
| ExpectUniqueSample(name, sample.InMilliseconds(), expected_bucket_count, |
| location); |
| } |
| |
| void HistogramTester::ExpectBucketCount(StringPiece name, |
| HistogramBase::Sample sample, |
| HistogramBase::Count expected_count, |
| const Location& location) const { |
| HistogramBase* histogram = StatisticsRecorder::FindHistogram(name); |
| if (histogram) { |
| int actual_count; |
| GetBucketCountForSamples(*histogram, sample, &actual_count, |
| /*total_count=*/nullptr); |
| |
| EXPECT_EQ(expected_count, actual_count) |
| << "Histogram \"" << name |
| << "\" does not have the right number of samples (" << expected_count |
| << ") in the expected bucket (" << sample << "). It has (" |
| << actual_count << ").\n" |
| << SnapshotToString(*histogram) << "\n" |
| << "(expected at " << location.ToString() << ")"; |
| } else { |
| // No histogram means there were zero samples. |
| EXPECT_EQ(0, expected_count) |
| << "Histogram \"" << name << "\" does not exist. " |
| << "(expected at " << location.ToString() << ")"; |
| } |
| } |
| |
| void HistogramTester::ExpectTotalCount(StringPiece name, |
| HistogramBase::Count expected_count, |
| const Location& location) const { |
| HistogramBase* histogram = StatisticsRecorder::FindHistogram(name); |
| if (histogram) { |
| int actual_count = GetTotalCountForSamples(*histogram); |
| |
| EXPECT_EQ(expected_count, actual_count) |
| << "Histogram \"" << name |
| << "\" does not have the right total number of samples (" |
| << expected_count << "). It has (" << actual_count << ").\n" |
| << SnapshotToString(*histogram) << "\n" |
| << "(expected at " << location.ToString() << ")"; |
| } else { |
| // No histogram means there were zero samples. |
| EXPECT_EQ(0, expected_count) |
| << "Histogram \"" << name << "\" does not exist. " |
| << "(expected at " << location.ToString() << ")"; |
| } |
| } |
| |
| void HistogramTester::ExpectTimeBucketCount(StringPiece name, |
| TimeDelta sample, |
| HistogramBase::Count count, |
| const Location& location) const { |
| ExpectBucketCount(name, sample.InMilliseconds(), count, location); |
| } |
| |
| int64_t HistogramTester::GetTotalSum(StringPiece name) const { |
| HistogramBase* histogram = StatisticsRecorder::FindHistogram(name); |
| if (!histogram) |
| return 0; |
| |
| int64_t original_sum = 0; |
| auto original_samples_it = histograms_snapshot_.find(name); |
| if (original_samples_it != histograms_snapshot_.end()) |
| original_sum = original_samples_it->second->sum(); |
| |
| std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples(); |
| return samples->sum() - original_sum; |
| } |
| |
| std::vector<Bucket> HistogramTester::GetAllSamples(StringPiece name) const { |
| std::vector<Bucket> samples; |
| std::unique_ptr<HistogramSamples> snapshot = |
| GetHistogramSamplesSinceCreation(name); |
| if (snapshot) { |
| for (auto it = snapshot->Iterator(); !it->Done(); it->Next()) { |
| HistogramBase::Sample sample; |
| int64_t max; |
| HistogramBase::Count count; |
| it->Get(&sample, &max, &count); |
| samples.emplace_back(sample, count); |
| } |
| } |
| return samples; |
| } |
| |
| HistogramBase::Count HistogramTester::GetBucketCount( |
| StringPiece name, |
| HistogramBase::Sample sample) const { |
| HistogramBase* histogram = StatisticsRecorder::FindHistogram(name); |
| HistogramBase::Count count = 0; |
| if (histogram) { |
| GetBucketCountForSamples(*histogram, sample, &count, |
| /*total_count=*/nullptr); |
| } |
| return count; |
| } |
| |
| void HistogramTester::GetBucketCountForSamples( |
| const HistogramBase& histogram, |
| HistogramBase::Sample sample, |
| HistogramBase::Count* count, |
| HistogramBase::Count* total_count) const { |
| std::unique_ptr<HistogramSamples> samples = histogram.SnapshotSamples(); |
| *count = samples->GetCount(sample); |
| if (total_count) |
| *total_count = samples->TotalCount(); |
| auto histogram_data = histograms_snapshot_.find(histogram.histogram_name()); |
| if (histogram_data != histograms_snapshot_.end()) { |
| *count -= histogram_data->second->GetCount(sample); |
| if (total_count) |
| *total_count -= histogram_data->second->TotalCount(); |
| } |
| } |
| |
| HistogramTester::CountsMap HistogramTester::GetTotalCountsForPrefix( |
| StringPiece prefix) const { |
| EXPECT_TRUE(prefix.find('.') != StringPiece::npos) |
| << "|prefix| ought to contain at least one period, to avoid matching too" |
| << " many histograms."; |
| |
| CountsMap result; |
| |
| // Find candidate matches by using the logic built into GetSnapshot(). |
| for (const HistogramBase* histogram : StatisticsRecorder::GetHistograms()) { |
| if (!StartsWith(histogram->histogram_name(), prefix, |
| CompareCase::SENSITIVE)) { |
| continue; |
| } |
| std::unique_ptr<HistogramSamples> new_samples = |
| GetHistogramSamplesSinceCreation(histogram->histogram_name()); |
| // Omit unchanged histograms from the result. |
| if (new_samples->TotalCount()) { |
| result[histogram->histogram_name()] = new_samples->TotalCount(); |
| } |
| } |
| return result; |
| } |
| |
| std::unique_ptr<HistogramSamples> |
| HistogramTester::GetHistogramSamplesSinceCreation( |
| StringPiece histogram_name) const { |
| HistogramBase* histogram = StatisticsRecorder::FindHistogram(histogram_name); |
| // Whether the histogram exists or not may not depend on the current test |
| // calling this method, but rather on which tests ran before and possibly |
| // generated a histogram or not (see http://crbug.com/473689). To provide a |
| // response which is independent of the previously run tests, this method |
| // creates empty samples in the absence of the histogram, rather than |
| // returning null. |
| if (!histogram) { |
| return std::unique_ptr<HistogramSamples>( |
| new SampleMap(HashMetricName(histogram_name))); |
| } |
| std::unique_ptr<HistogramSamples> named_samples = |
| histogram->SnapshotSamples(); |
| auto original_samples_it = histograms_snapshot_.find(histogram_name); |
| if (original_samples_it != histograms_snapshot_.end()) |
| named_samples->Subtract(*original_samples_it->second.get()); |
| return named_samples; |
| } |
| |
| std::string HistogramTester::GetAllHistogramsRecorded() const { |
| std::string output; |
| |
| for (const auto* const histogram : StatisticsRecorder::GetHistograms()) { |
| std::unique_ptr<HistogramSamples> named_samples = |
| histogram->SnapshotSamples(); |
| |
| for (const auto& histogram_data : histograms_snapshot_) { |
| if (histogram_data.first == histogram->histogram_name()) |
| named_samples->Subtract(*histogram_data.second); |
| } |
| |
| if (named_samples->TotalCount()) { |
| auto current_count = histogram->SnapshotSamples()->TotalCount(); |
| StringAppendF(&output, "Histogram: %s recorded %d new samples.\n", |
| histogram->histogram_name(), named_samples->TotalCount()); |
| if (current_count != named_samples->TotalCount()) { |
| StringAppendF(&output, |
| "WARNING: There were samples recorded to this histogram " |
| "before tester instantiation.\n"); |
| } |
| histogram->WriteAscii(&output); |
| StringAppendF(&output, "\n"); |
| } |
| } |
| |
| return output; |
| } |
| |
| int HistogramTester::GetTotalCountForSamples( |
| const base::HistogramBase& histogram) const { |
| std::unique_ptr<HistogramSamples> samples = histogram.SnapshotSamples(); |
| int actual_count = samples->TotalCount(); |
| auto histogram_data = histograms_snapshot_.find(histogram.histogram_name()); |
| if (histogram_data != histograms_snapshot_.end()) |
| actual_count -= histogram_data->second->TotalCount(); |
| return actual_count; |
| } |
| |
| std::string HistogramTester::SnapshotToString( |
| const base::HistogramBase& histogram) const { |
| std::unique_ptr<HistogramSamples> snapshot = |
| GetHistogramSamplesSinceCreation(histogram.histogram_name()); |
| |
| base::Value::Dict graph_dict = |
| snapshot->ToGraphDict(histogram.histogram_name(), histogram.flags()); |
| std::string tmp; |
| // The header message describes this histogram samples (name of the histogram |
| // and median of the samples). The body contains an ASCII art histogram of the |
| // samples. |
| tmp.append(*graph_dict.FindString("header")); |
| tmp.append("\n"); |
| tmp.append(*graph_dict.FindString("body")); |
| return tmp; |
| } |
| |
| void PrintTo(const Bucket& bucket, std::ostream* os) { |
| *os << "Bucket " << bucket.min << ": " << bucket.count; |
| } |
| |
| } // namespace base |