blob: 9926dc8264a221ba86363dac8284724f2b9cefcb [file] [log] [blame]
// Copyright 2019 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 <memory>
#include <vector>
#include "base/metrics/histogram_samples.h"
#include "base/metrics/statistics_recorder.h"
#include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.h"
#include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing_session.h"
#include "chrome/browser/ui/app_list/arc/arc_app_test.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_profile.h"
#include "components/exo/shell_surface_util.h"
#include "components/exo/surface.h"
#include "components/exo/wm_helper.h"
#include "components/exo/wm_helper_chromeos.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/public/activation_change_observer.h"
namespace arc {
namespace {
constexpr char kFocusAppPackage[] = "focus.app.package";
constexpr char kFocusAppActivity[] = "focus.app.package.Activity";
constexpr char kFocusCategory[] = "OnlineGame";
constexpr char kNonFocusAppPackage[] = "nonfocus.app.package";
constexpr char kNonFocusAppActivity[] = "nonfocus.app.package.Activity";
// For 20 frames.
constexpr base::TimeDelta kTestPeriod =
base::TimeDelta::FromSeconds(1) / (60 / 20);
// Creates app window as ARC++ window.
views::Widget* CreateArcWindow(const std::string& window_app_id) {
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(5, 5, 20, 20);
params.context = nullptr;
views::Widget* widget = new views::Widget();
widget->Init(std::move(params));
// Set ARC id before showing the window to be recognized in
// ArcAppWindowLauncherController.
exo::SetShellApplicationId(widget->GetNativeWindow(), window_app_id);
exo::SetShellMainSurface(widget->GetNativeWindow(), new exo::Surface());
widget->Show();
widget->Activate();
return widget;
}
// Creates name of histogram with required statistics.
std::string GetFocusStatisticName(const std::string& name) {
return base::StringPrintf("Arc.Runtime.Performance.%s.%s", name.c_str(),
kFocusCategory);
}
// Reads statistics value from histogram.
int64_t ReadFocusStatistics(const std::string& name) {
const base::HistogramBase* histogram =
base::StatisticsRecorder::FindHistogram(GetFocusStatisticName(name));
DCHECK(histogram);
std::unique_ptr<base::HistogramSamples> samples =
histogram->SnapshotFinalDelta();
DCHECK(samples.get());
DCHECK_EQ(1, samples->TotalCount());
return samples->sum();
}
} // namespace
// BrowserWithTestWindowTest contains required ash/shell support that would not
// be possible to use directly.
class ArcAppPerformanceTracingTest : public BrowserWithTestWindowTest {
public:
ArcAppPerformanceTracingTest() = default;
~ArcAppPerformanceTracingTest() override = default;
// testing::Test:
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
wm_helper_ = std::make_unique<exo::WMHelperChromeOS>();
exo::WMHelper::SetInstance(wm_helper_.get());
arc_test_.SetUp(profile());
ArcAppPerformanceTracing::SetFocusAppForTesting(
kFocusAppPackage, kFocusAppActivity, kFocusCategory);
performance_tracing_ = std::make_unique<ArcAppPerformanceTracing>(
profile(), nullptr /* bridge, unused */);
performance_tracing_->SetTracingPeriodForTesting(kTestPeriod);
}
void TearDown() override {
performance_tracing_->Shutdown();
performance_tracing_.reset();
arc_test_.TearDown();
exo::WMHelper::SetInstance(nullptr);
wm_helper_.reset();
BrowserWithTestWindowTest::TearDown();
}
protected:
// Ensures that tracing is active.
void StartArcFocusAppTracing() {
views::Widget* const arc_widget = CreateArcWindow("org.chromium.arc.1");
DCHECK(arc_widget && arc_widget->GetNativeWindow());
performance_tracing().OnWindowActivated(
wm::ActivationChangeObserver::ActivationReason::ACTIVATION_CLIENT,
arc_widget->GetNativeWindow(), arc_widget->GetNativeWindow());
performance_tracing().OnTaskCreated(1 /* task_Id */, kFocusAppPackage,
kFocusAppActivity,
std::string() /* intent */);
DCHECK(performance_tracing().session());
DCHECK(!performance_tracing().session()->tracing_active());
performance_tracing().session()->FireTimerForTesting();
DCHECK(performance_tracing().session());
DCHECK(performance_tracing().session()->tracing_active());
}
// Sends sequence of commits where each commit is delayed for specific delta
// from |deltas|.
void PlaySequence(const std::vector<base::TimeDelta>& deltas) {
DCHECK(performance_tracing().session());
DCHECK(performance_tracing().session()->tracing_active());
base::Time timestamp = base::Time::Now();
performance_tracing().session()->OnCommitForTesting(timestamp);
for (const base::TimeDelta& delta : deltas) {
timestamp += delta;
performance_tracing().session()->OnCommitForTesting(timestamp);
}
// Fire timer at the end to finish statistics tracing.
performance_tracing().session()->FireTimerForTesting();
}
ArcAppPerformanceTracing& performance_tracing() {
return *performance_tracing_;
}
private:
ArcAppTest arc_test_;
std::unique_ptr<exo::WMHelper> wm_helper_;
std::unique_ptr<ArcAppPerformanceTracing> performance_tracing_;
DISALLOW_COPY_AND_ASSIGN(ArcAppPerformanceTracingTest);
};
TEST_F(ArcAppPerformanceTracingTest, TracingScheduled) {
// By default it is inactive.
EXPECT_FALSE(performance_tracing().session());
// Report task first.
performance_tracing().OnTaskCreated(1 /* task_Id */, kFocusAppPackage,
kFocusAppActivity,
std::string() /* intent */);
EXPECT_FALSE(performance_tracing().session());
// Create window second.
views::Widget* const arc_widget1 = CreateArcWindow("org.chromium.arc.1");
ASSERT_TRUE(arc_widget1);
ASSERT_TRUE(arc_widget1->GetNativeWindow());
performance_tracing().OnWindowActivated(
wm::ActivationChangeObserver::ActivationReason::ACTIVATION_CLIENT,
arc_widget1->GetNativeWindow(), nullptr /* lost_active */);
ASSERT_TRUE(performance_tracing().session());
EXPECT_FALSE(performance_tracing().session()->tracing_active());
// Test reverse order, create window first.
views::Widget* const arc_widget2 = CreateArcWindow("org.chromium.arc.2");
ASSERT_TRUE(arc_widget2);
ASSERT_TRUE(arc_widget2->GetNativeWindow());
performance_tracing().OnWindowActivated(
wm::ActivationChangeObserver::ActivationReason::ACTIVATION_CLIENT,
arc_widget2->GetNativeWindow(), arc_widget2->GetNativeWindow());
// Task is not yet created, this also resets previous tracing.
EXPECT_FALSE(performance_tracing().session());
// Report task second.
performance_tracing().OnTaskCreated(2 /* task_Id */, kFocusAppPackage,
kFocusAppActivity,
std::string() /* intent */);
ASSERT_TRUE(performance_tracing().session());
EXPECT_FALSE(performance_tracing().session()->tracing_active());
}
TEST_F(ArcAppPerformanceTracingTest, TracingNotScheduledForNonFocusApp) {
views::Widget* const arc_widget = CreateArcWindow("org.chromium.arc.1");
ASSERT_TRUE(arc_widget);
ASSERT_TRUE(arc_widget->GetNativeWindow());
performance_tracing().OnWindowActivated(
wm::ActivationChangeObserver::ActivationReason::ACTIVATION_CLIENT,
arc_widget->GetNativeWindow(), arc_widget->GetNativeWindow());
EXPECT_FALSE(performance_tracing().session());
performance_tracing().OnTaskCreated(1 /* task_Id */, kNonFocusAppPackage,
kNonFocusAppActivity,
std::string() /* intent */);
EXPECT_FALSE(performance_tracing().session());
}
TEST_F(ArcAppPerformanceTracingTest, TracingStoppedOnIdle) {
StartArcFocusAppTracing();
const base::TimeDelta normal_interval = base::TimeDelta::FromSeconds(1) / 60;
base::Time timestamp = base::Time::Now();
performance_tracing().session()->OnCommitForTesting(timestamp);
// Expected updates;
timestamp += normal_interval;
performance_tracing().session()->OnCommitForTesting(timestamp);
ASSERT_TRUE(performance_tracing().session());
EXPECT_TRUE(performance_tracing().session()->tracing_active());
timestamp += normal_interval * 5;
performance_tracing().session()->OnCommitForTesting(timestamp);
ASSERT_TRUE(performance_tracing().session());
EXPECT_TRUE(performance_tracing().session()->tracing_active());
// Too long update.
timestamp += normal_interval * 10;
performance_tracing().session()->OnCommitForTesting(timestamp);
// Tracing is rescheduled and no longer active.
ASSERT_TRUE(performance_tracing().session());
EXPECT_FALSE(performance_tracing().session()->tracing_active());
}
TEST_F(ArcAppPerformanceTracingTest, StatisticsReported) {
StartArcFocusAppTracing();
const base::TimeDelta normal_interval = base::TimeDelta::FromSeconds(1) / 60;
const base::TimeDelta error1 = base::TimeDelta::FromMicroseconds(100);
const base::TimeDelta error2 = base::TimeDelta::FromMicroseconds(200);
const base::TimeDelta error3 = base::TimeDelta::FromMicroseconds(300);
const std::vector<base::TimeDelta> sequence = {
normal_interval + error1,
normal_interval + error2,
// One frame skip
normal_interval * 2 + error3,
normal_interval - error1,
normal_interval - error2,
// Two frames skip
normal_interval * 3 - error3,
normal_interval + error1,
normal_interval + error2,
normal_interval * 2 + error3,
normal_interval - error1,
normal_interval * 2 - error2,
normal_interval - error3,
normal_interval + error1,
normal_interval + error2,
normal_interval + error3,
};
EXPECT_FALSE(performance_tracing().WasReported(kFocusCategory));
PlaySequence(sequence);
EXPECT_TRUE(performance_tracing().WasReported(kFocusCategory));
EXPECT_EQ(45L, ReadFocusStatistics("FPS"));
EXPECT_EQ(216L, ReadFocusStatistics("CommitDeviation"));
EXPECT_EQ(48L, ReadFocusStatistics("RenderQuality"));
}
} // namespace arc