| // Copyright 2020 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/scanning/zeroconf_scanner_detector.h" |
| |
| #include <algorithm> |
| #include <functional> |
| #include <iterator> |
| #include <memory> |
| #include <random> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/functional/bind.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/strings/strcat.h" |
| #include "base/test/task_environment.h" |
| #include "chrome/browser/ash/scanning/zeroconf_scanner_detector_utils.h" |
| #include "chrome/browser/local_discovery/fake_service_discovery_device_lister.h" |
| #include "chrome/browser/local_discovery/service_discovery_device_lister.h" |
| #include "chromeos/ash/components/scanning/scanner.h" |
| #include "net/base/ip_address.h" |
| #include "testing/gmock/include/gmock/gmock-matchers.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| using local_discovery::FakeServiceDiscoveryDeviceLister; |
| using local_discovery::ServiceDescription; |
| using local_discovery::ServiceDiscoveryDeviceLister; |
| |
| MATCHER_P(ScannerIsEqual, expected_scanner, "") { |
| return arg.display_name == expected_scanner.display_name && |
| arg.manufacturer == expected_scanner.manufacturer && |
| arg.model == expected_scanner.model && |
| arg.uuid == expected_scanner.uuid && arg.pdl == expected_scanner.pdl && |
| arg.device_names == expected_scanner.device_names && |
| arg.ip_addresses == expected_scanner.ip_addresses; |
| } |
| |
| MATCHER_P(ScannersAreEqual, expected_scanners, "") { |
| if (arg.size() != expected_scanners.size()) { |
| return false; |
| } |
| |
| std::vector<Scanner> sorted_expected = expected_scanners; |
| std::vector<Scanner> sorted_actual = arg; |
| std::sort(sorted_expected.begin(), sorted_expected.end(), |
| [](const Scanner& a, const Scanner& b) -> bool { |
| return a.display_name < b.display_name; |
| }); |
| std::sort(sorted_actual.begin(), sorted_actual.end(), |
| [](const Scanner& a, const Scanner& b) -> bool { |
| return a.display_name < b.display_name; |
| }); |
| for (size_t i = 0; i < sorted_expected.size(); ++i) { |
| EXPECT_THAT(sorted_actual[i], ScannerIsEqual(sorted_expected[i])); |
| } |
| |
| return true; |
| } |
| |
| // TODO(b/184743530): Move these functions and the copies in |
| // zeroconf_printer_detector_unittest.cc into a shared file. |
| // Determine basic scanner attributes deterministically but pseudorandomly based |
| // on the scanner name. The exact values returned here are not important. The |
| // important parts are that there's variety based on the name, and it's |
| // deterministic. |
| |
| // Gets an IP address for this scanner. 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()); |
| } |
| |
| // Give an IPv6 address. |
| return net::IPAddress(rng(), rng(), rng(), rng(), rng(), rng(), rng(), rng(), |
| rng(), rng(), rng(), rng(), rng(), rng(), rng(), rng()); |
| } |
| |
| // Gets a port number for this scanner. |
| int GetPortFor(const std::string& name) { |
| return (std::hash<std::string>()(name) % 1000) + 1; |
| } |
| |
| // A class used to create an expected scanner, allowing the caller to specify |
| // only necessary values. This corresponds to MakeServiceDescription() |
| // below. Given the same name and correct service type, this generates the |
| // expected Scanner that the ZeroconfScannerDetector should create when it gets |
| // the ServiceDescription created by MakeServiceDescription(). This needs to be |
| // kept in sync with MakeServiceDescription(). |
| class ScannerBuilder { |
| public: |
| ScannerBuilder(const std::string& name, const std::string& service_type) |
| : name_(name), service_type_(service_type) {} |
| ~ScannerBuilder() = default; |
| ScannerBuilder(const ScannerBuilder& other) = default; |
| ScannerBuilder& operator=(const ScannerBuilder& other) = default; |
| |
| ScannerBuilder& WithManufacturerAndModel(const std::string& manufacturer, |
| const std::string& model) { |
| manufacturer_ = manufacturer; |
| model_ = model; |
| return *this; |
| } |
| ScannerBuilder& WithRs(const std::optional<std::string>& rs) { |
| rs_ = rs; |
| return *this; |
| } |
| ScannerBuilder& WithUuid(const std::string& uuid) { |
| uuid_ = uuid; |
| return *this; |
| } |
| ScannerBuilder& WithPdl(const std::vector<std::string>& pdl) { |
| pdl_ = pdl; |
| return *this; |
| } |
| Scanner Build() { |
| const net::IPAddress ip_address = GetIPAddressFor(name_); |
| const int port = GetPortFor(name_); |
| auto scanner = |
| CreateSaneScanner(name_, service_type_, manufacturer_, model_, uuid_, |
| rs_, pdl_, ip_address, port); |
| CHECK(scanner.has_value()); |
| return scanner.value(); |
| } |
| |
| private: |
| std::string name_; |
| std::string service_type_; |
| std::string manufacturer_; |
| std::string model_; |
| std::string uuid_; |
| std::optional<std::string> rs_; |
| std::vector<std::string> pdl_; |
| }; |
| |
| // Merges all of the Scanners in |scanners| into a single Scanner. Used to |
| // create the expected result of a scanner announced by more than one lister. |
| Scanner MergeScanners(const std::vector<Scanner>& scanners) { |
| if (scanners.empty()) { |
| return Scanner(); |
| } |
| |
| Scanner merged_scanner = scanners[0]; |
| for (auto it = std::next(scanners.begin()); it != scanners.end(); ++it) { |
| merged_scanner.device_names.insert(it->device_names.begin(), |
| it->device_names.end()); |
| merged_scanner.ip_addresses.insert(it->ip_addresses.begin(), |
| it->ip_addresses.end()); |
| } |
| |
| return merged_scanner; |
| } |
| |
| // Similar to MakeServiceDescription below except the caller can add arbitrary |
| // |metadata|. |
| ServiceDescription MakeServiceDescription(const std::string& name, |
| const std::string& service_type, |
| std::vector<std::string> metadata) { |
| ServiceDescription service_description; |
| service_description.service_name = base::StrCat({name, ".", service_type}); |
| service_description.address.set_host(base::StrCat({name, ".local"})); |
| service_description.address.set_port(GetPortFor(name)); |
| service_description.ip_address = GetIPAddressFor(name); |
| service_description.metadata = std::move(metadata); |
| return service_description; |
| } |
| |
| // Creates a deterministic ServiceDescription based on the service name and |
| // type. See the note on ScannerBuilder above. This must be kept in sync |
| // with ScannerBuilder. |
| ServiceDescription MakeServiceDescription( |
| const std::string& name, |
| const std::string& service_type, |
| const std::optional<std::string>& rs) { |
| std::vector<std::string> metadata; |
| if (rs.has_value()) { |
| metadata.push_back(base::StrCat({"rs=", rs.value()})); |
| } |
| return MakeServiceDescription(name, service_type, std::move(metadata)); |
| } |
| |
| } // namespace |
| |
| class ZeroconfScannerDetectorTest : public testing::Test { |
| public: |
| ZeroconfScannerDetectorTest() = default; |
| ~ZeroconfScannerDetectorTest() override = default; |
| |
| void SetUp() override { |
| auto* runner = task_environment_.GetMainThreadTaskRunner().get(); |
| auto escl_lister = std::make_unique<FakeServiceDiscoveryDeviceLister>( |
| runner, ZeroconfScannerDetector::kEsclServiceType); |
| escl_lister_ = escl_lister.get(); |
| auto escls_lister = std::make_unique<FakeServiceDiscoveryDeviceLister>( |
| runner, ZeroconfScannerDetector::kEsclsServiceType); |
| escls_lister_ = escls_lister.get(); |
| auto generic_scanner_lister = |
| std::make_unique<FakeServiceDiscoveryDeviceLister>( |
| runner, ZeroconfScannerDetector::kGenericScannerServiceType); |
| generic_scanner_lister_ = generic_scanner_lister.get(); |
| listers_[ZeroconfScannerDetector::kEsclServiceType] = |
| std::move(escl_lister); |
| listers_[ZeroconfScannerDetector::kEsclsServiceType] = |
| std::move(escls_lister); |
| listers_[ZeroconfScannerDetector::kGenericScannerServiceType] = |
| std::move(generic_scanner_lister); |
| } |
| |
| void CreateDetector() { |
| detector_ = ZeroconfScannerDetector::CreateForTesting(std::move(listers_)); |
| |
| // Ownership of the previously allocated listers map is transferred to the |
| // detector, so the unique_ptr values of the listers map are no longer valid |
| // at this point. The lister's raw pointers are kept as separate members to |
| // keep the lister fakes accessible after ownership is transferred into the |
| // detector. |
| listers_.clear(); |
| detector_->RegisterScannersDetectedCallback( |
| base::BindRepeating(&ZeroconfScannerDetectorTest::OnScannersDetected, |
| base::Unretained(this))); |
| escl_lister_->SetDelegate(detector_.get()); |
| escls_lister_->SetDelegate(detector_.get()); |
| generic_scanner_lister_->SetDelegate(detector_.get()); |
| } |
| |
| // ScannerDetector callback. |
| void OnScannersDetected(std::vector<Scanner> scanners) { |
| scanners_ = std::move(scanners); |
| } |
| |
| protected: |
| // Runs pending tasks regardless of delay. |
| void CompleteTasks() { task_environment_.FastForwardUntilNoTasksRemain(); } |
| |
| base::test::TaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| |
| // Device lister 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, DanglingUntriaged> escl_lister_; |
| raw_ptr<FakeServiceDiscoveryDeviceLister, DanglingUntriaged> escls_lister_; |
| raw_ptr<FakeServiceDiscoveryDeviceLister, DanglingUntriaged> |
| generic_scanner_lister_; |
| |
| // Detector under test. |
| std::unique_ptr<ZeroconfScannerDetector> detector_; |
| |
| // Latest scanners received in OnScannersDetected(). |
| std::vector<Scanner> scanners_; |
| |
| 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 *_lister_ variables instead. |
| ZeroconfScannerDetector::ListersMap listers_; |
| }; |
| |
| // Test that an eSCL scanner can be detected. |
| TEST_F(ZeroconfScannerDetectorTest, EsclScanner) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, "")); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithRs("") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that an eSCLS scanner can be detected. |
| TEST_F(ZeroconfScannerDetectorTest, EsclsScanner) { |
| escls_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclsServiceType, "")); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclsServiceType) |
| .WithRs("") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that a generic Epson _scanner._tcp scanner can be detected. |
| TEST_F(ZeroconfScannerDetectorTest, EpsonGenericScanner) { |
| generic_scanner_lister_->Announce(MakeServiceDescription( |
| "EPSONScanner1", ZeroconfScannerDetector::kGenericScannerServiceType, |
| "")); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("EPSONScanner1", |
| ZeroconfScannerDetector::kGenericScannerServiceType) |
| .WithRs("") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that a generic non-Epson _scanner._tcp scanner is not listed. |
| TEST_F(ZeroconfScannerDetectorTest, NonEpsonGenericScanner) { |
| generic_scanner_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kGenericScannerServiceType, "")); |
| CreateDetector(); |
| CompleteTasks(); |
| EXPECT_TRUE(scanners_.empty()); |
| } |
| |
| // Test that the same scanner detected by two listers is merged into a single |
| // Scanner. |
| TEST_F(ZeroconfScannerDetectorTest, MergedScanner) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, "")); |
| escls_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclsServiceType, "")); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = {MergeScanners( |
| {ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithRs("") |
| .Build(), |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclsServiceType) |
| .WithRs("") |
| .Build()})}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that the same Epson scanner detected by three listers is merged into a |
| // single Scanner. |
| TEST_F(ZeroconfScannerDetectorTest, MergedEpsonScanner) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "EPSONScanner1", ZeroconfScannerDetector::kEsclServiceType, "")); |
| escls_lister_->Announce(MakeServiceDescription( |
| "EPSONScanner1", ZeroconfScannerDetector::kEsclsServiceType, "")); |
| generic_scanner_lister_->Announce(MakeServiceDescription( |
| "EPSONScanner1", ZeroconfScannerDetector::kGenericScannerServiceType, |
| "")); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = {MergeScanners( |
| {ScannerBuilder("EPSONScanner1", |
| ZeroconfScannerDetector::kEsclServiceType) |
| .WithRs("") |
| .Build(), |
| ScannerBuilder("EPSONScanner1", |
| ZeroconfScannerDetector::kEsclsServiceType) |
| .WithRs("") |
| .Build(), |
| ScannerBuilder("EPSONScanner1", |
| ZeroconfScannerDetector::kGenericScannerServiceType) |
| .WithRs("") |
| .Build()})}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that two separate scanners can be detected. |
| TEST_F(ZeroconfScannerDetectorTest, EsclAndEsclsScanners) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, "")); |
| escls_lister_->Announce(MakeServiceDescription( |
| "Scanner2", ZeroconfScannerDetector::kEsclsServiceType, "")); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithRs("") |
| .Build(), |
| ScannerBuilder("Scanner2", ZeroconfScannerDetector::kEsclsServiceType) |
| .WithRs("") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that calling GetScanners() returns the same scanners reported in |
| // OnScannersDetected(). |
| TEST_F(ZeroconfScannerDetectorTest, GetScanners) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, "")); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithRs("") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| EXPECT_THAT(detector_->GetScanners(), ScannersAreEqual(scanners_)); |
| } |
| |
| // Test that the detector detects a scanner that is announced after its |
| // creation. |
| TEST_F(ZeroconfScannerDetectorTest, AnnounceAfterDetectorCreation) { |
| CreateDetector(); |
| CompleteTasks(); |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, "")); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithRs("") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that failing to parse the service metadata is handled gracefully. |
| TEST_F(ZeroconfScannerDetectorTest, InvalidMetadata) { |
| ServiceDescription service_description = MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, ""); |
| service_description.metadata = {"no_equal_sign"}; |
| escl_lister_->Announce(service_description); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that a service without a service name does not get added as a detected |
| // scanner. |
| TEST_F(ZeroconfScannerDetectorTest, NoServiceName) { |
| ServiceDescription service_description = MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, ""); |
| service_description.service_name = ""; |
| escl_lister_->Announce(service_description); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = {}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that a service without an IP address does not get added as a detected |
| // scanner. |
| TEST_F(ZeroconfScannerDetectorTest, NoIpAddress) { |
| ServiceDescription service_description = MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, ""); |
| service_description.ip_address = net::IPAddress(); |
| escl_lister_->Announce(service_description); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = {}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that a service with a port number of 0 does not get added as a detected |
| // scanner. |
| TEST_F(ZeroconfScannerDetectorTest, PortIs0) { |
| ServiceDescription service_description = MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, ""); |
| service_description.address.set_port(0); |
| escl_lister_->Announce(service_description); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = {}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that a valid "rs" value gets incorporated into the device name. |
| TEST_F(ZeroconfScannerDetectorTest, Rs) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, "test")); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithRs("test") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that providing no "rs" value results in the default path being used in |
| // the device name. |
| TEST_F(ZeroconfScannerDetectorTest, NoRs) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, std::nullopt)); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that metadata with just ty works. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataTy) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| std::vector<std::string>{"ty=Manufacturer Model 123 "})); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithManufacturerAndModel("Manufacturer", "Model 123") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that metadata with just mfg and mdl works. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataMfgMdl) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| {"mfg=Real Manufacturer", "mdl=Model 123"})); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithManufacturerAndModel("Real Manufacturer", "Model 123") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that metadata with ty and mfg works. Since both mfg and mdl are not |
| // present, mfg will not be used and ty will be used. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataTyMfg) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| {"ty=Maker Model 123", "mfg=Bad-Manufacturer"})); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithManufacturerAndModel("Maker", "Model 123") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that metadata with ty and mdl works. Since both mfg and mdl are not |
| // present, mdl will not be used and ty will be used. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataTyMdl) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| {"ty=The Manufacturer Model 123", "mdl=Bad-Model"})); |
| CreateDetector(); |
| CompleteTasks(); |
| // Note that if manufacturer in the ty string contains a space, only the first |
| // word is stripped out for the manufacturer entry. That's why mfg/mdl are |
| // preferred when they are both present. |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithManufacturerAndModel("The", "Manufacturer Model 123") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Tests that metadata with ty, mfg, and mdl works. mfg and mdl should be used |
| // instead of the ty info. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataTyMfgMdl) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| {"ty=Bad-Manufacturer Bad-Model", "mfg=Manufacturer", "mdl=Model 123"})); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithManufacturerAndModel("Manufacturer", "Model 123") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that metadata with just usb_MFG and usb_MDL works. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataUsbMfgMdl) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| {"usb_MFG=Real Manufacturer", "usb_MDL=Model 123"})); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithManufacturerAndModel("Real Manufacturer", "Model 123") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that metadata with ty and usb_MFG works. Since both usb_MFG and usb_MDL |
| // are not present, usb_MFG will not be used and ty will be used. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataUsbTyMfg) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| {"ty=Maker Model 123", "usb_MFG=Bad-Manufacturer"})); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithManufacturerAndModel("Maker", "Model 123") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that metadata with ty and usb_MDL works. Since both usb_MFG and usb_MDL |
| // are not present, usb_MDL will not be used and ty will be used. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataUsbTyMdl) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| {"ty=The Manufacturer Model 123", "usb_MDL=Bad Model"})); |
| CreateDetector(); |
| CompleteTasks(); |
| // Note that if manufacturer in the ty string contains a space, only the first |
| // word is stripped out for the manufacturer entry. That's why |
| // usb_MFG/usb_MDL are preferred when they are both present. |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithManufacturerAndModel("The", "Manufacturer Model 123") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Tests that metadata with ty, usb_MFG, and usb_MDL works. usb_MFG and usb_MDL |
| // should be used instead of the ty info. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataUsbTyMfgMdl) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| {"ty=Bad-Manufacturer Bad-Model", "usb_MFG=The Manufacturer", |
| "usb_MDL=Model 123"})); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithManufacturerAndModel("The Manufacturer", "Model 123") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Tests that metadata with UUID works. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataUuid) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| std::vector<std::string>{"UUID=12345-67890"})); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithUuid("12345-67890") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Tests that metadata with uuid works. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataUuidLowercase) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| std::vector<std::string>{"uuid=12345-67890"})); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithUuid("12345-67890") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Tests that metadata with PDL works. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataPdl) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, |
| std::vector<std::string>{"pdl=pdl-1,pdl-2"})); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithPdl({"pdl-1", "pdl-2"}) |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that metadata for a skipped scanner doesn't get converted. |
| TEST_F(ZeroconfScannerDetectorTest, MetadataSkippedRecord) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kGenericScannerServiceType, |
| {"mfg=EPSON", "mdl=XP-7100 Series"})); |
| CreateDetector(); |
| CompleteTasks(); |
| EXPECT_TRUE(scanners_.empty()); |
| } |
| |
| // Test that a detected scanner can be removed. |
| TEST_F(ZeroconfScannerDetectorTest, RemoveAddedScanner) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, std::nullopt)); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| escl_lister_->Remove("Scanner1"); |
| CompleteTasks(); |
| expected_scanners.clear(); |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that removing an undetected scanner is ignored. |
| TEST_F(ZeroconfScannerDetectorTest, RemoveUnaddedScanner) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, std::nullopt)); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| escl_lister_->Remove("Scanner2"); |
| CompleteTasks(); |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that removing a scanner from only one of two listers it was announced on |
| // does not completely remove the scanner (i.e. it goes from being a merged |
| // scanner to a single unmerged scanner). |
| TEST_F(ZeroconfScannerDetectorTest, RemovePartOfMergedScanner) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, "")); |
| escls_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclsServiceType, "")); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = {MergeScanners( |
| {ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithRs("") |
| .Build(), |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclsServiceType) |
| .WithRs("") |
| .Build()})}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| escl_lister_->Remove("Scanner1"); |
| CompleteTasks(); |
| expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclsServiceType) |
| .WithRs("") |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| } |
| |
| // Test that a cache flush correctly removes scanners. |
| TEST_F(ZeroconfScannerDetectorTest, CacheFlush) { |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, "")); |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner2", ZeroconfScannerDetector::kEsclServiceType, std::nullopt)); |
| escls_lister_->Announce(MakeServiceDescription( |
| "Scanner3", ZeroconfScannerDetector::kEsclsServiceType, "")); |
| escls_lister_->Announce(MakeServiceDescription( |
| "Scanner4", ZeroconfScannerDetector::kEsclsServiceType, "test")); |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner5", ZeroconfScannerDetector::kEsclServiceType, std::nullopt)); |
| CreateDetector(); |
| CompleteTasks(); |
| std::vector<Scanner> expected_scanners = { |
| ScannerBuilder("Scanner1", ZeroconfScannerDetector::kEsclServiceType) |
| .WithRs("") |
| .Build(), |
| ScannerBuilder("Scanner2", ZeroconfScannerDetector::kEsclServiceType) |
| .Build(), |
| ScannerBuilder("Scanner3", ZeroconfScannerDetector::kEsclsServiceType) |
| .WithRs("") |
| .Build(), |
| ScannerBuilder("Scanner4", ZeroconfScannerDetector::kEsclsServiceType) |
| .WithRs("test") |
| .Build(), |
| ScannerBuilder("Scanner5", ZeroconfScannerDetector::kEsclServiceType) |
| .Build()}; |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| escls_lister_->Clear(); |
| CompleteTasks(); |
| // With the eSCLS lister cleared, all scanners should be cleared. |
| expected_scanners.clear(); |
| EXPECT_THAT(scanners_, ScannersAreEqual(expected_scanners)); |
| |
| // Discovery should have started after dealing with the cache flush. |
| EXPECT_TRUE(escls_lister_->discovery_started()); |
| } |
| |
| // Verify tasks are cleaned up properly when the detector is destroyed. |
| TEST_F(ZeroconfScannerDetectorTest, DestroyedWithTasksPending) { |
| CreateDetector(); |
| escl_lister_->Announce(MakeServiceDescription( |
| "Scanner1", ZeroconfScannerDetector::kEsclServiceType, "")); |
| // Run listers but don't run the delayed tasks. |
| task_environment_.RunUntilIdle(); |
| detector_.reset(); |
| CompleteTasks(); |
| SUCCEED(); |
| } |
| |
| } // namespace ash |