// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/power/ml/user_activity_manager.h"
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "ash/constants/ash_features.h"
#include "base/cancelable_callback.h"
#include "base/functional/bind.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/timer/timer.h"
#include "chrome/browser/ash/power/ml/idle_event_notifier.h"
#include "chrome/browser/ash/power/ml/smart_dim/ml_agent.h"
#include "chrome/browser/ash/power/ml/user_activity_event.pb.h"
#include "chrome/browser/ash/power/ml/user_activity_ukm_logger.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tabs/tab_activity_simulator.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/test_browser_window_aura.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power_manager/idle.pb.h"
#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
#include "chromeos/services/machine_learning/public/cpp/fake_service_connection.h"
#include "chromeos/services/machine_learning/public/cpp/service_connection.h"
#include "components/session_manager/session_manager_types.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/web_contents_tester.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/user_activity/user_activity_detector.h"
namespace ash {
namespace power {
namespace ml {
using content::WebContentsTester;
void EqualEvent(const UserActivityEvent::Event& expected_event,
const UserActivityEvent::Event& result_event) {
EXPECT_EQ(expected_event.type(), result_event.type());
EXPECT_EQ(expected_event.reason(), result_event.reason());
EXPECT_EQ(expected_event.log_duration_sec(), result_event.log_duration_sec());
EXPECT_EQ(expected_event.screen_dim_occurred(),
result_event.screen_dim_occurred());
EXPECT_EQ(expected_event.screen_off_occurred(),
result_event.screen_off_occurred());
EXPECT_EQ(expected_event.screen_lock_occurred(),
result_event.screen_lock_occurred());
}
void EqualModelPrediction(
const UserActivityEvent::ModelPrediction& expected_prediction,
const UserActivityEvent::ModelPrediction& result_prediction) {
EXPECT_EQ(expected_prediction.model_applied(),
result_prediction.model_applied());
EXPECT_EQ(expected_prediction.response(), result_prediction.response());
if (expected_prediction.response() !=
UserActivityEvent::ModelPrediction::MODEL_ERROR) {
EXPECT_EQ(expected_prediction.decision_threshold(),
result_prediction.decision_threshold());
EXPECT_EQ(expected_prediction.inactivity_score(),
result_prediction.inactivity_score());
} else {
EXPECT_FALSE(result_prediction.has_decision_threshold());
EXPECT_FALSE(result_prediction.has_inactivity_score());
}
}
// Testing UKM logger.
class TestingUserActivityUkmLogger : public UserActivityUkmLogger {
public:
TestingUserActivityUkmLogger() = default;
TestingUserActivityUkmLogger(const TestingUserActivityUkmLogger&) = delete;
TestingUserActivityUkmLogger& operator=(const TestingUserActivityUkmLogger&) =
delete;
~TestingUserActivityUkmLogger() override = default;
const std::vector<UserActivityEvent>& events() const { return events_; }
// UserActivityUkmLogger overrides:
void LogActivity(const UserActivityEvent& event) override {
events_.push_back(event);
}
private:
std::vector<UserActivityEvent> events_;
};
class UserActivityManagerTest : public ChromeRenderViewHostTestHarness {
public:
UserActivityManagerTest()
: ChromeRenderViewHostTestHarness(
base::test::TaskEnvironment::MainThreadType::UI,
base::test::TaskEnvironment::TimeSource::MOCK_TIME,
base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED) {}
UserActivityManagerTest(const UserActivityManagerTest&) = delete;
UserActivityManagerTest& operator=(const UserActivityManagerTest&) = delete;
~UserActivityManagerTest() override = default;
// ChromeRenderViewHostTestHarness:
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
chromeos::PowerManagerClient::InitializeFake();
mojo::PendingRemote<viz::mojom::VideoDetectorObserver> observer;
activity_logger_ = std::make_unique<UserActivityManager>(
&delegate_, ui::UserActivityDetector::Get(),
chromeos::PowerManagerClient::Get(), &session_manager_,
observer.InitWithNewPipeAndPassReceiver());
chromeos::machine_learning::ServiceConnection::
UseFakeServiceConnectionForTesting(&fake_service_connection_);
chromeos::machine_learning::ServiceConnection::GetInstance()->Initialize();
}
void TearDown() override {
activity_logger_.reset();
chromeos::PowerManagerClient::Shutdown();
ChromeRenderViewHostTestHarness::TearDown();
}
protected:
void ReportUserActivity(const ui::Event* event) {
activity_logger_->OnUserActivity(event);
}
// Requests a smart dim decision from UserActivityManager based on |data|.
// Populates |*should_defer| with the result once it is provided.
void ReportIdleEvent(const IdleEventNotifier::ActivityData& data,
bool* should_defer = nullptr) {
activity_logger_->UpdateAndGetSmartDimDecision(
data, base::BindOnce(
[](bool* should_defer, bool decision) {
if (should_defer)
*should_defer = decision;
},
should_defer));
}
void ReportLidEvent(chromeos::PowerManagerClient::LidState state) {
chromeos::FakePowerManagerClient::Get()->SetLidState(
state, base::TimeTicks::UnixEpoch());
}
void ReportPowerChangeEvent(
power_manager::PowerSupplyProperties::ExternalPower power,
float battery_percent) {
power_manager::PowerSupplyProperties proto;
proto.set_external_power(power);
proto.set_battery_percent(battery_percent);
chromeos::FakePowerManagerClient::Get()->UpdatePowerProperties(proto);
}
void ReportTabletModeEvent(chromeos::PowerManagerClient::TabletMode mode) {
chromeos::FakePowerManagerClient::Get()->SetTabletMode(
mode, base::TimeTicks::UnixEpoch());
}
void ReportVideoStart() { activity_logger_->OnVideoActivityStarted(); }
void ReportScreenIdleState(bool screen_dim, bool screen_off) {
power_manager::ScreenIdleState proto;
proto.set_dimmed(screen_dim);
proto.set_off(screen_off);
chromeos::FakePowerManagerClient::Get()->SendScreenIdleStateChanged(proto);
}
void ReportScreenLocked() {
session_manager_.SetSessionState(session_manager::SessionState::LOCKED);
}
void ReportSuspend(power_manager::SuspendImminent::Reason reason,
base::TimeDelta sleep_duration) {
chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(reason);
task_environment()->FastForwardBy(sleep_duration);
chromeos::FakePowerManagerClient::Get()->SendSuspendDone(sleep_duration);
}
void ReportInactivityDelays(base::TimeDelta screen_dim_delay,
base::TimeDelta screen_off_delay) {
power_manager::PowerManagementPolicy::Delays proto;
proto.set_screen_dim_ms(screen_dim_delay.InMilliseconds());
proto.set_screen_off_ms(screen_off_delay.InMilliseconds());
chromeos::FakePowerManagerClient::Get()->SetInactivityDelays(proto);
}
TabProperty UpdateOpenTabURL() {
return activity_logger_->UpdateOpenTabURL();
}
// Creates a test browser window and sets its visibility, activity and
// incognito status.
std::unique_ptr<Browser> CreateTestBrowser(bool is_visible,
bool is_focused,
bool is_incognito = false) {
Profile* const original_profile = profile();
Profile* const used_profile =
is_incognito
? original_profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)
: original_profile;
Browser::CreateParams params(used_profile, true);
auto dummy_window = std::make_unique<aura::Window>(nullptr);
dummy_window->Init(ui::LAYER_SOLID_COLOR);
root_window()->AddChild(dummy_window.get());
dummy_window->SetBounds(gfx::Rect(root_window()->bounds().size()));
if (is_visible) {
dummy_window->Show();
} else {
dummy_window->Hide();
}
std::unique_ptr<Browser> browser =
chrome::CreateBrowserWithAuraTestWindowForParams(
std::move(dummy_window), ¶ms);
if (is_focused) {
browser->window()->Activate();
} else {
browser->window()->Deactivate();
}
return browser;
}
// Adds a tab with specified url to the tab strip model. Also optionally sets
// the tab to be the active one in the tab strip model.
// If |mime_type| is an empty string, the content has a default text type.
// TODO(jiameng): there doesn't seem to be a way to set form entry (via
// page importance signal). Check if there's some other way to set it.
ukm::SourceId CreateTestWebContents(TabStripModel* const tab_strip_model,
const GURL& url,
bool is_active,
const std::string& mime_type = "") {
DCHECK(tab_strip_model);
DCHECK(!url.is_empty());
content::WebContents* contents =
tab_activity_simulator_.AddWebContentsAndNavigate(tab_strip_model, url);
if (is_active) {
tab_strip_model->ActivateTabAt(tab_strip_model->count() - 1);
}
if (!mime_type.empty())
WebContentsTester::For(contents)->SetMainFrameMimeType(mime_type);
WebContentsTester::For(contents)->TestSetIsLoading(false);
return contents->GetPrimaryMainFrame()->GetPageUkmSourceId();
}
TestingUserActivityUkmLogger delegate_;
// Only used to get SourceIds for URLs.
ukm::TestAutoSetUkmRecorder ukm_recorder_;
TabActivitySimulator tab_activity_simulator_;
chromeos::machine_learning::FakeServiceConnectionImpl
fake_service_connection_;
const GURL url1_ = GURL("https://example1.com/");
const GURL url2_ = GURL("https://example2.com/");
const GURL url3_ = GURL("https://example3.com/");
const GURL url4_ = GURL("https://example4.com/");
private:
std::unique_ptr<IdleEventNotifier> idle_event_notifier_;
session_manager::SessionManager session_manager_;
std::unique_ptr<UserActivityManager> activity_logger_;
};
// After an idle event, we have a ui::Event, we should expect one
// UserActivityEvent.
TEST_F(UserActivityManagerTest, LogAfterIdleEvent) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
task_environment()->FastForwardBy(base::Seconds(2));
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event.set_reason(UserActivityEvent::Event::USER_ACTIVITY);
expected_event.set_log_duration_sec(2);
expected_event.set_screen_dim_occurred(false);
expected_event.set_screen_off_occurred(false);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
EXPECT_FALSE(events[0].has_model_prediction());
EXPECT_EQ(0, events[0].features().previous_positive_actions_count());
EXPECT_EQ(0, events[0].features().previous_negative_actions_count());
}
// Get a user event before an idle event, we should not log it.
TEST_F(UserActivityManagerTest, LogBeforeIdleEvent) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
ReportUserActivity(nullptr);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
EXPECT_EQ(0U, delegate_.events().size());
}
// Get a user event, then an idle event, then another user event,
// we should log the last one.
TEST_F(UserActivityManagerTest, LogSecondEvent) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
ReportUserActivity(nullptr);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
// Another user event.
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event.set_reason(UserActivityEvent::Event::USER_ACTIVITY);
expected_event.set_log_duration_sec(0);
expected_event.set_screen_dim_occurred(false);
expected_event.set_screen_off_occurred(false);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
EXPECT_FALSE(events[0].has_model_prediction());
EXPECT_EQ(0, events[0].features().previous_positive_actions_count());
EXPECT_EQ(0, events[0].features().previous_negative_actions_count());
}
// Log multiple events.
TEST_F(UserActivityManagerTest, LogMultipleEvents) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Trigger the 1st idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
// First user event.
ReportUserActivity(nullptr);
// Trigger the 2nd idle event.
ReportIdleEvent(data);
// Second user event.
task_environment()->FastForwardBy(base::Seconds(2));
ReportUserActivity(nullptr);
// Trigger the 3rd idle event.
ReportIdleEvent(data);
task_environment()->FastForwardBy(base::Seconds(3));
ReportSuspend(power_manager::SuspendImminent_Reason_IDLE, base::Seconds(10));
// Trigger the 4th idle event.
ReportIdleEvent(data);
task_environment()->FastForwardBy(base::Seconds(4));
ReportSuspend(power_manager::SuspendImminent_Reason_IDLE, base::Seconds(10));
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(4U, events.size());
UserActivityEvent::Event expected_event1;
expected_event1.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event1.set_reason(UserActivityEvent::Event::USER_ACTIVITY);
expected_event1.set_log_duration_sec(0);
expected_event1.set_screen_dim_occurred(false);
expected_event1.set_screen_off_occurred(false);
expected_event1.set_screen_lock_occurred(false);
UserActivityEvent::Event expected_event2;
expected_event2.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event2.set_reason(UserActivityEvent::Event::USER_ACTIVITY);
expected_event2.set_log_duration_sec(2);
expected_event2.set_screen_dim_occurred(false);
expected_event2.set_screen_off_occurred(false);
expected_event2.set_screen_lock_occurred(false);
UserActivityEvent::Event expected_event3;
expected_event3.set_type(UserActivityEvent::Event::TIMEOUT);
expected_event3.set_reason(UserActivityEvent::Event::IDLE_SLEEP);
expected_event3.set_log_duration_sec(3);
expected_event3.set_screen_dim_occurred(false);
expected_event3.set_screen_off_occurred(false);
expected_event3.set_screen_lock_occurred(false);
UserActivityEvent::Event expected_event4;
expected_event4.set_type(UserActivityEvent::Event::TIMEOUT);
expected_event4.set_reason(UserActivityEvent::Event::IDLE_SLEEP);
expected_event4.set_log_duration_sec(4);
expected_event4.set_screen_dim_occurred(false);
expected_event4.set_screen_off_occurred(false);
expected_event4.set_screen_lock_occurred(false);
EqualEvent(expected_event1, events[0].event());
EqualEvent(expected_event2, events[1].event());
EqualEvent(expected_event3, events[2].event());
EqualEvent(expected_event4, events[3].event());
EXPECT_FALSE(events[0].has_model_prediction());
EXPECT_FALSE(events[1].has_model_prediction());
EXPECT_FALSE(events[2].has_model_prediction());
EXPECT_FALSE(events[3].has_model_prediction());
EXPECT_EQ(0, events[0].features().previous_positive_actions_count());
EXPECT_EQ(0, events[0].features().previous_negative_actions_count());
EXPECT_EQ(0, events[1].features().previous_positive_actions_count());
EXPECT_EQ(1, events[1].features().previous_negative_actions_count());
EXPECT_EQ(0, events[2].features().previous_positive_actions_count());
EXPECT_EQ(2, events[2].features().previous_negative_actions_count());
EXPECT_EQ(1, events[3].features().previous_positive_actions_count());
EXPECT_EQ(2, events[3].features().previous_negative_actions_count());
}
TEST_F(UserActivityManagerTest, UserCloseLid) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
ReportLidEvent(chromeos::PowerManagerClient::LidState::OPEN);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
task_environment()->FastForwardBy(base::Seconds(2));
ReportLidEvent(chromeos::PowerManagerClient::LidState::CLOSED);
const std::vector<UserActivityEvent>& events = delegate_.events();
EXPECT_TRUE(events.empty());
}
TEST_F(UserActivityManagerTest, PowerChangeActivity) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
ReportPowerChangeEvent(power_manager::PowerSupplyProperties::AC, 23.0f);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
// We don't care about battery percentage change, but only power source.
ReportPowerChangeEvent(power_manager::PowerSupplyProperties::AC, 25.0f);
ReportPowerChangeEvent(power_manager::PowerSupplyProperties::DISCONNECTED,
28.0f);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event.set_reason(UserActivityEvent::Event::POWER_CHANGED);
expected_event.set_log_duration_sec(0);
expected_event.set_screen_dim_occurred(false);
expected_event.set_screen_off_occurred(false);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
}
TEST_F(UserActivityManagerTest, VideoActivity) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportVideoStart();
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event.set_reason(UserActivityEvent::Event::VIDEO_ACTIVITY);
expected_event.set_log_duration_sec(0);
expected_event.set_screen_dim_occurred(false);
expected_event.set_screen_off_occurred(false);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
}
// System remains idle, screen is dimmed then turned off, and system is finally
// suspended.
TEST_F(UserActivityManagerTest, SystemIdleSuspend) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
task_environment()->FastForwardBy(base::Seconds(20));
ReportScreenIdleState(true /* screen_dim */, false /* screen_off */);
task_environment()->FastForwardBy(base::Seconds(30));
ReportScreenIdleState(true /* screen_dim */, true /* screen_off */);
ReportSuspend(power_manager::SuspendImminent_Reason_IDLE, base::Seconds(10));
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::TIMEOUT);
expected_event.set_reason(UserActivityEvent::Event::IDLE_SLEEP);
expected_event.set_log_duration_sec(50);
expected_event.set_screen_dim_occurred(true);
expected_event.set_screen_off_occurred(true);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
}
// System remains idle, screen is dimmed then turned off, but system is not
// suspended.
TEST_F(UserActivityManagerTest, SystemIdleNotSuspend) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
task_environment()->FastForwardBy(base::Seconds(20));
ReportScreenIdleState(true /* screen_dim */, false /* screen_off */);
task_environment()->FastForwardBy(base::Seconds(30));
ReportScreenIdleState(true /* screen_dim */, true /* screen_off */);
task_environment()->RunUntilIdle();
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(0U, events.size());
}
// Test system idle interrupt by user activity.
// We should only observe user activity.
TEST_F(UserActivityManagerTest, SystemIdleInterrupted) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
task_environment()->FastForwardBy(base::Seconds(20));
ReportScreenIdleState(true /* screen_dim */, false /* screen_off */);
task_environment()->FastForwardBy(base::Seconds(30));
ReportScreenIdleState(true /* screen_dim */, true /* screen_off */);
task_environment()->FastForwardBy(base::Seconds(1));
ReportUserActivity(nullptr);
task_environment()->RunUntilIdle();
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event.set_reason(UserActivityEvent::Event::USER_ACTIVITY);
expected_event.set_log_duration_sec(51);
expected_event.set_screen_dim_occurred(true);
expected_event.set_screen_off_occurred(true);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
}
TEST_F(UserActivityManagerTest, ScreenLockNoSuspend) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportScreenLocked();
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(0U, events.size());
}
TEST_F(UserActivityManagerTest, ScreenLockWithSuspend) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportScreenLocked();
ReportSuspend(power_manager::SuspendImminent_Reason_IDLE, base::Seconds(1));
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::TIMEOUT);
expected_event.set_reason(UserActivityEvent::Event::IDLE_SLEEP);
expected_event.set_log_duration_sec(0);
expected_event.set_screen_dim_occurred(false);
expected_event.set_screen_off_occurred(false);
expected_event.set_screen_lock_occurred(true);
EqualEvent(expected_event, events[0].event());
}
// As we log when SuspendImminent is received, sleep duration from SuspendDone
// doesn't make any difference.
TEST_F(UserActivityManagerTest, SuspendIdleShortSleepDuration) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
task_environment()->FastForwardBy(base::Seconds(20));
ReportSuspend(power_manager::SuspendImminent_Reason_IDLE, base::Seconds(1));
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::TIMEOUT);
expected_event.set_reason(UserActivityEvent::Event::IDLE_SLEEP);
expected_event.set_log_duration_sec(20);
expected_event.set_screen_dim_occurred(false);
expected_event.set_screen_off_occurred(false);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
}
TEST_F(UserActivityManagerTest, SuspendLidClosed) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportSuspend(power_manager::SuspendImminent_Reason_LID_CLOSED,
base::Seconds(10));
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::OFF);
expected_event.set_reason(UserActivityEvent::Event::LID_CLOSED);
expected_event.set_log_duration_sec(0);
expected_event.set_screen_dim_occurred(false);
expected_event.set_screen_off_occurred(false);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
}
TEST_F(UserActivityManagerTest, SuspendOther) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Trigger an idle event.
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportSuspend(power_manager::SuspendImminent_Reason_OTHER, base::Seconds(10));
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::OFF);
expected_event.set_reason(UserActivityEvent::Event::MANUAL_SLEEP);
expected_event.set_log_duration_sec(0);
expected_event.set_screen_dim_occurred(false);
expected_event.set_screen_off_occurred(false);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
}
// Test feature extraction.
TEST_F(UserActivityManagerTest, FeatureExtraction) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
ReportLidEvent(chromeos::PowerManagerClient::LidState::OPEN);
ReportTabletModeEvent(chromeos::PowerManagerClient::TabletMode::UNSUPPORTED);
ReportPowerChangeEvent(power_manager::PowerSupplyProperties::AC, 23.0f);
IdleEventNotifier::ActivityData data;
data.last_activity_day = UserActivityEvent_Features_DayOfWeek_MON;
data.last_activity_time_of_day = base::Seconds(100);
data.recent_time_active = base::Seconds(10);
data.time_since_last_mouse = base::Seconds(20);
data.time_since_last_touch = base::Seconds(30);
data.video_playing_time = base::Seconds(90);
data.time_since_video_ended = base::Seconds(2);
data.key_events_in_last_hour = 0;
data.mouse_events_in_last_hour = 10;
data.touch_events_in_last_hour = 20;
ReportIdleEvent(data);
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
const UserActivityEvent::Features& features = events[0].features();
EXPECT_EQ(UserActivityEvent::Features::CLAMSHELL, features.device_mode());
EXPECT_EQ(23.0f, features.battery_percent());
EXPECT_FALSE(features.on_battery());
EXPECT_EQ(UserActivityEvent::Features::UNMANAGED,
features.device_management());
EXPECT_EQ(UserActivityEvent_Features_DayOfWeek_MON,
features.last_activity_day());
EXPECT_EQ(100, features.last_activity_time_sec());
EXPECT_EQ(10, features.recent_time_active_sec());
EXPECT_EQ(20, features.time_since_last_mouse_sec());
EXPECT_EQ(30, features.time_since_last_touch_sec());
EXPECT_EQ(90, features.video_playing_time_sec());
EXPECT_EQ(2, features.time_since_video_ended_sec());
EXPECT_EQ(0, features.key_events_in_last_hour());
EXPECT_EQ(10, features.mouse_events_in_last_hour());
EXPECT_EQ(20, features.touch_events_in_last_hour());
EXPECT_FALSE(features.has_last_user_activity_time_sec());
EXPECT_FALSE(features.has_time_since_last_key_sec());
EXPECT_FALSE(features.screen_dimmed_initially());
EXPECT_FALSE(features.screen_off_initially());
EXPECT_FALSE(features.screen_locked_initially());
}
TEST_F(UserActivityManagerTest, ManagedDevice) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
profile()
->ScopedCrosSettingsTestHelper()
->InstallAttributes()
->SetCloudManaged("fake-managed.com", "device-id");
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
const UserActivityEvent::Features& features = events[0].features();
EXPECT_EQ(UserActivityEvent::Features::MANAGED, features.device_management());
}
TEST_F(UserActivityManagerTest, DimAndOffDelays) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
ReportInactivityDelays(base::Milliseconds(2000) /* screen_dim_delay */,
base::Milliseconds(3000) /* screen_off_delay */);
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
const UserActivityEvent::Features& features = events[0].features();
EXPECT_EQ(2, features.on_to_dim_sec());
EXPECT_EQ(1, features.dim_to_screen_off_sec());
}
TEST_F(UserActivityManagerTest, DimDelays) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
ReportInactivityDelays(base::Milliseconds(2000) /* screen_dim_delay */,
base::TimeDelta() /* screen_off_delay */);
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
const UserActivityEvent::Features& features = events[0].features();
EXPECT_EQ(2, features.on_to_dim_sec());
EXPECT_TRUE(!features.has_dim_to_screen_off_sec());
}
TEST_F(UserActivityManagerTest, OffDelays) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
ReportInactivityDelays(base::TimeDelta() /* screen_dim_delay */,
base::Milliseconds(4000) /* screen_off_delay */);
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
const UserActivityEvent::Features& features = events[0].features();
EXPECT_EQ(4, features.dim_to_screen_off_sec());
EXPECT_TRUE(!features.has_on_to_dim_sec());
}
// Screen is off when idle event is reported. No subsequent change in screen
// state.
TEST_F(UserActivityManagerTest, InitialScreenOff) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
ReportScreenIdleState(true /* screen_dim */, true /* screen_off */);
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportScreenIdleState(false /* screen_dim */, true /* screen_off */);
task_environment()->FastForwardBy(base::Seconds(7));
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
const UserActivityEvent::Features& features = events[0].features();
EXPECT_TRUE(features.screen_dimmed_initially());
EXPECT_TRUE(features.screen_off_initially());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event.set_reason(UserActivityEvent::Event::USER_ACTIVITY);
expected_event.set_log_duration_sec(7);
expected_event.set_screen_dim_occurred(false);
expected_event.set_screen_off_occurred(false);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
}
// Screen is off when idle event is reported. No subsequent change in screen
// state.
TEST_F(UserActivityManagerTest, InitialScreenStateFlipped) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
ReportScreenIdleState(true /* screen_dim */, false /* screen_off */);
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportScreenIdleState(false /* screen_dim */, false /* screen_off */);
task_environment()->FastForwardBy(base::Seconds(7));
ReportScreenIdleState(true /* screen_dim */, true /* screen_off */);
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
const UserActivityEvent::Features& features = events[0].features();
EXPECT_TRUE(features.screen_dimmed_initially());
EXPECT_FALSE(features.screen_off_initially());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event.set_reason(UserActivityEvent::Event::USER_ACTIVITY);
expected_event.set_log_duration_sec(7);
expected_event.set_screen_dim_occurred(true);
expected_event.set_screen_off_occurred(true);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
}
// Screen is off when idle event is reported. No subsequent change in screen
// state.
TEST_F(UserActivityManagerTest, ScreenOffStateChanged) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
const IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportScreenIdleState(true /* screen_dim */, false /* screen_off */);
ReportScreenIdleState(true /* screen_dim */, true /* screen_off */);
task_environment()->FastForwardBy(base::Seconds(7));
ReportScreenIdleState(false /* screen_dim */, false /* screen_off */);
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
const UserActivityEvent::Features& features = events[0].features();
EXPECT_FALSE(features.screen_dimmed_initially());
EXPECT_FALSE(features.screen_off_initially());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event.set_reason(UserActivityEvent::Event::USER_ACTIVITY);
expected_event.set_log_duration_sec(7);
expected_event.set_screen_dim_occurred(true);
expected_event.set_screen_off_occurred(true);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
}
TEST_F(UserActivityManagerTest, ScreenDimDeferredWithFinalEvent) {
base::HistogramTester histogram_tester;
const std::map<std::string, std::string> params = {
{"dim_threshold", "0.651"}};
base::test::ScopedFeatureList scoped_feature_list;
SmartDimMlAgent::GetInstance()->ResetForTesting();
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kUserActivityPrediction, params);
// sigmoid(0.43) * 100 = 60
fake_service_connection_.SetOutputValue(std::vector<int64_t>{1L},
std::vector<double>{0.43});
const IdleEventNotifier::ActivityData data;
bool should_defer = false;
ReportIdleEvent(data, &should_defer);
task_environment()->RunUntilIdle();
ReportUserActivity(nullptr);
EXPECT_TRUE(should_defer);
histogram_tester.ExpectTotalCount(
"PowerML.SmartDimModel.RequestCompleteDuration", 1);
histogram_tester.ExpectBucketCount("PowerML.SmartDimComponent.WorkerType", 0,
1);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event.set_reason(UserActivityEvent::Event::USER_ACTIVITY);
expected_event.set_log_duration_sec(0);
expected_event.set_screen_dim_occurred(false);
expected_event.set_screen_off_occurred(false);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
UserActivityEvent::ModelPrediction expected_prediction;
expected_prediction.set_decision_threshold(65);
expected_prediction.set_inactivity_score(60);
expected_prediction.set_model_applied(true);
expected_prediction.set_response(UserActivityEvent::ModelPrediction::NO_DIM);
EqualModelPrediction(expected_prediction, events[0].model_prediction());
}
TEST_F(UserActivityManagerTest, ScreenDimDeferredWithoutFinalEvent) {
base::HistogramTester histogram_tester;
const std::map<std::string, std::string> params = {
{"dim_threshold", "0.651"}};
base::test::ScopedFeatureList scoped_feature_list;
SmartDimMlAgent::GetInstance()->ResetForTesting();
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kUserActivityPrediction, params);
// sigmoid(0.43) * 100 = 60
fake_service_connection_.SetOutputValue(std::vector<int64_t>{1L},
std::vector<double>{0.43});
const IdleEventNotifier::ActivityData data;
bool should_defer = false;
ReportIdleEvent(data, &should_defer);
task_environment()->RunUntilIdle();
EXPECT_TRUE(should_defer);
histogram_tester.ExpectTotalCount(
"PowerML.SmartDimModel.RequestCompleteDuration", 1);
histogram_tester.ExpectBucketCount("PowerML.SmartDimComponent.WorkerType", 0,
1);
const std::vector<UserActivityEvent>& events = delegate_.events();
EXPECT_TRUE(events.empty());
}
// Tests the cancellation of a Smart Dim decision request, immediately after it
// has been requested.
TEST_F(UserActivityManagerTest, ScreenDimRequestCanceled) {
base::HistogramTester histogram_tester;
const std::map<std::string, std::string> params = {
{"dim_threshold", "0.651"}};
base::test::ScopedFeatureList scoped_feature_list;
SmartDimMlAgent::GetInstance()->ResetForTesting();
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kUserActivityPrediction, params);
// sigmoid(0.43) * 100 = 60
fake_service_connection_.SetOutputValue(std::vector<int64_t>{1L},
std::vector<double>{0.43});
const IdleEventNotifier::ActivityData data;
bool should_defer = false;
ReportIdleEvent(data, &should_defer);
// Report user activity immediately after the idle event, so that
// the SmartDimModel doesn't get a chance to run.
ReportUserActivity(nullptr);
task_environment()->RunUntilIdle();
EXPECT_FALSE(should_defer);
histogram_tester.ExpectTotalCount(
"PowerML.SmartDimModel.RequestCompleteDuration", 0);
histogram_tester.ExpectTotalCount(
"PowerML.SmartDimModel.RequestCanceledDuration", 1);
histogram_tester.ExpectBucketCount("PowerML.SmartDimComponent.WorkerType", 0,
1);
// Since the pending SmartDim decision request was canceled, we shouldn't
// have any UserActivityEvent generated.
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(0U, events.size());
}
// Tests the cancellation of a Smart Dim decision request, when two idle events
// occur in quick succession. This verifies that only one request is serviced.
TEST_F(UserActivityManagerTest, ScreenDimConsecutiveRequests) {
base::HistogramTester histogram_tester;
const std::map<std::string, std::string> params = {
{"dim_threshold", "0.651"}};
base::test::ScopedFeatureList scoped_feature_list;
SmartDimMlAgent::GetInstance()->ResetForTesting();
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kUserActivityPrediction, params);
// sigmoid(0.43) * 100 = 60
fake_service_connection_.SetOutputValue(std::vector<int64_t>{1L},
std::vector<double>{0.43});
const IdleEventNotifier::ActivityData data;
bool should_defer_1 = false;
bool should_defer_2 = false;
ReportIdleEvent(data, &should_defer_1);
ReportIdleEvent(data, &should_defer_2);
task_environment()->RunUntilIdle();
ReportUserActivity(nullptr);
EXPECT_NE(should_defer_1, should_defer_2);
histogram_tester.ExpectTotalCount(
"PowerML.SmartDimModel.RequestCompleteDuration", 1);
histogram_tester.ExpectTotalCount(
"PowerML.SmartDimModel.RequestCanceledDuration", 1);
histogram_tester.ExpectBucketCount("PowerML.SmartDimComponent.WorkerType", 0,
2);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::Event expected_event;
expected_event.set_type(UserActivityEvent::Event::REACTIVATE);
expected_event.set_reason(UserActivityEvent::Event::USER_ACTIVITY);
expected_event.set_log_duration_sec(0);
expected_event.set_screen_dim_occurred(false);
expected_event.set_screen_off_occurred(false);
expected_event.set_screen_lock_occurred(false);
EqualEvent(expected_event, events[0].event());
UserActivityEvent::ModelPrediction expected_prediction;
expected_prediction.set_decision_threshold(65);
expected_prediction.set_inactivity_score(60);
expected_prediction.set_model_applied(true);
expected_prediction.set_response(UserActivityEvent::ModelPrediction::NO_DIM);
EqualModelPrediction(expected_prediction, events[0].model_prediction());
}
TEST_F(UserActivityManagerTest, ScreenDimNotDeferred) {
base::HistogramTester histogram_tester;
const std::map<std::string, std::string> params = {
{"dim_threshold", base::NumberToString(0.0)}};
base::test::ScopedFeatureList scoped_feature_list;
SmartDimMlAgent::GetInstance()->ResetForTesting();
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kUserActivityPrediction, params);
// sigmoid(0.43) * 100 = 60
fake_service_connection_.SetOutputValue(std::vector<int64_t>{1L},
std::vector<double>{0.43});
const IdleEventNotifier::ActivityData data;
bool should_defer = false;
ReportIdleEvent(data, &should_defer);
task_environment()->RunUntilIdle();
ReportUserActivity(nullptr);
EXPECT_FALSE(should_defer);
histogram_tester.ExpectTotalCount(
"PowerML.SmartDimModel.RequestCompleteDuration", 1);
histogram_tester.ExpectBucketCount("PowerML.SmartDimComponent.WorkerType", 0,
1);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
UserActivityEvent::ModelPrediction expected_prediction;
expected_prediction.set_decision_threshold(50);
expected_prediction.set_inactivity_score(60);
expected_prediction.set_model_applied(true);
expected_prediction.set_response(UserActivityEvent::ModelPrediction::DIM);
EqualModelPrediction(expected_prediction, events[0].model_prediction());
}
TEST_F(UserActivityManagerTest, TwoScreenDimImminentWithEventInBetween) {
base::HistogramTester histogram_tester;
const std::map<std::string, std::string> params = {
{"dim_threshold", base::NumberToString(0.0)}};
base::test::ScopedFeatureList scoped_feature_list;
SmartDimMlAgent::GetInstance()->ResetForTesting();
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kUserActivityPrediction, params);
// 1st ScreenDimImminent gets deferred
// sigmoid(-0.4) * 100 = 40
fake_service_connection_.SetOutputValue(std::vector<int64_t>{1L},
std::vector<double>{-0.4});
const IdleEventNotifier::ActivityData data;
bool should_defer = false;
ReportIdleEvent(data, &should_defer);
task_environment()->RunUntilIdle();
EXPECT_TRUE(should_defer);
task_environment()->FastForwardBy(base::Seconds(6));
ReportSuspend(power_manager::SuspendImminent_Reason_IDLE, base::Seconds(3));
// 2nd ScreenDimImminent is not deferred despite model score says so.
// sigmoid(-1.35) * 100 = 20
fake_service_connection_.SetOutputValue(std::vector<int64_t>{1L},
std::vector<double>{-1.35});
task_environment()->FastForwardBy(base::Seconds(10));
ReportIdleEvent(data, &should_defer);
task_environment()->RunUntilIdle();
EXPECT_FALSE(should_defer);
histogram_tester.ExpectTotalCount(
"PowerML.SmartDimModel.RequestCompleteDuration", 2);
histogram_tester.ExpectBucketCount("PowerML.SmartDimComponent.WorkerType", 0,
2);
// Log when a SuspendImminent is received
task_environment()->FastForwardBy(base::Seconds(20));
ReportSuspend(power_manager::SuspendImminent_Reason_IDLE, base::Seconds(3));
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(2U, events.size());
// The first screen dim imminent event.
UserActivityEvent::Event expected_event1;
expected_event1.set_type(UserActivityEvent::Event::TIMEOUT);
expected_event1.set_reason(UserActivityEvent::Event::IDLE_SLEEP);
expected_event1.set_log_duration_sec(6);
expected_event1.set_screen_dim_occurred(false);
expected_event1.set_screen_off_occurred(false);
expected_event1.set_screen_lock_occurred(false);
EqualEvent(expected_event1, events[0].event());
UserActivityEvent::ModelPrediction expected_prediction1;
expected_prediction1.set_decision_threshold(50);
expected_prediction1.set_inactivity_score(40);
expected_prediction1.set_model_applied(true);
expected_prediction1.set_response(UserActivityEvent::ModelPrediction::NO_DIM);
EqualModelPrediction(expected_prediction1, events[0].model_prediction());
// The second screen dim imminent event.
UserActivityEvent::Event expected_event2;
expected_event2.set_type(UserActivityEvent::Event::TIMEOUT);
expected_event2.set_reason(UserActivityEvent::Event::IDLE_SLEEP);
expected_event2.set_log_duration_sec(20);
expected_event2.set_screen_dim_occurred(false);
expected_event2.set_screen_off_occurred(false);
expected_event2.set_screen_lock_occurred(false);
EqualEvent(expected_event2, events[1].event());
UserActivityEvent::ModelPrediction expected_prediction2;
expected_prediction2.set_decision_threshold(50);
expected_prediction2.set_inactivity_score(20);
expected_prediction2.set_model_applied(false);
expected_prediction2.set_response(UserActivityEvent::ModelPrediction::NO_DIM);
EqualModelPrediction(expected_prediction2, events[1].model_prediction());
}
TEST_F(UserActivityManagerTest, TwoScreenDimImminentWithoutEventInBetween) {
base::HistogramTester histogram_tester;
const std::map<std::string, std::string> params = {
{"dim_threshold", base::NumberToString(0.0)}};
base::test::ScopedFeatureList scoped_feature_list;
SmartDimMlAgent::GetInstance()->ResetForTesting();
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kUserActivityPrediction, params);
// 1st ScreenDimImminent gets deferred
// sigmoid(-0.4) * 100 = 40
fake_service_connection_.SetOutputValue(std::vector<int64_t>{1L},
std::vector<double>{-0.4});
const IdleEventNotifier::ActivityData data;
bool should_defer = false;
ReportIdleEvent(data, &should_defer);
task_environment()->RunUntilIdle();
EXPECT_TRUE(should_defer);
// 2nd ScreenDimImminent is not deferred despite model score says so.
// sigmoid(-1.35) * 100 = 20
fake_service_connection_.SetOutputValue(std::vector<int64_t>{1L},
std::vector<double>{-1.35});
task_environment()->FastForwardBy(base::Seconds(10));
ReportIdleEvent(data, &should_defer);
task_environment()->RunUntilIdle();
EXPECT_FALSE(should_defer);
histogram_tester.ExpectTotalCount(
"PowerML.SmartDimModel.RequestCompleteDuration", 2);
histogram_tester.ExpectBucketCount("PowerML.SmartDimComponent.WorkerType", 0,
2);
// Log when a SuspendImminent is received
task_environment()->FastForwardBy(base::Seconds(20));
ReportSuspend(power_manager::SuspendImminent_Reason_IDLE, base::Seconds(3));
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(2U, events.size());
// The current event logged is after the earlier idle event.
UserActivityEvent::Event expected_event1;
expected_event1.set_type(UserActivityEvent::Event::TIMEOUT);
expected_event1.set_reason(UserActivityEvent::Event::IDLE_SLEEP);
expected_event1.set_log_duration_sec(20);
expected_event1.set_screen_dim_occurred(false);
expected_event1.set_screen_off_occurred(false);
expected_event1.set_screen_lock_occurred(false);
EqualEvent(expected_event1, events[1].event());
UserActivityEvent::ModelPrediction expected_prediction1;
expected_prediction1.set_decision_threshold(50);
expected_prediction1.set_inactivity_score(20);
expected_prediction1.set_model_applied(false);
expected_prediction1.set_response(UserActivityEvent::ModelPrediction::NO_DIM);
EqualModelPrediction(expected_prediction1, events[1].model_prediction());
UserActivityEvent::Event expected_event2 = expected_event1;
expected_event2.set_log_duration_sec(30);
EqualEvent(expected_event2, events[0].event());
UserActivityEvent::ModelPrediction expected_prediction2;
expected_prediction2.set_decision_threshold(50);
expected_prediction2.set_inactivity_score(40);
expected_prediction2.set_model_applied(true);
expected_prediction2.set_response(UserActivityEvent::ModelPrediction::NO_DIM);
EqualModelPrediction(expected_prediction2, events[0].model_prediction());
}
// Test is flaky. See https://crbug.com/938055.
TEST_F(UserActivityManagerTest, DISABLED_BasicTabs) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
std::unique_ptr<Browser> browser =
CreateTestBrowser(true /* is_visible */, true /* is_focused */);
BrowserList::GetInstance()->SetLastActive(browser.get());
TabStripModel* tab_strip_model = browser->tab_strip_model();
const ukm::SourceId source_id1 = CreateTestWebContents(
tab_strip_model, url1_, true /* is_active */, "application/pdf");
site_engagement::SiteEngagementService::Get(profile())->ResetBaseScoreForURL(
url1_, 95);
CreateTestWebContents(tab_strip_model, url2_, false /* is_active */);
IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
const UserActivityEvent::Features& features = events[0].features();
EXPECT_EQ(features.source_id(), source_id1);
EXPECT_EQ(features.tab_domain(), url1_.host());
EXPECT_FALSE(features.tab_domain().empty());
EXPECT_EQ(features.engagement_score(), 90);
EXPECT_FALSE(features.has_form_entry());
tab_strip_model->CloseAllTabs();
}
// Test is flaky. See https://crbug.com/938141.
TEST_F(UserActivityManagerTest, DISABLED_MultiBrowsersAndTabs) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
// Simulates three browsers:
// - browser1 is the last active but minimized and so not visible.
// - browser2 and browser3 are both visible but browser2 is the topmost.
std::unique_ptr<Browser> browser1 =
CreateTestBrowser(false /* is_visible */, false /* is_focused */);
std::unique_ptr<Browser> browser2 =
CreateTestBrowser(true /* is_visible */, true /* is_focused */);
std::unique_ptr<Browser> browser3 =
CreateTestBrowser(true /* is_visible */, false /* is_focused */);
BrowserList::GetInstance()->SetLastActive(browser3.get());
BrowserList::GetInstance()->SetLastActive(browser2.get());
BrowserList::GetInstance()->SetLastActive(browser1.get());
TabStripModel* tab_strip_model1 = browser1->tab_strip_model();
CreateTestWebContents(tab_strip_model1, url1_, false /* is_active */);
CreateTestWebContents(tab_strip_model1, url2_, true /* is_active */);
TabStripModel* tab_strip_model2 = browser2->tab_strip_model();
const ukm::SourceId source_id3 =
CreateTestWebContents(tab_strip_model2, url3_, true /* is_active */);
TabStripModel* tab_strip_model3 = browser3->tab_strip_model();
CreateTestWebContents(tab_strip_model3, url4_, true /* is_active */);
IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
const UserActivityEvent::Features& features = events[0].features();
EXPECT_EQ(features.source_id(), source_id3);
EXPECT_EQ(features.tab_domain(), url3_.host());
EXPECT_EQ(features.engagement_score(), 0);
EXPECT_FALSE(features.has_form_entry());
tab_strip_model1->CloseAllTabs();
tab_strip_model2->CloseAllTabs();
tab_strip_model3->CloseAllTabs();
}
TEST_F(UserActivityManagerTest, Incognito) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
std::unique_ptr<Browser> browser = CreateTestBrowser(
true /* is_visible */, true /* is_focused */, true /* is_incognito */);
BrowserList::GetInstance()->SetLastActive(browser.get());
TabStripModel* tab_strip_model = browser->tab_strip_model();
CreateTestWebContents(tab_strip_model, url1_, true /* is_active */);
CreateTestWebContents(tab_strip_model, url2_, false /* is_active */);
IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
const UserActivityEvent::Features& features = events[0].features();
EXPECT_FALSE(features.has_source_id());
EXPECT_FALSE(features.has_tab_domain());
EXPECT_FALSE(features.has_engagement_score());
EXPECT_FALSE(features.has_has_form_entry());
tab_strip_model->CloseAllTabs();
}
TEST_F(UserActivityManagerTest, NoOpenTabs) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kUserActivityPrediction);
std::unique_ptr<Browser> browser =
CreateTestBrowser(true /* is_visible */, true /* is_focused */);
IdleEventNotifier::ActivityData data;
ReportIdleEvent(data);
ReportUserActivity(nullptr);
const std::vector<UserActivityEvent>& events = delegate_.events();
ASSERT_EQ(1U, events.size());
const UserActivityEvent::Features& features = events[0].features();
EXPECT_FALSE(features.has_source_id());
EXPECT_FALSE(features.has_tab_domain());
EXPECT_FALSE(features.has_engagement_score());
EXPECT_FALSE(features.has_has_form_entry());
}
} // namespace ml
} // namespace power
} // namespace ash