| // Copyright 2018 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/zeroconf_printer_detector.h" | 
 |  | 
 | #include <algorithm> | 
 | #include <functional> | 
 | #include <map> | 
 | #include <memory> | 
 | #include <random> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "base/containers/flat_set.h" | 
 | #include "base/functional/bind.h" | 
 | #include "base/memory/raw_ptr.h" | 
 | #include "base/strings/strcat.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/test/task_environment.h" | 
 | #include "chrome/browser/local_discovery/fake_service_discovery_device_lister.h" | 
 | #include "chrome/browser/local_discovery/service_discovery_device_lister.h" | 
 | #include "net/base/ip_address.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | namespace ash { | 
 | namespace { | 
 |  | 
 | using ::local_discovery::FakeServiceDiscoveryDeviceLister; | 
 | 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; | 
 | } | 
 |  | 
 | // Enums for MakeExpectedPrinter(). | 
 | enum class ServiceType { | 
 |   kIpp,     // IPP | 
 |   kIpps,    // IPPS | 
 |   kIppE,    // IPP-Everywhere | 
 |   kIppsE,   // IPPS-Everywhere | 
 |   kSocket,  // Socket | 
 |   kLpd,     // LPD | 
 | }; | 
 |  | 
 | // This corresponds to MakeServiceDescription() below. Given the same name (and | 
 | // the correct service type), this generates the DetectedPrinter record we | 
 | // expect from ZeroconfPrinterDetector when it gets that ServiceDescription. | 
 | // This needs to be kept in sync with MakeServiceDescription(). | 
 | PrinterDetector::DetectedPrinter MakeExpectedPrinter(const std::string& name, | 
 |                                                      ServiceType service_type) { | 
 |   PrinterDetector::DetectedPrinter detected; | 
 |   chromeos::Printer& printer = detected.printer; | 
 |   net::IPAddress ip_address = GetIPAddressFor(name); | 
 |   int port = GetPortFor(name); | 
 |   std::string scheme; | 
 |   std::string rp = base::StrCat({name, "_rp"}); | 
 |   switch (service_type) { | 
 |     case ServiceType::kIpp: | 
 |       scheme = "ipp"; | 
 |       break; | 
 |     case ServiceType::kIpps: | 
 |       scheme = "ipps"; | 
 |       break; | 
 |     case ServiceType::kIppE: | 
 |       scheme = "ipp"; | 
 |       printer.mutable_ppd_reference()->autoconf = true; | 
 |       break; | 
 |     case ServiceType::kIppsE: | 
 |       scheme = "ipps"; | 
 |       printer.mutable_ppd_reference()->autoconf = true; | 
 |       break; | 
 |     case ServiceType::kSocket: | 
 |       scheme = "socket"; | 
 |       rp = ""; | 
 |       break; | 
 |     case ServiceType::kLpd: | 
 |       scheme = "lpd"; | 
 |       break; | 
 |   } | 
 |   printer.SetUri(base::StringPrintf("%s://%s.local:%d/%s", scheme.c_str(), | 
 |                                     name.c_str(), port, rp.c_str())); | 
 |  | 
 |   printer.set_uuid(base::StrCat({name, "_UUID"})); | 
 |   printer.set_display_name(name); | 
 |   printer.set_description(base::StrCat({name, "_note"})); | 
 |   printer.set_make_and_model(base::StrCat({name, "_ty"})); | 
 |   detected.ppd_search_data.make_and_model.push_back(printer.make_and_model()); | 
 |   detected.ppd_search_data.make_and_model.push_back( | 
 |       base::StrCat({name, "_product"})); | 
 |   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"})); | 
 |   } | 
 |  | 
 |   return detected; | 
 | } | 
 |  | 
 | // Creates a deterministic ServiceDescription based on the service name and | 
 | // type. See the note on MakeExpectedPrinter() above. This must be kept in sync | 
 | // with MakeExpectedPrinter(). | 
 | ServiceDescription MakeServiceDescription(const std::string& name, | 
 |                                           const std::string& service_type) { | 
 |   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; | 
 | } | 
 |  | 
 | class ZeroconfPrinterDetectorTest : public testing::Test { | 
 |  public: | 
 |   ZeroconfPrinterDetectorTest() { | 
 |     auto* runner = 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(); | 
 |     auto socket_lister = std::make_unique<FakeServiceDiscoveryDeviceLister>( | 
 |         runner, ZeroconfPrinterDetector::kSocketServiceName); | 
 |     socket_lister_ = socket_lister.get(); | 
 |     auto lpd_lister = std::make_unique<FakeServiceDiscoveryDeviceLister>( | 
 |         runner, ZeroconfPrinterDetector::kLpdServiceName); | 
 |     lpd_lister_ = lpd_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); | 
 |     listers_[ZeroconfPrinterDetector::kSocketServiceName] = | 
 |         std::move(socket_lister); | 
 |     listers_[ZeroconfPrinterDetector::kLpdServiceName] = std::move(lpd_lister); | 
 |   } | 
 |   ~ZeroconfPrinterDetectorTest() override = default; | 
 |  | 
 |   void CreateDetectorWithIppRejectList( | 
 |       base::flat_set<std::string> ipp_reject_list) { | 
 |     detector_ = ZeroconfPrinterDetector::CreateForTesting( | 
 |         &listers_, std::move(ipp_reject_list)); | 
 |     // 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_->RegisterPrintersFoundCallback(base::BindRepeating( | 
 |         &ZeroconfPrinterDetectorTest::OnPrintersFound, base::Unretained(this))); | 
 |     ipp_lister_->SetDelegate(detector_.get()); | 
 |     ipps_lister_->SetDelegate(detector_.get()); | 
 |     ippe_lister_->SetDelegate(detector_.get()); | 
 |     ippse_lister_->SetDelegate(detector_.get()); | 
 |     socket_lister_->SetDelegate(detector_.get()); | 
 |     lpd_lister_->SetDelegate(detector_.get()); | 
 |   } | 
 |  | 
 |   void CreateDetector() { CreateDetectorWithIppRejectList({}); } | 
 |  | 
 |   // 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_FALSE(printers_found_callbacks_.empty()); | 
 |     ExpectPrintersEq(printers, printers_found_callbacks_.back()); | 
 |     ExpectPrintersEq(printers, detector_->GetPrinters()); | 
 |   } | 
 |  | 
 |   // Expect that the detected printers list is empty. | 
 |   void ExpectPrintersEmpty() { | 
 |     // Assert that the most recent callbacks are empty. | 
 |     ASSERT_FALSE(printers_found_callbacks_.empty()); | 
 |     ASSERT_TRUE(printers_found_callbacks_.back().empty()); | 
 |     ASSERT_TRUE(detector_->GetPrinters().empty()); | 
 |   } | 
 |  | 
 |   // 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.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 callback. | 
 |   void OnPrintersFound( | 
 |       const std::vector<PrinterDetector::DetectedPrinter>& printers) { | 
 |     printers_found_callbacks_.push_back(printers); | 
 |   } | 
 |  | 
 |  protected: | 
 |   // Runs pending tasks regardless of delay. | 
 |   void CompleteTasks() { task_environment_.FastForwardUntilNoTasksRemain(); } | 
 |  | 
 |   base::test::TaskEnvironment task_environment_{ | 
 |       base::test::TaskEnvironment::TimeSource::MOCK_TIME}; | 
 |  | 
 |   // 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. | 
 |   raw_ptr<FakeServiceDiscoveryDeviceLister, ExperimentalAsh> ipp_lister_; | 
 |   raw_ptr<FakeServiceDiscoveryDeviceLister, ExperimentalAsh> ipps_lister_; | 
 |   raw_ptr<FakeServiceDiscoveryDeviceLister, ExperimentalAsh> ippe_lister_; | 
 |   raw_ptr<FakeServiceDiscoveryDeviceLister, ExperimentalAsh> ippse_lister_; | 
 |   raw_ptr<FakeServiceDiscoveryDeviceLister, ExperimentalAsh> socket_lister_; | 
 |   raw_ptr<FakeServiceDiscoveryDeviceLister, ExperimentalAsh> lpd_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(MakeServiceDescription( | 
 |       "Printer1", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer1", ServiceType::kIpp)}); | 
 | } | 
 |  | 
 | TEST_F(ZeroconfPrinterDetectorTest, SingleIppsPrinter) { | 
 |   ipps_lister_->Announce(MakeServiceDescription( | 
 |       "Printer2", ZeroconfPrinterDetector::kIppsServiceName)); | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer2", ServiceType::kIpps)}); | 
 | } | 
 |  | 
 | TEST_F(ZeroconfPrinterDetectorTest, SingleIppEverywherePrinter) { | 
 |   ippe_lister_->Announce(MakeServiceDescription( | 
 |       "Printer3", ZeroconfPrinterDetector::kIppEverywhereServiceName)); | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer3", ServiceType::kIppE)}); | 
 | } | 
 |  | 
 | TEST_F(ZeroconfPrinterDetectorTest, SingleIppsEverywherePrinter) { | 
 |   ippse_lister_->Announce(MakeServiceDescription( | 
 |       "Printer4", ZeroconfPrinterDetector::kIppsEverywhereServiceName)); | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer4", ServiceType::kIppsE)}); | 
 | } | 
 |  | 
 | TEST_F(ZeroconfPrinterDetectorTest, SingleSocketPrinter) { | 
 |   socket_lister_->Announce(MakeServiceDescription( | 
 |       "Printer5", ZeroconfPrinterDetector::kSocketServiceName)); | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer5", ServiceType::kSocket)}); | 
 | } | 
 |  | 
 | TEST_F(ZeroconfPrinterDetectorTest, SingleLpdPrinter) { | 
 |   lpd_lister_->Announce(MakeServiceDescription( | 
 |       "Printer6", ZeroconfPrinterDetector::kLpdServiceName)); | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer6", ServiceType::kLpd)}); | 
 | } | 
 |  | 
 | // Test that an announce after the detector creation shows up as a printer. | 
 | TEST_F(ZeroconfPrinterDetectorTest, AnnounceAfterDetectorCreation) { | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   ippse_lister_->Announce(MakeServiceDescription( | 
 |       "Printer4", ZeroconfPrinterDetector::kIppsEverywhereServiceName)); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer4", ServiceType::kIppsE)}); | 
 | } | 
 |  | 
 | // 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(MakeServiceDescription( | 
 |       "Printer1", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   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"); | 
 |   CompleteTasks(); | 
 |   ASSERT_TRUE(printers_found_callbacks_.back().empty()); | 
 |   ipps_lister_->Announce(MakeServiceDescription( | 
 |       "Printer1", ZeroconfPrinterDetector::kIppsServiceName)); | 
 |   CompleteTasks(); | 
 |   ASSERT_EQ(1U, printers_found_callbacks_.back().size()); | 
 |   // 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"); | 
 |   CompleteTasks(); | 
 |   ASSERT_TRUE(printers_found_callbacks_.back().empty()); | 
 |   ippe_lister_->Announce(MakeServiceDescription( | 
 |       "Printer1", ZeroconfPrinterDetector::kIppEverywhereServiceName)); | 
 |   CompleteTasks(); | 
 |   ASSERT_EQ(1U, printers_found_callbacks_.back().size()); | 
 |   // 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"); | 
 |   CompleteTasks(); | 
 |   ASSERT_TRUE(printers_found_callbacks_.back().empty()); | 
 |   ippse_lister_->Announce(MakeServiceDescription( | 
 |       "Printer1", ZeroconfPrinterDetector::kIppsEverywhereServiceName)); | 
 |   CompleteTasks(); | 
 |   ASSERT_EQ(1U, printers_found_callbacks_.back().size()); | 
 |   // Id should be the same. | 
 |   ASSERT_EQ(id, printers_found_callbacks_.back()[0].printer.id()); | 
 |  | 
 |   // Remove it as an IPPS-Everywhere printer, add it as a socket printer. | 
 |   ippse_lister_->Remove("Printer1"); | 
 |   CompleteTasks(); | 
 |   ASSERT_TRUE(printers_found_callbacks_.back().empty()); | 
 |   socket_lister_->Announce(MakeServiceDescription( | 
 |       "Printer1", ZeroconfPrinterDetector::kSocketServiceName)); | 
 |   CompleteTasks(); | 
 |   ASSERT_EQ(1U, printers_found_callbacks_.back().size()); | 
 |   // Id should be the same. | 
 |   ASSERT_EQ(id, printers_found_callbacks_.back()[0].printer.id()); | 
 |  | 
 |   // Remove it as a socket printer, add it as an LPD printer. | 
 |   socket_lister_->Remove("Printer1"); | 
 |   CompleteTasks(); | 
 |   ASSERT_TRUE(printers_found_callbacks_.back().empty()); | 
 |   lpd_lister_->Announce(MakeServiceDescription( | 
 |       "Printer1", ZeroconfPrinterDetector::kLpdServiceName)); | 
 |   CompleteTasks(); | 
 |   ASSERT_EQ(1U, printers_found_callbacks_.back().size()); | 
 |   // 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(MakeServiceDescription( | 
 |       "Printer5", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   ipp_lister_->Announce(MakeServiceDescription( | 
 |       "Printer6", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   ipp_lister_->Announce(MakeServiceDescription( | 
 |       "Printer7", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   ipp_lister_->Announce(MakeServiceDescription( | 
 |       "Printer8", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   ipp_lister_->Announce(MakeServiceDescription( | 
 |       "Printer9", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer5", ServiceType::kIpp), | 
 |                      MakeExpectedPrinter("Printer6", ServiceType::kIpp), | 
 |                      MakeExpectedPrinter("Printer7", ServiceType::kIpp), | 
 |                      MakeExpectedPrinter("Printer8", ServiceType::kIpp), | 
 |                      MakeExpectedPrinter("Printer9", ServiceType::kIpp)}); | 
 |   ipp_lister_->Remove("Printer7"); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer5", ServiceType::kIpp), | 
 |                      MakeExpectedPrinter("Printer6", ServiceType::kIpp), | 
 |                      MakeExpectedPrinter("Printer8", ServiceType::kIpp), | 
 |                      MakeExpectedPrinter("Printer9", ServiceType::kIpp)}); | 
 | } | 
 |  | 
 | // 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, Socket, LPD. | 
 | TEST_F(ZeroconfPrinterDetectorTest, ServiceTypePriorities) { | 
 |   // Advertise on all services. | 
 |   ipp_lister_->Announce(MakeServiceDescription( | 
 |       "Printer5", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   ipps_lister_->Announce(MakeServiceDescription( | 
 |       "Printer5", ZeroconfPrinterDetector::kIppsServiceName)); | 
 |   ippe_lister_->Announce(MakeServiceDescription( | 
 |       "Printer5", ZeroconfPrinterDetector::kIppEverywhereServiceName)); | 
 |   ippse_lister_->Announce(MakeServiceDescription( | 
 |       "Printer5", ZeroconfPrinterDetector::kIppsEverywhereServiceName)); | 
 |   socket_lister_->Announce(MakeServiceDescription( | 
 |       "Printer5", ZeroconfPrinterDetector::kSocketServiceName)); | 
 |   lpd_lister_->Announce(MakeServiceDescription( | 
 |       "Printer5", ZeroconfPrinterDetector::kLpdServiceName)); | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   // IPPS-E is highest priority. | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer5", ServiceType::kIppsE)}); | 
 |   ippse_lister_->Remove("Printer5"); | 
 |   CompleteTasks(); | 
 |   // IPP-E is highest remaining priority. | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer5", ServiceType::kIppE)}); | 
 |  | 
 |   ippe_lister_->Remove("Printer5"); | 
 |   CompleteTasks(); | 
 |   // IPPS is highest remaining priority. | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer5", ServiceType::kIpps)}); | 
 |  | 
 |   ipps_lister_->Remove("Printer5"); | 
 |   CompleteTasks(); | 
 |   // IPP is highest remaining priority. | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer5", ServiceType::kIpp)}); | 
 |  | 
 |   ipp_lister_->Remove("Printer5"); | 
 |   CompleteTasks(); | 
 |   // Socket is highest remaining entry. | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer5", ServiceType::kSocket)}); | 
 |  | 
 |   socket_lister_->Remove("Printer5"); | 
 |   CompleteTasks(); | 
 |   // LPD is only remaining entry. | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer5", ServiceType::kLpd)}); | 
 |  | 
 |   lpd_lister_->Remove("Printer5"); | 
 |   CompleteTasks(); | 
 |   // No entries left. | 
 |   ExpectPrintersEmpty(); | 
 | } | 
 |  | 
 | // Test a printer that is known not to work with IPP/IPPS and make sure a | 
 | // different protocol is chosen. | 
 | TEST_F(ZeroconfPrinterDetectorTest, RejectIpp) { | 
 |   std::string bad_ipp_printer = "manufacturer awesome printer-name"; | 
 |   base::flat_set<std::string> reject_list; | 
 |   // We have to add the _ty suffix to match how MakeServiceDescription and | 
 |   // MakeExpectedPrinter work. | 
 |   reject_list.insert(bad_ipp_printer + "_ty"); | 
 |   // Advertise on IPP and LPD services. | 
 |   ipp_lister_->Announce(MakeServiceDescription( | 
 |       bad_ipp_printer, ZeroconfPrinterDetector::kIppServiceName)); | 
 |   ipps_lister_->Announce(MakeServiceDescription( | 
 |       bad_ipp_printer, ZeroconfPrinterDetector::kIppsServiceName)); | 
 |   lpd_lister_->Announce(MakeServiceDescription( | 
 |       bad_ipp_printer, ZeroconfPrinterDetector::kLpdServiceName)); | 
 |   CreateDetectorWithIppRejectList(reject_list); | 
 |   CompleteTasks(); | 
 |  | 
 |   // Should be rejected for IPPS-E and IPP-E, so it should only exist in LPD. | 
 |   ExpectPrintersAre({MakeExpectedPrinter(bad_ipp_printer, ServiceType::kLpd)}); | 
 |  | 
 |   lpd_lister_->Remove(bad_ipp_printer); | 
 |   CompleteTasks(); | 
 |   // No entries left. | 
 |   ExpectPrintersEmpty(); | 
 | } | 
 |  | 
 | // Test that cache flushes appropriately remove entries. | 
 | TEST_F(ZeroconfPrinterDetectorTest, CacheFlushes) { | 
 |   ipp_lister_->Announce(MakeServiceDescription( | 
 |       "Printer6", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   ipp_lister_->Announce(MakeServiceDescription( | 
 |       "Printer7", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   ipps_lister_->Announce(MakeServiceDescription( | 
 |       "Printer7", ZeroconfPrinterDetector::kIppsServiceName)); | 
 |   ipps_lister_->Announce(MakeServiceDescription( | 
 |       "Printer8", ZeroconfPrinterDetector::kIppsServiceName)); | 
 |   ippe_lister_->Announce(MakeServiceDescription( | 
 |       "Printer8", ZeroconfPrinterDetector::kIppEverywhereServiceName)); | 
 |   ippe_lister_->Announce(MakeServiceDescription( | 
 |       "Printer9", ZeroconfPrinterDetector::kIppEverywhereServiceName)); | 
 |   ippse_lister_->Announce(MakeServiceDescription( | 
 |       "Printer9", ZeroconfPrinterDetector::kIppsEverywhereServiceName)); | 
 |   ippse_lister_->Announce(MakeServiceDescription( | 
 |       "Printer10", ZeroconfPrinterDetector::kIppsEverywhereServiceName)); | 
 |   socket_lister_->Announce(MakeServiceDescription( | 
 |       "Printer10", ZeroconfPrinterDetector::kSocketServiceName)); | 
 |   socket_lister_->Announce(MakeServiceDescription( | 
 |       "Printer11", ZeroconfPrinterDetector::kSocketServiceName)); | 
 |   lpd_lister_->Announce(MakeServiceDescription( | 
 |       "Printer11", ZeroconfPrinterDetector::kLpdServiceName)); | 
 |   lpd_lister_->Announce(MakeServiceDescription( | 
 |       "Printer12", ZeroconfPrinterDetector::kLpdServiceName)); | 
 |  | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer6", ServiceType::kIpp), | 
 |                      MakeExpectedPrinter("Printer7", ServiceType::kIpps), | 
 |                      MakeExpectedPrinter("Printer8", ServiceType::kIppE), | 
 |                      MakeExpectedPrinter("Printer9", ServiceType::kIppsE), | 
 |                      MakeExpectedPrinter("Printer10", ServiceType::kIppsE), | 
 |                      MakeExpectedPrinter("Printer11", ServiceType::kSocket), | 
 |                      MakeExpectedPrinter("Printer12", ServiceType::kLpd)}); | 
 |  | 
 |   ipps_lister_->Clear(); | 
 |  | 
 |   CompleteTasks(); | 
 |   // With the IPPS lister cleared, all printers should be cleared. | 
 |   ExpectPrintersEmpty(); | 
 |  | 
 |   // We should have restarted discovery after dealing with the cache flush. | 
 |   EXPECT_TRUE(ipps_lister_->discovery_started()); | 
 |  | 
 |   // Just for kicks, announce something new at this point. | 
 |   ipps_lister_->Announce(MakeServiceDescription( | 
 |       "Printer13", ZeroconfPrinterDetector::kIppsServiceName)); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer13", ServiceType::kIpps)}); | 
 |  | 
 |   // Clear out the IPPS lister, which will clear all printers too. | 
 |   ipps_lister_->Clear(); | 
 |   CompleteTasks(); | 
 |  | 
 |   // With the IPPS lister cleared, Printer13 should disappear. | 
 |   ExpectPrintersEmpty(); | 
 |   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(MakeServiceDescription( | 
 |       "Printer12", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   ipps_lister_->Announce(MakeServiceDescription( | 
 |       "Printer12", ZeroconfPrinterDetector::kIppsServiceName)); | 
 |   ipps_lister_->Announce(MakeServiceDescription( | 
 |       "Printer13", ZeroconfPrinterDetector::kIppsServiceName)); | 
 |   ippse_lister_->Announce(MakeServiceDescription( | 
 |       "Printer14", ZeroconfPrinterDetector::kIppsEverywhereServiceName)); | 
 |   ipps_lister_->Announce(MakeServiceDescription( | 
 |       "Printer15", ZeroconfPrinterDetector::kIppsServiceName)); | 
 |   socket_lister_->Announce(MakeServiceDescription( | 
 |       "Printer16", ZeroconfPrinterDetector::kSocketServiceName)); | 
 |   lpd_lister_->Announce(MakeServiceDescription( | 
 |       "Printer17", ZeroconfPrinterDetector::kLpdServiceName)); | 
 |  | 
 |   CreateDetector(); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer12", ServiceType::kIpps), | 
 |                      MakeExpectedPrinter("Printer13", ServiceType::kIpps), | 
 |                      MakeExpectedPrinter("Printer14", ServiceType::kIppsE), | 
 |                      MakeExpectedPrinter("Printer15", ServiceType::kIpps), | 
 |                      MakeExpectedPrinter("Printer16", ServiceType::kSocket), | 
 |                      MakeExpectedPrinter("Printer17", ServiceType::kLpd)}); | 
 |  | 
 |   ippe_lister_->Announce(MakeServiceDescription( | 
 |       "Printer13", ZeroconfPrinterDetector::kIppEverywhereServiceName)); | 
 |   ipp_lister_->Announce(MakeServiceDescription( | 
 |       "Printer18", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersAre({MakeExpectedPrinter("Printer12", ServiceType::kIpps), | 
 |                      MakeExpectedPrinter("Printer13", ServiceType::kIppE), | 
 |                      MakeExpectedPrinter("Printer14", ServiceType::kIppsE), | 
 |                      MakeExpectedPrinter("Printer15", ServiceType::kIpps), | 
 |                      MakeExpectedPrinter("Printer16", ServiceType::kSocket), | 
 |                      MakeExpectedPrinter("Printer17", ServiceType::kLpd), | 
 |                      MakeExpectedPrinter("Printer18", ServiceType::kIpp)}); | 
 |  | 
 |   ipp_lister_->Remove("NonexistantPrinter"); | 
 |   ipps_lister_->Remove("Printer12"); | 
 |   ipps_lister_->Clear(); | 
 |   CompleteTasks(); | 
 |   ExpectPrintersEmpty(); | 
 | } | 
 |  | 
 | // Verify tasks are cleaned up properly when class is destroyed. | 
 | TEST_F(ZeroconfPrinterDetectorTest, DestroyedWithTasksPending) { | 
 |   CreateDetector(); | 
 |   // Cause a callback to be queued. | 
 |   ipp_lister_->Announce(MakeServiceDescription( | 
 |       "TestPrinter", ZeroconfPrinterDetector::kIppServiceName)); | 
 |   // Run listers but don't run the delayed tasks. | 
 |   task_environment_.RunUntilIdle(); | 
 |  | 
 |   // Delete the detector. | 
 |   detector_.reset(); | 
 |  | 
 |   // Clear task queues where we would crash if we did something wrong. | 
 |   task_environment_.FastForwardUntilNoTasksRemain(); | 
 |   SUCCEED(); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace ash |