chromium/chrome/browser/ash/policy/status_collector/app_info_generator_unittest.cc

// Copyright 2020 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/policy/status_collector/app_info_generator.h"

#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/app_service/app_service_test.h"
#include "chrome/browser/apps/app_service/publisher_host.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
#include "chrome/browser/web_applications/test/web_app_test_utils.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_provider_factory.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/test/base/testing_profile.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/session_manager/core/session_manager.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

using ::testing::AllOf;
using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::Field;
using ::testing::IsEmpty;
using ::testing::Matcher;
using ::testing::MatcherInterface;
using ::testing::Matches;
using ::testing::MatchResultListener;
using ::testing::Property;
using ::testing::ResultOf;

namespace em = enterprise_management;

namespace {

class TimePeriodMatcher : public MatcherInterface<const em::TimePeriod&> {
 public:
  explicit TimePeriodMatcher(const em::TimePeriod& time_period)
      : start_(base::Time::FromMillisecondsSinceUnixEpoch(
            time_period.start_timestamp())),
        end_(base::Time::FromMillisecondsSinceUnixEpoch(
            time_period.end_timestamp())) {}

  bool MatchAndExplain(const em::TimePeriod& time_period,
                       MatchResultListener* listener) const override {
    bool start_timestamp_equal =
        time_period.start_timestamp() == start_.InMillisecondsSinceUnixEpoch();
    if (!start_timestamp_equal) {
      *listener << " |start_timestamp| is "
                << base::Time::FromMillisecondsSinceUnixEpoch(
                       time_period.start_timestamp());
    }
    bool end_timestamp_equal =
        time_period.end_timestamp() == end_.InMillisecondsSinceUnixEpoch();
    if (!end_timestamp_equal) {
      *listener << " |end_timestamp| is "
                << base::Time::FromMillisecondsSinceUnixEpoch(
                       time_period.end_timestamp());
    }
    return start_timestamp_equal && end_timestamp_equal;
  }

  void DescribeTo(std::ostream* os) const override {
    *os << "equal to TimePeriod"
        << "(" << start_ << ", " << end_ << "]";
  }

 private:
  base::Time start_;
  base::Time end_;
};

auto EqApp(const std::string& app_id,
           const std::string& name,
           const em::AppInfo::Status status,
           const std::string& version,
           const em::AppInfo::AppType app_type,
           const std::vector<em::TimePeriod> app_activities = {}) {
  auto GetActivity =
      [](const em::AppInfo& input) -> std::vector<em::TimePeriod> {
    std::vector<em::TimePeriod> activities;
    for (const em::TimePeriod& time_period : input.active_time_periods()) {
      activities.push_back(time_period);
    }
    return activities;
  };

  std::vector<Matcher<const em::TimePeriod&>> activity_matchers;
  for (const em::TimePeriod& activity : app_activities) {
    activity_matchers.push_back(
        Matcher<const em::TimePeriod&>(new TimePeriodMatcher(activity)));
  }

  return AllOf(Property(&em::AppInfo::app_id, app_id),
               Property(&em::AppInfo::app_name, name),
               Property(&em::AppInfo::status, status),
               Property(&em::AppInfo::version, version),
               Property(&em::AppInfo::app_type, app_type),
               ResultOf(GetActivity, ElementsAreArray(activity_matchers)));
}

auto MakeActivity(const base::Time& start_time, const base::Time& end_time) {
  em::TimePeriod time_period;
  time_period.set_start_timestamp(start_time.InMillisecondsSinceUnixEpoch());
  time_period.set_end_timestamp(end_time.InMillisecondsSinceUnixEpoch());
  return time_period;
}

apps::AppPtr MakeApp(const std::string& app_id,
                     const std::string& name,
                     apps::Readiness readiness,
                     const std::string& version,
                     apps::AppType app_type) {
  auto app = std::make_unique<apps::App>(app_type, app_id);
  app->name = name;
  app->readiness = readiness;
  app->version = version;
  return app;
}

}  // namespace

namespace policy {

class AppInfoGeneratorTest : public ::testing::Test {
 public:
  AppInfoGeneratorTest() = default;

 protected:
  void PushApp(apps::AppPtr app) {
    apps::AppType app_type = app->app_type;
    std::vector<apps::AppPtr> deltas;
    deltas.push_back(std::move(app));
    AppServiceProxy()->OnApps(std::move(deltas), app_type,
                              /*should_notify_initialized=*/false);
  }

  void PushApp(const std::string& app_id,
               const std::string& name,
               apps::Readiness readiness,
               const std::string& version,
               apps::AppType app_type) {
    PushApp(MakeApp(app_id, name, readiness, version, app_type));
  }

  class Instance {
   public:
    explicit Instance(const std::string& app_id) {
      window_ = std::make_unique<aura::Window>(nullptr);
      window_->Init(ui::LAYER_NOT_DRAWN);
      instance_ = std::make_unique<apps::Instance>(
          app_id, base::UnguessableToken::Create(), window_.get());
    }

    apps::Instance* instance() const { return instance_.get(); }

    std::unique_ptr<apps::Instance> instance_;
    std::unique_ptr<aura::Window> window_;
  };

  void PushAppInstance(const Instance& instance, apps::InstanceState state) {
    auto time = test_clock_.Now();
    auto clone = instance.instance()->Clone();
    clone->UpdateState(state, time);

    GetInstanceRegistry().OnInstance(std::move(clone));
  }

  std::unique_ptr<TestingProfile> CreateProfile(const AccountId& account_id,
                                                bool is_affiliated = true) {
    TestingProfile::Builder profile_builder;
    profile_builder.SetProfileName(account_id.GetUserEmail());
    auto profile = profile_builder.Build();
    user_manager_->AddUserWithAffiliationAndTypeAndProfile(
        account_id, is_affiliated, user_manager::UserType::kRegular,
        profile.get());
    return profile;
  }

  void SetUp() override {
    auto user_manager = std::make_unique<ash::FakeChromeUserManager>();
    user_manager_ = user_manager.get();
    user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
        std::move(user_manager));
    account_id_ = AccountId::FromUserEmail("[email protected]");
    profile_ = CreateProfile(account_id_);
    test_clock().SetNow(MakeLocalTime("25-MAR-2020 1:30am"));

    // Wait for AppServiceProxy to be ready.
    app_service_test_.SetUp(profile_.get());

    auto* provider = web_app::FakeWebAppProvider::Get(profile_.get());
    provider->SetStartSystemOnStart(true);
    provider->Start();

    app_registrar_ = &provider->GetRegistrarMutable();
  }

  apps::AppServiceProxy* AppServiceProxy() {
    return apps::AppServiceProxyFactory::GetForProfile(profile_.get());
  }

  apps::InstanceRegistry& GetInstanceRegistry() {
    return apps::AppServiceProxyFactory::GetForProfile(profile_.get())
        ->InstanceRegistry();
  }

  std::unique_ptr<AppInfoGenerator> GetGenerator(
      base::TimeDelta max_stored_past_activity_interval = base::Days(0)) {
    return std::make_unique<AppInfoGenerator>(
        nullptr, max_stored_past_activity_interval, &test_clock());
  }

  std::unique_ptr<AppInfoGenerator> GetReadyGenerator() {
    auto generator = GetGenerator();
    generator->OnLogin(profile());
    generator->OnReportingChanged(true);
    return generator;
  }

  void RegisterApp(std::unique_ptr<web_app::WebApp> web_app) {
    webapps::AppId app_id = web_app->app_id();
    DCHECK(!app_registrar_->GetAppById(app_id));
    app_registrar_->registry().emplace(std::move(app_id), std::move(web_app));
  }

  Profile* profile() { return profile_.get(); }

  ash::FakeChromeUserManager* user_manager() { return user_manager_; }

  AccountId account_id() { return account_id_; }

  static auto EqActivity(const base::Time& start_time,
                         const base::Time& end_time) {
    return AllOf(Property(&em::TimePeriod::start_timestamp,
                          start_time.InMillisecondsSinceUnixEpoch()),
                 Property(&em::TimePeriod::end_timestamp,
                          end_time.InMillisecondsSinceUnixEpoch()));
  }

  base::Time MakeLocalTime(const std::string& time_string) {
    base::Time time;
    EXPECT_TRUE(base::Time::FromString(time_string.c_str(), &time));
    return time;
  }

  base::Time MakeUTCTime(const std::string& time_string) {
    base::Time time;
    EXPECT_TRUE(base::Time::FromUTCString(time_string.c_str(), &time));
    return time;
  }

  base::SimpleTestClock& test_clock() { return test_clock_; }

 private:
  apps::ScopedOmitBorealisAppsForTesting scoped_omit_borealis_apps_for_testing_;
  apps::ScopedOmitBuiltInAppsForTesting scoped_omit_built_in_apps_for_testing_;
  apps::ScopedOmitPluginVmAppsForTesting
      scoped_omit_plugin_vm_apps_for_testing_;
  content::BrowserTaskEnvironment task_environment_;
  AccountId account_id_;
  std::unique_ptr<TestingProfile> profile_;
  raw_ptr<web_app::WebAppRegistrarMutable> app_registrar_;
  raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged> user_manager_;
  std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
  TestingPrefServiceSimple pref_service_;

  session_manager::SessionManager session_manager_;

  base::SimpleTestClock test_clock_;
  apps::AppServiceTest app_service_test_;
};

TEST_F(AppInfoGeneratorTest, GenerateInventoryList) {
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);
  PushApp("b", "SecondApp", apps::Readiness::kReady, "1.2",
          apps::AppType::kChromeApp);
  PushApp("c", "ThirdApp", apps::Readiness::kUninstalledByUser, "",
          apps::AppType::kCrostini);

  user_manager()->LoginUser(account_id(), true);
  auto generator = GetReadyGenerator();
  auto result = generator->Generate();

  EXPECT_THAT(
      result.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC),
                  EqApp("b", "SecondApp", em::AppInfo_Status_STATUS_INSTALLED,
                        "1.2", em::AppInfo_AppType_TYPE_EXTENSION),
                  EqApp("c", "ThirdApp", em::AppInfo_Status_STATUS_UNINSTALLED,
                        "", em::AppInfo_AppType_TYPE_CROSTINI)));
}

TEST_F(AppInfoGeneratorTest, GenerateWebApp) {
  user_manager()->LoginUser(account_id(), true);
  auto generator = GetReadyGenerator();
  webapps::AppId app_id;
  {
    auto web_app = web_app::test::CreateWebApp(
        GURL("http://app.com/app/path"), web_app::WebAppManagement::kDefault);
    app_id = web_app->app_id();
    auto app =
        MakeApp(web_app->app_id(), "App", apps::Readiness::kUninstalledByUser,
                "", apps::AppType::kWeb);
    // For web apps, |publisher_id| is set to the start URL.
    app->publisher_id = web_app->start_url().spec();
    PushApp(std::move(app));
    RegisterApp(std::move(web_app));
  }

  Instance app_instance(app_id);
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 8:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);

  test_clock().SetNow(MakeLocalTime("30-MAR-2020 11:00am"));
  auto result = generator->Generate();

  EXPECT_THAT(
      result.value(),
      ElementsAre(EqApp("http://app.com/", "http://app.com/",
                        em::AppInfo_Status_STATUS_UNINSTALLED, "",
                        em::AppInfo_AppType_TYPE_WEB,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 5:00am"))})));
}

TEST_F(AppInfoGeneratorTest, GenerateSystemWebApp) {
  user_manager()->LoginUser(account_id(), true);
  auto generator = GetReadyGenerator();
  webapps::AppId app_id;
  {
    auto web_app = web_app::test::CreateWebApp(
        GURL("http://app.com/app/path"), web_app::WebAppManagement::kDefault);
    app_id = web_app->app_id();
    auto app =
        MakeApp(web_app->app_id(), "App", apps::Readiness::kUninstalledByUser,
                "", apps::AppType::kSystemWeb);
    // For system web apps, |publisher_id| is set to the start URL.
    app->publisher_id = web_app->start_url().spec();
    PushApp(std::move(app));
    RegisterApp(std::move(web_app));
  }

  Instance app_instance(app_id);
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 8:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);

  test_clock().SetNow(MakeLocalTime("30-MAR-2020 11:00am"));
  auto result = generator->Generate();

  EXPECT_THAT(
      result.value(),
      ElementsAre(EqApp("http://app.com/", "http://app.com/",
                        em::AppInfo_Status_STATUS_UNINSTALLED, "",
                        em::AppInfo_AppType_TYPE_WEB,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 5:00am"))})));
}

TEST_F(AppInfoGeneratorTest, MultipleInstances) {
  user_manager()->LoginUser(account_id(), true);
  auto generator = GetReadyGenerator();
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);
  Instance app_instance("a");
  Instance app_instance2("a");
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 1:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 2:30pm"));
  PushAppInstance(app_instance2, apps::InstanceState::kStarted);
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 4:30pm"));
  PushAppInstance(app_instance2, apps::InstanceState::kDestroyed);

  test_clock().SetNow(MakeLocalTime("30-MAR-2020 11:00am"));
  auto result = generator->Generate();

  EXPECT_THAT(
      result.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 3:00am"))})));
}

TEST_F(AppInfoGeneratorTest, ShouldNotReport) {
  user_manager()->LoginUser(account_id(), true);
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);

  auto generator = GetGenerator();
  generator->OnReportingChanged(false);
  generator->OnLogin(profile());
  auto result = generator->Generate();

  EXPECT_FALSE(result.has_value());
}

TEST_F(AppInfoGeneratorTest, UnaffiliatedUser) {
  auto unaffiliated_account_id =
      AccountId::FromUserEmail("[email protected]");
  auto unaffiliated_profile =
      CreateProfile(unaffiliated_account_id, /* is_affiliated= */ false);
  user_manager()->LoginUser(unaffiliated_account_id, true);
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);

  auto generator = GetGenerator();
  generator->OnReportingChanged(true);
  generator->OnLogin(unaffiliated_profile.get());
  auto result = generator->Generate();

  EXPECT_FALSE(result.has_value());
}

TEST_F(AppInfoGeneratorTest, SecondaryUser) {
  user_manager()->LoginUser(account_id(), true);
  auto secondary_account_id = AccountId::FromUserEmail("[email protected]");
  auto secondary_profile =
      CreateProfile(secondary_account_id, /* is_affiliated= */ true);
  user_manager()->LoginUser(secondary_account_id, true);
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);

  auto generator = GetGenerator();
  generator->OnReportingChanged(true);
  generator->OnLogin(secondary_profile.get());
  auto result = generator->Generate();

  EXPECT_FALSE(result.has_value());
}

TEST_F(AppInfoGeneratorTest, OnReportedSuccessfully) {
  user_manager()->LoginUser(account_id(), true);
  auto generator = GetReadyGenerator();
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);
  Instance app_instance("a");
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);
  test_clock().SetNow(MakeLocalTime("30-MAR-2020 11:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);

  test_clock().SetNow(MakeLocalTime("30-MAR-2020 11:55pm"));
  auto report_time = test_clock().Now();
  test_clock().SetNow(MakeLocalTime("31-MAR-2020 12:10pm"));
  generator->OnReportedSuccessfully(report_time);

  test_clock().SetNow(MakeLocalTime("31-MAR-2020 5:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);
  test_clock().SetNow(MakeLocalTime("31-MAR-2020 11:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);
  auto result = generator->Generate();

  EXPECT_THAT(
      result.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("31-MAR-2020 12:00am"),
                                      MakeUTCTime("31-MAR-2020 6:00am"))})));
}

TEST_F(AppInfoGeneratorTest, OnWillReport) {
  user_manager()->LoginUser(account_id(), true);
  auto generator = GetReadyGenerator();
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);
  Instance app_instance("a");
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:30pm"));
  PushAppInstance(app_instance, static_cast<apps::InstanceState>(
                                    apps::InstanceState::kStarted |
                                    apps::InstanceState::kHidden));

  test_clock().SetNow(MakeLocalTime("30-MAR-2020 5:30pm"));
  generator->OnWillReport();
  auto result = generator->Generate();

  test_clock().SetNow(MakeLocalTime("31-MAR-2020 8:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);
  auto result2 = generator->Generate();

  EXPECT_THAT(
      result.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 8:30am")),
                         MakeActivity(MakeUTCTime("30-MAR-2020 12:00am"),
                                      MakeUTCTime("30-MAR-2020 5:30pm"))})));
  EXPECT_THAT(
      result2.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 8:30am")),
                         MakeActivity(MakeUTCTime("30-MAR-2020 12:00am"),
                                      MakeUTCTime("31-MAR-2020 12:00am")),
                         MakeActivity(MakeUTCTime("31-MAR-2020 12:00am"),
                                      MakeUTCTime("31-MAR-2020 8:30pm"))})));
}

TEST_F(AppInfoGeneratorTest, OnWillReport_DeviceLocked) {
  user_manager()->LoginUser(account_id(), true);
  auto generator = GetReadyGenerator();
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);
  Instance app_instance("a");
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:00pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 4:00pm"));
  generator->OnLocked();

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 5:00pm"));
  generator->OnWillReport();
  auto result1 = generator->Generate();

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 7:00pm"));
  generator->OnUnlocked();

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 7:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);
  auto result2 = generator->Generate();

  EXPECT_THAT(
      result1.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 1:00am"))})));

  EXPECT_THAT(
      result2.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 1:30am"))})));
}

TEST_F(AppInfoGeneratorTest, OnResumeActive_DeviceLocked) {
  user_manager()->LoginUser(account_id(), true);
  auto generator = GetReadyGenerator();
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);
  Instance app_instance("a");
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:00pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 4:00pm"));
  generator->OnLocked();

  auto suspend_time = MakeLocalTime("29-MAR-2020 5:00pm");

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 6:00pm"));
  generator->OnResumeActive(suspend_time);
  auto result1 = generator->Generate();

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 7:00pm"));
  generator->OnUnlocked();

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 7:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);
  auto result2 = generator->Generate();

  EXPECT_THAT(
      result1.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 1:00am"))})));

  EXPECT_THAT(
      result2.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 1:30am"))})));
}

TEST_F(AppInfoGeneratorTest, OnLogoutOnLogin) {
  user_manager()->LoginUser(account_id(), true);
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);
  auto generator = GetGenerator();
  generator->OnReportingChanged(true);
  generator->OnLogin(profile());
  generator->OnLogout(profile());
  Instance app_instance("a");
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 1:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);

  test_clock().SetNow(MakeLocalTime("30-MAR-2020 11:00am"));
  auto result = generator->Generate();

  EXPECT_FALSE(result.has_value());

  generator->OnLogin(profile());

  test_clock().SetNow(MakeLocalTime("30-MAR-2020 2:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);
  test_clock().SetNow(MakeLocalTime("30-MAR-2020 5:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);

  test_clock().SetNow(MakeLocalTime("31-MAR-2020 11:00am"));
  auto result2 = generator->Generate();

  EXPECT_THAT(
      result2.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("30-MAR-2020 12:00am"),
                                      MakeUTCTime("30-MAR-2020 3:00am"))})));
}

TEST_F(AppInfoGeneratorTest, OnLocked) {
  user_manager()->LoginUser(account_id(), true);
  auto generator = GetReadyGenerator();
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);
  Instance app_instance("a");
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 5:30pm"));
  generator->OnLocked();

  test_clock().SetNow(MakeLocalTime("30-MAR-2020 11:00am"));
  auto result = generator->Generate();

  EXPECT_THAT(
      result.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 2:00am"))})));
}

TEST_F(AppInfoGeneratorTest, OnUnlocked) {
  user_manager()->LoginUser(account_id(), true);
  auto generator = GetReadyGenerator();
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);
  Instance app_instance("a");
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 4:00pm"));
  generator->OnLocked();

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 5:00pm"));
  generator->OnUnlocked();

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 6:35pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);

  test_clock().SetNow(MakeLocalTime("30-MAR-2020 11:00am"));
  auto result = generator->Generate();

  EXPECT_THAT(
      result.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 2:05am"))})));
}

TEST_F(AppInfoGeneratorTest, OnResumeActive) {
  user_manager()->LoginUser(account_id(), true);
  auto generator = GetReadyGenerator();
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);
  Instance app_instance("a");
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:30pm"));
  PushAppInstance(app_instance, apps::InstanceState::kStarted);

  auto suspend_time = MakeLocalTime("29-MAR-2020 4:00pm");
  test_clock().SetNow(suspend_time);

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 5:00pm"));
  generator->OnResumeActive(suspend_time);

  test_clock().SetNow(MakeLocalTime("29-MAR-2020 6:35pm"));
  PushAppInstance(app_instance, apps::InstanceState::kDestroyed);

  test_clock().SetNow(MakeLocalTime("30-MAR-2020 11:00am"));
  auto result = generator->Generate();

  EXPECT_THAT(
      result.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 2:05am"))})));
}

TEST_F(AppInfoGeneratorTest, OnLoginRemoveOldUsage) {
  user_manager()->LoginUser(account_id(), true);
  PushApp("a", "FirstApp", apps::Readiness::kDisabledByPolicy, "1.1",
          apps::AppType::kArc);
  PushApp("b", "SecondApp", apps::Readiness::kReady, "1.2",
          apps::AppType::kChromeApp);
  auto max_days_past = base::Days(
      1);  // Exclude all past usage except for UTC today and yesterday.
  auto generator = GetGenerator(max_days_past);
  generator->OnReportingChanged(true);
  generator->OnLogin(profile());

  Instance app_instance1("a");
  test_clock().SetNow(MakeLocalTime("28-MAR-2020 1:30am"));
  PushAppInstance(app_instance1, apps::InstanceState::kStarted);
  test_clock().SetNow(MakeLocalTime("28-MAR-2020 3:30am"));
  PushAppInstance(app_instance1, apps::InstanceState::kDestroyed);
  Instance app_instance2("b");
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 1:30am"));
  PushAppInstance(app_instance2, apps::InstanceState::kStarted);
  test_clock().SetNow(MakeLocalTime("29-MAR-2020 3:30am"));
  PushAppInstance(app_instance2, apps::InstanceState::kDestroyed);

  generator->OnLogout(profile());
  test_clock().SetNow(MakeLocalTime("30-MAR-2020 11:00am"));
  generator->OnLogin(profile());

  auto result = generator->Generate();

  EXPECT_THAT(
      result.value(),
      ElementsAre(EqApp("a", "FirstApp", em::AppInfo_Status_STATUS_DISABLED,
                        "1.1", em::AppInfo_AppType_TYPE_ARC),
                  EqApp("b", "SecondApp", em::AppInfo_Status_STATUS_INSTALLED,
                        "1.2", em::AppInfo_AppType_TYPE_EXTENSION,
                        {MakeActivity(MakeUTCTime("29-MAR-2020 12:00am"),
                                      MakeUTCTime("29-MAR-2020 2:00am"))})));
}

}  // namespace policy