| Index: components/metrics/leak_detector/leak_analyzer_unittest.cc | 
| diff --git a/components/metrics/leak_detector/leak_analyzer_unittest.cc b/components/metrics/leak_detector/leak_analyzer_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..00087085af8ebe47ed4547ce250eedec7535cbeb | 
| --- /dev/null | 
| +++ b/components/metrics/leak_detector/leak_analyzer_unittest.cc | 
| @@ -0,0 +1,362 @@ | 
| +// Copyright 2015 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "components/metrics/leak_detector/leak_analyzer.h" | 
| + | 
| +#include "base/macros.h" | 
| +#include "components/metrics/leak_detector/custom_allocator.h" | 
| +#include "components/metrics/leak_detector/ranked_list.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| + | 
| +namespace metrics { | 
| +namespace leak_detector { | 
| + | 
| +namespace { | 
| + | 
| +// Default ranking size and threshold used for leak analysis. | 
| +const int kDefaultRankedListSize = 10; | 
| +const int kDefaultLeakThreshold = 5; | 
| + | 
| +// Makes it easier to instantiate LeakDetectorValueTypes. Instantiates with an | 
| +// integer value that indicates an allocation size. Storing the size allows us | 
| +// to track the storage of the LeakDetectorValueType object within LeakAnalyzer. | 
| +// | 
| +// There is no need to test this with call stacks in addition to sizes because | 
| +// call stacks will be contained in a LeakDetectorValueType object as well. | 
| +LeakDetectorValueType Size(uint32_t value) { | 
| +  return LeakDetectorValueType(value); | 
| +} | 
| + | 
| +}  // namespace | 
| + | 
| +class LeakAnalyzerTest : public ::testing::Test { | 
| + public: | 
| +  LeakAnalyzerTest() {} | 
| + | 
| +  void SetUp() override { CustomAllocator::Initialize(); } | 
| +  void TearDown() override { EXPECT_TRUE(CustomAllocator::Shutdown()); } | 
| + | 
| + private: | 
| +  DISALLOW_COPY_AND_ASSIGN(LeakAnalyzerTest); | 
| +}; | 
| + | 
| +TEST_F(LeakAnalyzerTest, Empty) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| +  EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, SingleSize) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    list.Add(Size(24), 10); | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    // No leaks should have been detected. | 
| +    EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, VariousSizesWithoutIncrease) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    list.Add(Size(24), 30); | 
| +    list.Add(Size(32), 10); | 
| +    list.Add(Size(56), 90); | 
| +    list.Add(Size(64), 40); | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    // No leaks should have been detected. | 
| +    EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, VariousSizesWithEqualIncrease) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    list.Add(Size(24), 30 + i * 10); | 
| +    list.Add(Size(32), 10 + i * 10); | 
| +    list.Add(Size(56), 90 + i * 10); | 
| +    list.Add(Size(64), 40 + i * 10); | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    // No leaks should have been detected. | 
| +    EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, NotEnoughRunsToTriggerLeakReport) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  // Run this one iteration short of the number of cycles needed to trigger a | 
| +  // leak report. Because LeakAnalyzer requires |kDefaultLeakThreshold| | 
| +  // suspicions based on deltas between AddSample() calls, the below loop needs | 
| +  // to run |kDefaultLeakThreshold + 1| times to trigger a leak report. | 
| +  for (int i = 0; i <= kDefaultLeakThreshold - 1; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    list.Add(Size(24), 30 + i * 10);  // This one has a potential leak. | 
| +    list.Add(Size(32), 10 + i * 2); | 
| +    list.Add(Size(56), 90 + i); | 
| +    list.Add(Size(64), 40 + i / 2); | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    // No leaks should have been detected. | 
| +    EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, LeakSingleSize) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  // Run this past the number of iterations required to trigger a leak report. | 
| +  for (int i = 0; i < kDefaultLeakThreshold + 10; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    list.Add(Size(32), 10); | 
| +    list.Add(Size(56), 90); | 
| +    list.Add(Size(24), 30 + i * 10);  // This one has a potential leak. | 
| +    list.Add(Size(64), 40); | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    // No leaks should have been detected initially... | 
| +    if (i < kDefaultLeakThreshold) { | 
| +      EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +    } else { | 
| +      // ... but there should be reported leaks once the threshold is reached. | 
| +      const auto& leaks = analyzer.suspected_leaks(); | 
| +      ASSERT_EQ(1U, leaks.size()); | 
| +      EXPECT_EQ(24U, leaks[0].size()); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, LeakSingleSizeOthersAlsoIncreasing) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  for (int i = 0; i < kDefaultLeakThreshold + 10; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    list.Add(Size(24), 30 + i * 10);  // This one has a potential leak. | 
| +    list.Add(Size(32), 10 + i * 2); | 
| +    list.Add(Size(56), 90 + i); | 
| +    list.Add(Size(64), 40 + i / 2); | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    // No leaks should have been detected initially... | 
| +    if (i < kDefaultLeakThreshold) { | 
| +      EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +    } else { | 
| +      // ... but there should be reported leaks once the threshold is reached. | 
| +      const auto& leaks = analyzer.suspected_leaks(); | 
| +      ASSERT_EQ(1U, leaks.size()); | 
| +      EXPECT_EQ(24U, leaks[0].size()); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, LeakMultipleSizes) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  for (int i = 0; i < kDefaultLeakThreshold + 10; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    list.Add(Size(24), 30 + i * 5); | 
| +    list.Add(Size(32), 10 + i * 40); | 
| +    list.Add(Size(56), 90 + i * 30); | 
| +    list.Add(Size(64), 40 + i * 20); | 
| +    list.Add(Size(80), 20 + i * 3); | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    // No leaks should have been detected initially... | 
| +    if (i < kDefaultLeakThreshold) { | 
| +      EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +    } else { | 
| +      // ... but there should be reported leaks once the threshold is reached. | 
| +      const auto& leaks = analyzer.suspected_leaks(); | 
| +      ASSERT_EQ(3U, leaks.size()); | 
| +      // These should be in order of increasing allocation size. | 
| +      EXPECT_EQ(32U, leaks[0].size()); | 
| +      EXPECT_EQ(56U, leaks[1].size()); | 
| +      EXPECT_EQ(64U, leaks[2].size()); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, LeakMultipleSizesValueOrder) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  for (int i = 0; i <= kDefaultLeakThreshold; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    // These are similar to LeakMultipleSizes, but the relative order of | 
| +    // allocation increases is different from the relative order of sizes. | 
| +    list.Add(Size(24), 30 + i * 5); | 
| +    list.Add(Size(32), 10 + i * 20); | 
| +    list.Add(Size(56), 90 + i * 40); | 
| +    list.Add(Size(64), 40 + i * 30); | 
| +    list.Add(Size(80), 20 + i * 3); | 
| +    analyzer.AddSample(list.Pass()); | 
| +  } | 
| + | 
| +  const auto& leaks = analyzer.suspected_leaks(); | 
| +  ASSERT_EQ(3U, leaks.size()); | 
| +  // These should be in order of increasing allocation size, NOT in order of | 
| +  // allocation count or deltas. | 
| +  EXPECT_EQ(32U, leaks[0].size()); | 
| +  EXPECT_EQ(56U, leaks[1].size()); | 
| +  EXPECT_EQ(64U, leaks[2].size()); | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, EqualIncreasesNoLeak) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    list.Add(Size(24), 30 + i * 10); | 
| +    list.Add(Size(32), 10 + i * 10); | 
| +    list.Add(Size(56), 90 + i * 10); | 
| +    list.Add(Size(64), 40 + i * 10); | 
| +    list.Add(Size(80), 20 + i * 10); | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, NotBigEnoughDeltaGap) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    // These all have different increments but there is no clear group of | 
| +    // increases that are larger than the rest. | 
| +    list.Add(Size(24), 30 + i * 80); | 
| +    list.Add(Size(32), 10 + i * 45); | 
| +    list.Add(Size(56), 90 + i * 25); | 
| +    list.Add(Size(64), 40 + i * 15); | 
| +    list.Add(Size(80), 20 + i * 10); | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, RepeatedRisesUntilLeakFound) { | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  // Remember, there is an extra iteration beyond |kDefaultLeakThreshold| needed | 
| +  // to actually trigger the leak detection. | 
| +  for (int i = 0; i <= kDefaultLeakThreshold - 2; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    list.Add(Size(24), 30 + i * 10); | 
| +    list.Add(Size(32), 10); | 
| +    list.Add(Size(56), 90); | 
| +    list.Add(Size(64), 40); | 
| +    list.Add(Size(80), 20); | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +  } | 
| + | 
| +  // Drop back down to 30. | 
| +  for (int i = 0; i <= kDefaultLeakThreshold - 1; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    list.Add(Size(24), 30 + i * 10); | 
| +    list.Add(Size(32), 10); | 
| +    list.Add(Size(56), 90); | 
| +    list.Add(Size(64), 40); | 
| +    list.Add(Size(80), 20); | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +  } | 
| + | 
| +  // Drop back down to 30. | 
| +  for (int i = 0; i <= kDefaultLeakThreshold; ++i) { | 
| +    // Initially there should not be any leak detected. | 
| +    EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| + | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    list.Add(Size(24), 30 + i * 10); | 
| +    list.Add(Size(32), 10); | 
| +    list.Add(Size(56), 90); | 
| +    list.Add(Size(64), 40); | 
| +    list.Add(Size(80), 20); | 
| +    analyzer.AddSample(list.Pass()); | 
| +  } | 
| +  const auto& leaks = analyzer.suspected_leaks(); | 
| +  ASSERT_EQ(1U, leaks.size()); | 
| +  EXPECT_EQ(24U, leaks[0].size()); | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, LeakWithMultipleGroupsOfDeltas) { | 
| +  const int kRankedListSize = 20; | 
| +  LeakAnalyzer analyzer(kRankedListSize, kDefaultLeakThreshold); | 
| + | 
| +  for (int i = 0; i <= kDefaultLeakThreshold; ++i) { | 
| +    RankedList list(kRankedListSize); | 
| +    list.Add(Size(24), 30 + i * 10);  // A group of smaller deltas. | 
| +    list.Add(Size(32), 10 + i * 3); | 
| +    list.Add(Size(80), 20 + i * 5); | 
| +    list.Add(Size(40), 30 + i * 7); | 
| +    list.Add(Size(56), 90); | 
| +    list.Add(Size(64), 40); | 
| +    list.Add(Size(128), 100); | 
| +    list.Add(Size(44), 100 + i * 10);  // A group of medium deltas. | 
| +    list.Add(Size(16), 60 + i * 50); | 
| +    list.Add(Size(4), 20 + i * 40); | 
| +    list.Add(Size(8), 100 + i * 60); | 
| +    list.Add(Size(48), 100); | 
| +    list.Add(Size(72), 60 + i * 240);  // A group of largest deltas. | 
| +    list.Add(Size(28), 100); | 
| +    list.Add(Size(100), 100 + i * 200); | 
| +    list.Add(Size(104), 60 + i * 128); | 
| +    analyzer.AddSample(list.Pass()); | 
| +  } | 
| +  // Only the group of largest deltas should be caught. | 
| +  const auto& leaks = analyzer.suspected_leaks(); | 
| +  ASSERT_EQ(3U, leaks.size()); | 
| +  // These should be in order of increasing allocation size. | 
| +  EXPECT_EQ(72U, leaks[0].size()); | 
| +  EXPECT_EQ(100U, leaks[1].size()); | 
| +  EXPECT_EQ(104U, leaks[2].size()); | 
| +} | 
| + | 
| +TEST_F(LeakAnalyzerTest, LeakMultipleSizesWithLargeThreshold) { | 
| +  const int kLeakThreshold = 50; | 
| +  LeakAnalyzer analyzer(kDefaultRankedListSize, kLeakThreshold); | 
| + | 
| +  for (int i = 0; i <= kLeakThreshold + 10; ++i) { | 
| +    RankedList list(kDefaultRankedListSize); | 
| +    // * - Cluster of larger deltas | 
| +    list.Add(Size(24), 30 + i * 5); | 
| +    list.Add(Size(32), 10 + i * 40);  // * | 
| +    list.Add(Size(56), 90 + i * 30);  // * | 
| +    list.Add(Size(40), 30 + i * 7); | 
| +    list.Add(Size(64), 40 + i * 25);  // * | 
| +    list.Add(Size(80), 20 + i * 3); | 
| +    list.Add(Size(128), 100); | 
| +    list.Add(Size(44), 100 + i * 10); | 
| +    list.Add(Size(16), 60 + i * 50);  // * | 
| +    analyzer.AddSample(list.Pass()); | 
| + | 
| +    // No leaks should have been detected initially... | 
| +    if (i < kLeakThreshold) { | 
| +      EXPECT_TRUE(analyzer.suspected_leaks().empty()); | 
| +    } else { | 
| +      // ... but there should be reported leaks once the threshold is reached. | 
| +      const auto& leaks = analyzer.suspected_leaks(); | 
| +      ASSERT_EQ(4U, leaks.size()); | 
| +      // These should be in order of increasing allocation size. | 
| +      EXPECT_EQ(16U, leaks[0].size()); | 
| +      EXPECT_EQ(32U, leaks[1].size()); | 
| +      EXPECT_EQ(56U, leaks[2].size()); | 
| +      EXPECT_EQ(64U, leaks[3].size()); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +}  // namespace leak_detector | 
| +}  // namespace metrics | 
|  |