// 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