Index: base/metrics/sample_vector_unittest.cc |
diff --git a/base/metrics/sample_vector_unittest.cc b/base/metrics/sample_vector_unittest.cc |
index 864a44dcbf3a7fac5de6e8037e9f955f9959f29f..2aef015668784c954aa75d0a65135729433c821a 100644 |
--- a/base/metrics/sample_vector_unittest.cc |
+++ b/base/metrics/sample_vector_unittest.cc |
@@ -7,18 +7,29 @@ |
#include <limits.h> |
#include <stddef.h> |
+#include <atomic> |
#include <memory> |
#include <vector> |
#include "base/metrics/bucket_ranges.h" |
#include "base/metrics/histogram.h" |
+#include "base/metrics/persistent_memory_allocator.h" |
#include "base/test/gtest_util.h" |
#include "testing/gtest/include/gtest/gtest.h" |
namespace base { |
-namespace { |
-TEST(SampleVectorTest, AccumulateTest) { |
+// This framework class has "friend" access to the SampleVector for accessing |
+// non-public methods and fields. |
+class SampleVectorTest : public testing::Test { |
+ public: |
+ const HistogramBase::AtomicCount* GetSamplesCounts( |
+ const SampleVectorBase& samples) { |
+ return samples.counts(); |
+ } |
+}; |
+ |
+TEST_F(SampleVectorTest, Accumulate) { |
// Custom buckets: [1, 5) [5, 10) |
BucketRanges ranges(3); |
ranges.set_range(0, 1); |
@@ -45,7 +56,7 @@ TEST(SampleVectorTest, AccumulateTest) { |
EXPECT_EQ(samples.TotalCount(), samples.redundant_count()); |
} |
-TEST(SampleVectorTest, Accumulate_LargeValuesDontOverflow) { |
+TEST_F(SampleVectorTest, Accumulate_LargeValuesDontOverflow) { |
// Custom buckets: [1, 250000000) [250000000, 500000000) |
BucketRanges ranges(3); |
ranges.set_range(0, 1); |
@@ -72,7 +83,7 @@ TEST(SampleVectorTest, Accumulate_LargeValuesDontOverflow) { |
EXPECT_EQ(samples.TotalCount(), samples.redundant_count()); |
} |
-TEST(SampleVectorTest, AddSubtractTest) { |
+TEST_F(SampleVectorTest, AddSubtract) { |
// Custom buckets: [0, 1) [1, 2) [2, 3) [3, INT_MAX) |
BucketRanges ranges(5); |
ranges.set_range(0, 0); |
@@ -116,7 +127,7 @@ TEST(SampleVectorTest, AddSubtractTest) { |
EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount()); |
} |
-TEST(SampleVectorDeathTest, BucketIndexTest) { |
+TEST_F(SampleVectorTest, BucketIndexDeath) { |
// 8 buckets with exponential layout: |
// [0, 1) [1, 2) [2, 4) [4, 8) [8, 16) [16, 32) [32, 64) [64, INT_MAX) |
BucketRanges ranges(9); |
@@ -158,7 +169,7 @@ TEST(SampleVectorDeathTest, BucketIndexTest) { |
EXPECT_DCHECK_DEATH(samples2.Accumulate(10, 100)); |
} |
-TEST(SampleVectorDeathTest, AddSubtractBucketNotMatchTest) { |
+TEST_F(SampleVectorTest, AddSubtractBucketNotMatchDeath) { |
// Custom buckets 1: [1, 3) [3, 5) |
BucketRanges ranges1(3); |
ranges1.set_range(0, 1); |
@@ -197,7 +208,7 @@ TEST(SampleVectorDeathTest, AddSubtractBucketNotMatchTest) { |
EXPECT_DCHECK_DEATH(samples1.Subtract(samples2)); |
} |
-TEST(SampleVectorIteratorTest, IterateTest) { |
+TEST_F(SampleVectorTest, Iterate) { |
BucketRanges ranges(5); |
ranges.set_range(0, 0); |
ranges.set_range(1, 1); |
@@ -257,7 +268,7 @@ TEST(SampleVectorIteratorTest, IterateTest) { |
EXPECT_EQ(4, i); |
} |
-TEST(SampleVectorIteratorDeathTest, IterateDoneTest) { |
+TEST_F(SampleVectorTest, IterateDoneDeath) { |
BucketRanges ranges(5); |
ranges.set_range(0, 0); |
ranges.set_range(1, 1); |
@@ -282,5 +293,251 @@ TEST(SampleVectorIteratorDeathTest, IterateDoneTest) { |
EXPECT_FALSE(it->Done()); |
} |
-} // namespace |
+TEST_F(SampleVectorTest, SingleSample) { |
+ // Custom buckets: [1, 5) [5, 10) |
+ BucketRanges ranges(3); |
+ ranges.set_range(0, 1); |
+ ranges.set_range(1, 5); |
+ ranges.set_range(2, 10); |
+ SampleVector samples(&ranges); |
+ |
+ // Ensure that a single value accumulates correctly. |
+ EXPECT_FALSE(GetSamplesCounts(samples)); |
+ samples.Accumulate(3, 200); |
+ EXPECT_EQ(200, samples.GetCount(3)); |
+ EXPECT_FALSE(GetSamplesCounts(samples)); |
+ samples.Accumulate(3, 400); |
+ EXPECT_EQ(600, samples.GetCount(3)); |
+ EXPECT_FALSE(GetSamplesCounts(samples)); |
+ EXPECT_EQ(3 * 600, samples.sum()); |
+ EXPECT_EQ(600, samples.TotalCount()); |
+ EXPECT_EQ(600, samples.redundant_count()); |
+ |
+ // Ensure that the iterator returns only one value. |
+ HistogramBase::Sample min; |
+ int64_t max; |
+ HistogramBase::Count count; |
+ std::unique_ptr<SampleCountIterator> it = samples.Iterator(); |
+ ASSERT_FALSE(it->Done()); |
+ it->Get(&min, &max, &count); |
+ EXPECT_EQ(1, min); |
+ EXPECT_EQ(5, max); |
+ EXPECT_EQ(600, count); |
+ it->Next(); |
+ EXPECT_TRUE(it->Done()); |
+ |
+ // Ensure that it can be merged to another single-sample vector. |
+ SampleVector samples_copy(&ranges); |
+ samples_copy.Add(samples); |
+ EXPECT_FALSE(GetSamplesCounts(samples_copy)); |
+ EXPECT_EQ(3 * 600, samples_copy.sum()); |
+ EXPECT_EQ(600, samples_copy.TotalCount()); |
+ EXPECT_EQ(600, samples_copy.redundant_count()); |
+ |
+ // A different value should cause creation of the counts array. |
+ samples.Accumulate(8, 100); |
+ EXPECT_TRUE(GetSamplesCounts(samples)); |
+ EXPECT_EQ(600, samples.GetCount(3)); |
+ EXPECT_EQ(100, samples.GetCount(8)); |
+ EXPECT_EQ(3 * 600 + 8 * 100, samples.sum()); |
+ EXPECT_EQ(600 + 100, samples.TotalCount()); |
+ EXPECT_EQ(600 + 100, samples.redundant_count()); |
+ |
+ // The iterator should now return both values. |
+ it = samples.Iterator(); |
+ ASSERT_FALSE(it->Done()); |
+ it->Get(&min, &max, &count); |
+ EXPECT_EQ(1, min); |
+ EXPECT_EQ(5, max); |
+ EXPECT_EQ(600, count); |
+ it->Next(); |
+ ASSERT_FALSE(it->Done()); |
+ it->Get(&min, &max, &count); |
+ EXPECT_EQ(5, min); |
+ EXPECT_EQ(10, max); |
+ EXPECT_EQ(100, count); |
+ it->Next(); |
+ EXPECT_TRUE(it->Done()); |
+ |
+ // Ensure that it can merged to a single-sample vector. |
+ samples_copy.Add(samples); |
+ EXPECT_TRUE(GetSamplesCounts(samples_copy)); |
+ EXPECT_EQ(3 * 1200 + 8 * 100, samples_copy.sum()); |
+ EXPECT_EQ(1200 + 100, samples_copy.TotalCount()); |
+ EXPECT_EQ(1200 + 100, samples_copy.redundant_count()); |
+} |
+ |
+TEST_F(SampleVectorTest, PersistentSampleVector) { |
+ LocalPersistentMemoryAllocator allocator(64 << 10, 0, ""); |
+ std::atomic<PersistentMemoryAllocator::Reference> samples_ref; |
+ samples_ref.store(0, std::memory_order_relaxed); |
+ HistogramSamples::Metadata samples_meta; |
+ memset(&samples_meta, 0, sizeof(samples_meta)); |
+ |
+ // Custom buckets: [1, 5) [5, 10) |
+ BucketRanges ranges(3); |
+ ranges.set_range(0, 1); |
+ ranges.set_range(1, 5); |
+ ranges.set_range(2, 10); |
+ |
+ // Persistent allocation. |
+ const size_t counts_bytes = |
+ sizeof(HistogramBase::AtomicCount) * ranges.bucket_count(); |
+ const DelayedPersistentAllocation allocation(&allocator, &samples_ref, 1, |
+ counts_bytes, false); |
+ |
+ PersistentSampleVector samples1(0, &ranges, &samples_meta, allocation); |
+ EXPECT_FALSE(GetSamplesCounts(samples1)); |
+ samples1.Accumulate(3, 200); |
+ EXPECT_EQ(200, samples1.GetCount(3)); |
+ EXPECT_FALSE(GetSamplesCounts(samples1)); |
+ EXPECT_EQ(0, samples1.GetCount(8)); |
+ EXPECT_FALSE(GetSamplesCounts(samples1)); |
+ |
+ PersistentSampleVector samples2(0, &ranges, &samples_meta, allocation); |
+ EXPECT_EQ(200, samples2.GetCount(3)); |
+ EXPECT_FALSE(GetSamplesCounts(samples2)); |
+ |
+ HistogramBase::Sample min; |
+ int64_t max; |
+ HistogramBase::Count count; |
+ std::unique_ptr<SampleCountIterator> it = samples2.Iterator(); |
+ ASSERT_FALSE(it->Done()); |
+ it->Get(&min, &max, &count); |
+ EXPECT_EQ(1, min); |
+ EXPECT_EQ(5, max); |
+ EXPECT_EQ(200, count); |
+ it->Next(); |
+ EXPECT_TRUE(it->Done()); |
+ |
+ samples1.Accumulate(8, 100); |
+ EXPECT_TRUE(GetSamplesCounts(samples1)); |
+ |
+ EXPECT_FALSE(GetSamplesCounts(samples2)); |
+ EXPECT_EQ(200, samples2.GetCount(3)); |
+ EXPECT_EQ(100, samples2.GetCount(8)); |
+ EXPECT_TRUE(GetSamplesCounts(samples2)); |
+ EXPECT_EQ(3 * 200 + 8 * 100, samples2.sum()); |
+ EXPECT_EQ(300, samples2.TotalCount()); |
+ EXPECT_EQ(300, samples2.redundant_count()); |
+ |
+ it = samples2.Iterator(); |
+ ASSERT_FALSE(it->Done()); |
+ it->Get(&min, &max, &count); |
+ EXPECT_EQ(1, min); |
+ EXPECT_EQ(5, max); |
+ EXPECT_EQ(200, count); |
+ it->Next(); |
+ ASSERT_FALSE(it->Done()); |
+ it->Get(&min, &max, &count); |
+ EXPECT_EQ(5, min); |
+ EXPECT_EQ(10, max); |
+ EXPECT_EQ(100, count); |
+ it->Next(); |
+ EXPECT_TRUE(it->Done()); |
+ |
+ PersistentSampleVector samples3(0, &ranges, &samples_meta, allocation); |
+ EXPECT_TRUE(GetSamplesCounts(samples2)); |
+ EXPECT_EQ(200, samples3.GetCount(3)); |
+ EXPECT_EQ(100, samples3.GetCount(8)); |
+ EXPECT_EQ(3 * 200 + 8 * 100, samples3.sum()); |
+ EXPECT_EQ(300, samples3.TotalCount()); |
+ EXPECT_EQ(300, samples3.redundant_count()); |
+ |
+ it = samples3.Iterator(); |
+ ASSERT_FALSE(it->Done()); |
+ it->Get(&min, &max, &count); |
+ EXPECT_EQ(1, min); |
+ EXPECT_EQ(5, max); |
+ EXPECT_EQ(200, count); |
+ it->Next(); |
+ ASSERT_FALSE(it->Done()); |
+ it->Get(&min, &max, &count); |
+ EXPECT_EQ(5, min); |
+ EXPECT_EQ(10, max); |
+ EXPECT_EQ(100, count); |
+ it->Next(); |
+ EXPECT_TRUE(it->Done()); |
+} |
+ |
+TEST_F(SampleVectorTest, PersistentSampleVectorTestWithOutsideAlloc) { |
+ LocalPersistentMemoryAllocator allocator(64 << 10, 0, ""); |
+ std::atomic<PersistentMemoryAllocator::Reference> samples_ref; |
+ samples_ref.store(0, std::memory_order_relaxed); |
+ HistogramSamples::Metadata samples_meta; |
+ memset(&samples_meta, 0, sizeof(samples_meta)); |
+ |
+ // Custom buckets: [1, 5) [5, 10) |
+ BucketRanges ranges(3); |
+ ranges.set_range(0, 1); |
+ ranges.set_range(1, 5); |
+ ranges.set_range(2, 10); |
+ |
+ // Persistent allocation. |
+ const size_t counts_bytes = |
+ sizeof(HistogramBase::AtomicCount) * ranges.bucket_count(); |
+ const DelayedPersistentAllocation allocation(&allocator, &samples_ref, 1, |
+ counts_bytes, false); |
+ |
+ PersistentSampleVector samples1(0, &ranges, &samples_meta, allocation); |
+ EXPECT_FALSE(GetSamplesCounts(samples1)); |
+ samples1.Accumulate(3, 200); |
+ EXPECT_EQ(200, samples1.GetCount(3)); |
+ EXPECT_FALSE(GetSamplesCounts(samples1)); |
+ |
+ // Because the delayed allocation can be shared with other objects (the |
+ // |offset| parameter allows concatinating multiple data blocks into the |
+ // same allocation), it's possible that the allocation gets realized from |
+ // the outside even though the data block being accessed is all zero. |
+ allocation.Get(); |
+ EXPECT_EQ(200, samples1.GetCount(3)); |
+ EXPECT_FALSE(GetSamplesCounts(samples1)); |
+ |
+ HistogramBase::Sample min; |
+ int64_t max; |
+ HistogramBase::Count count; |
+ std::unique_ptr<SampleCountIterator> it = samples1.Iterator(); |
+ ASSERT_FALSE(it->Done()); |
+ it->Get(&min, &max, &count); |
+ EXPECT_EQ(1, min); |
+ EXPECT_EQ(5, max); |
+ EXPECT_EQ(200, count); |
+ it->Next(); |
+ EXPECT_TRUE(it->Done()); |
+ |
+ // A duplicate samples object should still see the single-sample entry even |
+ // when storage is available. |
+ PersistentSampleVector samples2(0, &ranges, &samples_meta, allocation); |
+ EXPECT_EQ(200, samples2.GetCount(3)); |
+ |
+ // New accumulations, in both directions, of the existing value should work. |
+ samples1.Accumulate(3, 50); |
+ EXPECT_EQ(250, samples1.GetCount(3)); |
+ EXPECT_EQ(250, samples2.GetCount(3)); |
+ samples2.Accumulate(3, 50); |
+ EXPECT_EQ(300, samples1.GetCount(3)); |
+ EXPECT_EQ(300, samples2.GetCount(3)); |
+ |
+ it = samples1.Iterator(); |
+ ASSERT_FALSE(it->Done()); |
+ it->Get(&min, &max, &count); |
+ EXPECT_EQ(1, min); |
+ EXPECT_EQ(5, max); |
+ EXPECT_EQ(300, count); |
+ it->Next(); |
+ EXPECT_TRUE(it->Done()); |
+ |
+ samples1.Accumulate(8, 100); |
+ EXPECT_TRUE(GetSamplesCounts(samples1)); |
+ EXPECT_EQ(300, samples1.GetCount(3)); |
+ EXPECT_EQ(300, samples2.GetCount(3)); |
+ EXPECT_EQ(100, samples1.GetCount(8)); |
+ EXPECT_EQ(100, samples2.GetCount(8)); |
+ samples2.Accumulate(8, 100); |
+ EXPECT_EQ(300, samples1.GetCount(3)); |
+ EXPECT_EQ(300, samples2.GetCount(3)); |
+ EXPECT_EQ(200, samples1.GetCount(8)); |
+ EXPECT_EQ(200, samples2.GetCount(8)); |
+} |
+ |
} // namespace base |