1 // Copyright 2016 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 #include "components/subresource_filter/core/common/scoped_timers.h"
6 
7 #include "base/test/metrics/histogram_tester.h"
8 #include "base/time/time.h"
9 #include "components/subresource_filter/core/common/time_measurements.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 
12 namespace {
13 
14 class MockExportFunctor {
15  public:
number_of_calls() const16   int number_of_calls() const { return number_of_calls_; }
operator ()(base::TimeDelta)17   void operator()(base::TimeDelta) { ++number_of_calls_; }
18 
19  private:
20   int number_of_calls_ = 0;
21 };
22 
23 template <typename TimerFactory>
ExpectFunctorIsCalledOnceOnDestruction()24 void ExpectFunctorIsCalledOnceOnDestruction() {
25   MockExportFunctor export_functor;
26   {
27     auto scoped_timer = TimerFactory::Start(export_functor);
28     EXPECT_EQ(0, export_functor.number_of_calls());
29   }
30   const int expected_number_of_calls = TimerFactory::IsSupported() ? 1 : 0;
31   EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
32 }
33 
34 template <typename TimerFactory>
ExpectStoredLambdaIsInvokedOnceOnDestruction()35 void ExpectStoredLambdaIsInvokedOnceOnDestruction() {
36   bool export_is_called = false;
37   auto export_functor = [&export_is_called](base::TimeDelta) {
38     EXPECT_FALSE(export_is_called);
39     export_is_called = true;
40   };
41 
42   {
43     auto scoped_timer = TimerFactory::Start(export_functor);
44     EXPECT_FALSE(export_is_called);
45   }
46   EXPECT_EQ(TimerFactory::IsSupported(), export_is_called);
47 }
48 
49 template <typename TimerFactory>
ExpectInlineLambdaIsInvokedOnceOnDestruction()50 void ExpectInlineLambdaIsInvokedOnceOnDestruction() {
51   bool export_is_called = false;
52   {
53     auto scoped_timer =
54         TimerFactory::Start([&export_is_called](base::TimeDelta) {
55           EXPECT_FALSE(export_is_called);
56           export_is_called = true;
57         });
58     EXPECT_FALSE(export_is_called);
59   }
60   EXPECT_EQ(TimerFactory::IsSupported(), export_is_called);
61 }
62 
63 template <typename TimerFactory>
ExpectWellBehavedStartIf(bool condition)64 void ExpectWellBehavedStartIf(bool condition) {
65   bool export_is_called = false;
66   auto export_functor = [&export_is_called](base::TimeDelta) {
67     EXPECT_FALSE(export_is_called);
68     export_is_called = true;
69   };
70 
71   {
72     auto scoped_timer = TimerFactory::StartIf(condition, export_functor);
73     EXPECT_FALSE(export_is_called);
74   }
75   EXPECT_EQ(condition && TimerFactory::IsSupported(), export_is_called);
76 }
77 
78 template <typename TimerFactory>
ExpectWellBehavedMoveContructor()79 void ExpectWellBehavedMoveContructor() {
80   MockExportFunctor export_functor;
81   const int expected_number_of_calls = TimerFactory::IsSupported() ? 1 : 0;
82   {
83     auto scoped_timer = TimerFactory::Start(export_functor);
84     EXPECT_EQ(0, export_functor.number_of_calls());
85     {
86       auto another_scoped_timer = std::move(scoped_timer);
87       EXPECT_EQ(0, export_functor.number_of_calls());
88     }
89     // |another_scoped_timer| should have called |export_functor|.
90     EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
91   }
92   // But |scoped_timer| should have not since then.
93   EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
94 }
95 
96 template <typename TimerFactory>
ExpectWellBehavedMoveAssignment()97 void ExpectWellBehavedMoveAssignment() {
98   MockExportFunctor export_functor;
99   const int expected_number_of_calls = TimerFactory::IsSupported() ? 1 : 0;
100   {
101     auto scoped_timer = TimerFactory::Start(export_functor);
102     EXPECT_EQ(0, export_functor.number_of_calls());
103     {
104       auto another_scoped_timer = std::move(scoped_timer);
105       scoped_timer = std::move(another_scoped_timer);
106       EXPECT_EQ(0, export_functor.number_of_calls());
107     }
108     // |another_scoped_timer| shouldn't have called |export_functor|.
109     EXPECT_EQ(0, export_functor.number_of_calls());
110   }
111   // But |scoped_timer| should have because it owns the measurement.
112   EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
113 }
114 
115 }  // namespace
116 
117 namespace subresource_filter {
118 
TEST(ScopedTimersTest,CallsFunctor)119 TEST(ScopedTimersTest, CallsFunctor) {
120   ExpectFunctorIsCalledOnceOnDestruction<ScopedTimers>();
121   ExpectFunctorIsCalledOnceOnDestruction<ScopedThreadTimers>();
122 }
123 
TEST(ScopedTimersTest,CallsStoredLambdaFunctor)124 TEST(ScopedTimersTest, CallsStoredLambdaFunctor) {
125   ExpectStoredLambdaIsInvokedOnceOnDestruction<ScopedTimers>();
126   ExpectStoredLambdaIsInvokedOnceOnDestruction<ScopedThreadTimers>();
127 }
128 
TEST(ScopedTimersTest,CallsInlineLambdaFunctor)129 TEST(ScopedTimersTest, CallsInlineLambdaFunctor) {
130   ExpectInlineLambdaIsInvokedOnceOnDestruction<ScopedTimers>();
131   ExpectInlineLambdaIsInvokedOnceOnDestruction<ScopedThreadTimers>();
132 }
133 
TEST(ScopedTimersTest,StartIf)134 TEST(ScopedTimersTest, StartIf) {
135   ExpectWellBehavedStartIf<ScopedTimers>(false);
136   ExpectWellBehavedStartIf<ScopedTimers>(true);
137 
138   ExpectWellBehavedStartIf<ScopedThreadTimers>(false);
139   ExpectWellBehavedStartIf<ScopedThreadTimers>(true);
140 }
141 
TEST(ScopedTimersTest,MoveConstructTimer)142 TEST(ScopedTimersTest, MoveConstructTimer) {
143   ExpectWellBehavedMoveContructor<ScopedTimers>();
144   ExpectWellBehavedMoveContructor<ScopedThreadTimers>();
145 }
146 
TEST(ScopedTimersTest,MoveAssignTimer)147 TEST(ScopedTimersTest, MoveAssignTimer) {
148   ExpectWellBehavedMoveAssignment<ScopedTimers>();
149   ExpectWellBehavedMoveAssignment<ScopedThreadTimers>();
150 }
151 
152 // Below are tests for macros in "time_measurements.h" -------------------------
153 // TODO(pkalinnikov): Move these to a separate file?
154 
TEST(ScopedTimersTest,ScopedUmaHistogramMacros)155 TEST(ScopedTimersTest, ScopedUmaHistogramMacros) {
156   base::HistogramTester tester;
157   {
158     SCOPED_UMA_HISTOGRAM_THREAD_TIMER("ScopedTimers.ThreadTimer");
159     SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER("ScopedTimers.MicroThreadTimer");
160     SCOPED_UMA_HISTOGRAM_MICRO_TIMER("ScopedTimers.MicroTimer");
161 
162     tester.ExpectTotalCount("ScopedTimers.ThreadTimer", 0);
163     tester.ExpectTotalCount("ScopedTimers.MicroThreadTimer", 0);
164     tester.ExpectTotalCount("ScopedTimers.MicroTimer", 0);
165   }
166 
167   int expected_count = ScopedTimers::IsSupported() ? 1 : 0;
168   tester.ExpectTotalCount("ScopedTimers.MicroTimer", expected_count);
169 
170   expected_count = ScopedThreadTimers::IsSupported() ? 1 : 0;
171   tester.ExpectTotalCount("ScopedTimers.ThreadTimer", expected_count);
172   tester.ExpectTotalCount("ScopedTimers.MicroThreadTimer", expected_count);
173 }
174 
TEST(ScopedTimersTest,UmaHistogramMicroTimesFromExportFunctor)175 TEST(ScopedTimersTest, UmaHistogramMicroTimesFromExportFunctor) {
176   base::HistogramTester tester;
177   auto export_functor = [](base::TimeDelta delta) {
178     UMA_HISTOGRAM_MICRO_TIMES("ScopedTimers.MicroTimes", delta);
179   };
180   {
181     auto scoped_timer = ScopedTimers::Start(export_functor);
182     tester.ExpectTotalCount("ScopedTimers.MicroTimes", 0);
183   }
184   tester.ExpectTotalCount("ScopedTimers.MicroTimes", 1);
185 }
186 
187 }  // namespace subresource_filter
188