chromium/chrome/browser/ash/mahi/mahi_browser_delegate_ash_unittest.cc

// Copyright 2024 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/mahi/mahi_browser_delegate_ash.h"

#include <memory>
#include <string>

#include "base/memory/raw_ref.h"
#include "base/test/task_environment.h"
#include "base/unguessable_token.h"
#include "chromeos/crosapi/mojom/mahi.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {

namespace {
// Check the received page content is equal to the expected page context.
void OnGetContent(crosapi::mojom::MahiPageContentPtr expected_page_content,
                  crosapi::mojom::MahiPageContentPtr page_content) {
  // If no page content is returned, both should be empty.
  if (!expected_page_content) {
    EXPECT_FALSE(page_content);
    return;
  }
  // Otherwise, the returned page content should be the same as expected.
  EXPECT_EQ(expected_page_content->client_id, page_content->client_id);
  EXPECT_EQ(expected_page_content->page_id, page_content->page_id);
  EXPECT_EQ(expected_page_content->page_content, page_content->page_content);
}

}  // namespace

class FakeMahiBrowserCppClient : public crosapi::mojom::MahiBrowserClient {
 public:
  explicit FakeMahiBrowserCppClient(
      MahiBrowserDelegateAsh& mahi_browser_delegate)
      : client_id_(base::UnguessableToken::Create()),
        mahi_browser_delegate_(mahi_browser_delegate) {
    mahi_browser_delegate_->RegisterCppClient(this, client_id_);
  }
  FakeMahiBrowserCppClient(const FakeMahiBrowserCppClient&) = delete;
  FakeMahiBrowserCppClient& operator=(const FakeMahiBrowserCppClient&) = delete;
  ~FakeMahiBrowserCppClient() override {
    mahi_browser_delegate_->UnregisterClient(client_id_);
  }

  void set_valid_id(const base::UnguessableToken& id) { valid_id_ = id; }
  void set_content(const std::u16string content) { content_ = content; }
  const base::UnguessableToken& client_id() { return client_id_; }
  const base::UnguessableToken& last_id() { return last_id_; }

  void GetContent(const base::UnguessableToken& content_id,
                  GetContentCallback callback) override {
    last_id_ = content_id;
    if (!valid_id_ || valid_id_ != content_id) {
      std::move(callback).Run(std::move(nullptr));
      return;
    }

    crosapi::mojom::MahiPageContentPtr page_content =
        crosapi::mojom::MahiPageContent::New();
    page_content->client_id = client_id_;
    page_content->page_id = content_id;
    page_content->page_content = content_;

    std::move(callback).Run(std::move(page_content));
  }

 private:
  base::UnguessableToken client_id_;
  const raw_ref<MahiBrowserDelegateAsh> mahi_browser_delegate_;

  // The valid id for the content.
  base::UnguessableToken valid_id_;
  std::u16string content_;
  base::UnguessableToken last_id_;
};

class MahiBrowserDelegateTest : public testing::Test {
 public:
  MahiBrowserDelegateAsh& mahi_browser_delegate() {
    return mahi_browser_delegate_;
  }

  void RunUntilIdle() { task_environment_.RunUntilIdle(); }

 private:
  MahiBrowserDelegateAsh mahi_browser_delegate_;
  base::test::TaskEnvironment task_environment_;
};

TEST_F(MahiBrowserDelegateTest, GetContent) {
  std::unique_ptr<FakeMahiBrowserCppClient> client1 =
      std::make_unique<FakeMahiBrowserCppClient>(mahi_browser_delegate());
  const base::UnguessableToken client1_content_id =
      base::UnguessableToken::Create();
  const std::u16string client1_content = u"content for client 1";
  client1->set_valid_id(client1_content_id);
  client1->set_content(client1_content);

  // Basic functioning.
  // Unmatched contents won't be returned.
  const base::UnguessableToken random_id = base::UnguessableToken::Create();
  ASSERT_NE(client1_content_id, random_id);

  crosapi::mojom::MahiPageContentPtr null_page_content = nullptr;
  mahi_browser_delegate().GetContentFromClient(
      client1->client_id(), random_id,
      base::BindOnce(&OnGetContent, std::move(null_page_content)));
  RunUntilIdle();
  // Client1 receives the request.
  EXPECT_EQ(client1->last_id(), random_id);

  // Matched contents will be returned properly.
  crosapi::mojom::MahiPageContentPtr expected_page_content1 =
      crosapi::mojom::MahiPageContent::New();
  expected_page_content1->client_id = client1->client_id();
  expected_page_content1->page_id = client1_content_id;
  expected_page_content1->page_content = client1_content;
  mahi_browser_delegate().GetContentFromClient(
      client1->client_id(), client1_content_id,
      base::BindOnce(&OnGetContent, std::move(expected_page_content1)));
  RunUntilIdle();
  EXPECT_EQ(client1->last_id(), client1_content_id);

  // Multiple clients.
  std::unique_ptr<FakeMahiBrowserCppClient> client2 =
      std::make_unique<FakeMahiBrowserCppClient>(mahi_browser_delegate());
  const base::UnguessableToken client2_content_id =
      base::UnguessableToken::Create();
  ASSERT_NE(client1_content_id, client2_content_id);
  const std::u16string client2_content = u"content for client 2";
  client2->set_valid_id(client2_content_id);
  client2->set_content(client2_content);

  // Only the client with matched contents will return.
  crosapi::mojom::MahiPageContentPtr expected_page_content2 =
      crosapi::mojom::MahiPageContent::New();
  expected_page_content2->client_id = client2->client_id();
  expected_page_content2->page_id = client2_content_id;
  expected_page_content2->page_content = client2_content;
  mahi_browser_delegate().GetContentFromClient(
      client2->client_id(), client2_content_id,
      base::BindOnce(&OnGetContent, std::move(expected_page_content2)));
  RunUntilIdle();
  EXPECT_EQ(client2->last_id(), client2_content_id);
  // client1 is not visited.
  EXPECT_EQ(client1->last_id(), client1_content_id);

  // Destructed client will be removed from the list.
  const base::UnguessableToken client1_id = client1->client_id();
  client1.reset();
  mahi_browser_delegate().GetContentFromClient(
      client1_id, client1_content_id,
      base::BindOnce(&OnGetContent, std::move(null_page_content)));
  RunUntilIdle();
}

}  // namespace ash