blob: 49a291bdb3b6606971a0fb381796503fbf907f68 [file] [log] [blame]
// 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 "components/arc/metrics/arc_metrics_service.h"
#include <algorithm>
#include <array>
#include <memory>
#include <string>
#include <utility>
#include "base/metrics/histogram_samples.h"
#include "base/run_loop.h"
#include "base/test/histogram_tester.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_session_manager_client.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace arc {
namespace {
// The event names the container sends to Chrome.
constexpr std::array<const char*, 11> kBootEvents{
"boot_progress_start",
"boot_progress_preload_start",
"boot_progress_preload_end",
"boot_progress_system_run",
"boot_progress_pms_start",
"boot_progress_pms_system_scan_start",
"boot_progress_pms_data_scan_start",
"boot_progress_pms_scan_end",
"boot_progress_pms_ready",
"boot_progress_ams_ready",
"boot_progress_enable_screen"};
class ArcMetricsServiceTest : public testing::Test {
protected:
ArcMetricsServiceTest()
: arc_service_manager_(std::make_unique<ArcServiceManager>()),
context_(std::make_unique<TestBrowserContext>()),
service_(
ArcMetricsService::GetForBrowserContextForTesting(context_.get())) {
chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
std::make_unique<chromeos::FakeSessionManagerClient>());
GetSessionManagerClient()->set_arc_available(true);
}
~ArcMetricsServiceTest() override {
service_->Shutdown();
chromeos::DBusThreadManager::Shutdown();
}
ArcMetricsService* service() { return service_; }
void SetArcStartTimeInMs(uint64_t arc_start_time_in_ms) {
const base::TimeTicks arc_start_time =
base::TimeDelta::FromMilliseconds(arc_start_time_in_ms) +
base::TimeTicks();
GetSessionManagerClient()->set_arc_start_time(arc_start_time);
}
std::vector<mojom::BootProgressEventPtr> GetBootProgressEvents(
uint64_t start_in_ms,
uint64_t step_in_ms) {
std::vector<mojom::BootProgressEventPtr> events;
for (size_t i = 0; i < kBootEvents.size(); ++i) {
events.emplace_back(mojom::BootProgressEvent::New(
kBootEvents[i], start_in_ms + (step_in_ms * i)));
}
return events;
}
private:
chromeos::FakeSessionManagerClient* GetSessionManagerClient() {
return static_cast<chromeos::FakeSessionManagerClient*>(
chromeos::DBusThreadManager::Get()->GetSessionManagerClient());
}
content::TestBrowserThreadBundle thread_bundle_;
std::unique_ptr<ArcServiceManager> arc_service_manager_;
std::unique_ptr<TestBrowserContext> context_;
ArcMetricsService* const service_;
DISALLOW_COPY_AND_ASSIGN(ArcMetricsServiceTest);
};
// Tests that ReportBootProgress() actually records UMA stats.
TEST_F(ArcMetricsServiceTest, ReportBootProgress_FirstBoot) {
// Start the full ARC container at t=10. Also set boot_progress_start to 10,
// boot_progress_preload_start to 11, and so on.
constexpr uint64_t kArcStartTimeMs = 10;
SetArcStartTimeInMs(kArcStartTimeMs);
std::vector<mojom::BootProgressEventPtr> events(
GetBootProgressEvents(kArcStartTimeMs, 1 /* step_in_ms */));
// Call ReportBootProgress() and then confirm that
// Arc.boot_progress_start.FirstBoot is recorded with 0 (ms),
// Arc.boot_progress_preload_start.FirstBoot is with 1 (ms), etc.
base::HistogramTester tester;
service()->ReportBootProgress(std::move(events), mojom::BootType::FIRST_BOOT);
base::RunLoop().RunUntilIdle();
for (size_t i = 0; i < kBootEvents.size(); ++i) {
tester.ExpectUniqueSample(
std::string("Arc.") + kBootEvents[i] + ".FirstBoot", i,
1 /* count of the sample */);
}
// Confirm that Arc.AndroidBootTime.FirstBoot is also recorded, and has the
// same value as "Arc.boot_progress_enable_screen.FirstBoot".
std::unique_ptr<base::HistogramSamples> samples =
tester.GetHistogramSamplesSinceCreation(
"Arc." + std::string(kBootEvents.back()) + ".FirstBoot");
ASSERT_TRUE(samples.get());
tester.ExpectUniqueSample("Arc.AndroidBootTime.FirstBoot", samples->sum(), 1);
}
// Does the same but with negative values and FIRST_BOOT_AFTER_UPDATE.
TEST_F(ArcMetricsServiceTest, ReportBootProgress_FirstBootAfterUpdate) {
// Start the full ARC container at t=10. Also set boot_progress_start to 5,
// boot_progress_preload_start to 7, and so on. This can actually happen
// because the mini container can finish up to boot_progress_preload_end
// before the full container is started.
constexpr uint64_t kArcStartTimeMs = 10;
SetArcStartTimeInMs(kArcStartTimeMs);
std::vector<mojom::BootProgressEventPtr> events(
GetBootProgressEvents(kArcStartTimeMs - 5, 2 /* step_in_ms */));
// Call ReportBootProgress() and then confirm that
// Arc.boot_progress_start.FirstBoot is recorded with 0 (ms),
// Arc.boot_progress_preload_start.FirstBoot is with 0 (ms), etc. Unlike our
// performance dashboard where negative performance numbers are treated as-is,
// UMA treats them as zeros.
base::HistogramTester tester;
// This time, use mojom::BootType::FIRST_BOOT_AFTER_UPDATE.
service()->ReportBootProgress(std::move(events),
mojom::BootType::FIRST_BOOT_AFTER_UPDATE);
base::RunLoop().RunUntilIdle();
for (size_t i = 0; i < kBootEvents.size(); ++i) {
const int expected = std::max<int>(0, i * 2 - 5);
tester.ExpectUniqueSample(
std::string("Arc.") + kBootEvents[i] + ".FirstBootAfterUpdate",
expected, 1);
}
std::unique_ptr<base::HistogramSamples> samples =
tester.GetHistogramSamplesSinceCreation(
"Arc." + std::string(kBootEvents.back()) + ".FirstBootAfterUpdate");
ASSERT_TRUE(samples.get());
tester.ExpectUniqueSample("Arc.AndroidBootTime.FirstBootAfterUpdate",
samples->sum(), 1);
}
// Does the same but with REGULAR_BOOT.
TEST_F(ArcMetricsServiceTest, ReportBootProgress_RegularBoot) {
constexpr uint64_t kArcStartTimeMs = 10;
SetArcStartTimeInMs(kArcStartTimeMs);
std::vector<mojom::BootProgressEventPtr> events(
GetBootProgressEvents(kArcStartTimeMs - 5, 2 /* step_in_ms */));
base::HistogramTester tester;
service()->ReportBootProgress(std::move(events),
mojom::BootType::REGULAR_BOOT);
base::RunLoop().RunUntilIdle();
for (size_t i = 0; i < kBootEvents.size(); ++i) {
const int expected = std::max<int>(0, i * 2 - 5);
tester.ExpectUniqueSample(
std::string("Arc.") + kBootEvents[i] + ".RegularBoot", expected, 1);
}
std::unique_ptr<base::HistogramSamples> samples =
tester.GetHistogramSamplesSinceCreation(
"Arc." + std::string(kBootEvents.back()) + ".RegularBoot");
ASSERT_TRUE(samples.get());
tester.ExpectUniqueSample("Arc.AndroidBootTime.RegularBoot", samples->sum(),
1);
}
// Tests that no UMA is recorded when nothing is reported.
TEST_F(ArcMetricsServiceTest, ReportBootProgress_EmptyResults) {
SetArcStartTimeInMs(100);
std::vector<mojom::BootProgressEventPtr> events; // empty
base::HistogramTester tester;
service()->ReportBootProgress(std::move(events), mojom::BootType::FIRST_BOOT);
base::RunLoop().RunUntilIdle();
for (size_t i = 0; i < kBootEvents.size(); ++i) {
tester.ExpectTotalCount(std::string("Arc.") + kBootEvents[i] + ".FirstBoot",
0);
}
tester.ExpectTotalCount("Arc.AndroidBootTime.FirstBoot", 0);
}
// Tests that no UMA is recorded when BootType is invalid.
TEST_F(ArcMetricsServiceTest, ReportBootProgress_InvalidBootType) {
SetArcStartTimeInMs(100);
std::vector<mojom::BootProgressEventPtr> events(
GetBootProgressEvents(123, 456));
base::HistogramTester tester;
service()->ReportBootProgress(std::move(events), mojom::BootType::UNKNOWN);
base::RunLoop().RunUntilIdle();
for (const std::string& suffix :
{".FirstBoot", ".FirstBootAfterUpdate", ".RegularBoot"}) {
tester.ExpectTotalCount("Arc." + (kBootEvents.front() + suffix), 0);
tester.ExpectTotalCount("Arc." + (kBootEvents.back() + suffix), 0);
tester.ExpectTotalCount("Arc.AndroidBootTime" + suffix, 0);
}
}
TEST_F(ArcMetricsServiceTest, ReportNativeBridge) {
EXPECT_EQ(service()->native_bridge_type_for_testing(),
ArcMetricsService::NativeBridgeType::UNKNOWN);
service()->ReportNativeBridge(mojom::NativeBridgeType::NONE);
EXPECT_EQ(service()->native_bridge_type_for_testing(),
ArcMetricsService::NativeBridgeType::NONE);
service()->ReportNativeBridge(mojom::NativeBridgeType::HOUDINI);
EXPECT_EQ(service()->native_bridge_type_for_testing(),
ArcMetricsService::NativeBridgeType::HOUDINI);
service()->ReportNativeBridge(mojom::NativeBridgeType::NDK_TRANSLATION);
EXPECT_EQ(service()->native_bridge_type_for_testing(),
ArcMetricsService::NativeBridgeType::NDK_TRANSLATION);
}
TEST_F(ArcMetricsServiceTest, RecordNativeBridgeUMA) {
base::HistogramTester tester;
service()->RecordNativeBridgeUMA();
tester.ExpectUniqueSample(
"Arc.NativeBridge",
static_cast<int>(ArcMetricsService::NativeBridgeType::UNKNOWN), 1);
service()->ReportNativeBridge(mojom::NativeBridgeType::NONE);
// Check that ReportNativeBridge doesn't record histograms.
tester.ExpectTotalCount("Arc.NativeBridge", 1);
service()->RecordNativeBridgeUMA();
tester.ExpectBucketCount(
"Arc.NativeBridge",
static_cast<int>(ArcMetricsService::NativeBridgeType::NONE), 1);
service()->ReportNativeBridge(mojom::NativeBridgeType::HOUDINI);
tester.ExpectTotalCount("Arc.NativeBridge", 2);
service()->RecordNativeBridgeUMA();
tester.ExpectBucketCount(
"Arc.NativeBridge",
static_cast<int>(ArcMetricsService::NativeBridgeType::HOUDINI), 1);
service()->ReportNativeBridge(mojom::NativeBridgeType::NDK_TRANSLATION);
tester.ExpectTotalCount("Arc.NativeBridge", 3);
service()->RecordNativeBridgeUMA();
tester.ExpectBucketCount(
"Arc.NativeBridge",
static_cast<int>(ArcMetricsService::NativeBridgeType::NDK_TRANSLATION),
1);
tester.ExpectTotalCount("Arc.NativeBridge", 4);
}
} // namespace
} // namespace arc