chromium/chrome/browser/apps/platform_apps/api/arc_apps_private/arc_apps_private_apitest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>

#include "ash/components/arc/session/arc_bridge_service.h"
#include "ash/components/arc/session/arc_service_manager.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 "base/path_service.h"
#include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ash/arc/arc_util.h"
#include "chrome/browser/ash/arc/session/arc_session_manager.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
#include "content/public/test/browser_test.h"
#include "extensions/common/switches.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"

class ArcAppsPrivateApiTest : public extensions::ExtensionApiTest {
 public:
  ArcAppsPrivateApiTest() = default;
  ArcAppsPrivateApiTest(const ArcAppsPrivateApiTest&) = delete;
  ArcAppsPrivateApiTest& operator=(const ArcAppsPrivateApiTest&) = delete;
  ~ArcAppsPrivateApiTest() override = default;

 protected:
  // Helper function to create a fake app instance and wait for the instance to
  // be ready.
  void CreateAppInstance(ArcAppListPrefs* prefs) {
    app_instance_ = std::make_unique<arc::FakeAppInstance>(prefs);
    arc::ArcServiceManager::Get()->arc_bridge_service()->app()->SetInstance(
        app_instance());
    WaitForInstanceReady(
        arc::ArcServiceManager::Get()->arc_bridge_service()->app());
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    extensions::ExtensionApiTest::SetUpCommandLine(command_line);
    arc::SetArcAvailableCommandLineForTesting(command_line);
    // Allowlist the test platform app.
    command_line->AppendSwitchASCII(
        extensions::switches::kAllowlistedExtensionID,
        "fgkfegllpjfhppblcabhjjipnfelohej");
  }

  void SetUpInProcessBrowserTestFixture() override {
    extensions::ExtensionApiTest::SetUpInProcessBrowserTestFixture();
    arc::ArcSessionManager::SetUiEnabledForTesting(false);
    // SessionManagerClient will be destroyed in ChromeBrowserMain.
    ash::SessionManagerClient::InitializeFakeInMemory();
    ash::FakeSessionManagerClient::Get()->set_arc_available(true);
  }

  void SetUpOnMainThread() override {
    extensions::ExtensionApiTest::SetUpOnMainThread();
    arc::SetArcPlayStoreEnabledForProfile(profile(), true);
  }

  void TearDownOnMainThread() override {
    extensions::ExtensionApiTest::TearDownOnMainThread();
    if (app_instance_) {
      arc::ArcServiceManager::Get()->arc_bridge_service()->app()->CloseInstance(
          app_instance());
    }
    app_instance_.reset();
  }

  arc::FakeAppInstance* app_instance() { return app_instance_.get(); }

 private:
  std::unique_ptr<arc::FakeAppInstance> app_instance_;
};

IN_PROC_BROWSER_TEST_F(ArcAppsPrivateApiTest, GetPackageNameAndLaunchApp) {
  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(browser()->profile());
  ASSERT_TRUE(prefs);
  CreateAppInstance(prefs);
  // Add one launchable app and one non-launchable app.
  std::vector<arc::mojom::AppInfoPtr> one_app;
  one_app.emplace_back(
      arc::mojom::AppInfo::New("App_0", "Package_0", "Dummy_activity_0"));
  app_instance()->SendRefreshAppList(one_app);
  static_cast<arc::mojom::AppHost*>(prefs)->OnTaskCreated(
      0 /* task_id */, "Package_1", "Dummy_activity_1", "App_1",
      std::nullopt /* intent */, 0 /* session_id */);

  // Stopping the service makes the app non-ready.
  arc::ArcServiceManager::Get()->arc_bridge_service()->app()->CloseInstance(
      app_instance());
  EXPECT_EQ(0u, app_instance()->launch_requests().size());
  // Verify |chrome.arcAppsPrivate.getLaunchableApps| returns the package name
  // of the launchable app only. The JS test will attempt to launch the app.
  EXPECT_TRUE(RunExtensionTest(
      "arc_app_launcher/launch_app",
      {.custom_arg = "Package_0", .launch_as_platform_app = true}))
      << message_;

  // Verify the app is not launched because it's not ready.
  EXPECT_EQ(0u, app_instance()->launch_requests().size());
  // Restarting the service makes the app ready. Verify the app is launched
  // successfully.
  CreateAppInstance(prefs);
  app_instance()->SendRefreshAppList(one_app);
  EXPECT_EQ(0u, app_instance()->launch_requests().size());
  ASSERT_EQ(1u, app_instance()->launch_intents().size());
  EXPECT_NE(app_instance()->launch_intents()[0].find(
                "component=Package_0/Dummy_activity_0;"),
            std::string::npos);
}

IN_PROC_BROWSER_TEST_F(ArcAppsPrivateApiTest, OnInstalled) {
  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(browser()->profile());
  ASSERT_TRUE(prefs);
  CreateAppInstance(prefs);

  std::vector<arc::mojom::AppInfoPtr> launchable_apps;
  launchable_apps.emplace_back(
      arc::mojom::AppInfo::New("App_0", "Package_0", "Dummy_activity_0"));

  // The JS test will observe the onInstalled event and attempt to launch the
  // newly installed app.
  SetCustomArg("Package_0");
  extensions::ResultCatcher catcher;
  ExtensionTestMessageListener ready_listener("ready");

  base::FilePath path =
      test_data_dir_.AppendASCII("arc_app_launcher/install_event");
  const extensions::Extension* app = LoadExtension(path);
  ASSERT_TRUE(app);
  EXPECT_TRUE(ready_listener.WaitUntilSatisfied());

  EXPECT_EQ(0u, app_instance()->launch_requests().size());
  // Add one launchable app and one non-launchable app.
  app_instance()->SendRefreshAppList(launchable_apps);
  static_cast<arc::mojom::AppHost*>(prefs)->OnTaskCreated(
      0 /* task_id */, "Package_1", "Dummy_activity_1", "App_1",
      std::nullopt /* intent */, 0 /* session_id */);
  // Verify the JS test receives the onInstalled event for the launchable app
  // only, and the app is launched successfully.
  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
  ASSERT_EQ(1u, app_instance()->launch_requests().size());
  EXPECT_TRUE(
      app_instance()->launch_requests()[0]->IsForApp(*launchable_apps[0]));
}