// Copyright 2017 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 "third_party/blink/renderer/platform/bindings/runtime_call_stats.h" #include "base/test/simple_test_tick_clock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" namespace blink { namespace { RuntimeCallStats::CounterId test_counter_1_id = RuntimeCallStats::CounterId::kTestCounter1; RuntimeCallStats::CounterId test_counter_2_id = RuntimeCallStats::CounterId::kTestCounter2; } // namespace class RuntimeCallStatsTest : public testing::Test { public: void SetUp() override { // Add one millisecond because RuntimeCallTimer uses |start_ticks_| = // base::TimeTicks() to represent that the timer is not running. clock_.SetNowTicks(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1)); } void TearDown() override { features_backup_.Restore(); } void AdvanceClock(int milliseconds) { clock_.Advance(base::TimeDelta::FromMilliseconds(milliseconds)); } const base::TickClock* clock() { return &clock_; } private: RuntimeEnabledFeatures::Backup features_backup_; base::SimpleTestTickClock clock_; }; TEST_F(RuntimeCallStatsTest, InitialCountShouldBeZero) { RuntimeCallCounter counter("counter"); EXPECT_EQ(0ul, counter.GetCount()); } TEST_F(RuntimeCallStatsTest, StatsCounterNameIsCorrect) { RuntimeCallStats stats(clock()); EXPECT_STREQ("Blink_TestCounter1", stats.GetCounter(test_counter_1_id)->GetName()); } TEST_F(RuntimeCallStatsTest, TestBindingsCountersForMethods) { RuntimeCallStats stats(clock()); RuntimeCallCounter* method_counter = stats.GetCounter(RuntimeCallStats::CounterId::kBindingsMethodTestCounter); EXPECT_STREQ("Blink_BindingsMethodTestCounter", method_counter->GetName()); } TEST_F(RuntimeCallStatsTest, TestBindingsCountersForReadOnlyAttributes) { RuntimeCallStats stats(clock()); RuntimeCallCounter* getter_counter = stats.GetCounter(RuntimeCallStats::CounterId:: kBindingsReadOnlyAttributeTestCounter_Getter); EXPECT_STREQ("Blink_BindingsReadOnlyAttributeTestCounter_Getter", getter_counter->GetName()); } TEST_F(RuntimeCallStatsTest, TestBindingsCountersForAttributes) { RuntimeCallStats stats(clock()); RuntimeCallCounter* getter_counter = stats.GetCounter( RuntimeCallStats::CounterId::kBindingsAttributeTestCounter_Getter); RuntimeCallCounter* setter_counter = stats.GetCounter( RuntimeCallStats::CounterId::kBindingsAttributeTestCounter_Setter); EXPECT_STREQ("Blink_BindingsAttributeTestCounter_Getter", getter_counter->GetName()); EXPECT_STREQ("Blink_BindingsAttributeTestCounter_Setter", setter_counter->GetName()); } TEST_F(RuntimeCallStatsTest, CountIsUpdatedAfterLeave) { RuntimeCallTimer timer(clock()); RuntimeCallStats stats(clock()); RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id); stats.Enter(&timer, test_counter_1_id); EXPECT_EQ(0ul, counter->GetCount()); stats.Leave(&timer); EXPECT_EQ(1ul, counter->GetCount()); } TEST_F(RuntimeCallStatsTest, TimeIsUpdatedAfterLeave) { RuntimeCallStats stats(clock()); RuntimeCallTimer timer(clock()); RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id); stats.Enter(&timer, test_counter_1_id); AdvanceClock(50); stats.Leave(&timer); EXPECT_EQ(50, counter->GetTime().InMilliseconds()); } TEST_F(RuntimeCallStatsTest, CountAndTimeAreUpdatedAfterMultipleExecutions) { RuntimeCallStats stats(clock()); RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id); const unsigned func_duration = 20; const unsigned loops = 5; RuntimeCallStatsTest* test = this; auto func = [&stats, test]() { RuntimeCallTimer timer(test->clock()); stats.Enter(&timer, test_counter_1_id); test->AdvanceClock(func_duration); stats.Leave(&timer); }; for (unsigned i = 0; i < loops; i++) func(); EXPECT_EQ((uint64_t)loops, counter->GetCount()); EXPECT_EQ(loops * func_duration, counter->GetTime().InMilliseconds()); } TEST_F(RuntimeCallStatsTest, NestedTimersTest) { RuntimeCallStats stats(clock()); RuntimeCallCounter* outer_counter = stats.GetCounter(test_counter_1_id); RuntimeCallCounter* inner_counter = stats.GetCounter(test_counter_2_id); const unsigned inner_func_duration = 50; const unsigned outer_func_duration = 20; RuntimeCallStatsTest* test = this; auto inner_func = [&stats, test]() { RuntimeCallTimer timer(test->clock()); stats.Enter(&timer, test_counter_2_id); test->AdvanceClock(inner_func_duration); stats.Leave(&timer); }; auto outer_func = [&stats, &inner_func, test]() { RuntimeCallTimer timer(test->clock()); stats.Enter(&timer, test_counter_1_id); inner_func(); test->AdvanceClock(outer_func_duration); stats.Leave(&timer); }; outer_func(); EXPECT_EQ(1ul, outer_counter->GetCount()); EXPECT_EQ(1ul, inner_counter->GetCount()); EXPECT_EQ(outer_func_duration, outer_counter->GetTime().InMilliseconds()); EXPECT_EQ(inner_func_duration, inner_counter->GetTime().InMilliseconds()); } TEST_F(RuntimeCallStatsTest, RuntimeCallTimerScopeTest) { RuntimeCallStats stats(clock()); RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id); RuntimeCallStatsTest* test = this; auto func = [&stats, test]() { RuntimeCallTimerScope scope(&stats, test_counter_1_id); test->AdvanceClock(50); }; func(); EXPECT_EQ(1ul, counter->GetCount()); EXPECT_EQ(50, counter->GetTime().InMilliseconds()); func(); EXPECT_EQ(2ul, counter->GetCount()); EXPECT_EQ(100, counter->GetTime().InMilliseconds()); } TEST_F(RuntimeCallStatsTest, RecursiveFunctionWithScopeTest) { RuntimeCallStats stats(clock()); RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id); RuntimeCallStatsTest* test = this; std::function recursive_func; recursive_func = [&stats, &recursive_func, test](int x) { RuntimeCallTimerScope scope(&stats, test_counter_1_id); if (x <= 0) return; test->AdvanceClock(50); recursive_func(x - 1); }; recursive_func(5); EXPECT_EQ(6ul, counter->GetCount()); EXPECT_EQ(250, counter->GetTime().InMilliseconds()); } TEST_F(RuntimeCallStatsTest, ReuseTimer) { RuntimeCallStats stats(clock()); RuntimeCallTimer timer(clock()); RuntimeCallCounter* counter1 = stats.GetCounter(test_counter_1_id); RuntimeCallCounter* counter2 = stats.GetCounter(test_counter_2_id); stats.Enter(&timer, test_counter_1_id); AdvanceClock(50); stats.Leave(&timer); timer.Reset(); stats.Enter(&timer, test_counter_2_id); AdvanceClock(25); stats.Leave(&timer); EXPECT_EQ(1ul, counter1->GetCount()); EXPECT_EQ(1ul, counter2->GetCount()); EXPECT_EQ(50, counter1->GetTime().InMilliseconds()); EXPECT_EQ(25, counter2->GetTime().InMilliseconds()); } TEST_F(RuntimeCallStatsTest, ResetCallStats) { RuntimeCallStats stats(clock()); RuntimeCallCounter* counter1 = stats.GetCounter(test_counter_1_id); RuntimeCallCounter* counter2 = stats.GetCounter(test_counter_2_id); { RuntimeCallTimerScope scope1(&stats, test_counter_1_id); RuntimeCallTimerScope scope2(&stats, test_counter_2_id); } EXPECT_EQ(1ul, counter1->GetCount()); EXPECT_EQ(1ul, counter2->GetCount()); stats.Reset(); EXPECT_EQ(0ul, counter1->GetCount()); EXPECT_EQ(0ul, counter2->GetCount()); } TEST_F(RuntimeCallStatsTest, TestEnterAndLeaveMacrosWithCallStatsDisabled) { ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(false); RuntimeCallStats stats(clock()); RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id); RuntimeCallTimer timer(clock()); RUNTIME_CALL_STATS_ENTER_WITH_RCS(&stats, &timer, test_counter_1_id); AdvanceClock(25); RUNTIME_CALL_STATS_LEAVE_WITH_RCS(&stats, &timer); EXPECT_EQ(0ul, counter->GetCount()); EXPECT_EQ(0, counter->GetTime().InMilliseconds()); } TEST_F(RuntimeCallStatsTest, TestEnterAndLeaveMacrosWithCallStatsEnabled) { ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(true); RuntimeCallStats stats(clock()); RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id); RuntimeCallTimer timer(clock()); RUNTIME_CALL_STATS_ENTER_WITH_RCS(&stats, &timer, test_counter_1_id); AdvanceClock(25); RUNTIME_CALL_STATS_LEAVE_WITH_RCS(&stats, &timer); EXPECT_EQ(1ul, counter->GetCount()); EXPECT_EQ(25, counter->GetTime().InMilliseconds()); } TEST_F(RuntimeCallStatsTest, TestScopeMacroWithCallStatsDisabled) { ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(false); RuntimeCallStats stats(clock()); RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id); { RUNTIME_CALL_TIMER_SCOPE_WITH_RCS(&stats, test_counter_1_id); AdvanceClock(25); } EXPECT_EQ(0ul, counter->GetCount()); EXPECT_EQ(0, counter->GetTime().InMilliseconds()); } TEST_F(RuntimeCallStatsTest, TestScopeMacroWithCallStatsEnabled) { ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(true); RuntimeCallStats stats(clock()); RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id); { RUNTIME_CALL_TIMER_SCOPE_WITH_RCS(&stats, test_counter_1_id); AdvanceClock(25); } EXPECT_EQ(1ul, counter->GetCount()); EXPECT_EQ(25, counter->GetTime().InMilliseconds()); } TEST_F(RuntimeCallStatsTest, TestScopeWithOptionalMacroWithCallStatsDisabled) { ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(false); RuntimeCallStats stats(clock()); RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id); { base::Optional scope; RUNTIME_CALL_TIMER_SCOPE_WITH_OPTIONAL_RCS(scope, &stats, test_counter_1_id); AdvanceClock(25); } EXPECT_EQ(0ul, counter->GetCount()); EXPECT_EQ(0, counter->GetTime().InMilliseconds()); } TEST_F(RuntimeCallStatsTest, TestScopeWithOptionalMacroWithCallStatsEnabled) { ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(true); RuntimeCallStats stats(clock()); RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id); { base::Optional scope; RUNTIME_CALL_TIMER_SCOPE_WITH_OPTIONAL_RCS(scope, &stats, test_counter_1_id); AdvanceClock(25); } EXPECT_EQ(1ul, counter->GetCount()); EXPECT_EQ(25, counter->GetTime().InMilliseconds()); } } // namespace blink