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

#include <functional>
#include <memory>
#include <string_view>
#include <utility>

#include "base/json/json_string_value_serializer.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/single_thread_task_runner.h"
#include "base/values.h"
#include "chrome/common/printing/printer_capabilities.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_task_environment.h"
#include "printing/backend/print_backend.h"
#include "printing/backend/test_print_backend.h"
#include "printing/buildflags/buildflags.h"
#include "printing/print_job_constants.h"
#include "printing/printing_features.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"

#if BUILDFLAG(ENABLE_OOP_PRINTING)
#include "chrome/browser/printing/print_backend_service_manager.h"
#include "chrome/browser/printing/print_backend_service_test_impl.h"
#else
#include "base/notreached.h"
#endif

#if BUILDFLAG(IS_WIN) && BUILDFLAG(ENABLE_OOP_PRINTING)
#include <vector>

#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/values_test_util.h"
#include "chrome/browser/printing/printer_xml_parser_impl.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#endif  // BUILDFLAG(IS_WIN) && BUILDFLAG(ENABLE_OOP_PRINTING)

namespace printing {

namespace {

#if BUILDFLAG(IS_WIN) && BUILDFLAG(ENABLE_OOP_PRINTING)
constexpr char kVendorCapabilities[] = "vendor_capability";

// XML with feature not of interest.
constexpr char kXmlTestFeature[] =
    R"(<?xml version="1.0" encoding="UTF-8"?>
    <psf:PrintCapabilities>
      <psf:Feature name="TestFeature">
        <psf:Property name="psf:SelectionType">
          <psf:Value xsi:type="xsd:QName">psk:PickOne</psf:Value>
        </psf:Property>
        <psf:Option constrained="psk:None">
          <psf:ScoredProperty name="TestTimeStamp">
            <psf:Value xsi:type="xsd:integer">0</psf:Value>
          </psf:ScoredProperty>
        </psf:Option>
      </psf:Feature>
    </psf:PrintCapabilities>)";

constexpr char kXmlPageOutputQuality[] =
    R"(<?xml version="1.0" encoding="UTF-8"?>
    <psf:PrintCapabilities>
      <psf:Feature name="psk:PageOutputQuality">
        <psf:Property name="psf:SelectionType">
          <psf:Value xsi:type="xsd:QName">psk:PickOne</psf:Value>
        </psf:Property>
        <psf:Property name="psk:DisplayName">
          <psf:Value xsi:type="xsd:string">Quality</psf:Value>
        </psf:Property>
        <psf:Option name="ns0000:Draft" constrained="psk:None">
          <psf:Property name="psk:DisplayName">
            <psf:Value xsi:type="xsd:string">Draft</psf:Value>
          </psf:Property>
        </psf:Option>
        <psf:Option name="ns0000:Standard" constrained="psk:None">
          <psf:Property name="psk:DisplayName">
            <psf:Value xsi:type="xsd:string">Standard</psf:Value>
          </psf:Property>
        </psf:Option>
      </psf:Feature>
    </psf:PrintCapabilities>)";

constexpr char kJsonPageOutputQuality[] = R"({
  "display_name": "Page output quality",
  "id": "page_output_quality",
  "select_cap": {
    "option": [ {
        "display_name": "Draft",
        "value": "ns0000:Draft"
      }, {
        "display_name": "Standard",
        "value": "ns0000:Standard"
      } ]
  },
  "type": "SELECT"
})";
#endif  // BUILDFLAG(IS_WIN) && BUILDFLAG(ENABLE_OOP_PRINTING)

// Used as a callback to `GetDefaultPrinter()` in tests.
// Records value returned by `GetDefaultPrinter()`.
void RecordGetDefaultPrinter(std::string& default_printer_out,
                             const std::string& default_printer) {}

// Used as a callback to `StartGetPrinters()` in tests.
// Increases `call_count` and records values returned by `StartGetPrinters()`.
void RecordPrinterList(size_t& call_count,
                       base::Value::List& printers_out,
                       base::Value::List printers) {}

// Used as a callback to `StartGetPrinters` in tests.
// Records that the test is done.
void RecordPrintersDone(bool& is_done_out) {}

void RecordGetCapability(base::Value::Dict& capabilities_out,
                         base::Value::Dict capability) {}

// Converts JSON string to `base::Value` object.
// On failure, fills `error` string and the return value is not a list.
base::Value GetJSONAsValue(std::string_view json, std::string& error) {}

}  // namespace

// Base testing class for `LocalPrinterHandlerDefault`.  Contains the base
// logic to allow for using either a local task runner or a service to make
// print backend calls, and to possibly enable fallback when using a service.
// Tests to trigger those different paths can be done by overloading
// `UseService()` and `SupportFallback()`.
class LocalPrinterHandlerDefaultTestBase : public testing::Test {};

// Testing class to cover `LocalPrinterHandlerDefault` handling using either a
// local task runner or a service.  Makes no attempt to cover fallback when
// using a service, which is handled separately by
// `LocalPrinterHandlerDefaultWithServiceTest`
class LocalPrinterHandlerDefaultTest
    : public LocalPrinterHandlerDefaultTestBase,
      public testing::WithParamInterface<bool> {};

#if BUILDFLAG(ENABLE_OOP_PRINTING)

// Testing class to cover `LocalPrinterHandlerDefault` handling using only a
// service.  This can check different behavior for whether fallback is enabled,
// Mojom data validation conditions, or service termination.
// This unit test fixture does not actually run the PrintBackendService
// out-of-process, nor does it actually perform sandboxing.
class LocalPrinterHandlerDefaultWithServiceTest
    : public LocalPrinterHandlerDefaultTestBase {};

#if BUILDFLAG(IS_WIN)
class LocalPrinterHandlerDefaultWithServiceEnableXpsTest
    : public LocalPrinterHandlerDefaultWithServiceTest {
 public:
  bool EnableXpsCapabilities() override { return true; }
};
#endif  // BUILDFLAG(IS_WIN)

INSTANTIATE_TEST_SUITE_P();

#else

// Without OOP printing we only test local test runner configuration.
INSTANTIATE_TEST_SUITE_P(/*no prefix */,
                         LocalPrinterHandlerDefaultTest,
                         testing::Values(false));

#endif  // BUILDFLAG(ENABLE_OOP_PRINTING)

// Tests that getting default printer is successful.
TEST_P(LocalPrinterHandlerDefaultTest, GetDefaultPrinter) {}

// Tests that getting default printer gives empty string when no printers are
// installed.
TEST_P(LocalPrinterHandlerDefaultTest, GetDefaultPrinterNoneInstalled) {}

#if BUILDFLAG(ENABLE_OOP_PRINTING)

// Tests that getting the default printer fails if the print backend service
// terminates early, such as it would from a crash.
TEST_F(LocalPrinterHandlerDefaultWithServiceTest,
       GetDefaultPrinterTerminatedService) {}

#endif  // BUILDFLAG(ENABLE_OOP_PRINTING)

TEST_P(LocalPrinterHandlerDefaultTest, GetPrinters) {}

TEST_P(LocalPrinterHandlerDefaultTest, GetPrintersNoneRegistered) {}

#if BUILDFLAG(ENABLE_OOP_PRINTING)

// Tests that enumerating printers fails when there is invalid printer data.
TEST_F(LocalPrinterHandlerDefaultWithServiceTest,
       GetPrintersInvalidPrinterDataFails) {}

// Tests that enumerating printers fails if the print backend service
// terminates early, such as it would from a crash.
TEST_F(LocalPrinterHandlerDefaultWithServiceTest,
       GetPrintersTerminatedService) {}

#endif  // BUILDFLAG(ENABLE_OOP_PRINTING)

// Tests that fetching capabilities for an existing installed printer is
// successful.
TEST_P(LocalPrinterHandlerDefaultTest, StartGetCapabilityValidPrinter) {}

// Tests that fetching capabilities bails early when the provided printer
// can't be found.
TEST_P(LocalPrinterHandlerDefaultTest, StartGetCapabilityInvalidPrinter) {}

#if BUILDFLAG(ENABLE_OOP_PRINTING)

// Test that installed printers to which the user does not have permission to
// access will fail to get any capabilities.
TEST_P(LocalPrinterHandlerDefaultTest, StartGetCapabilityAccessDenied) {}

// Tests that fetching capabilities can eventually succeed with fallback
// processing when a printer requires elevated permissions.
TEST_F(LocalPrinterHandlerDefaultWithServiceTest,
       StartGetCapabilityElevatedPermissionsSucceeds) {}

// Tests that fetching capabilities fails when there is invalid printer data.
TEST_F(LocalPrinterHandlerDefaultWithServiceTest,
       StartGetCapabilityInvalidPrinterDataFails) {}

// Tests that fetching capabilities fails if the print backend service
// terminates early, such as it would from a crash.
TEST_F(LocalPrinterHandlerDefaultWithServiceTest,
       StartGetCapabilityTerminatedService) {}

#if BUILDFLAG(IS_WIN)

// Tests that fetching XPS capabilities succeeds if the XML string is valid,
// even when there are no XPS capabilities of interest.
TEST_F(LocalPrinterHandlerDefaultWithServiceEnableXpsTest,
       FetchXpsPrinterCapabilitiesValidXps) {
  AddPrinter("printer1", "default1", "description1", /*is_default=*/true,
             /*requires_elevated_permissions=*/false);
  SetPrinterXml("printer1", kXmlTestFeature);

  base::Value::Dict fetched_caps;
  local_printer_handler()->StartGetCapability(
      /*destination_id=*/"printer1",
      base::BindOnce(&RecordGetCapability, std::ref(fetched_caps)));
  RunUntilIdle();

  // Fetching capabilities should still succeed.
  const base::Value::Dict* capabilities =
      fetched_caps.FindDict(kSettingCapabilities);
  ASSERT_TRUE(capabilities);
  EXPECT_TRUE(fetched_caps.FindDict(kPrinter));

  // None of the capabilities of interest exist in the XML, so no XML
  // capabilities should be added.
  const base::Value::Dict* printer = capabilities->FindDict(kPrinter);
  ASSERT_TRUE(printer);
  ASSERT_FALSE(printer->FindList(kVendorCapabilities));
}

// Tests that XPS capabilities are included when fetching capabilities of
// interest.
TEST_F(LocalPrinterHandlerDefaultWithServiceEnableXpsTest,
       FetchXpsPrinterCapabilitiesValidXpsCapabilityWithInterest) {
  AddPrinter("printer1", "default1", "description1", /*is_default=*/true,
             /*requires_elevated_permissions=*/false);
  SetPrinterXml("printer1", kXmlPageOutputQuality);

  base::Value::Dict fetched_caps;
  local_printer_handler()->StartGetCapability(
      /*destination_id=*/"printer1",
      base::BindOnce(&RecordGetCapability, std::ref(fetched_caps)));

  RunUntilIdle();

  const base::Value::Dict* capabilities =
      fetched_caps.FindDict(kSettingCapabilities);
  ASSERT_TRUE(capabilities);
  EXPECT_TRUE(fetched_caps.FindDict(kPrinter));

  // Check for XPS capabilities added.
  const base::Value::Dict* printer = capabilities->FindDict(kPrinter);
  ASSERT_TRUE(printer);

  const base::Value::List* vendor_capabilities =
      printer->FindList(kVendorCapabilities);
  ASSERT_TRUE(vendor_capabilities);
  ASSERT_EQ(vendor_capabilities->size(), 1u);
  EXPECT_EQ(vendor_capabilities->front(),
            base::test::ParseJson(kJsonPageOutputQuality));
}

// Tests that fetching capabilities fails when the XPS string is invalid and
// cannot be processed.
TEST_F(LocalPrinterHandlerDefaultWithServiceEnableXpsTest,
       FetchXpsPrinterCapabilitiesInvalidXps) {
  AddPrinter("printer1", "default1", "description1", /*is_default=*/true,
             /*requires_elevated_permissions=*/false);
  SetPrinterXml("printer1", "");

  base::Value::Dict fetched_caps;
  local_printer_handler()->StartGetCapability(
      /*destination_id=*/"printer1",
      base::BindOnce(&RecordGetCapability, std::ref(fetched_caps)));

  RunUntilIdle();

  const base::Value::Dict* capabilities =
      fetched_caps.FindDict(kSettingCapabilities);
  ASSERT_FALSE(capabilities);
}

#endif  // BUILDFLAG(IS_WIN)

#endif  // BUILDFLAG(ENABLE_OOP_PRINTING)

}  // namespace printing