blob: 7719e43f11a194944fd26d3f056c201e9e46f869 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/printing/zeroconf_printer_detector.h"
#include <algorithm>
#include <functional>
#include <random>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/stl_util.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "chrome/browser/local_discovery/service_discovery_device_lister.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace {
using local_discovery::ServiceDescription;
using local_discovery::ServiceDiscoveryDeviceLister;
// Determine basic printer attributes deterministically but pseudorandomly based
// on the printer name. The exact values returned here are not really
// important, the important parts are that there's variety based on the name,
// and it's deterministic.
// Should this printer provide usb_MFG and usb_MDL fields?
bool GetUsbFor(const std::string& name) {
return std::hash<std::string>()(name) & 1;
}
// Get an IP address for this printer. The returned address may be IPv4 or IPv6
net::IPAddress GetIPAddressFor(const std::string& name) {
std::mt19937 rng(std::hash<std::string>()(name));
if (rng() & 1) {
// Give an IPv4 address.
return net::IPAddress(rng(), rng(), rng(), rng());
} else {
// Give an IPv6 address.
return net::IPAddress(rng(), rng(), rng(), rng(), rng(), rng(), rng(),
rng(), rng(), rng(), rng(), rng(), rng(), rng(),
rng(), rng());
}
}
int GetPortFor(const std::string& name) {
return (std::hash<std::string>()(name) % 1000) + 1;
}
// Bitfield flags for MakeExpectedPrinter()
int kFlagSSL = 0x1; // Use ipps, not ipp.
int kFlagIPPE = 0x2; // Printer can be autoconfigured with IPP-Everywhere
// This corresponds to FakeServiceDeviceLister::MakeServiceDescription. Given
// the same name (and the correct ssl/ippe flags based on the service type) this
// generates the DetectedPrinter record we expect from ZeroconfPrinterDectector
// when it gets that ServiceDescription. This needs to be kept in sync with
// FakeServiceDeviceLister::MakeServiceDescription.
PrinterDetector::DetectedPrinter MakeExpectedPrinter(const std::string& name,
int flags) {
PrinterDetector::DetectedPrinter detected;
Printer& printer = detected.printer;
net::IPAddress ip_address = GetIPAddressFor(name);
int port = GetPortFor(name);
bool ssl = flags & kFlagSSL;
printer.set_effective_uri(
base::StringPrintf("ipp%s://%s:%d/%s_rp", ssl ? "s" : "",
ip_address.ToString().c_str(), port, name.c_str()));
printer.set_uri(base::StringPrintf("ipp%s://%s.local:%d/%s_rp",
ssl ? "s" : "", name.c_str(), port,
name.c_str()));
printer.set_uuid(base::StrCat({name, "_UUID"}));
printer.set_display_name(base::StrCat({name, "_ty"}));
printer.set_description(base::StrCat({name, "_note"}));
printer.set_make_and_model(base::StrCat({name, "_product"}));
detected.ppd_search_data.make_and_model.push_back(printer.display_name());
detected.ppd_search_data.make_and_model.push_back(printer.make_and_model());
if (GetUsbFor(name)) {
// We should get an effective make and model guess from the usb fields
// if they exist.
detected.ppd_search_data.make_and_model.push_back(
base::StrCat({name, "_usb_MFG ", name, "_usb_MDL"}));
}
if (flags & kFlagIPPE) {
printer.mutable_ppd_reference()->autoconf = true;
}
return detected;
}
// This is a thin wrapper around Delegate that defers callbacks until
// the actual delegate is initialized, then calls all deferred callbacks. Once
// the actual delegate is initialized, this just becomes a simple passthrough.
class DeferringDelegate : public ServiceDiscoveryDeviceLister::Delegate {
public:
void OnDeviceChanged(const std::string& service_type,
bool added,
const ServiceDescription& service_description) override {
if (actual_) {
actual_->OnDeviceChanged(service_type, added, service_description);
} else {
deferred_callbacks_.push_back(base::BindOnce(
&DeferringDelegate::OnDeviceChanged, base::Unretained(this),
service_type, added, service_description));
}
}
// Not guaranteed to be called after OnDeviceChanged.
void OnDeviceRemoved(const std::string& service_type,
const std::string& service_name) override {
if (actual_) {
actual_->OnDeviceRemoved(service_type, service_name);
} else {
deferred_callbacks_.push_back(
base::BindOnce(&DeferringDelegate::OnDeviceRemoved,
base::Unretained(this), service_type, service_name));
}
}
void OnDeviceCacheFlushed(const std::string& service_type) override {
if (actual_) {
actual_->OnDeviceCacheFlushed(service_type);
} else {
deferred_callbacks_.push_back(
base::BindOnce(&DeferringDelegate::OnDeviceCacheFlushed,
base::Unretained(this), service_type));
}
}
void SetActual(ServiceDiscoveryDeviceLister::Delegate* actual) {
CHECK(!actual_);
actual_ = actual;
for (auto& cb : deferred_callbacks_) {
std::move(cb).Run();
}
deferred_callbacks_.clear();
}
private:
std::vector<base::OnceCallback<void()>> deferred_callbacks_;
ServiceDiscoveryDeviceLister::Delegate* actual_ = nullptr;
};
// A fake ServiceDiscoveryDeviceLister. This provides an implementation
// of ServiceDiscoveryDeviceLister that tests can use to trigger addition
// and removal of devices.
//
// There's some hackery here to handle constructor order constraints. There's a
// circular dependency in that ZeroconfPrinterDetector (which is a device lister
// delegate) needs its device lister set to be supplied at construction time,
// and each device lister needs to know about its delegate for callbacks. Thus
// we use DeferringDelegate to queue callbacks triggered before we have the
// delegate reference in this class, and invoke those queued callbacks when the
// Delegate is set.
class FakeServiceDiscoveryDeviceLister : public ServiceDiscoveryDeviceLister {
public:
FakeServiceDiscoveryDeviceLister(base::TaskRunner* task_runner,
const std::string& service_type)
: task_runner_(task_runner), service_type_(service_type) {}
~FakeServiceDiscoveryDeviceLister() override = default;
// The only thing we care about with Start() is that it's called before
// DiscoverNewDevices.
void Start() override {
if (start_called_) {
ADD_FAILURE() << "Start called multiple times";
}
start_called_ = true;
}
// When DiscoverNewDevices is called, all updates we've queued up until this
// point are invoked.
void DiscoverNewDevices() override {
if (!start_called_) {
ADD_FAILURE() << "DiscoverNewDevices called before Start";
}
discovery_started_ = true;
for (const auto& update : queued_updates_) {
SendUpdate(update);
}
queued_updates_.clear();
}
const std::string& service_type() const override { return service_type_; }
void SetDelegate(ServiceDiscoveryDeviceLister::Delegate* delegate) {
deferring_delegate_.SetActual(delegate);
}
// Announce a new service or update it if we've seen it before and already
// announced it. If discovery hasn't started yet, queue the description
// to be sent when discovery is started.
void Announce(const std::string& name) {
ServiceDescription description = MakeServiceDescription(name);
if (!discovery_started_) {
queued_updates_.push_back(description);
} else {
SendUpdate(description);
}
}
void Remove(const std::string& name) {
std::string service_name = base::StrCat({name, ".", service_type_});
announced_services_.erase(service_name);
CHECK(task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ServiceDiscoveryDeviceLister::Delegate::OnDeviceRemoved,
base::Unretained(&deferring_delegate_), service_type_,
service_name)));
}
// Simulate an event that clears downstream caches and the lister.
void Clear() {
announced_services_.clear();
discovery_started_ = false;
CHECK(task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&ServiceDiscoveryDeviceLister::Delegate::OnDeviceCacheFlushed,
base::Unretained(&deferring_delegate_), service_type_)));
}
// Create a deterministic ServiceDescription based on the name and this
// lister's service_type. See the note on MakeExpectedPrinter, above. This
// is a member function instead of a free function because the service_type_
// impacts some of the fields. This must be kept in sync with
// MakeExpectedPrinter.
ServiceDescription MakeServiceDescription(const std::string& name) {
ServiceDescription sd;
sd.service_name = base::StrCat({name, ".", service_type_});
sd.metadata.push_back(base::StrCat({"ty=", name, "_ty"}));
sd.metadata.push_back(base::StrCat({"product=(", name, "_product)"}));
if (GetUsbFor(name)) {
sd.metadata.push_back(base::StrCat({"usb_MFG=", name, "_usb_MFG"}));
sd.metadata.push_back(base::StrCat({"usb_MDL=", name, "_usb_MDL"}));
}
sd.metadata.push_back(base::StrCat({"rp=", name, "_rp"}));
sd.metadata.push_back(base::StrCat({"note=", name, "_note"}));
sd.metadata.push_back(base::StrCat({"UUID=", name, "_UUID"}));
sd.address.set_host(base::StrCat({name, ".local"}));
sd.ip_address = GetIPAddressFor(name);
sd.address.set_port(GetPortFor(name));
return sd;
}
bool discovery_started() { return discovery_started_; }
private:
void SendUpdate(const ServiceDescription& description) {
bool is_new;
if (!base::ContainsKey(announced_services_, description.service_name)) {
is_new = true;
announced_services_.insert(description.service_name);
} else {
is_new = false;
}
CHECK(task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ServiceDiscoveryDeviceLister::Delegate::OnDeviceChanged,
base::Unretained(&deferring_delegate_), service_type_,
is_new, description)));
}
base::TaskRunner* task_runner_;
// Services which have previously posted an update, and therefore are no
// longer 'new' for the purposes of the OnDeviceChanged callback.
std::set<std::string> announced_services_;
// Updates added to the class before discovery started.
std::vector<ServiceDescription> queued_updates_;
// Has Start() been called?
bool start_called_ = false;
// Has DiscoverNewDevices been called?
bool discovery_started_ = false;
std::string service_type_;
DeferringDelegate deferring_delegate_;
};
class ZeroconfPrinterDetectorTest : public testing::Test,
public PrinterDetector::Observer {
public:
ZeroconfPrinterDetectorTest() {
auto* runner = scoped_task_environment_.GetMainThreadTaskRunner().get();
auto ipp_lister = std::make_unique<FakeServiceDiscoveryDeviceLister>(
runner, ZeroconfPrinterDetector::kIppServiceName);
ipp_lister_ = ipp_lister.get();
auto ipps_lister = std::make_unique<FakeServiceDiscoveryDeviceLister>(
runner, ZeroconfPrinterDetector::kIppsServiceName);
ipps_lister_ = ipps_lister.get();
auto ippe_lister = std::make_unique<FakeServiceDiscoveryDeviceLister>(
runner, ZeroconfPrinterDetector::kIppEverywhereServiceName);
ippe_lister_ = ippe_lister.get();
auto ippse_lister = std::make_unique<FakeServiceDiscoveryDeviceLister>(
runner, ZeroconfPrinterDetector::kIppsEverywhereServiceName);
ippse_lister_ = ippse_lister.get();
listers_[ZeroconfPrinterDetector::kIppServiceName] = std::move(ipp_lister);
listers_[ZeroconfPrinterDetector::kIppsServiceName] =
std::move(ipps_lister);
listers_[ZeroconfPrinterDetector::kIppEverywhereServiceName] =
std::move(ippe_lister);
listers_[ZeroconfPrinterDetector::kIppsEverywhereServiceName] =
std::move(ippse_lister);
}
~ZeroconfPrinterDetectorTest() override = default;
void CreateDetector() {
detector_ = ZeroconfPrinterDetector::CreateForTesting(&listers_);
// The previously allocated listers_ are swapped into the detector_, and so
// the unique_ptr values of the listers_ map are no longer valid at this
// point. The ipp[se]_lister_ raw pointers are kept as seperate members to
// keep the lister fakes accessible after ownership is transferred into the
// detector.
listers_.clear();
detector_->AddObserver(this);
ipp_lister_->SetDelegate(detector_.get());
ipps_lister_->SetDelegate(detector_.get());
ippe_lister_->SetDelegate(detector_.get());
ippse_lister_->SetDelegate(detector_.get());
}
// Expect that the most up-to-date results from the detector match those
// in printers.
void ExpectPrintersAre(
const std::vector<PrinterDetector::DetectedPrinter>& printers) {
// The last observer callback should tell us the same thing as the querying
// the detector manually.
ASSERT_GE(printers_found_callbacks_.size(), 1U);
ExpectPrintersEq(printers, printers_found_callbacks_.back());
ExpectPrintersEq(printers, detector_->GetPrinters());
}
// Expect that the given vectors have the same contents. The ordering
// may be different.
void ExpectPrintersEq(
const std::vector<PrinterDetector::DetectedPrinter>& expected,
const std::vector<PrinterDetector::DetectedPrinter>& actual) {
if (expected.size() != actual.size()) {
ADD_FAILURE() << "Printers size mismatch, found " << actual.size()
<< " expected " << expected.size();
return;
}
std::vector<PrinterDetector::DetectedPrinter> sorted_expected = expected;
std::vector<PrinterDetector::DetectedPrinter> sorted_actual = actual;
std::sort(sorted_expected.begin(), sorted_expected.end(),
[](const PrinterDetector::DetectedPrinter& a,
const PrinterDetector::DetectedPrinter& b) -> bool {
return a.printer.uuid() < b.printer.uuid();
});
std::sort(sorted_actual.begin(), sorted_actual.end(),
[](const PrinterDetector::DetectedPrinter& a,
const PrinterDetector::DetectedPrinter& b) -> bool {
return a.printer.uuid() < b.printer.uuid();
});
for (size_t i = 0; i < sorted_expected.size(); ++i) {
ExpectPrinterEq(sorted_expected[i], sorted_actual[i]);
}
}
void ExpectPrinterEq(const PrinterDetector::DetectedPrinter& expected,
const PrinterDetector::DetectedPrinter& actual) {
EXPECT_EQ(expected.printer.effective_uri(), actual.printer.effective_uri());
EXPECT_EQ(expected.printer.uri(), actual.printer.uri());
// We don't have a good way to directly check for an expected id.
EXPECT_EQ(expected.printer.uuid(), actual.printer.uuid());
EXPECT_EQ(expected.printer.display_name(), actual.printer.display_name());
EXPECT_EQ(expected.printer.description(), actual.printer.description());
EXPECT_EQ(expected.printer.IsIppEverywhere(),
actual.printer.IsIppEverywhere());
EXPECT_EQ(expected.printer.make_and_model(),
actual.printer.make_and_model());
EXPECT_EQ(expected.ppd_search_data.usb_vendor_id,
actual.ppd_search_data.usb_vendor_id);
EXPECT_EQ(expected.ppd_search_data.usb_product_id,
actual.ppd_search_data.usb_product_id);
EXPECT_EQ(expected.ppd_search_data.make_and_model,
actual.ppd_search_data.make_and_model);
}
// PrinterDetector::Observer callback.
void OnPrintersFound(
const std::vector<PrinterDetector::DetectedPrinter>& printers) override {
printers_found_callbacks_.push_back(printers);
}
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
// Device listers fakes. These are initialized when the test is constructed.
// These pointers don't involve ownership; ownership of the listers starts
// with this class in listers_ when the test starts, and is transferred to
// detector_ when the detector is created. Throughout, the listers remain
// available to the test via these pointers.
FakeServiceDiscoveryDeviceLister* ipp_lister_;
FakeServiceDiscoveryDeviceLister* ipps_lister_;
FakeServiceDiscoveryDeviceLister* ippe_lister_;
FakeServiceDiscoveryDeviceLister* ippse_lister_;
// Detector under test.
std::unique_ptr<ZeroconfPrinterDetector> detector_;
// Saved copies of all the things given to OnPrintersFound.
std::vector<std::vector<PrinterDetector::DetectedPrinter>>
printers_found_callbacks_;
private:
// Temporary storage for the device listers, between the time the test is
// constructed and the detector is created. Tests shouldn't access this
// directly, use the ipp*_lister_ variables instead.
std::map<std::string, std::unique_ptr<ServiceDiscoveryDeviceLister>> listers_;
};
// Very basic stuff, one printer of each protocol we support.
TEST_F(ZeroconfPrinterDetectorTest, SingleIppPrinter) {
ipp_lister_->Announce("Printer1");
CreateDetector();
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre({MakeExpectedPrinter("Printer1", 0)});
}
TEST_F(ZeroconfPrinterDetectorTest, SingleIppsPrinter) {
ipps_lister_->Announce("Printer2");
CreateDetector();
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre({MakeExpectedPrinter("Printer2", kFlagSSL)});
}
TEST_F(ZeroconfPrinterDetectorTest, SingleIppEverywherePrinter) {
ippe_lister_->Announce("Printer3");
CreateDetector();
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre({MakeExpectedPrinter("Printer3", kFlagIPPE)});
}
TEST_F(ZeroconfPrinterDetectorTest, SingleIppsEverywherePrinter) {
ippse_lister_->Announce("Printer4");
CreateDetector();
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre({MakeExpectedPrinter("Printer4", kFlagSSL | kFlagIPPE)});
}
// Test that an announce after the detector creation shows up as a printer.
TEST_F(ZeroconfPrinterDetectorTest, AnnounceAfterDetectorCreation) {
CreateDetector();
scoped_task_environment_.RunUntilIdle();
ippse_lister_->Announce("Printer4");
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre({MakeExpectedPrinter("Printer4", kFlagSSL | kFlagIPPE)});
}
// Test that we use the same printer ID regardless of which service type it
// comes to us from.
TEST_F(ZeroconfPrinterDetectorTest, StableIds) {
ipp_lister_->Announce("Printer1");
CreateDetector();
scoped_task_environment_.RunUntilIdle();
ASSERT_FALSE(printers_found_callbacks_.empty());
ASSERT_EQ(1U, printers_found_callbacks_.back().size());
// Grab the id when it's an IPPS printer We should continue to get the same id
// regardless of service type.
std::string id = printers_found_callbacks_.back()[0].printer.id();
// Remove it as an IPP printer, add it as an IPPS printer.
ipp_lister_->Remove("Printer1");
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(printers_found_callbacks_.back().empty());
ipps_lister_->Announce("Printer1");
scoped_task_environment_.RunUntilIdle();
ASSERT_FALSE(printers_found_callbacks_.back().empty());
// Id should be the same.
ASSERT_EQ(id, printers_found_callbacks_.back()[0].printer.id());
// Remove it as an IPPS printer, add it as an IPP-Everywhere printer.
ipps_lister_->Remove("Printer1");
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(printers_found_callbacks_.back().empty());
ippe_lister_->Announce("Printer1");
scoped_task_environment_.RunUntilIdle();
ASSERT_FALSE(printers_found_callbacks_.back().empty());
// Id should be the same.
ASSERT_EQ(id, printers_found_callbacks_.back()[0].printer.id());
// Remove it as an IPP-Everywhere printer, add it as an IPPS-Everywhere
// printer.
ippe_lister_->Remove("Printer1");
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(printers_found_callbacks_.back().empty());
ippse_lister_->Announce("Printer1");
scoped_task_environment_.RunUntilIdle();
ASSERT_FALSE(printers_found_callbacks_.back().empty());
// Id should be the same.
ASSERT_EQ(id, printers_found_callbacks_.back()[0].printer.id());
}
// Test a basic removal.
TEST_F(ZeroconfPrinterDetectorTest, Removal) {
ipp_lister_->Announce("Printer5");
ipp_lister_->Announce("Printer6");
ipp_lister_->Announce("Printer7");
ipp_lister_->Announce("Printer8");
ipp_lister_->Announce("Printer9");
CreateDetector();
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre(
{MakeExpectedPrinter("Printer5", 0), MakeExpectedPrinter("Printer6", 0),
MakeExpectedPrinter("Printer7", 0), MakeExpectedPrinter("Printer8", 0),
MakeExpectedPrinter("Printer9", 0)});
ipp_lister_->Remove("Printer7");
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre(
{MakeExpectedPrinter("Printer5", 0), MakeExpectedPrinter("Printer6", 0),
MakeExpectedPrinter("Printer8", 0), MakeExpectedPrinter("Printer9", 0)});
}
// Test that, when the same printer appears in multiple services, we
// use the highest priority one. Priorities, from highest to lowest
// are IPPS-E, IPP-E, IPPS, IPP.
TEST_F(ZeroconfPrinterDetectorTest, ServiceTypePriorities) {
// Advertise on all 4 services.
ipp_lister_->Announce("Printer5");
ipps_lister_->Announce("Printer5");
ippe_lister_->Announce("Printer5");
ippse_lister_->Announce("Printer5");
CreateDetector();
scoped_task_environment_.RunUntilIdle();
// IPPS-E is highest priority.
ExpectPrintersAre({MakeExpectedPrinter("Printer5", kFlagSSL | kFlagIPPE)});
ippse_lister_->Remove("Printer5");
scoped_task_environment_.RunUntilIdle();
// IPP-E is highest remaining priority.
ExpectPrintersAre({MakeExpectedPrinter("Printer5", kFlagIPPE)});
ippe_lister_->Remove("Printer5");
scoped_task_environment_.RunUntilIdle();
// IPPS is highest remaining priority.
ExpectPrintersAre({MakeExpectedPrinter("Printer5", kFlagSSL)});
ipps_lister_->Remove("Printer5");
scoped_task_environment_.RunUntilIdle();
// IPP is only remaining entry.
ExpectPrintersAre({MakeExpectedPrinter("Printer5", 0)});
ipp_lister_->Remove("Printer5");
scoped_task_environment_.RunUntilIdle();
// No entries left.
ExpectPrintersAre({});
}
// Test that cache flushes appropriately remove entries.
TEST_F(ZeroconfPrinterDetectorTest, CacheFlushes) {
ipp_lister_->Announce("Printer6");
ipp_lister_->Announce("Printer7");
ipps_lister_->Announce("Printer7");
ipps_lister_->Announce("Printer8");
ippe_lister_->Announce("Printer8");
ippe_lister_->Announce("Printer9");
ippse_lister_->Announce("Printer9");
ippse_lister_->Announce("Printer10");
CreateDetector();
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre({MakeExpectedPrinter("Printer6", 0),
MakeExpectedPrinter("Printer7", kFlagSSL),
MakeExpectedPrinter("Printer8", kFlagIPPE),
MakeExpectedPrinter("Printer9", kFlagSSL | kFlagIPPE),
MakeExpectedPrinter("Printer10", kFlagSSL | kFlagIPPE)});
ipps_lister_->Clear();
scoped_task_environment_.RunUntilIdle();
// With the IPPS lister cleared, Printer7 should fall back to the IPP listing.
ExpectPrintersAre({MakeExpectedPrinter("Printer6", 0),
MakeExpectedPrinter("Printer7", 0),
MakeExpectedPrinter("Printer8", kFlagIPPE),
MakeExpectedPrinter("Printer9", kFlagSSL | kFlagIPPE),
MakeExpectedPrinter("Printer10", kFlagSSL | kFlagIPPE)});
// We should have restarted discovery after dealing with the cache flush.
EXPECT_TRUE(ipps_lister_->discovery_started());
ipp_lister_->Clear();
scoped_task_environment_.RunUntilIdle();
// With the IPP lister cleared, Printers 6 and 7 no longer should appear.
ExpectPrintersAre({MakeExpectedPrinter("Printer8", kFlagIPPE),
MakeExpectedPrinter("Printer9", kFlagSSL | kFlagIPPE),
MakeExpectedPrinter("Printer10", kFlagSSL | kFlagIPPE)});
EXPECT_TRUE(ipps_lister_->discovery_started());
ippse_lister_->Clear();
scoped_task_environment_.RunUntilIdle();
// With the IPPSE lister cleared, Printer10 should disappear, and Printer9
// should fall back to the IPPE.
ExpectPrintersAre({MakeExpectedPrinter("Printer8", kFlagIPPE),
MakeExpectedPrinter("Printer9", kFlagIPPE)});
EXPECT_TRUE(ippse_lister_->discovery_started());
// Just for kicks, announce something new at this point.
ipps_lister_->Announce("Printer11");
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre({MakeExpectedPrinter("Printer8", kFlagIPPE),
MakeExpectedPrinter("Printer9", kFlagIPPE),
MakeExpectedPrinter("Printer11", kFlagSSL)});
// Clear out the IPPE lister, leaving only the new printer we announced
// on the IPPS lister.
ippe_lister_->Clear();
scoped_task_environment_.RunUntilIdle();
// With the IPPSE lister cleared, Printer10 should disappear, and Printer9
// should fall back to the IPPE entry.
ExpectPrintersAre({MakeExpectedPrinter("Printer11", kFlagSSL)});
EXPECT_TRUE(ippe_lister_->discovery_started());
}
// Test some general traffic with a mix of everything we expect to handle.
TEST_F(ZeroconfPrinterDetectorTest, GeneralMixedTraffic) {
ipp_lister_->Announce("Printer12");
ipps_lister_->Announce("Printer12");
ipps_lister_->Announce("Printer13");
ippse_lister_->Announce("Printer14");
ipps_lister_->Announce("Printer15");
CreateDetector();
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre({MakeExpectedPrinter("Printer12", kFlagSSL),
MakeExpectedPrinter("Printer13", kFlagSSL),
MakeExpectedPrinter("Printer14", kFlagSSL | kFlagIPPE),
MakeExpectedPrinter("Printer15", kFlagSSL)});
ippe_lister_->Announce("Printer13");
ipp_lister_->Announce("Printer16");
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre({MakeExpectedPrinter("Printer12", kFlagSSL),
MakeExpectedPrinter("Printer13", kFlagIPPE),
MakeExpectedPrinter("Printer14", kFlagSSL | kFlagIPPE),
MakeExpectedPrinter("Printer15", kFlagSSL),
MakeExpectedPrinter("Printer16", 0)});
ipp_lister_->Remove("NonexistantPrinter");
ipps_lister_->Remove("Printer12");
ipps_lister_->Clear();
ipp_lister_->Announce("Printer17");
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre({MakeExpectedPrinter("Printer12", 0),
MakeExpectedPrinter("Printer13", kFlagIPPE),
MakeExpectedPrinter("Printer14", kFlagSSL | kFlagIPPE),
MakeExpectedPrinter("Printer16", 0),
MakeExpectedPrinter("Printer17", 0)});
ipp_lister_->Clear();
ippse_lister_->Clear();
ippe_lister_->Clear();
scoped_task_environment_.RunUntilIdle();
ExpectPrintersAre({});
}
} // namespace
} // namespace chromeos