#include "ash/metrics/feature_discovery_duration_reporter_impl.h"
#include "ash/public/cpp/ash_prefs.h"
#include "ash/public/cpp/feature_discovery_metric_util.h"
#include "ash/public/cpp/tablet_mode.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/compiler_specific.h"
#include "base/containers/contains.h"
#include "base/test/metrics/histogram_tester.h"
namespace ash {
namespace {
constexpr char kPrimaryUserEmail[] = "user1@example.com";
constexpr char kSecondaryUserEmail[] = "user2@example.com";
constexpr char kMockHistogram[] = "FeatureDiscoveryTestMockFeature";
const char kMockFeatureClamshellHistogram[] =
"FeatureDiscoveryTestMockFeature.clamshell";
const char kMockFeatureTabletHistogram[] =
"FeatureDiscoveryTestMockFeature.tablet";
SessionControllerImpl* GetSessionController() {
return Shell::Get()->session_controller();
}
FeatureDiscoveryDurationReporterImpl* GetFeatureDiscoveryDurationReporter() {
return Shell::Get()->feature_discover_reporter();
}
}
class FeatureDiscoveryDurationReporterImplTest : public AshTestBase {
public:
FeatureDiscoveryDurationReporterImplTest()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
FeatureDiscoveryDurationReporterImplTest(
const FeatureDiscoveryDurationReporterImplTest&) = delete;
FeatureDiscoveryDurationReporterImplTest& operator=(
const FeatureDiscoveryDurationReporterImplTest&) = delete;
~FeatureDiscoveryDurationReporterImplTest() override = default;
bool IsReporterActive() {
return GetFeatureDiscoveryDurationReporter()->is_active();
}
bool IsMockFeatureUnderActiveObservation() {
const auto& active_time_recordings =
GetFeatureDiscoveryDurationReporter()->active_time_recordings_;
return base::Contains(active_time_recordings,
feature_discovery::TrackableFeature::kMockFeature);
}
void SetUp() override {
AshTestBase::SetUp();
GetSessionController()->ClearUserSessionsForTest();
SimulateUserLogin({kPrimaryUserEmail});
SimulateUserLogin({kSecondaryUserEmail});
SwitchActiveUser(primary_account_id_);
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::LOCKED);
}
AccountId primary_account_id_ = AccountId::FromUserEmail(kPrimaryUserEmail);
AccountId secondary_account_id_ =
AccountId::FromUserEmail(kSecondaryUserEmail);
};
TEST_F(FeatureDiscoveryDurationReporterImplTest, OnlyRecordForNewPrimaryUser) {
EXPECT_FALSE(IsReporterActive());
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::ACTIVE);
EXPECT_TRUE(IsReporterActive());
SwitchActiveUser(secondary_account_id_);
EXPECT_EQ(session_manager::SessionState::ACTIVE,
GetSessionController()->GetSessionState());
EXPECT_FALSE(IsReporterActive());
base::HistogramTester histogram_tester;
FeatureDiscoveryDurationReporterImpl* reporter =
GetFeatureDiscoveryDurationReporter();
reporter->MaybeActivateObservation(
feature_discovery::TrackableFeature::kMockFeature);
task_environment()->FastForwardBy(base::Minutes(1));
reporter->MaybeFinishObservation(
feature_discovery::TrackableFeature::kMockFeature);
histogram_tester.ExpectTotalCount(kMockHistogram, 0);
}
TEST_F(FeatureDiscoveryDurationReporterImplTest, CountDurationInOneSession) {
EXPECT_FALSE(IsReporterActive());
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::ACTIVE);
EXPECT_TRUE(IsReporterActive());
base::HistogramTester histogram_tester;
FeatureDiscoveryDurationReporterImpl* reporter =
GetFeatureDiscoveryDurationReporter();
reporter->MaybeFinishObservation(
feature_discovery::TrackableFeature::kMockFeature);
histogram_tester.ExpectTotalCount(kMockHistogram, 0);
reporter->MaybeActivateObservation(
feature_discovery::TrackableFeature::kMockFeature);
task_environment()->FastForwardBy(base::Minutes(1));
reporter->MaybeFinishObservation(
feature_discovery::TrackableFeature::kMockFeature);
histogram_tester.ExpectUniqueTimeSample(kMockHistogram, base::Minutes(1), 1);
reporter->MaybeActivateObservation(
feature_discovery::TrackableFeature::kMockFeature);
task_environment()->FastForwardBy(base::Minutes(1));
reporter->MaybeFinishObservation(
feature_discovery::TrackableFeature::kMockFeature);
histogram_tester.ExpectTotalCount(kMockHistogram, 1);
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::LOCKED);
EXPECT_FALSE(IsReporterActive());
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::ACTIVE);
EXPECT_TRUE(IsReporterActive());
EXPECT_FALSE(IsMockFeatureUnderActiveObservation());
}
TEST_F(FeatureDiscoveryDurationReporterImplTest,
CountDurationAcrossSessionStates) {
EXPECT_FALSE(IsReporterActive());
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::ACTIVE);
EXPECT_TRUE(IsReporterActive());
base::HistogramTester histogram_tester;
FeatureDiscoveryDurationReporterImpl* reporter =
GetFeatureDiscoveryDurationReporter();
reporter->MaybeActivateObservation(
feature_discovery::TrackableFeature::kMockFeature);
constexpr base::TimeDelta delta1(base::Minutes(1));
task_environment()->FastForwardBy(delta1);
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::LOCKED);
EXPECT_FALSE(IsReporterActive());
task_environment()->FastForwardBy(base::Minutes(3));
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::ACTIVE);
constexpr base::TimeDelta delta2(base::Minutes(5));
task_environment()->FastForwardBy(delta2);
reporter->MaybeFinishObservation(
feature_discovery::TrackableFeature::kMockFeature);
histogram_tester.ExpectUniqueTimeSample(kMockHistogram, delta1 + delta2, 1);
reporter->MaybeActivateObservation(
feature_discovery::TrackableFeature::kMockFeature);
task_environment()->FastForwardBy(base::Minutes(1));
reporter->MaybeFinishObservation(
feature_discovery::TrackableFeature::kMockFeature);
histogram_tester.ExpectTotalCount(kMockHistogram, 1);
}
TEST_F(FeatureDiscoveryDurationReporterImplTest, VerifyFeatureNameIsUnique) {
auto cmp = [](const char* a, const char* b) {
return UNSAFE_TODO(std::strcmp(a, b)) > 0;
};
std::set<const char*, decltype(cmp)> feature_names(cmp);
for (const auto& feature_info : feature_discovery::kTrackableFeatureArray) {
bool success = feature_names.emplace(feature_info.name).second;
EXPECT_TRUE(success) << " " << feature_info.name
<< " is used more than once";
}
}
class FeatureDiscoveryDurationReporterDataSplitTest
: public FeatureDiscoveryDurationReporterImplTest,
public testing::WithParamInterface</*is_tablet=*/bool> {
public:
FeatureDiscoveryDurationReporterDataSplitTest() = default;
FeatureDiscoveryDurationReporterDataSplitTest(
const FeatureDiscoveryDurationReporterDataSplitTest&) = delete;
FeatureDiscoveryDurationReporterDataSplitTest& operator=(
const FeatureDiscoveryDurationReporterDataSplitTest&) = delete;
~FeatureDiscoveryDurationReporterDataSplitTest() override = default;
void SetUp() override {
FeatureDiscoveryDurationReporterImplTest::SetUp();
TabletMode::Get()->SetEnabledForTest(GetParam());
}
};
INSTANTIATE_TEST_SUITE_P(All,
FeatureDiscoveryDurationReporterDataSplitTest,
testing::Bool());
TEST_P(FeatureDiscoveryDurationReporterDataSplitTest, Basics) {
EXPECT_FALSE(IsReporterActive());
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::ACTIVE);
EXPECT_TRUE(IsReporterActive());
base::HistogramTester histogram_tester;
FeatureDiscoveryDurationReporterImpl* reporter =
GetFeatureDiscoveryDurationReporter();
reporter->MaybeActivateObservation(
feature_discovery::TrackableFeature::kModeSeparateMockFeature);
constexpr base::TimeDelta delta(base::Minutes(1));
task_environment()->FastForwardBy(delta);
reporter->MaybeFinishObservation(
feature_discovery::TrackableFeature::kModeSeparateMockFeature);
if (GetParam()) {
histogram_tester.ExpectUniqueTimeSample(kMockFeatureTabletHistogram, delta,
1);
histogram_tester.ExpectTotalCount(kMockFeatureClamshellHistogram, 0);
} else {
histogram_tester.ExpectUniqueTimeSample(kMockFeatureClamshellHistogram,
delta, 1);
histogram_tester.ExpectTotalCount(kMockFeatureTabletHistogram, 0);
}
}
TEST_P(FeatureDiscoveryDurationReporterDataSplitTest,
SwitchModeBeforeMetricReport) {
EXPECT_FALSE(IsReporterActive());
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::ACTIVE);
EXPECT_TRUE(IsReporterActive());
base::HistogramTester histogram_tester;
FeatureDiscoveryDurationReporterImpl* reporter =
GetFeatureDiscoveryDurationReporter();
reporter->MaybeActivateObservation(
feature_discovery::TrackableFeature::kModeSeparateMockFeature);
constexpr base::TimeDelta delta1(base::Minutes(1));
task_environment()->FastForwardBy(delta1);
TabletMode::Get()->SetEnabledForTest(!GetParam());
constexpr base::TimeDelta delta2(base::Minutes(2));
task_environment()->FastForwardBy(delta2);
reporter->MaybeFinishObservation(
feature_discovery::TrackableFeature::kModeSeparateMockFeature);
if (GetParam()) {
histogram_tester.ExpectUniqueTimeSample(kMockFeatureTabletHistogram,
delta1 + delta2, 1);
histogram_tester.ExpectTotalCount(kMockFeatureClamshellHistogram, 0);
} else {
histogram_tester.ExpectUniqueTimeSample(kMockFeatureClamshellHistogram,
delta1 + delta2, 1);
histogram_tester.ExpectTotalCount(kMockFeatureTabletHistogram, 0);
}
}
TEST_P(FeatureDiscoveryDurationReporterDataSplitTest, CheckUnsplitMetric) {
EXPECT_FALSE(IsReporterActive());
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::ACTIVE);
EXPECT_TRUE(IsReporterActive());
base::HistogramTester histogram_tester;
FeatureDiscoveryDurationReporterImpl* reporter =
GetFeatureDiscoveryDurationReporter();
reporter->MaybeActivateObservation(
feature_discovery::TrackableFeature::kMockFeature);
constexpr base::TimeDelta delta(base::Minutes(1));
task_environment()->FastForwardBy(delta);
reporter->MaybeFinishObservation(
feature_discovery::TrackableFeature::kMockFeature);
histogram_tester.ExpectUniqueTimeSample(kMockHistogram, delta, 1);
}
}