chromium/chrome/browser/ash/crosapi/lacros_shelf_item_tracker_unittest.cc

// Copyright 2023 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/crosapi/lacros_shelf_item_tracker.h"

#include <memory>
#include <string>

#include "ash/public/cpp/shelf_model.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/test/ash_test_base.h"
#include "chrome/browser/ui/ash/shelf/lacros_shelf_item_controller.h"
#include "chromeos/crosapi/cpp/crosapi_constants.h"
#include "chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom.h"
#include "components/exo/window_properties.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/aura/window.h"
#include "ui/gfx/image/image_skia.h"

namespace crosapi {

namespace {

gfx::ImageSkia CreateGreenSquareIcon() {
  int size_px = 128;
  SkBitmap bitmap;
  bitmap.allocN32Pixels(size_px, size_px);
  bitmap.eraseColor(SK_ColorGREEN);
  return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
}

}  // namespace

// This class does nothing.
class TestShelfItemDelegate : public LacrosShelfItemController {
 public:
  explicit TestShelfItemDelegate(const ash::ShelfID& shelf_id)
      : LacrosShelfItemController(shelf_id) {}
  ~TestShelfItemDelegate() override = default;

  void AddWindow(aura::Window* window) override {}
  std::u16string GetTitle() override { return u"test title"; }

  // ShelfItemDelegate:
  void ExecuteCommand(bool from_context_menu,
                      int64_t command_id,
                      int32_t event_flags,
                      int64_t display_id) override {}
  void Close() override {}
};

class TestLacrosShelfItemTracker : public LacrosShelfItemTracker {
 public:
  TestLacrosShelfItemTracker() = default;

  void RemoveFromShelf(const ash::ShelfID& shelf_id) override {
    last_removed_shelf_id_ = shelf_id;
    LacrosShelfItemTracker::RemoveFromShelf(shelf_id);
  }

  ash::ShelfItemDelegate* AddOrUpdateShelfItemAndReturnDelegate(
      mojom::WindowDataPtr window_data) override {
    last_updated_shelf_id_ = ash::ShelfID(window_data->item_id);
    return LacrosShelfItemTracker::AddOrUpdateShelfItemAndReturnDelegate(
        std::move(window_data));
  }

  const ash::ShelfID& last_updated_shelf_id() { return last_updated_shelf_id_; }
  const ash::ShelfID& last_removed_shelf_id() { return last_removed_shelf_id_; }

 private:
  std::unique_ptr<ash::ShelfItemDelegate> CreateDelegateByInstanceType(
      const ash::ShelfID& shelf_id,
      mojom::InstanceType instance_type) override {
    return std::make_unique<TestShelfItemDelegate>(shelf_id);
  }

  ash::ShelfID last_updated_shelf_id_;
  ash::ShelfID last_removed_shelf_id_;
};

class LacrosShelfItemTrackerTest : public ash::AshTestBase {
 public:
  ash::ShelfModel* shelf_model() { return ash::ShelfModel::Get(); }

  std::unique_ptr<aura::Window> CreateAndInitWindowWithId(std::string id) {
    auto window = std::make_unique<aura::Window>(nullptr);
    window->SetProperty(exo::kApplicationIdKey, id);
    CHECK(*(window->GetProperty(exo::kApplicationIdKey)) == id);
    window->Init(ui::LAYER_NOT_DRAWN);
    return window;
  }

  std::string lacros_window_prefix() { return kLacrosAppIdPrefix; }

  void AddOrUpdateWindow(std::string item_id,
                         std::string window_id,
                         gfx::ImageSkia icon) {
    mojom::WindowDataPtr window_data = mojom::WindowData::New(
        item_id, window_id, mojom::InstanceType::kIsolatedWebAppInstaller,
        icon);
    tracker_.AddOrUpdateWindow(std::move(window_data));
  }

  const ash::ShelfID& last_updated_shelf_id() {
    return tracker_.last_updated_shelf_id();
  }
  const ash::ShelfID& last_removed_shelf_id() {
    return tracker_.last_removed_shelf_id();
  }

 private:
  TestLacrosShelfItemTracker tracker_;
};

TEST_F(LacrosShelfItemTrackerTest, SingleShelfItemInitWindowBeforeCrosapi) {
  std::string item_id = "test_item_id";
  std::string window_id = lacros_window_prefix() + "test_window_id";
  gfx::ImageSkia icon = CreateGreenSquareIcon();

  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
  ASSERT_EQ(shelf_model()->item_count(), 0);

  auto window = CreateAndInitWindowWithId(window_id);
  AddOrUpdateWindow(item_id, window_id, icon);

  ASSERT_EQ(shelf_model()->item_count(), 1);
  EXPECT_TRUE(shelf_model()->items()[0].image.BackedBySameObjectAs(icon));
  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id"));
}

TEST_F(LacrosShelfItemTrackerTest, SingleShelfItemInitWindowAfterCrosapi) {
  std::string item_id = "test_item_id";
  std::string window_id = lacros_window_prefix() + " test_window_id";
  gfx::ImageSkia icon = CreateGreenSquareIcon();

  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
  ASSERT_EQ(shelf_model()->item_count(), 0);

  AddOrUpdateWindow(item_id, window_id, icon);
  auto window = CreateAndInitWindowWithId(window_id);

  ASSERT_EQ(shelf_model()->item_count(), 1);
  EXPECT_TRUE(shelf_model()->items()[0].image.BackedBySameObjectAs(icon));
  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id"));
}

TEST_F(LacrosShelfItemTrackerTest, IgnoresNotLacrosWindow) {
  std::string item_id = "test_item_id";
  std::string window_id = " test_window_id";
  gfx::ImageSkia icon = CreateGreenSquareIcon();

  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
  ASSERT_EQ(shelf_model()->item_count(), 0);

  AddOrUpdateWindow(item_id, window_id, icon);
  auto window = CreateAndInitWindowWithId(window_id);

  ASSERT_EQ(shelf_model()->item_count(), 0);
  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID());
}

TEST_F(LacrosShelfItemTrackerTest, SingleShelfItemUpdateIcon) {
  std::string item_id = "test_item_id";
  std::string window_id = lacros_window_prefix() + " test_window_id";
  gfx::ImageSkia icon = CreateGreenSquareIcon();

  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
  ASSERT_EQ(shelf_model()->item_count(), 0);

  auto window = CreateAndInitWindowWithId(window_id);
  AddOrUpdateWindow(item_id, window_id, gfx::ImageSkia());

  ASSERT_EQ(shelf_model()->item_count(), 1);
  EXPECT_TRUE(shelf_model()->items()[0].image.isNull());

  AddOrUpdateWindow(item_id, window_id, icon);
  ASSERT_EQ(shelf_model()->item_count(), 1);
  EXPECT_TRUE(shelf_model()->items()[0].image.BackedBySameObjectAs(icon));
}

TEST_F(LacrosShelfItemTrackerTest, SingleShelfItemMultipleWindow) {
  std::string item_id = "test_item_id";
  gfx::ImageSkia icon = CreateGreenSquareIcon();

  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
  ASSERT_EQ(shelf_model()->item_count(), 0);

  std::string window_id_0 = lacros_window_prefix() + " test_window_id_0";
  auto window_0 = CreateAndInitWindowWithId(window_id_0);
  AddOrUpdateWindow(item_id, window_id_0, icon);

  ASSERT_EQ(shelf_model()->item_count(), 1);
  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id"));

  std::string window_id_1 = lacros_window_prefix() + " test_window_id_1";
  auto window_1 = CreateAndInitWindowWithId(window_id_1);
  AddOrUpdateWindow(item_id, window_id_1, icon);

  EXPECT_EQ(shelf_model()->item_count(), 1);
  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id"));

  std::string window_id_2 = lacros_window_prefix() + " test_window_id_2";
  auto window_2 = CreateAndInitWindowWithId(window_id_2);
  AddOrUpdateWindow(item_id, window_id_2, icon);

  EXPECT_EQ(shelf_model()->item_count(), 1);
  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id"));
}

TEST_F(LacrosShelfItemTrackerTest, MultipleShelfItem) {
  gfx::ImageSkia icon = CreateGreenSquareIcon();

  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
  ASSERT_EQ(shelf_model()->item_count(), 0);

  std::string item_id_0 = "test_item_id_0";
  std::string window_id_0 = lacros_window_prefix() + " test_window_id_0";
  auto window_0 = CreateAndInitWindowWithId(window_id_0);
  AddOrUpdateWindow(item_id_0, window_id_0, icon);

  ASSERT_EQ(shelf_model()->item_count(), 1);
  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id_0"));

  std::string item_id_1 = "test_item_id_1";
  std::string window_id_1 = lacros_window_prefix() + " test_window_id_1";
  auto window_1 = CreateAndInitWindowWithId(window_id_1);
  AddOrUpdateWindow(item_id_1, window_id_1, icon);

  EXPECT_EQ(shelf_model()->item_count(), 2);
  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id_1"));

  std::string item_id_2 = "test_item_id_2";
  std::string window_id_2 = lacros_window_prefix() + " test_window_id_2";
  auto window_2 = CreateAndInitWindowWithId(window_id_2);
  AddOrUpdateWindow(item_id_2, window_id_2, icon);

  EXPECT_EQ(shelf_model()->item_count(), 3);
  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id_2"));
}

TEST_F(LacrosShelfItemTrackerTest, ShelfItemRemovedWhenAllWindowDestoryed) {
  std::string item_id = "test_item_id";
  gfx::ImageSkia icon = CreateGreenSquareIcon();

  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
  ASSERT_EQ(shelf_model()->item_count(), 0);

  std::string window_id_0 = lacros_window_prefix() + " test_window_id_0";
  auto window_0 = CreateAndInitWindowWithId(window_id_0);
  AddOrUpdateWindow(item_id, window_id_0, icon);

  ASSERT_EQ(shelf_model()->item_count(), 1);

  std::string window_id_1 = lacros_window_prefix() + " test_window_id_1";
  auto window_1 = CreateAndInitWindowWithId(window_id_1);
  AddOrUpdateWindow(item_id, window_id_1, icon);

  EXPECT_EQ(shelf_model()->item_count(), 1);

  std::string window_id_2 = lacros_window_prefix() + " test_window_id_2";
  auto window_2 = CreateAndInitWindowWithId(window_id_2);
  AddOrUpdateWindow(item_id, window_id_2, icon);

  EXPECT_EQ(shelf_model()->item_count(), 1);

  window_2.reset();
  EXPECT_EQ(shelf_model()->item_count(), 1);

  window_0.reset();
  EXPECT_EQ(shelf_model()->item_count(), 1);

  window_1.reset();
  EXPECT_EQ(shelf_model()->item_count(), 0);
}

}  // namespace crosapi