blob: 955ad06903d38882755eef1c45f4d11daba95633 [file] [log] [blame]
// Copyright (c) 2014 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/browser_watcher/watcher_metrics_provider_win.h"
#include <cstdlib>
#include "base/process/process_handle.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "base/test/histogram_tester.h"
#include "base/test/test_reg_util_win.h"
#include "base/win/registry.h"
#include "components/browser_watcher/exit_funnel_win.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_watcher {
namespace {
const wchar_t kRegistryPath[] = L"Software\\WatcherMetricsProviderWinTest";
class WatcherMetricsProviderWinTest : public testing::Test {
public:
typedef testing::Test Super;
virtual void SetUp() override {
Super::SetUp();
override_manager_.OverrideRegistry(HKEY_CURRENT_USER);
}
void AddProcessExitCode(bool use_own_pid, int exit_code) {
int pid = 0;
if (use_own_pid) {
pid = base::GetCurrentProcId();
} else {
// Make sure not to accidentally collide with own pid.
do {
pid = rand();
} while (pid == static_cast<int>(base::GetCurrentProcId()));
}
base::win::RegKey key(HKEY_CURRENT_USER, kRegistryPath, KEY_WRITE);
// Make up a unique key, starting with the given pid.
base::string16 key_name(base::StringPrintf(L"%d-%d", pid, rand()));
// Write the exit code to registry.
ULONG result = key.WriteValue(key_name.c_str(), exit_code);
ASSERT_EQ(result, ERROR_SUCCESS);
}
size_t ExitCodeRegistryPathValueCount() {
base::win::RegKey key(HKEY_CURRENT_USER, kRegistryPath, KEY_READ);
return key.GetValueCount();
}
void AddExitFunnelEvent(int pid, const base::char16* name, int64 value) {
base::string16 key_name =
base::StringPrintf(L"%ls\\%d-%d", kRegistryPath, pid, pid);
base::win::RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_WRITE);
ASSERT_EQ(key.WriteValue(name, &value, sizeof(value), REG_QWORD),
ERROR_SUCCESS);
}
protected:
registry_util::RegistryOverrideManager override_manager_;
base::HistogramTester histogram_tester_;
};
} // namespace
TEST_F(WatcherMetricsProviderWinTest, RecordsStabilityHistogram) {
// Record multiple success exits.
for (size_t i = 0; i < 11; ++i)
AddProcessExitCode(false, 0);
// Record a single failure.
AddProcessExitCode(false, 100);
WatcherMetricsProviderWin provider(kRegistryPath);
provider.ProvideStabilityMetrics(NULL);
histogram_tester_.ExpectBucketCount(
WatcherMetricsProviderWin::kBrowserExitCodeHistogramName, 0, 11);
histogram_tester_.ExpectBucketCount(
WatcherMetricsProviderWin::kBrowserExitCodeHistogramName, 100, 1);
histogram_tester_.ExpectTotalCount(
WatcherMetricsProviderWin::kBrowserExitCodeHistogramName, 12);
// Verify that the reported values are gone.
EXPECT_EQ(ExitCodeRegistryPathValueCount(), 0);
}
TEST_F(WatcherMetricsProviderWinTest, DoesNotReportOwnProcessId) {
// Record multiple success exits.
for (size_t i = 0; i < 11; ++i)
AddProcessExitCode(i, 0);
// Record own process as STILL_ACTIVE.
AddProcessExitCode(true, STILL_ACTIVE);
WatcherMetricsProviderWin provider(kRegistryPath);
provider.ProvideStabilityMetrics(NULL);
histogram_tester_.ExpectUniqueSample(
WatcherMetricsProviderWin::kBrowserExitCodeHistogramName, 0, 11);
// Verify that the reported values are gone.
EXPECT_EQ(ExitCodeRegistryPathValueCount(), 1);
}
TEST_F(WatcherMetricsProviderWinTest, RecordsOrderedExitFunnelEvents) {
// Record an exit funnel with a given set of timings and check that the
// ordering is correct on the reported histograms.
// Note the recorded times are in microseconds, but the reporting is in
// milliseconds, hence the times 1000.
AddExitFunnelEvent(100, L"One", 1000 * 1000);
AddExitFunnelEvent(100, L"Two", 1010 * 1000);
AddExitFunnelEvent(100, L"Three", 990 * 1000);
WatcherMetricsProviderWin provider(kRegistryPath);
provider.ProvideStabilityMetrics(NULL);
histogram_tester_.ExpectUniqueSample("Stability.ExitFunnel.Three", 0, 1);
histogram_tester_.ExpectUniqueSample("Stability.ExitFunnel.One", 10, 1);
histogram_tester_.ExpectUniqueSample("Stability.ExitFunnel.Two", 20, 1);
// Make sure the subkey is deleted on reporting.
base::win::RegistryKeyIterator it(HKEY_CURRENT_USER, kRegistryPath);
ASSERT_EQ(it.SubkeyCount(), 0);
}
TEST_F(WatcherMetricsProviderWinTest, ReadsExitFunnelWrites) {
// Test that the metrics provider picks up the writes from
ExitFunnel funnel;
ASSERT_TRUE(funnel.Init(kRegistryPath, base::GetCurrentProcessHandle()));
// Each named event can only exist in a single copy.
ASSERT_TRUE(funnel.RecordEvent(L"One"));
ASSERT_TRUE(funnel.RecordEvent(L"One"));
ASSERT_TRUE(funnel.RecordEvent(L"One"));
ASSERT_TRUE(funnel.RecordEvent(L"Two"));
ASSERT_TRUE(funnel.RecordEvent(L"Three"));
WatcherMetricsProviderWin provider(kRegistryPath);
provider.ProvideStabilityMetrics(NULL);
histogram_tester_.ExpectTotalCount("Stability.ExitFunnel.One", 1);
histogram_tester_.ExpectTotalCount("Stability.ExitFunnel.Two", 1);
histogram_tester_.ExpectTotalCount("Stability.ExitFunnel.Three", 1);
// Make sure the subkey has been deleted on reporting.
base::win::RegistryKeyIterator it(HKEY_CURRENT_USER, kRegistryPath);
ASSERT_EQ(it.SubkeyCount(), 0);
}
} // namespace browser_watcher