1 // Copyright (c) 2014 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.
5 #include "components/browser_watcher/watcher_metrics_provider_win.h"
9 #include "base/process/process_handle.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/test/histogram_tester.h"
13 #include "base/test/test_reg_util_win.h"
14 #include "base/win/registry.h"
15 #include "components/browser_watcher/exit_funnel_win.h"
16 #include "testing/gtest/include/gtest/gtest.h"
18 namespace browser_watcher
{
22 const wchar_t kRegistryPath
[] = L
"Software\\WatcherMetricsProviderWinTest";
24 class WatcherMetricsProviderWinTest
: public testing::Test
{
26 typedef testing::Test Super
;
28 virtual void SetUp() override
{
31 override_manager_
.OverrideRegistry(HKEY_CURRENT_USER
);
34 void AddProcessExitCode(bool use_own_pid
, int exit_code
) {
37 pid
= base::GetCurrentProcId();
39 // Make sure not to accidentally collide with own pid.
42 } while (pid
== static_cast<int>(base::GetCurrentProcId()));
45 base::win::RegKey
key(HKEY_CURRENT_USER
, kRegistryPath
, KEY_WRITE
);
47 // Make up a unique key, starting with the given pid.
48 base::string16
key_name(base::StringPrintf(L
"%d-%d", pid
, rand()));
50 // Write the exit code to registry.
51 ULONG result
= key
.WriteValue(key_name
.c_str(), exit_code
);
52 ASSERT_EQ(result
, ERROR_SUCCESS
);
55 size_t ExitCodeRegistryPathValueCount() {
56 base::win::RegKey
key(HKEY_CURRENT_USER
, kRegistryPath
, KEY_READ
);
57 return key
.GetValueCount();
60 void AddExitFunnelEvent(int pid
, const base::char16
* name
, int64 value
) {
61 base::string16 key_name
=
62 base::StringPrintf(L
"%ls\\%d-%d", kRegistryPath
, pid
, pid
);
64 base::win::RegKey
key(HKEY_CURRENT_USER
, key_name
.c_str(), KEY_WRITE
);
65 ASSERT_EQ(key
.WriteValue(name
, &value
, sizeof(value
), REG_QWORD
),
70 registry_util::RegistryOverrideManager override_manager_
;
71 base::HistogramTester histogram_tester_
;
76 TEST_F(WatcherMetricsProviderWinTest
, RecordsStabilityHistogram
) {
77 // Record multiple success exits.
78 for (size_t i
= 0; i
< 11; ++i
)
79 AddProcessExitCode(false, 0);
81 // Record a single failure.
82 AddProcessExitCode(false, 100);
84 WatcherMetricsProviderWin
provider(kRegistryPath
, true);
86 provider
.ProvideStabilityMetrics(NULL
);
87 histogram_tester_
.ExpectBucketCount(
88 WatcherMetricsProviderWin::kBrowserExitCodeHistogramName
, 0, 11);
89 histogram_tester_
.ExpectBucketCount(
90 WatcherMetricsProviderWin::kBrowserExitCodeHistogramName
, 100, 1);
91 histogram_tester_
.ExpectTotalCount(
92 WatcherMetricsProviderWin::kBrowserExitCodeHistogramName
, 12);
94 // Verify that the reported values are gone.
95 EXPECT_EQ(ExitCodeRegistryPathValueCount(), 0);
98 TEST_F(WatcherMetricsProviderWinTest
, DoesNotReportOwnProcessId
) {
99 // Record multiple success exits.
100 for (size_t i
= 0; i
< 11; ++i
)
101 AddProcessExitCode(i
, 0);
103 // Record own process as STILL_ACTIVE.
104 AddProcessExitCode(true, STILL_ACTIVE
);
106 WatcherMetricsProviderWin
provider(kRegistryPath
, true);
108 provider
.ProvideStabilityMetrics(NULL
);
109 histogram_tester_
.ExpectUniqueSample(
110 WatcherMetricsProviderWin::kBrowserExitCodeHistogramName
, 0, 11);
112 // Verify that the reported values are gone.
113 EXPECT_EQ(ExitCodeRegistryPathValueCount(), 1);
116 TEST_F(WatcherMetricsProviderWinTest
, RecordsOrderedExitFunnelEvents
) {
117 // Record an exit funnel with a given set of timings and check that the
118 // ordering is correct on the reported histograms.
119 // Note the recorded times are in microseconds, but the reporting is in
120 // milliseconds, hence the times 1000.
121 AddExitFunnelEvent(100, L
"One", 1000 * 1000);
122 AddExitFunnelEvent(100, L
"Two", 1010 * 1000);
123 AddExitFunnelEvent(100, L
"Three", 990 * 1000);
125 WatcherMetricsProviderWin
provider(kRegistryPath
, true);
127 provider
.ProvideStabilityMetrics(NULL
);
128 histogram_tester_
.ExpectUniqueSample("Stability.ExitFunnel.Three", 0, 1);
129 histogram_tester_
.ExpectUniqueSample("Stability.ExitFunnel.One", 10, 1);
130 histogram_tester_
.ExpectUniqueSample("Stability.ExitFunnel.Two", 20, 1);
132 // Make sure the subkey is deleted on reporting.
133 base::win::RegistryKeyIterator
it(HKEY_CURRENT_USER
, kRegistryPath
);
134 ASSERT_EQ(it
.SubkeyCount(), 0);
137 TEST_F(WatcherMetricsProviderWinTest
, ReadsExitFunnelWrites
) {
138 // Test that the metrics provider picks up the writes from the funnel.
141 // Events against our own process should not get reported.
142 ASSERT_TRUE(funnel
.Init(kRegistryPath
, base::GetCurrentProcessHandle()));
143 ASSERT_TRUE(funnel
.RecordEvent(L
"Forgetaboutit"));
145 // Reset the funnel to a pseudo process. The PID 4 is the system process,
146 // which tests can hopefully never open.
147 ASSERT_TRUE(funnel
.InitImpl(kRegistryPath
, 4, base::Time::Now()));
149 // Each named event can only exist in a single copy.
150 ASSERT_TRUE(funnel
.RecordEvent(L
"One"));
151 ASSERT_TRUE(funnel
.RecordEvent(L
"One"));
152 ASSERT_TRUE(funnel
.RecordEvent(L
"One"));
153 ASSERT_TRUE(funnel
.RecordEvent(L
"Two"));
154 ASSERT_TRUE(funnel
.RecordEvent(L
"Three"));
156 WatcherMetricsProviderWin
provider(kRegistryPath
, true);
158 provider
.ProvideStabilityMetrics(NULL
);
159 histogram_tester_
.ExpectTotalCount("Stability.ExitFunnel.One", 1);
160 histogram_tester_
.ExpectTotalCount("Stability.ExitFunnel.Two", 1);
161 histogram_tester_
.ExpectTotalCount("Stability.ExitFunnel.Three", 1);
163 // Make sure the subkey for the pseudo process has been deleted on reporting.
164 base::win::RegistryKeyIterator
it(HKEY_CURRENT_USER
, kRegistryPath
);
165 ASSERT_EQ(it
.SubkeyCount(), 1);
168 TEST_F(WatcherMetricsProviderWinTest
, ClearsExitFunnelWriteWhenNotReporting
) {
169 // Tests that the metrics provider cleans up, but doesn't report exit funnels
170 // when funnel reporting is quenched.
173 // Events against our own process should not get reported.
174 ASSERT_TRUE(funnel
.Init(kRegistryPath
, base::GetCurrentProcessHandle()));
175 ASSERT_TRUE(funnel
.RecordEvent(L
"Forgetaboutit"));
177 // Reset the funnel to a pseudo process. The PID 4 is the system process,
178 // which tests can hopefully never open.
179 ASSERT_TRUE(funnel
.InitImpl(kRegistryPath
, 4, base::Time::Now()));
181 // Each named event can only exist in a single copy.
182 ASSERT_TRUE(funnel
.RecordEvent(L
"One"));
183 ASSERT_TRUE(funnel
.RecordEvent(L
"One"));
184 ASSERT_TRUE(funnel
.RecordEvent(L
"One"));
185 ASSERT_TRUE(funnel
.RecordEvent(L
"Two"));
186 ASSERT_TRUE(funnel
.RecordEvent(L
"Three"));
188 // Turn off exit funnel reporting.
189 WatcherMetricsProviderWin
provider(kRegistryPath
, false);
191 provider
.ProvideStabilityMetrics(NULL
);
192 histogram_tester_
.ExpectTotalCount("Stability.ExitFunnel.One", 0);
193 histogram_tester_
.ExpectTotalCount("Stability.ExitFunnel.Two", 0);
194 histogram_tester_
.ExpectTotalCount("Stability.ExitFunnel.Three", 0);
196 // Make sure the subkey for the pseudo process has been deleted on reporting.
197 base::win::RegistryKeyIterator
it(HKEY_CURRENT_USER
, kRegistryPath
);
198 ASSERT_EQ(it
.SubkeyCount(), 1);
201 } // namespace browser_watcher