chromium/chrome/browser/ash/app_list/arc/arc_app_test.cc

// Copyright 2016 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/app_list/arc/arc_app_test.h"

#include <vector>

#include "ash/components/arc/arc_util.h"
#include "ash/components/arc/mojom/app.mojom-shared.h"
#include "ash/components/arc/session/arc_bridge_service.h"
#include "ash/components/arc/session/arc_service_manager.h"
#include "ash/components/arc/session/arc_session_runner.h"
#include "ash/components/arc/test/arc_util_test_support.h"
#include "ash/components/arc/test/connection_holder_util.h"
#include "ash/components/arc/test/fake_app_instance.h"
#include "ash/components/arc/test/fake_arc_session.h"
#include "ash/components/arc/test/fake_compatibility_mode_instance.h"
#include "ash/constants/ash_features.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.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/promise_apps/promise_app_service.h"
#include "chrome/browser/apps/app_service/publishers/arc_apps_factory.h"
#include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ash/app_list/arc/arc_app_list_prefs_factory.h"
#include "chrome/browser/ash/arc/arc_util.h"
#include "chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler.h"
#include "chrome/browser/ash/arc/session/arc_session_manager.h"
#include "chrome/browser/ash/arc/test/test_arc_session_manager.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/ash/components/dbus/concierge/concierge_client.h"
#include "components/arc/intent_helper/arc_intent_helper_bridge.h"
#include "components/arc/test/fake_intent_helper_host.h"
#include "components/arc/test/fake_intent_helper_instance.h"
#include "components/user_manager/scoped_user_manager.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

constexpr char kPackageName1[] = "fake.package.name1";
constexpr char kPackageName2[] = "fake.package.name2";
constexpr char kPackageName3[] = "fake.package.name3";
constexpr char kPackageName4[] = "fake.package.name4";
constexpr char kPackageName5[] = "fake.package.name5";

constexpr char kWebAppInfoTitle4[] = "package4";
constexpr char kWebAppInfoStartURL4[] = "https://example.com/app?start";
constexpr char kWebAppInfoScope4[] = "https://example.com/app";
constexpr char kWebAppInfoCertificateFingerprint4[] = "abc";

const std::vector<std::string> kSupportedLocales5 = {"en-US", "ja"};
constexpr char kSelectedLocale5[] = "en-US";

}  // namespace

// static
std::string ArcAppTest::GetAppId(const arc::mojom::AppInfo& app_info) {
  return ArcAppListPrefs::GetAppId(app_info.package_name, app_info.activity);
}

// static
std::string ArcAppTest::GetAppId(const arc::mojom::ShortcutInfo& shortcut) {
  return ArcAppListPrefs::GetAppId(shortcut.package_name, shortcut.intent_uri);
}

// static
std::vector<arc::mojom::ArcPackageInfoPtr> ArcAppTest::ClonePackages(
    const std::vector<arc::mojom::ArcPackageInfoPtr>& packages) {
  std::vector<arc::mojom::ArcPackageInfoPtr> result;
  for (const auto& package : packages)
    result.emplace_back(package->Clone());
  return result;
}

// static
std::vector<arc::mojom::AppInfoPtr> ArcAppTest::CloneApps(
    const std::vector<arc::mojom::AppInfoPtr>& apps) {
  std::vector<arc::mojom::AppInfoPtr> result;
  for (const auto& app : apps)
    result.emplace_back(app->Clone());
  return result;
}

ArcAppTest::ArcAppTest(UserManagerMode user_manager_mode)
    : fake_user_manager_(user_manager_mode == UserManagerMode::kDoNothing
                             ? nullptr
                             : std::make_unique<ash::FakeChromeUserManager>()) {
  CreateFakeAppsAndPackages();
}

ArcAppTest::~ArcAppTest() = default;

void ArcAppTest::SetUp(Profile* profile) {
  if (!ash::ConciergeClient::Get()) {
    concierge_client_initialized_ = true;
    ash::ConciergeClient::InitializeFake(/*fake_cicerone_client=*/nullptr);
  }
  arc::SetArcAvailableCommandLineForTesting(
      base::CommandLine::ForCurrentProcess());
  DCHECK(!profile_);
  profile_ = profile;

  if (!session_manager::SessionManager::Get()) {
    session_manager_ = std::make_unique<session_manager::SessionManager>();
  }

  arc::ResetArcAllowedCheckForTesting(profile_);

  if (fake_user_manager_.Get()) {
    const user_manager::User* user = CreateUserAndLogin();

    // If for any reason the garbage collector kicks in while we are waiting for
    // an icon, have the user-to-profile mapping ready to avoid using the real
    // profile manager (which is null).
    ash::ProfileHelper::Get()->SetUserToProfileMappingForTesting(user,
                                                                 profile_);
  }

  // A valid |arc_app_list_prefs_| is needed for the ARC bridge service and the
  // ARC auth service.
  arc_app_list_pref_ = ArcAppListPrefs::Get(profile_);
  if (!arc_app_list_pref_) {
    ArcAppListPrefsFactory::GetInstance()->RecreateServiceInstanceForTesting(
        profile_);
  }
  arc_service_manager_ = std::make_unique<arc::ArcServiceManager>();
  arc_session_manager_ =
      arc::CreateTestArcSessionManager(std::make_unique<arc::ArcSessionRunner>(
          base::BindRepeating(arc::FakeArcSession::Create)));
  DCHECK(arc::ArcSessionManager::Get());
  arc::ArcSessionManager::SetUiEnabledForTesting(false);
  arc_session_manager_->SetProfile(profile_);
  arc_session_manager_->Initialize();
  arc_play_store_enabled_preference_handler_ =
      std::make_unique<arc::ArcPlayStoreEnabledPreferenceHandler>(
          profile_, arc_session_manager_.get());
  arc_play_store_enabled_preference_handler_->Start();

  if (initialize_real_intent_helper_bridge_) {
    arc::ArcIntentHelperBridge::GetForBrowserContextForTesting(profile_);
    intent_helper_instance_ = std::make_unique<arc::FakeIntentHelperInstance>();
    arc_service_manager_->arc_bridge_service()->intent_helper()->SetInstance(
        intent_helper_instance_.get());
    WaitForInstanceReady(
        arc_service_manager_->arc_bridge_service()->intent_helper());
  }

  arc_app_list_pref_ = ArcAppListPrefs::Get(profile_);
  DCHECK(arc_app_list_pref_);
  if (wait_default_apps_)
    WaitForDefaultApps();
  WaitForRemoveAllApps();

  if (ash::features::ArePromiseIconsEnabled()) {
    apps::AppServiceProxyFactory::GetForProfile(profile_)
        ->PromiseAppService()
        ->SetSkipAlmanacForTesting(true);
  }

  // Check initial conditions.
  if (activate_arc_on_start_) {
    if (!arc::ShouldArcAlwaysStart())
      arc::SetArcPlayStoreEnabledForProfile(profile_, true);
    if (!arc::IsArcPlayStoreEnabledPreferenceManagedForProfile(profile_))
      EXPECT_TRUE(arc_session_manager_->enable_requested());

    app_instance_ = std::make_unique<arc::FakeAppInstance>(arc_app_list_pref_);
    arc_service_manager_->arc_bridge_service()->app()->SetInstance(
        app_instance_.get());

    // TODO(khmel): Resolve this gracefully. Set of default app tests does not
    // expect waiting in ArcAppTest setup.
    if (wait_default_apps_)
      WaitForInstanceReady(arc_service_manager_->arc_bridge_service()->app());

    // Some test doesn't need to wait for compatibility_mode connection.
    if (wait_compatibility_mode_) {
      compatibility_mode_instance_ =
          std::make_unique<arc::FakeCompatibilityModeInstance>();
      arc_service_manager_->arc_bridge_service()
          ->compatibility_mode()
          ->SetInstance(compatibility_mode_instance_.get());
      WaitForInstanceReady(
          arc_service_manager_->arc_bridge_service()->compatibility_mode());
    }
  }

  if (start_app_service_publisher_) {
    // Ensure that the singleton apps::ArcApps is constructed.
    apps::ArcAppsFactory::GetForProfile(profile_);
  }
}

void ArcAppTest::WaitForDefaultApps() {
  DCHECK(arc_app_list_pref_);
  base::RunLoop run_loop;
  arc_app_list_pref_->SetDefaultAppsReadyCallback(run_loop.QuitClosure());
  run_loop.Run();
}

void ArcAppTest::WaitForRemoveAllApps() {
  DCHECK(arc_app_list_pref_);
  if (arc_app_list_pref_->is_remove_all_in_progress()) {
    base::RunLoop run_loop;
    arc_app_list_pref_->SetRemoveAllCallbackForTesting(run_loop.QuitClosure());
    run_loop.Run();
  }
}

void ArcAppTest::CreateFakeAppsAndPackages() {
  arc::mojom::AppInfo app;
  // Make sure we have enough data for test.
  for (int i = 1; i <= 5; ++i) {
    arc::mojom::AppInfoPtr app_info = arc::mojom::AppInfo::New(
        base::StringPrintf("Fake App %d", i),
        base::StringPrintf("fake.package.name%d", i),
        base::StringPrintf("fake.app.%d.activity", i), false /* sticky */);
    app_info->app_category = arc::mojom::AppCategory::kUndefined;
    fake_apps_.emplace_back(std::move(app_info));
  }
  fake_apps_[0]->sticky = true;

  for (int i = 1; i <= 3; ++i) {
    arc::mojom::AppInfoPtr app_info = arc::mojom::AppInfo::New(
        base::StringPrintf("TestApp%d", i), base::StringPrintf("test.app%d", i),
        base::StringPrintf("test.app%d.activity", i), true /* sticky */);
    app_info->app_category = arc::mojom::AppCategory::kUndefined;
    fake_default_apps_.emplace_back(std::move(app_info));
  }

  base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
      permissions1;
  permissions1.emplace(arc::mojom::AppPermission::CAMERA,
                       arc::mojom::PermissionState::New(false /* granted */,
                                                        false /* managed */));
  fake_packages_.emplace_back(arc::mojom::ArcPackageInfo::New(
      kPackageName1 /* package_name */, 1 /* package_version */,
      1 /* last_backup_android_id */, 1 /* last_backup_time */,
      false /* sync */, false /* system */, false /* vpn_provider */,
      nullptr /* web_app_info */, std::nullopt, std::move(permissions1),
      std::nullopt /* version_name */, false /* preinstalled */));

  base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
      permissions2;
  permissions2.emplace(arc::mojom::AppPermission::CAMERA,
                       arc::mojom::PermissionState::New(false /* granted */,
                                                        false /* managed */));
  permissions2.emplace(arc::mojom::AppPermission::MICROPHONE,
                       arc::mojom::PermissionState::New(false /* granted */,
                                                        false /* managed */));
  fake_packages_.emplace_back(arc::mojom::ArcPackageInfo::New(
      kPackageName2 /* package_name */, 2 /* package_version */,
      2 /* last_backup_android_id */, 2 /* last_backup_time */, true /* sync */,
      false /* system */, false /* vpn_provider */, nullptr /* web_app_info */,
      std::nullopt, std::move(permissions2), std::nullopt /* version_name */,
      false /* preinstalled */));

  base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
      permissions3;
  permissions3.emplace(arc::mojom::AppPermission::CAMERA,
                       arc::mojom::PermissionState::New(false /* granted */,
                                                        false /* managed */));
  permissions3.emplace(arc::mojom::AppPermission::MICROPHONE,
                       arc::mojom::PermissionState::New(false /* granted */,
                                                        false /* managed */));
  permissions3.emplace(arc::mojom::AppPermission::LOCATION,
                       arc::mojom::PermissionState::New(true /* granted */,
                                                        false /* managed */));
  fake_packages_.emplace_back(arc::mojom::ArcPackageInfo::New(
      kPackageName3 /* package_name */, 3 /* package_version */,
      3 /* last_backup_android_id */, 3 /* last_backup_time */,
      false /* sync */, false /* system */, false /* vpn_provider */,
      nullptr /* web_app_info */, std::nullopt, std::move(permissions3),
      std::nullopt /* version_name */, false /* preinstalled */));

  base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
      permissions4;
  fake_packages_.emplace_back(arc::mojom::ArcPackageInfo::New(
      kPackageName4,
      /*package_version=*/4,
      /*last_backup_android_id=*/4,
      /*last_backup_time=*/4,
      /*sync=*/false,
      /*system=*/false,
      /*vpn_provider=*/false,
      /*web_app_info=*/
      arc::mojom::WebAppInfo::New(kWebAppInfoTitle4, kWebAppInfoStartURL4,
                                  kWebAppInfoScope4,
                                  /*theme_color=*/0, /*is_web_only_twa=*/true,
                                  kWebAppInfoCertificateFingerprint4),
      /*deprecated_permissions=*/std::nullopt,
      /*permission_states=*/std::move(permissions4),
      /*version_name=*/std::nullopt,
      /*preinstalled=*/false));

  base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
      permissions5;
  fake_packages_.emplace_back(arc::mojom::ArcPackageInfo::New(
      kPackageName5 /* package_name */, 5 /* package_version */,
      5 /* last_backup_android_id */, 5 /* last_backup_time */,
      false /* sync */, false /* system */, false /* vpn_provider */,
      nullptr /* web_app_info */, std::nullopt, std::move(permissions5),
      std::nullopt /* version_name */, false /* preinstalled */,
      arc::mojom::InstallPriority::kUndefined /* priority */,
      arc::mojom::PackageLocaleInfo::New(kSupportedLocales5,
                                         kSelectedLocale5)));

  for (int i = 1; i <= 5; ++i) {
    arc::mojom::ShortcutInfo shortcut_info;
    shortcut_info.name = base::StringPrintf("Fake Shortcut %d", i);
    shortcut_info.package_name = base::StringPrintf("fake.shortcut.%d", i);
    shortcut_info.intent_uri =
        base::StringPrintf("#Intent;fake.shortcut.%d.intent_uri", i);
    shortcut_info.icon_resource_id =
        base::StringPrintf("fake.shortcut.%d.icon_resource_id", i);
    fake_shortcuts_.push_back(shortcut_info);
  }
}

void ArcAppTest::TearDown() {
  if (start_app_service_publisher_)
    apps::ArcAppsFactory::GetInstance()->ShutDownForTesting(profile_);
  if (compatibility_mode_instance_) {
    compatibility_mode_instance_.reset();
  }
  if (intent_helper_instance_) {
    arc_service_manager_->arc_bridge_service()->intent_helper()->CloseInstance(
        intent_helper_instance_.get());
    intent_helper_instance_.reset();
    intent_helper_host_.reset();
  }
  if (initialize_real_intent_helper_bridge_)
    arc::ArcIntentHelperBridge::ShutDownForTesting(profile_);
  app_instance_.reset();
  arc_play_store_enabled_preference_handler_.reset();
  arc_session_manager_.reset();
  if (!persist_service_manager_)
    arc_service_manager_.reset();
  arc::ResetArcAllowedCheckForTesting(profile_);
  // ConciergeClient may be initialized from other testing utility, such as
  // ash::AshTestHelper::SetUp(), so Shutdown() only when it is initialized in
  // ArcAppTest::SetUp().
  if (concierge_client_initialized_) {
    ash::ConciergeClient::Shutdown();
    concierge_client_initialized_ = false;
  }
  profile_ = nullptr;
  session_manager_.reset();
}

void ArcAppTest::StopArcInstance() {
  arc_service_manager_->arc_bridge_service()->app()->CloseInstance(
      app_instance_.get());
}

void ArcAppTest::RestartArcInstance() {
  auto* bridge_service = arc_service_manager_->arc_bridge_service();
  bridge_service->app()->CloseInstance(app_instance_.get());
  app_instance_ = std::make_unique<arc::FakeAppInstance>(arc_app_list_pref_);
  bridge_service->app()->SetInstance(app_instance_.get());
  WaitForInstanceReady(bridge_service->app());
}

void ArcAppTest::SetUpIntentHelper() {
  DCHECK(profile_);
  auto* arc_bridge_service = arc_service_manager_->arc_bridge_service();
  intent_helper_host_ = std::make_unique<arc::FakeIntentHelperHost>(
      arc_bridge_service->intent_helper());
  intent_helper_instance_ = std::make_unique<arc::FakeIntentHelperInstance>();
  arc_bridge_service->intent_helper()->SetInstance(
      intent_helper_instance_.get());
  WaitForInstanceReady(arc_bridge_service->intent_helper());
}

const user_manager::User* ArcAppTest::CreateUserAndLogin() {
  const AccountId account_id(AccountId::FromUserEmailGaiaId(
      profile_->GetProfileUserName(), "1234567890"));
  const user_manager::User* user = fake_user_manager_->AddUser(account_id);
  fake_user_manager_->LoginUser(account_id);
  return user;
}

void ArcAppTest::AddPackage(arc::mojom::ArcPackageInfoPtr package) {
  if (FindPackage(package->package_name))
    return;
  fake_packages_.push_back(std::move(package));
}

void ArcAppTest::UpdatePackage(arc::mojom::ArcPackageInfoPtr updated_package) {
  auto it = base::ranges::find(fake_packages_, updated_package->package_name,
                               &arc::mojom::ArcPackageInfo::package_name);
  if (it != fake_packages_.end()) {
    *it = std::move(updated_package);
  }
}

void ArcAppTest::RemovePackage(const std::string& package_name) {
  std::erase_if(fake_packages_, [package_name](const auto& package) {
    return package->package_name == package_name;
  });
}

bool ArcAppTest::FindPackage(const std::string& package_name) {
  return base::Contains(fake_packages_, package_name,
                        &arc::mojom::ArcPackageInfo::package_name);
}