chromium/chrome/browser/ash/borealis/borealis_app_uninstaller_unittest.cc

// Copyright 2021 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/borealis/borealis_app_uninstaller.h"

#include <memory>

#include "base/functional/bind.h"
#include "base/test/bind.h"
#include "chrome/browser/ash/borealis/borealis_app_launcher.h"
#include "chrome/browser/ash/borealis/borealis_installer.h"
#include "chrome/browser/ash/borealis/borealis_metrics.h"
#include "chrome/browser/ash/borealis/borealis_service_fake.h"
#include "chrome/browser/ash/borealis/borealis_util.h"
#include "chrome/browser/ash/borealis/testing/callback_factory.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace borealis {
namespace {

using CallbackFactory =
    StrictCallbackFactory<void(BorealisAppUninstaller::UninstallResult)>;

class BorealisInstallerMock : public BorealisInstaller {
 public:
  BorealisInstallerMock() = default;
  ~BorealisInstallerMock() override = default;
  MOCK_METHOD(bool, IsProcessing, (), ());
  MOCK_METHOD(void, Start, (), ());
  MOCK_METHOD(void, Cancel, (), ());
  MOCK_METHOD(void,
              Uninstall,
              (base::OnceCallback<void(BorealisUninstallResult)>),
              ());
  MOCK_METHOD(void, AddObserver, (Observer * observer), ());
  MOCK_METHOD(void, RemoveObserver, (Observer * observer), ());
};

class BorealisLauncherMock : public BorealisAppLauncher {
 public:
  BorealisLauncherMock() = default;
  ~BorealisLauncherMock() override = default;
  MOCK_METHOD(void,
              Launch,
              (std::string app_id,
               const std::vector<std::string>& args,
               BorealisLaunchSource source,
               OnLaunchedCallback callback),
              ());
  MOCK_METHOD(void,
              Launch,
              (std::string app_id,
               BorealisLaunchSource source,
               OnLaunchedCallback callback),
              ());
};

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

 protected:
  void SetUp() override {
    CreateProfile();
    mock_installer_ =
        std::make_unique<testing::StrictMock<BorealisInstallerMock>>();
    mock_launcher_ =
        std::make_unique<testing::StrictMock<BorealisLauncherMock>>();
    BorealisServiceFake* fake_service =
        BorealisServiceFake::UseFakeForTesting(profile_.get());
    fake_service->SetInstallerForTesting(mock_installer_.get());
    fake_service->SetAppLauncherForTesting(mock_launcher_.get());
    list_ = std::make_unique<vm_tools::apps::ApplicationList>();
    list_->set_vm_name("borealis");
    list_->set_container_name("penguin");
    list_->set_vm_type(vm_tools::apps::BOREALIS);
  }

  void TearDown() override {
    profile_.reset();
    mock_installer_.reset();
    mock_launcher_.reset();
    list_.reset();
  }

  // Sets up the registry with a dummy app. Returns its app id.
  std::string AddDummyApp(vm_tools::apps::ApplicationList* list,
                          const std::string& desktop_file_id,
                          std::string exec) {
    vm_tools::apps::App* app = list->add_apps();
    app->set_desktop_file_id(desktop_file_id);
    app->set_exec(exec);
    vm_tools::apps::App::LocaleString::Entry* entry =
        app->mutable_name()->add_values();
    entry->set_locale(std::string());
    entry->set_value(desktop_file_id);
    app->set_no_display(false);
    guest_os::GuestOsRegistryServiceFactory::GetForProfile(profile_.get())
        ->UpdateApplicationList(*list);
    return guest_os::GuestOsRegistryService::GenerateAppId(
        desktop_file_id, list->vm_name(), list->container_name());
  }

  std::unique_ptr<TestingProfile> profile_;
  std::unique_ptr<testing::StrictMock<BorealisInstallerMock>> mock_installer_;
  std::unique_ptr<testing::StrictMock<BorealisLauncherMock>> mock_launcher_;
  std::unique_ptr<vm_tools::apps::ApplicationList> list_;
  content::BrowserTaskEnvironment task_environment_;

 private:
  void CreateProfile() {
    TestingProfile::Builder profile_builder;
    profile_builder.SetProfileName("defaultprofile");
    profile_ = profile_builder.Build();
  }
};

TEST_F(BorealisAppUninstallerTest, BorealisAppUninstallsBorealis) {
  CallbackFactory callback_check;
  EXPECT_CALL(callback_check,
              Call(BorealisAppUninstaller::UninstallResult::kSuccess));
  BorealisAppUninstaller uninstaller = BorealisAppUninstaller(profile_.get());
  EXPECT_CALL(*mock_installer_, Uninstall(testing::_))
      .WillOnce(testing::Invoke(
          [](base::OnceCallback<void(BorealisUninstallResult)> callback) {
            std::move(callback).Run(BorealisUninstallResult::kSuccess);
          }));
  uninstaller.Uninstall(kInstallerAppId, callback_check.BindOnce());
}

TEST_F(BorealisAppUninstallerTest, BorealisMainAppUninstallsBorealis) {
  CallbackFactory callback_check;
  EXPECT_CALL(callback_check,
              Call(BorealisAppUninstaller::UninstallResult::kSuccess));
  BorealisAppUninstaller uninstaller = BorealisAppUninstaller(profile_.get());
  EXPECT_CALL(*mock_installer_, Uninstall(testing::_))
      .WillOnce(testing::Invoke(
          [](base::OnceCallback<void(BorealisUninstallResult)> callback) {
            std::move(callback).Run(BorealisUninstallResult::kSuccess);
          }));
  uninstaller.Uninstall(kClientAppId, callback_check.BindOnce());
}

TEST_F(BorealisAppUninstallerTest, BorealisGameUninstalls) {
  std::string steam_id = AddDummyApp(list_.get(), "steam", "steam");
  std::string game_id =
      AddDummyApp(list_.get(), "baz.desktop", "steam://rungameid/1439770");
  CallbackFactory callback_check;
  EXPECT_CALL(callback_check,
              Call(BorealisAppUninstaller::UninstallResult::kSuccess));
  BorealisAppUninstaller uninstaller = BorealisAppUninstaller(profile_.get());
  std::vector<std::string> v = {"steam://uninstall/1439770"};
  EXPECT_CALL(
      *mock_launcher_,
      Launch(steam_id, v, BorealisLaunchSource::kAppUninstaller, testing::_))
      .WillOnce(testing::Invoke(
          [&](std::string app_id, const std::vector<std::string>& args,
              BorealisLaunchSource source,
              BorealisAppLauncher::OnLaunchedCallback callback) {
            std::move(callback).Run(
                BorealisAppLauncher::LaunchResult::kSuccess);
          }));
  uninstaller.Uninstall(game_id, callback_check.BindOnce());
}

TEST_F(BorealisAppUninstallerTest, NonExistentAppFails) {
  CallbackFactory callback_check;
  EXPECT_CALL(callback_check,
              Call(BorealisAppUninstaller::UninstallResult::kError));
  BorealisAppUninstaller uninstaller = BorealisAppUninstaller(profile_.get());
  uninstaller.Uninstall("IdontExist", callback_check.BindOnce());
}

TEST_F(BorealisAppUninstallerTest, AppWithEmptyExecFails) {
  std::string baz_id = AddDummyApp(list_.get(), "baz.desktop", "");
  CallbackFactory callback_check;
  EXPECT_CALL(callback_check,
              Call(BorealisAppUninstaller::UninstallResult::kError));
  BorealisAppUninstaller uninstaller = BorealisAppUninstaller(profile_.get());
  uninstaller.Uninstall(baz_id, callback_check.BindOnce());
}

TEST_F(BorealisAppUninstallerTest, AppWithInvalidExecFails) {
  std::string baz_id =
      AddDummyApp(list_.get(), "test.desktop", "desktopname with no id");
  CallbackFactory callback_check;
  EXPECT_CALL(callback_check,
              Call(BorealisAppUninstaller::UninstallResult::kError));
  BorealisAppUninstaller uninstaller = BorealisAppUninstaller(profile_.get());
  uninstaller.Uninstall(baz_id, callback_check.BindOnce());
}

}  // namespace
}  // namespace borealis