chromium/chrome/browser/ui/webui/print_preview/extension_printer_service_provider_lacros_browsertest.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/ui/webui/print_preview/extension_printer_service_provider_lacros.h"

#include <memory>

#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/test/values_test_util.h"
#include "base/unguessable_token.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/webui/print_preview/extension_printer_handler.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/crosapi/mojom/extension_printer.mojom.h"
#include "chromeos/lacros/lacros_service.h"
#include "content/public/test/browser_test.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace printing {

namespace {

base::Value::List CreateTestPrinters() {
  return base::test::ParseJsonList(R"(
    [ {
      "description": "A virtual printer for testing",
      "extensionId": "jbljdigmdjodgkcllikhggoepmmffbam",
      "extensionName": "Test Printer Provider",
      "id": "jbljdigmdjodgkcllikhggoepmmffbam:test-printer-01",
      "name": "Test Printer 01"
    }, {
      "description": "A virtual printer for testing",
      "extensionId": "jbljdigmdjodgkcllikhggoepmmffbam",
      "extensionName": "Test Printer Provider",
      "id": "jbljdigmdjodgkcllikhggoepmmffbam:test-printer-02",
      "name": "Test Printer 02"
    } ]
  )");
}

base::Value::Dict CreateTestCapability() {
  return base::test::ParseJsonDict(R"(
    {
      "version": "1.0",
      "printer": {
        "supported_content_type": [
          {"content_type": "application/pdf"}
        ]
      }
    })");
}

using ::testing::_;
using ::testing::Mock;
using ::testing::SizeIs;

class MockExtensionPrinterServiceAsh
    : public crosapi::mojom::ExtensionPrinterService {
 public:
  MockExtensionPrinterServiceAsh() {
    ON_CALL(*this, RegisterServiceProvider)
        .WillByDefault(testing::Invoke(
            [&](mojo::PendingRemote<
                crosapi::mojom::ExtensionPrinterServiceProvider> provider) {
              remote_.Bind(std::move(provider));
            }));
  }

  // mojom::ExtensionPrinterService:
  MOCK_METHOD(
      void,
      RegisterServiceProvider,
      (mojo::PendingRemote<crosapi::mojom::ExtensionPrinterServiceProvider>),
      (override));

  MOCK_METHOD(void,
              PrintersAdded,
              (const base::UnguessableToken& request_id,
               base::Value::List printers,
               bool is_done),
              (override));

  mojo::Receiver<crosapi::mojom::ExtensionPrinterService> receiver_{this};
  mojo::Remote<crosapi::mojom::ExtensionPrinterServiceProvider> remote_;
};

class MockExtensionPrinterHandler : public PrinterHandler {
 public:
  MockExtensionPrinterHandler() = default;

  MOCK_METHOD(void, Reset, (), (override));

  // Mock the methods you want to interact with:
  MOCK_METHOD(void,
              StartGetPrinters,
              (AddedPrintersCallback added_printers_callback,
               GetPrintersDoneCallback done_callback),
              (override));

  MOCK_METHOD(void,
              StartGetCapability,
              (const std::string& destination_id,
               GetCapabilityCallback callback),
              (override));
  MOCK_METHOD(void,
              StartGrantPrinterAccess,
              (const std::string& printer_id, GetPrinterInfoCallback callback),
              (override));
  MOCK_METHOD(void,
              StartPrint,
              (const std::u16string& job_title,
               base::Value::Dict settings,
               scoped_refptr<base::RefCountedMemory> print_data,
               PrintCallback callback),
              (override));
};

class ExtensionPrinterServiceProviderLacrosBrowserTest
    : public InProcessBrowserTest {
 public:
  ExtensionPrinterServiceProviderLacrosBrowserTest()
      : request_id_(base::UnguessableToken::Create()) {}

  // InProcessBrowserTest:
  void SetUpOnMainThread() override {
    InProcessBrowserTest::SetUpOnMainThread();

    // Inject the mock interface.
    chromeos::LacrosService::Get()->InjectRemoteForTesting(
        mock_extension_printer_service_.receiver_.BindNewPipeAndPassRemote());
  }

 protected:
  const base::UnguessableToken request_id_;
  MockExtensionPrinterServiceAsh mock_extension_printer_service_;
};

}  // namespace

// Verifies that `ExtensionPrinterServiceProviderLacros` calls the
// ExtensionPrinterService interface to register itself as a service provider.
IN_PROC_BROWSER_TEST_F(ExtensionPrinterServiceProviderLacrosBrowserTest,
                       RegisterServiceProvider) {
  EXPECT_CALL(mock_extension_printer_service_, RegisterServiceProvider);

  ExtensionPrinterServiceProviderLacros provider{browser()->profile()};

  chromeos::LacrosService::Get()
      ->GetRemote<crosapi::mojom::ExtensionPrinterService>()
      .FlushForTesting();
}

// Verifies that `ExtensionPrinterServiceProviderLacros` calls the
// ExtensionPrinterService's PrintersAdded interface to report printers found
// and to signal done.
IN_PROC_BROWSER_TEST_F(ExtensionPrinterServiceProviderLacrosBrowserTest,
                       DispatchGetPrintersRequest) {
  EXPECT_CALL(mock_extension_printer_service_, RegisterServiceProvider);
  // The last call is for signaling done.
  EXPECT_CALL(mock_extension_printer_service_,
              PrintersAdded(request_id_, SizeIs(0), true));
  // The first call is for reporting none empty printer set.
  EXPECT_CALL(mock_extension_printer_service_,
              PrintersAdded(request_id_, SizeIs(2), false));

  auto mock_handler = std::make_unique<MockExtensionPrinterHandler>();
  EXPECT_CALL(*mock_handler, StartGetPrinters(_, _))
      .WillOnce(
          [](PrinterHandler::AddedPrintersCallback added_printers_callback,
             PrinterHandler::GetPrintersDoneCallback done_callback) {
            // Run the "added_printers_callback" with the test printers.
            std::move(added_printers_callback).Run(CreateTestPrinters());
            // Run the "done_callback" to signal completion.
            std::move(done_callback).Run();
          });

  ExtensionPrinterServiceProviderLacros provider{browser()->profile()};

  provider.SetPrinterHandlerForTesting(std::move(mock_handler));
  provider.DispatchGetPrintersRequest(request_id_);

  chromeos::LacrosService::Get()
      ->GetRemote<crosapi::mojom::ExtensionPrinterService>()
      .FlushForTesting();
}

// Verifies that `ExtensionPrinterServiceProviderLacros` calls the
// ExtensionPrinterHandler's Reset interface when requested.
IN_PROC_BROWSER_TEST_F(ExtensionPrinterServiceProviderLacrosBrowserTest,
                       Reset) {
  auto mock_handler = std::make_unique<MockExtensionPrinterHandler>();
  EXPECT_CALL(*mock_handler, Reset());

  ExtensionPrinterServiceProviderLacros provider{browser()->profile()};

  provider.SetPrinterHandlerForTesting(std::move(mock_handler));
  provider.DispatchResetRequest();
}

// Verifies that `ExtensionPrinterServiceProviderLacros` calls the
// ExtensionPrinterHandler's StartGetCapability interface when requested.
IN_PROC_BROWSER_TEST_F(ExtensionPrinterServiceProviderLacrosBrowserTest,
                       StartGetCapability) {
  std::string captured_printer_id;
  auto mock_handler = std::make_unique<MockExtensionPrinterHandler>();
  EXPECT_CALL(*mock_handler, StartGetCapability(_, _))
      .WillOnce([&captured_printer_id](
                    const std::string& destination_id,
                    ExtensionPrinterHandler::GetCapabilityCallback callback) {
        captured_printer_id = destination_id;
        std::move(callback).Run(CreateTestCapability());
      });

  ExtensionPrinterServiceProviderLacros provider{browser()->profile()};

  provider.SetPrinterHandlerForTesting(std::move(mock_handler));

  base::test::TestFuture<base::Value::Dict> get_capability_future;
  const std::string printer_id =
      "jbljdigmdjodgkcllikhggoepmmffba1:test-printer-02";

  provider.DispatchStartGetCapability(printer_id,
                                      get_capability_future.GetCallback());
  // Verifies that the printer_id is passed to the printer handler.
  EXPECT_EQ(printer_id, captured_printer_id);

  // Verified that a capability is received correctly.
  const base::Value::Dict& capability = get_capability_future.Take();
  base::ExpectDictStringValue("1.0", capability, "version");

  const base::Value::List* supportedContentTypes =
      capability.FindListByDottedPath("printer.supported_content_type");
  ASSERT_TRUE(supportedContentTypes);
  EXPECT_EQ(supportedContentTypes->size(), 1u);

  const base::Value& contentType1 = (*supportedContentTypes)[0];
  EXPECT_TRUE(contentType1.is_dict());
  base::ExpectDictStringValue("application/pdf", contentType1.GetDict(),
                              "content_type");
}

// Verifies that DispatchStartPrint calls the handler correctly.
IN_PROC_BROWSER_TEST_F(ExtensionPrinterServiceProviderLacrosBrowserTest,
                       DispatchStartPrint) {
  // Sets up mock handler.
  auto mock_handler = std::make_unique<MockExtensionPrinterHandler>();

  // Capturing arguments passed to StartPrint for verification.
  std::u16string captured_job_title;
  base::Value::Dict captured_settings;
  scoped_refptr<::base::RefCountedMemory> captured_print_data;
  EXPECT_CALL(*mock_handler, StartPrint(_, _, _, _))
      .WillOnce([&](const std::u16string& job_title, base::Value::Dict settings,
                    scoped_refptr<::base::RefCountedMemory> print_data,
                    PrinterHandler::PrintCallback callback) {
        captured_job_title = job_title;
        captured_settings = std::move(settings);
        captured_print_data = print_data;
        // Simulate a successful print job. An empty value means successful.
        std::move(callback).Run(base::Value());
      });

  // Prepares test data.
  ExtensionPrinterServiceProviderLacros provider(browser()->profile());
  provider.SetPrinterHandlerForTesting(std::move(mock_handler));

  const std::u16string job_title = u"Test Print Job";
  base::Value::Dict settings = base::test::ParseJsonDict(R"(
    {
      "copies": 2,
      "color": "color"
    }
  )");
  scoped_refptr<base::RefCountedMemory> print_data =
      base::MakeRefCounted<base::RefCountedString>("Test print data");

  // Calls the method under test.
  base::test::TestFuture<crosapi::mojom::StartPrintStatus> print_future;
  provider.DispatchStartPrint(job_title, std::move(settings), print_data,
                              print_future.GetCallback());

  // Verify results.
  EXPECT_EQ(print_future.Get(), crosapi::mojom::StartPrintStatus::KOk);
  EXPECT_EQ(captured_job_title, job_title);
  EXPECT_EQ(captured_settings,
            base::test::ParseJsonDict(R"({"copies": 2, "color": "color"})"));
  EXPECT_TRUE(print_data->Equals(captured_print_data));
}

// Verifies that DispatchStartGrantPrinterAccess calls the handler correctly.
IN_PROC_BROWSER_TEST_F(ExtensionPrinterServiceProviderLacrosBrowserTest,
                       DispatchStartGrantPrinterAccess) {
  // Test data.
  const std::string test_printer_id =
      "test_printer_id_123:fake_ext_id:fake_device_guid";
  base::Value::Dict expected_printer_info = base::test::ParseJsonDict(R"(
    {
      "printerId": "test_printer_id_123",
      "name": "Test Printer"
    }
  )");

  auto mock_handler = std::make_unique<MockExtensionPrinterHandler>();

  EXPECT_CALL(*mock_handler, StartGrantPrinterAccess(test_printer_id, _))
      .WillOnce([&expected_printer_info](
                    const std::string& printer_id,
                    PrinterHandler::GetPrinterInfoCallback callback) {
        // Simulates successful printer access grant.
        std::move(callback).Run(expected_printer_info.Clone());
      });

  // Prepares and sets the provider with the mock handler.
  ExtensionPrinterServiceProviderLacros provider{browser()->profile()};
  provider.SetPrinterHandlerForTesting(std::move(mock_handler));

  base::test::TestFuture<base::Value::Dict> grant_access_future;
  provider.DispatchStartGrantPrinterAccess(test_printer_id,
                                           grant_access_future.GetCallback());
  // Verify results
  const base::Value::Dict& printer_info = grant_access_future.Get();
  EXPECT_EQ(printer_info, expected_printer_info);
}

}  // namespace printing