chromium/chrome/browser/ash/printing/synced_printers_manager_unittest.cc

// Copyright 2016 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/printing/synced_printers_manager.h"

#include <algorithm>
#include <memory>
#include <optional>
#include <utility>

#include "base/debug/dump_without_crashing.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "chrome/browser/ash/printing/printers_sync_bridge.h"
#include "chrome/browser/ash/printing/synced_printers_manager_factory.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/sync/model/data_type_store.h"
#include "components/sync/test/data_type_store_test_util.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {

namespace {

using ::chromeos::Printer;

constexpr char kTestPrinterId[] = "UUID-UUID-UUID-PRINTER";
constexpr char kTestPrinterId2[] = "UUID-UUID-UUID-PRINTR2";
constexpr char kTestUri[] = "ipps://printer.chromium.org/ipp/print";

// Helper class to record observed events.
class LoggingObserver : public SyncedPrintersManager::Observer {
 public:
  explicit LoggingObserver(SyncedPrintersManager* source) : manager_(source) {
    observation_.Observe(source);
  }

  void OnSavedPrintersChanged() override {
    saved_printers_ = manager_->GetSavedPrinters();
  }

  const std::vector<Printer>& saved_printers() const { return saved_printers_; }

 private:
  std::vector<Printer> saved_printers_;
  base::ScopedObservation<SyncedPrintersManager,
                          SyncedPrintersManager::Observer>
      observation_{this};
  raw_ptr<SyncedPrintersManager> manager_;
};

class SyncedPrintersManagerTest : public testing::Test {
 protected:
  SyncedPrintersManagerTest()
      : manager_(
            SyncedPrintersManager::Create(std::make_unique<PrintersSyncBridge>(
                syncer::DataTypeStoreTestUtil::FactoryForInMemoryStoreForTest(),
                base::BindRepeating(
                    base::IgnoreResult(&base::debug::DumpWithoutCrashing),
                    FROM_HERE,
                    base::Minutes(5))))) {
    base::RunLoop().RunUntilIdle();
  }

  // Must outlive |profile_|.
  content::BrowserTaskEnvironment task_environment_;

  std::unique_ptr<SyncedPrintersManager> manager_;
};

// Add a test failure if the ids of printers are not those in expected.  Order
// is not considered.
void ExpectObservedPrinterIdsAre(const std::vector<Printer>& printers,
                                 std::vector<std::string> expected_ids) {
  // Ensure all callbacks have completed before we check.
  base::RunLoop().RunUntilIdle();

  std::sort(expected_ids.begin(), expected_ids.end());
  std::vector<std::string> printer_ids;
  for (const Printer& printer : printers) {
    printer_ids.push_back(printer.id());
  }
  std::sort(printer_ids.begin(), printer_ids.end());
  if (printer_ids != expected_ids) {
    ADD_FAILURE() << "Expected to find ids: {"
                  << base::JoinString(expected_ids, ",") << "}; found ids: {"
                  << base::JoinString(printer_ids, ",") << "}";
  }
}

TEST_F(SyncedPrintersManagerTest, AddPrinter) {
  LoggingObserver observer(manager_.get());
  manager_->UpdateSavedPrinter(Printer(kTestPrinterId));

  auto printers = manager_->GetSavedPrinters();
  ASSERT_EQ(1U, printers.size());
  EXPECT_EQ(kTestPrinterId, printers[0].id());
  EXPECT_EQ(Printer::Source::SRC_USER_PREFS, printers[0].source());

  ExpectObservedPrinterIdsAre(observer.saved_printers(), {kTestPrinterId});
}

TEST_F(SyncedPrintersManagerTest, UpdatePrinterAssignsId) {
  manager_->UpdateSavedPrinter(Printer());
  auto printers = manager_->GetSavedPrinters();
  ASSERT_EQ(1U, printers.size());
  EXPECT_FALSE(printers[0].id().empty());
}

TEST_F(SyncedPrintersManagerTest, UpdatePrinter) {
  manager_->UpdateSavedPrinter(Printer(kTestPrinterId));
  Printer updated_printer(kTestPrinterId);
  updated_printer.SetUri(kTestUri);

  // Register observer so it only receives the update event.
  LoggingObserver observer(manager_.get());

  manager_->UpdateSavedPrinter(updated_printer);

  auto printers = manager_->GetSavedPrinters();
  ASSERT_EQ(1U, printers.size());
  EXPECT_EQ(kTestUri, printers[0].uri().GetNormalized(false));

  ExpectObservedPrinterIdsAre(observer.saved_printers(), {kTestPrinterId});
}

TEST_F(SyncedPrintersManagerTest, RemovePrinter) {
  manager_->UpdateSavedPrinter(Printer("OtherUUID"));
  manager_->UpdateSavedPrinter(Printer(kTestPrinterId));
  manager_->UpdateSavedPrinter(Printer());

  manager_->RemoveSavedPrinter(kTestPrinterId);

  auto printers = manager_->GetSavedPrinters();

  // One of the remaining ids should be "OtherUUID", the other should have
  // been automatically generated by the manager.
  ASSERT_EQ(2U, printers.size());
  EXPECT_NE(kTestPrinterId, printers.at(0).id());
  EXPECT_NE(kTestPrinterId, printers.at(1).id());
}

// Test that UpdateSavedPrinter saves a printer if it doesn't appear in the
// saved printer lists.
TEST_F(SyncedPrintersManagerTest, UpdateSavedPrinterSavesPrinter) {
  Printer saved(kTestPrinterId);

  // Install |saved| printer.
  manager_->UpdateSavedPrinter(saved);
  auto found_printer = manager_->GetPrinter(kTestPrinterId);
  ASSERT_TRUE(found_printer);
  EXPECT_TRUE(found_printer->display_name().empty());

  // Saving a printer we know about *should not* generate a configuration
  // update.
  manager_->UpdateSavedPrinter(*found_printer);
  EXPECT_EQ(1U, manager_->GetSavedPrinters().size());

  // Saving a printer we don't know about *should* generate a configuration
  // update.
  manager_->UpdateSavedPrinter(Printer(kTestPrinterId2));
  EXPECT_EQ(2U, manager_->GetSavedPrinters().size());
}

}  // namespace
}  // namespace ash