| // Copyright 2016 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 "chromeos/printing/ppd_provider.h" |
| |
| #include <algorithm> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/run_loop.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/bind.h" |
| #include "base/test/simple_test_clock.h" |
| #include "base/test/task_environment.h" |
| #include "base/test/test_message_loop.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/version.h" |
| #include "chromeos/printing/fake_printer_config_cache.h" |
| #include "chromeos/printing/ppd_cache.h" |
| #include "chromeos/printing/ppd_metadata_manager.h" |
| #include "chromeos/printing/printer_config_cache.h" |
| #include "chromeos/printing/printer_configuration.h" |
| #include "testing/gmock/include/gmock/gmock-matchers.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| using PrinterDiscoveryType = PrinterSearchData::PrinterDiscoveryType; |
| using ::testing::AllOf; |
| using ::testing::Eq; |
| using ::testing::Field; |
| using ::testing::StrEq; |
| using ::testing::UnorderedElementsAre; |
| |
| // A pseudo-ppd that should get cupsFilter lines extracted from it. |
| const char kCupsFilterPpdContents[] = R"( |
| Other random contents that we don't care about. |
| *cupsFilter: "application/vnd.cups-raster 0 my_filter" |
| More random contents that we don't care about |
| *cupsFilter: "application/vnd.cups-awesome 0 a_different_filter" |
| *cupsFilter: "application/vnd.cups-awesomesauce 0 filter3" |
| Yet more randome contents that we don't care about. |
| More random contents that we don't care about. |
| )"; |
| |
| // A pseudo-ppd that should get cupsFilter2 lines extracted from it. |
| // We also have cupsFilter lines in here, but since cupsFilter2 lines |
| // exist, the cupsFilter lines should be ignored. |
| const char kCupsFilter2PpdContents[] = R"( |
| Other random contents that we don't care about. |
| *cupsFilter: "application/vnd.cups-raster 0 my_filter" |
| More random contents that we don't care about |
| *cupsFilter2: "foo bar 0 the_real_filter" |
| *cupsFilter2: "bar baz 381 another_real_filter" |
| Yet more randome contents that we don't care about. |
| More random contents that we don't care about. |
| )"; |
| |
| // Known number of public method calls that the PpdProvider will defer |
| // before posting failures directly. |
| // * This value is left unspecified in the header. |
| // * This value must be kept in sync with the exact value in the |
| // implementation of PpdProvider. |
| constexpr int kMethodDeferralLimitForTesting = 20; |
| |
| // Default manufacturers metadata used for these tests. |
| const char kDefaultManufacturersJson[] = R"({ |
| "filesMap": { |
| "Manufacturer A": "manufacturer_a-en.json", |
| "Manufacturer B": "manufacturer_b-en.json" |
| } |
| })"; |
| |
| // Unowned raw pointers to helper classes composed into the |
| // PpdProvider at construct time. Used throughout to activate testing |
| // codepaths. |
| struct PpdProviderComposedMembers { |
| FakePrinterConfigCache* config_cache = nullptr; |
| FakePrinterConfigCache* manager_config_cache = nullptr; |
| PpdMetadataManager* metadata_manager = nullptr; |
| }; |
| |
| class PpdProviderTest : public ::testing::Test { |
| public: |
| // * Determines where the PpdCache class runs. |
| // * If set to kOnTestThread, the PpdCache class will use the |
| // task environment of the test fixture. |
| // * If set to kInBackgroundThreads, the PpdCache class will |
| // spawn its own background threads. |
| // * Prefer only to run cache on the test thread if you need to |
| // manipulate its sequencing independently of PpdProvider; |
| // otherwise, allowing it spawn its own background threads |
| // should be safe and good for exercising its codepaths. |
| enum class PpdCacheRunLocation { |
| kOnTestThread, |
| kInBackgroundThreads, |
| }; |
| |
| // * Determines whether the browser locale given to PpdProvider |
| // should be propagated to the composed PpdMetadataManager as its |
| // metadata locale as well. |
| // * Useful to the caller depending on whether or not one is |
| // interested in the codepaths that fetch and parse the locales |
| // metadata. |
| enum class PropagateLocaleToMetadataManager { |
| kDoNotPropagate, |
| kDoPropagate, |
| }; |
| |
| // Options passed to CreateProvider(). |
| struct CreateProviderOptions { |
| std::string browser_locale; |
| PpdCacheRunLocation where_ppd_cache_runs; |
| PropagateLocaleToMetadataManager propagate_locale; |
| }; |
| |
| PpdProviderTest() |
| : task_environment_(base::test::TaskEnvironment::MainThreadType::IO, |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} |
| |
| void SetUp() override { |
| ASSERT_TRUE(ppd_cache_temp_dir_.CreateUniqueTempDir()); |
| } |
| |
| // Creates and return a provider for a test that uses the given |options|. |
| scoped_refptr<PpdProvider> CreateProvider( |
| const CreateProviderOptions& options) { |
| switch (options.where_ppd_cache_runs) { |
| case PpdCacheRunLocation::kOnTestThread: |
| ppd_cache_ = PpdCache::CreateForTesting( |
| ppd_cache_temp_dir_.GetPath(), |
| task_environment_.GetMainThreadTaskRunner()); |
| break; |
| case PpdCacheRunLocation::kInBackgroundThreads: |
| default: |
| ppd_cache_ = PpdCache::Create(ppd_cache_temp_dir_.GetPath()); |
| break; |
| } |
| |
| auto manager_config_cache = std::make_unique<FakePrinterConfigCache>(); |
| provider_backdoor_.manager_config_cache = manager_config_cache.get(); |
| |
| auto manager = PpdMetadataManager::Create(options.browser_locale, &clock_, |
| std::move(manager_config_cache)); |
| provider_backdoor_.metadata_manager = manager.get(); |
| |
| switch (options.propagate_locale) { |
| case PropagateLocaleToMetadataManager::kDoNotPropagate: |
| // Nothing to do; the no-propagate case allows the |
| // PpdMetadataManager to acquire the metadata locale (or fail to |
| // do so) by natural means. |
| break; |
| case PropagateLocaleToMetadataManager::kDoPropagate: |
| default: |
| provider_backdoor_.metadata_manager->SetLocaleForTesting( |
| options.browser_locale); |
| break; |
| } |
| |
| auto config_cache = std::make_unique<FakePrinterConfigCache>(); |
| provider_backdoor_.config_cache = config_cache.get(); |
| |
| return PpdProvider::Create(base::Version("40.8.6753.09"), ppd_cache_, |
| std::move(manager), std::move(config_cache)); |
| } |
| |
| // Fills the fake Chrome OS Printing serving root with content. |
| // Must be called after CreateProvider(). |
| void StartFakePpdServer() { |
| for (const auto& entry : server_contents()) { |
| provider_backdoor_.config_cache->SetFetchResponseForTesting(entry.first, |
| entry.second); |
| provider_backdoor_.manager_config_cache->SetFetchResponseForTesting( |
| entry.first, entry.second); |
| } |
| } |
| |
| // Interceptor posts a *task* during destruction that actually unregisters |
| // things. So we have to run the message loop post-interceptor-destruction to |
| // actually unregister the URLs, otherwise they won't *actually* be |
| // unregistered until the next time we invoke the message loop. Which may be |
| // in the middle of the next test. |
| // |
| // Note this is harmless to call if we haven't started a fake ppd server. |
| void StopFakePpdServer() { |
| for (const auto& entry : server_contents()) { |
| provider_backdoor_.config_cache->Drop(entry.first); |
| provider_backdoor_.manager_config_cache->Drop(entry.first); |
| } |
| task_environment_.RunUntilIdle(); |
| } |
| |
| // Capture the result of a ResolveManufacturers() call. |
| void CaptureResolveManufacturers(PpdProvider::CallbackResultCode code, |
| const std::vector<std::string>& data) { |
| captured_resolve_manufacturers_.push_back({code, data}); |
| } |
| |
| // Capture the result of a ResolvePrinters() call. |
| void CaptureResolvePrinters(PpdProvider::CallbackResultCode code, |
| const PpdProvider::ResolvedPrintersList& data) { |
| captured_resolve_printers_.push_back({code, data}); |
| } |
| |
| // Capture the result of a ResolvePpd() call. |
| void CaptureResolvePpd(PpdProvider::CallbackResultCode code, |
| const std::string& ppd_contents) { |
| CapturedResolvePpdResults results; |
| results.code = code; |
| results.ppd_contents = ppd_contents; |
| captured_resolve_ppd_.push_back(results); |
| } |
| |
| // Capture the result of a ResolveUsbIds() call. |
| void CaptureResolvePpdReference(PpdProvider::CallbackResultCode code, |
| const Printer::PpdReference& ref, |
| const std::string& usb_manufacturer) { |
| captured_resolve_ppd_references_.push_back({code, ref, usb_manufacturer}); |
| } |
| |
| // Capture the result of a ResolvePpdLicense() call. |
| void CaptureResolvePpdLicense(PpdProvider::CallbackResultCode code, |
| const std::string& license) { |
| captured_resolve_ppd_license_.push_back({code, license}); |
| } |
| |
| void CaptureReverseLookup(PpdProvider::CallbackResultCode code, |
| const std::string& manufacturer, |
| const std::string& model) { |
| captured_reverse_lookup_.push_back({code, manufacturer, model}); |
| } |
| |
| // Discard the result of a ResolvePpd() call. |
| void DiscardResolvePpd(PpdProvider::CallbackResultCode code, |
| const std::string& contents) {} |
| |
| // Calls the ResolveManufacturer() method of the |provider| and |
| // waits for its completion. Ignores the returned string values and |
| // returns whether the result code was |
| // PpdProvider::CallbackResultCode::SUCCESS. |
| bool SuccessfullyResolveManufacturers(PpdProvider* provider) { |
| base::RunLoop run_loop; |
| PpdProvider::CallbackResultCode code; |
| provider->ResolveManufacturers(base::BindLambdaForTesting( |
| [&run_loop, &code]( |
| PpdProvider::CallbackResultCode result_code, |
| const std::vector<std::string>& unused_manufacturers) { |
| code = result_code; |
| run_loop.QuitClosure().Run(); |
| })); |
| run_loop.Run(); |
| return code == PpdProvider::CallbackResultCode::SUCCESS; |
| } |
| |
| protected: |
| // List of relevant endpoint for this FakeServer |
| std::vector<std::pair<std::string, std::string>> server_contents() const { |
| // Use brace initialization to express the desired server contents as "url", |
| // "contents" pairs. |
| return {{"metadata_v3/locales.json", |
| R"({ |
| "locales": [ "de", "en", "es" ] |
| })"}, |
| {"metadata_v3/manufacturers-en.json", kDefaultManufacturersJson}, |
| {"metadata_v3/manufacturer_a-en.json", |
| R"({ |
| "printers": [ { |
| "name": "printer_a", |
| "emm": "printer_a_ref" |
| }, { |
| "name": "printer_b", |
| "emm": "printer_b_ref" |
| } ] |
| })"}, |
| {"metadata_v3/manufacturer_b-en.json", |
| R"({ |
| "printers": [ { |
| "name": "printer_c", |
| "emm": "printer_c_ref" |
| } ] |
| })"}, |
| {"metadata_v3/index-01.json", |
| R"({ |
| "ppdIndex": { |
| "printer_a_ref": { |
| "ppdMetadata": [ { |
| "name": "printer_a.ppd", |
| "license": "fake_license" |
| } ] |
| } |
| } |
| })"}, |
| {"metadata_v3/index-02.json", |
| R"({ |
| "ppdIndex": { |
| "printer_b_ref": { |
| "ppdMetadata": [ { |
| "name": "printer_b.ppd" |
| } ] |
| } |
| } |
| })"}, |
| {"metadata_v3/index-03.json", |
| R"({ |
| "ppdIndex": { |
| "printer_c_ref": { |
| "ppdMetadata": [ { |
| "name": "printer_c.ppd" |
| } ] |
| } |
| } |
| })"}, |
| {"metadata_v3/index-04.json", |
| R"({ |
| "ppdIndex": { |
| "printer_d_ref": { |
| "ppdMetadata": [ { |
| "name": "printer_d.ppd" |
| } ] |
| } |
| } |
| })"}, |
| {"metadata_v3/index-05.json", |
| R"({ |
| "ppdIndex": { |
| "printer_e_ref": { |
| "ppdMetadata": [ { |
| "name": "printer_e.ppd" |
| } ] |
| } |
| } |
| })"}, |
| {"metadata_v3/index-08.json", |
| R"({ |
| "ppdIndex": { |
| "Some canonical reference": { |
| "ppdMetadata": [ { |
| "name": "unused.ppd" |
| } ] |
| } |
| } |
| })"}, |
| {"metadata_v3/index-10.json", |
| R"({ |
| "ppdIndex": { |
| "Some other canonical reference": { |
| "ppdMetadata": [ { |
| "name": "unused.ppd" |
| } ] |
| } |
| } |
| })"}, |
| {"metadata_v3/usb-031f.json", |
| R"({ |
| "usbIndex": { |
| "1592": { |
| "effectiveMakeAndModel": "Some canonical reference" |
| }, |
| "6535": { |
| "effectiveMakeAndModel": "Some other canonical reference" |
| } |
| } |
| })"}, |
| {"metadata_v3/usb-03f0.json", ""}, |
| {"metadata_v3/usb-1234.json", ""}, |
| {"metadata_v3/usb_vendor_ids.json", R"({ |
| "entries": [ { |
| "vendorId": 799, |
| "vendorName": "Seven Ninety Nine LLC" |
| }, { |
| "vendorId": 1008, |
| "vendorName": "HP" |
| } ] |
| })"}, |
| {"metadata_v3/reverse_index-en-01.json", |
| R"({ |
| "reverseIndex": { |
| "printer_a_ref": { |
| "manufacturer": "manufacturer_a_en", |
| "model": "printer_a" |
| } |
| } |
| })"}, |
| {"metadata_v3/reverse_index-en-19.json", |
| R"({ |
| "reverseIndex": { |
| "unused effective make and model": { |
| "manufacturer": "unused manufacturer", |
| "model": "unused model" |
| } |
| } |
| })"}, |
| {"ppds_for_metadata_v3/printer_a.ppd", kCupsFilterPpdContents}, |
| {"ppds_for_metadata_v3/printer_b.ppd", kCupsFilter2PpdContents}, |
| {"ppds_for_metadata_v3/printer_c.ppd", "c"}, |
| {"ppds_for_metadata_v3/printer_d.ppd", "d"}, |
| {"ppds_for_metadata_v3/printer_e.ppd", "e"}, |
| {"user_supplied_ppd_directory/user_supplied.ppd", "u"}}; |
| } |
| |
| // Environment for task schedulers. |
| base::test::TaskEnvironment task_environment_; |
| |
| std::vector< |
| std::pair<PpdProvider::CallbackResultCode, std::vector<std::string>>> |
| captured_resolve_manufacturers_; |
| |
| std::vector<std::pair<PpdProvider::CallbackResultCode, |
| PpdProvider::ResolvedPrintersList>> |
| captured_resolve_printers_; |
| |
| struct CapturedResolvePpdResults { |
| PpdProvider::CallbackResultCode code; |
| std::string ppd_contents; |
| }; |
| std::vector<CapturedResolvePpdResults> captured_resolve_ppd_; |
| |
| struct CapturedResolvePpdReferenceResults { |
| PpdProvider::CallbackResultCode code; |
| Printer::PpdReference ref; |
| std::string usb_manufacturer; |
| }; |
| |
| std::vector<CapturedResolvePpdReferenceResults> |
| captured_resolve_ppd_references_; |
| |
| struct CapturedReverseLookup { |
| PpdProvider::CallbackResultCode code; |
| std::string manufacturer; |
| std::string model; |
| }; |
| std::vector<CapturedReverseLookup> captured_reverse_lookup_; |
| |
| struct CapturedResolvePpdLicense { |
| PpdProvider::CallbackResultCode code; |
| std::string license; |
| }; |
| std::vector<CapturedResolvePpdLicense> captured_resolve_ppd_license_; |
| |
| base::ScopedTempDir ppd_cache_temp_dir_; |
| base::ScopedTempDir interceptor_temp_dir_; |
| |
| // Reference to the underlying ppd_cache_ so we can muck with it to test |
| // cache-dependent behavior of ppd_provider_. |
| scoped_refptr<PpdCache> ppd_cache_; |
| |
| PpdProviderComposedMembers provider_backdoor_; |
| |
| // Misc extra stuff needed for the test environment to function. |
| base::SimpleTestClock clock_; |
| }; |
| |
| // Tests that PpdProvider enqueues a bounded number of calls to |
| // ResolveManufacturers() and fails the oldest call when the queue is |
| // deemed full (implementation-specified detail). |
| TEST_F(PpdProviderTest, FailsOldestQueuedResolveManufacturers) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoNotPropagate}); |
| |
| // Prevents the provider from ever getting a metadata locale. |
| // We want it to stall out, forcing it to perpetually defer method |
| // calls to ResolveManufacturers(). |
| provider_backdoor_.manager_config_cache->DiscardFetchRequestFor( |
| "metadata_v3/locales.json"); |
| |
| for (int i = kMethodDeferralLimitForTesting; i >= 0; i--) { |
| provider->ResolveManufacturers(base::BindOnce( |
| &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); |
| } |
| |
| // The for loop above should have overflowed the deferral queue by |
| // a factor of one: the oldest call to ResolveManufacturers() should |
| // have been forced out and asked to fail, and we expect it to be |
| // sitting on the sequence right now. |
| ASSERT_EQ(1UL, task_environment_.GetPendingMainThreadTaskCount()); |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| |
| ASSERT_EQ(1UL, captured_resolve_manufacturers_.size()); |
| EXPECT_EQ(PpdProvider::CallbackResultCode::SERVER_ERROR, |
| captured_resolve_manufacturers_[0].first); |
| } |
| |
| // Tests that PpdProvider enqueues a bounded number of calls to |
| // ReverseLookup() and fails the oldest call when the queue is deemed |
| // full (implementation-specified detail). |
| TEST_F(PpdProviderTest, FailsOldestQueuedReverseLookup) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoNotPropagate}); |
| |
| // Prevents the provider from ever getting a metadata locale. |
| // We want it to stall out, forcing it to perpetually defer method |
| // calls to ReverseLookup(). |
| provider_backdoor_.manager_config_cache->DiscardFetchRequestFor( |
| "metadata_v3/locales.json"); |
| |
| for (int i = kMethodDeferralLimitForTesting; i >= 0; i--) { |
| provider->ReverseLookup( |
| "some effective-make-and-model string", |
| base::BindOnce(&PpdProviderTest::CaptureReverseLookup, |
| base::Unretained(this))); |
| } |
| |
| // The for loop above should have overflowed the deferral queue by |
| // a factor of one: the oldest call to ReverseLookup() should have |
| // been forced out and asked to fail, and we expect it to be sitting |
| // on the sequence right now. |
| ASSERT_EQ(1UL, task_environment_.GetPendingMainThreadTaskCount()); |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| |
| ASSERT_EQ(1UL, captured_reverse_lookup_.size()); |
| EXPECT_EQ(PpdProvider::CallbackResultCode::SERVER_ERROR, |
| captured_reverse_lookup_[0].code); |
| } |
| |
| // Test that we get back manufacturer maps as expected. |
| TEST_F(PpdProviderTest, ManufacturersFetch) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoNotPropagate}); |
| StartFakePpdServer(); |
| |
| // Issue two requests at the same time, both should be resolved properly. |
| provider->ResolveManufacturers(base::BindOnce( |
| &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); |
| provider->ResolveManufacturers(base::BindOnce( |
| &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| ASSERT_EQ(2UL, captured_resolve_manufacturers_.size()); |
| std::vector<std::string> expected_result( |
| {"Manufacturer A", "Manufacturer B"}); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_manufacturers_[0].first); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_manufacturers_[1].first); |
| EXPECT_TRUE(captured_resolve_manufacturers_[0].second == expected_result); |
| EXPECT_TRUE(captured_resolve_manufacturers_[1].second == expected_result); |
| } |
| |
| // Test that we get a reasonable error when we have no server to contact. Tis |
| // is almost exactly the same as the above test, we just don't bring up the fake |
| // server first. |
| TEST_F(PpdProviderTest, ManufacturersFetchNoServer) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoNotPropagate}); |
| |
| // Issue two requests at the same time, both should resolve properly |
| // (though they will fail). |
| provider->ResolveManufacturers(base::BindOnce( |
| &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); |
| provider->ResolveManufacturers(base::BindOnce( |
| &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); |
| task_environment_.FastForwardUntilNoTasksRemain(); |
| |
| ASSERT_EQ(2UL, captured_resolve_manufacturers_.size()); |
| EXPECT_EQ(PpdProvider::SERVER_ERROR, |
| captured_resolve_manufacturers_[0].first); |
| EXPECT_EQ(PpdProvider::SERVER_ERROR, |
| captured_resolve_manufacturers_[1].first); |
| EXPECT_TRUE(captured_resolve_manufacturers_[0].second.empty()); |
| EXPECT_TRUE(captured_resolve_manufacturers_[1].second.empty()); |
| } |
| |
| // Tests that mutiples requests for make-and-model resolution can be fulfilled |
| // simultaneously. |
| TEST_F(PpdProviderTest, RepeatedMakeModel) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| StartFakePpdServer(); |
| |
| PrinterSearchData unrecognized_printer; |
| unrecognized_printer.discovery_type = PrinterDiscoveryType::kManual; |
| unrecognized_printer.make_and_model = {"Printer Printer"}; |
| |
| PrinterSearchData recognized_printer; |
| recognized_printer.discovery_type = PrinterDiscoveryType::kManual; |
| recognized_printer.make_and_model = {"printer_a_ref"}; |
| |
| PrinterSearchData mixed; |
| mixed.discovery_type = PrinterDiscoveryType::kManual; |
| mixed.make_and_model = {"printer_a_ref", "Printer Printer"}; |
| |
| // Resolve the same thing repeatedly. |
| provider->ResolvePpdReference( |
| unrecognized_printer, |
| base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| provider->ResolvePpdReference( |
| mixed, base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| provider->ResolvePpdReference( |
| recognized_printer, |
| base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(static_cast<size_t>(3), captured_resolve_ppd_references_.size()); |
| EXPECT_EQ(PpdProvider::NOT_FOUND, captured_resolve_ppd_references_[0].code); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_references_[1].code); |
| EXPECT_EQ("printer_a_ref", |
| captured_resolve_ppd_references_[1].ref.effective_make_and_model); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_references_[2].code); |
| EXPECT_EQ("printer_a_ref", |
| captured_resolve_ppd_references_[2].ref.effective_make_and_model); |
| } |
| |
| // Test successful and unsuccessful usb resolutions. |
| TEST_F(PpdProviderTest, UsbResolution) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| StartFakePpdServer(); |
| |
| PrinterSearchData search_data; |
| search_data.discovery_type = PrinterDiscoveryType::kUsb; |
| |
| // Should get back "Some canonical reference" |
| search_data.usb_vendor_id = 0x031f; |
| search_data.usb_product_id = 1592; |
| provider->ResolvePpdReference( |
| search_data, base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| // Should get back "Some other canonical reference" |
| search_data.usb_vendor_id = 0x031f; |
| search_data.usb_product_id = 6535; |
| provider->ResolvePpdReference( |
| search_data, base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| |
| // Vendor id that exists, nonexistent device id, should get a NOT_FOUND. |
| // In our fake serving root, the manufacturer with vendor ID 0x031f |
| // (== 799) is named "Seven Ninety Nine LLC." |
| search_data.usb_vendor_id = 0x031f; |
| search_data.usb_product_id = 8162; |
| provider->ResolvePpdReference( |
| search_data, base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| |
| // Nonexistent vendor id, should get a NOT_FOUND in the real world, but |
| // the URL interceptor we're using considers all nonexistent files to |
| // be effectively CONNECTION REFUSED, so we just check for non-success |
| // on this one. |
| search_data.usb_vendor_id = 0x1234; |
| search_data.usb_product_id = 1782; |
| provider->ResolvePpdReference( |
| search_data, base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(captured_resolve_ppd_references_.size(), static_cast<size_t>(4)); |
| |
| // ResolvePpdReference() takes place in several asynchronous steps, so |
| // order is not guaranteed. |
| EXPECT_THAT( |
| captured_resolve_ppd_references_, |
| UnorderedElementsAre( |
| AllOf(Field(&CapturedResolvePpdReferenceResults::code, |
| Eq(PpdProvider::SUCCESS)), |
| Field(&CapturedResolvePpdReferenceResults::ref, |
| Field(&Printer::PpdReference::effective_make_and_model, |
| StrEq("Some canonical reference")))), |
| AllOf(Field(&CapturedResolvePpdReferenceResults::code, |
| Eq(PpdProvider::SUCCESS)), |
| Field(&CapturedResolvePpdReferenceResults::ref, |
| Field(&Printer::PpdReference::effective_make_and_model, |
| StrEq("Some other canonical reference")))), |
| AllOf(Field(&CapturedResolvePpdReferenceResults::code, |
| Eq(PpdProvider::NOT_FOUND)), |
| Field(&CapturedResolvePpdReferenceResults::usb_manufacturer, |
| StrEq("Seven Ninety Nine LLC"))), |
| Field(&CapturedResolvePpdReferenceResults::code, |
| Eq(PpdProvider::NOT_FOUND)))); |
| } |
| |
| // Test basic ResolvePrinters() functionality. At the same time, make |
| // sure we can get the PpdReference for each of the resolved printers. |
| TEST_F(PpdProviderTest, ResolvePrinters) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| StartFakePpdServer(); |
| |
| // Required setup calls to advance past PpdProvider's method deferral. |
| ASSERT_TRUE(provider_backdoor_.metadata_manager->SetManufacturersForTesting( |
| kDefaultManufacturersJson)); |
| ASSERT_TRUE(SuccessfullyResolveManufacturers(provider.get())); |
| |
| provider->ResolvePrinters( |
| "Manufacturer A", base::BindOnce(&PpdProviderTest::CaptureResolvePrinters, |
| base::Unretained(this))); |
| provider->ResolvePrinters( |
| "Manufacturer B", base::BindOnce(&PpdProviderTest::CaptureResolvePrinters, |
| base::Unretained(this))); |
| |
| task_environment_.RunUntilIdle(); |
| ASSERT_EQ(2UL, captured_resolve_printers_.size()); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_printers_[0].first); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_printers_[1].first); |
| EXPECT_EQ(2UL, captured_resolve_printers_[0].second.size()); |
| |
| // First capture should get back printer_a, and printer_b, with ppd |
| // reference effective make and models of printer_a_ref and printer_b_ref. |
| const auto& capture0 = captured_resolve_printers_[0].second; |
| ASSERT_EQ(2UL, capture0.size()); |
| EXPECT_EQ("printer_a", capture0[0].name); |
| EXPECT_EQ("printer_a_ref", capture0[0].ppd_ref.effective_make_and_model); |
| |
| EXPECT_EQ("printer_b", capture0[1].name); |
| EXPECT_EQ("printer_b_ref", capture0[1].ppd_ref.effective_make_and_model); |
| |
| // Second capture should get back printer_c with effective make and model of |
| // printer_c_ref |
| const auto& capture1 = captured_resolve_printers_[1].second; |
| ASSERT_EQ(1UL, capture1.size()); |
| EXPECT_EQ("printer_c", capture1[0].name); |
| EXPECT_EQ("printer_c_ref", capture1[0].ppd_ref.effective_make_and_model); |
| } |
| |
| // Test that if we give a bad reference to ResolvePrinters(), we get a |
| // SERVER_ERROR. There's currently no feedback that indicates |
| // specifically to the caller that they asked for the printers of |
| // a manufacturer we didn't previously advertise. |
| TEST_F(PpdProviderTest, ResolvePrintersBadReference) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| StartFakePpdServer(); |
| |
| // Required setup calls to advance past PpdProvider's method deferral. |
| ASSERT_TRUE(provider_backdoor_.metadata_manager->SetManufacturersForTesting( |
| kDefaultManufacturersJson)); |
| ASSERT_TRUE(SuccessfullyResolveManufacturers(provider.get())); |
| |
| provider->ResolvePrinters( |
| "bogus_doesnt_exist", |
| base::BindOnce(&PpdProviderTest::CaptureResolvePrinters, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| ASSERT_EQ(1UL, captured_resolve_printers_.size()); |
| EXPECT_EQ(PpdProvider::SERVER_ERROR, captured_resolve_printers_[0].first); |
| } |
| |
| // Test that if the server is unavailable, we get SERVER_ERRORs back out. |
| TEST_F(PpdProviderTest, ResolvePrintersNoServer) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| |
| // Required setup calls to advance past PpdProvider's method deferral. |
| ASSERT_TRUE(provider_backdoor_.metadata_manager->SetManufacturersForTesting( |
| kDefaultManufacturersJson)); |
| ASSERT_TRUE(SuccessfullyResolveManufacturers(provider.get())); |
| |
| provider->ResolvePrinters( |
| "Manufacturer A", base::BindOnce(&PpdProviderTest::CaptureResolvePrinters, |
| base::Unretained(this))); |
| provider->ResolvePrinters( |
| "Manufacturer B", base::BindOnce(&PpdProviderTest::CaptureResolvePrinters, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| ASSERT_EQ(2UL, captured_resolve_printers_.size()); |
| EXPECT_EQ(PpdProvider::SERVER_ERROR, captured_resolve_printers_[0].first); |
| EXPECT_EQ(PpdProvider::SERVER_ERROR, captured_resolve_printers_[1].first); |
| } |
| |
| // Test a successful ppd resolution from an effective_make_and_model reference. |
| TEST_F(PpdProviderTest, ResolveServerKeyPpd) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| StartFakePpdServer(); |
| Printer::PpdReference ref; |
| ref.effective_make_and_model = "printer_b_ref"; |
| provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| ref.effective_make_and_model = "printer_c_ref"; |
| provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(2UL, captured_resolve_ppd_.size()); |
| |
| // ResolvePpd() works in several asynchronous steps, so order of |
| // return is not guaranteed. |
| EXPECT_THAT( |
| captured_resolve_ppd_, |
| UnorderedElementsAre( |
| AllOf(Field(&CapturedResolvePpdResults::code, |
| PpdProvider::CallbackResultCode::SUCCESS), |
| Field(&CapturedResolvePpdResults::ppd_contents, |
| StrEq(kCupsFilter2PpdContents))), |
| AllOf(Field(&CapturedResolvePpdResults::code, |
| PpdProvider::CallbackResultCode::SUCCESS), |
| Field(&CapturedResolvePpdResults::ppd_contents, StrEq("c"))))); |
| } |
| |
| // Test that we *don't* resolve a ppd URL over non-file schemes. It's not clear |
| // whether we'll want to do this in the long term, but for now this is |
| // disallowed because we're not sure we completely understand the security |
| // implications. |
| TEST_F(PpdProviderTest, ResolveUserSuppliedUrlPpdFromNetworkFails) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| StartFakePpdServer(); |
| |
| Printer::PpdReference ref; |
| // PpdProvider::ResolvePpd() shall fail if a user-supplied PPD URL |
| // does not begin with the "file://" scheme. |
| ref.user_supplied_ppd_url = "nonfilescheme://unused"; |
| provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(1UL, captured_resolve_ppd_.size()); |
| EXPECT_EQ(PpdProvider::INTERNAL_ERROR, captured_resolve_ppd_[0].code); |
| EXPECT_TRUE(captured_resolve_ppd_[0].ppd_contents.empty()); |
| } |
| |
| // Test a successful ppd resolution from a user_supplied_url field when |
| // reading from a file. Note we shouldn't need the server to be up |
| // to do this successfully, as we should be able to do this offline. |
| TEST_F(PpdProviderTest, ResolveUserSuppliedUrlPpdFromFile) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath filename = temp_dir.GetPath().Append("my_spiffy.ppd"); |
| |
| std::string user_ppd_contents = "Woohoo"; |
| |
| ASSERT_TRUE(base::WriteFile(filename, user_ppd_contents)); |
| |
| Printer::PpdReference ref; |
| ref.user_supplied_ppd_url = |
| base::StringPrintf("file://%s", filename.MaybeAsASCII().c_str()); |
| provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(1UL, captured_resolve_ppd_.size()); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); |
| EXPECT_EQ(user_ppd_contents, captured_resolve_ppd_[0].ppd_contents); |
| } |
| |
| // Test that we cache ppd resolutions when we fetch them and that we can resolve |
| // from the cache without the server available. |
| TEST_F(PpdProviderTest, ResolvedPpdsGetCached) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| std::string user_ppd_contents = "Woohoo"; |
| Printer::PpdReference ref; |
| { |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath filename = temp_dir.GetPath().Append("my_spiffy.ppd"); |
| |
| ASSERT_TRUE(base::WriteFile(filename, user_ppd_contents)); |
| |
| ref.user_supplied_ppd_url = |
| base::StringPrintf("file://%s", filename.MaybeAsASCII().c_str()); |
| provider->ResolvePpd(ref, |
| base::BindOnce(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(1UL, captured_resolve_ppd_.size()); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); |
| EXPECT_EQ(user_ppd_contents, captured_resolve_ppd_[0].ppd_contents); |
| } |
| // ScopedTempDir goes out of scope, so the source file should now be |
| // deleted. But if we resolve again, we should hit the cache and |
| // still be successful. |
| |
| captured_resolve_ppd_.clear(); |
| |
| // Recreate the provider to make sure we don't have any memory caches which |
| // would mask problems with disk persistence. |
| provider = CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| |
| // Re-resolve. |
| provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(1UL, captured_resolve_ppd_.size()); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); |
| EXPECT_EQ(user_ppd_contents, captured_resolve_ppd_[0].ppd_contents); |
| } |
| |
| // Test that all entrypoints will correctly work with case-insensitve |
| // effective-make-and-model strings. |
| TEST_F(PpdProviderTest, CaseInsensitiveMakeAndModel) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| StartFakePpdServer(); |
| std::string ref = "pRiNteR_A_reF"; |
| |
| Printer::PpdReference ppd_ref; |
| ppd_ref.effective_make_and_model = ref; |
| provider->ResolvePpd(ppd_ref, |
| base::BindOnce(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| provider->ReverseLookup(ref, |
| base::BindOnce(&PpdProviderTest::CaptureReverseLookup, |
| base::Unretained(this))); |
| PrinterSearchData printer_info; |
| printer_info.make_and_model = {ref}; |
| provider->ResolvePpdReference( |
| printer_info, base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| |
| // Check PpdProvider::ResolvePpd |
| ASSERT_EQ(1UL, captured_resolve_ppd_.size()); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); |
| EXPECT_EQ(kCupsFilterPpdContents, captured_resolve_ppd_[0].ppd_contents); |
| |
| // Check PpdProvider::ReverseLookup |
| ASSERT_EQ(1UL, captured_reverse_lookup_.size()); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_reverse_lookup_[0].code); |
| EXPECT_EQ("manufacturer_a_en", captured_reverse_lookup_[0].manufacturer); |
| EXPECT_EQ("printer_a", captured_reverse_lookup_[0].model); |
| |
| // Check PpdProvider::ResolvePpdReference |
| ASSERT_EQ(1UL, captured_resolve_ppd_references_.size()); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_references_[0].code); |
| EXPECT_EQ("printer_a_ref", |
| captured_resolve_ppd_references_[0].ref.effective_make_and_model); |
| } |
| |
| // Tests that ResolvePpdLicense is able to correctly source the index and |
| // determine the name of the PPD license associated with the given effecive make |
| // and model (if any). |
| TEST_F(PpdProviderTest, ResolvePpdLicense) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoNotPropagate}); |
| StartFakePpdServer(); |
| |
| // For this effective_make_and_model, we expect that there is associated |
| // license. |
| const char kEmm1[] = "printer_a_ref"; |
| provider->ResolvePpdLicense( |
| kEmm1, base::BindOnce(&PpdProviderTest::CaptureResolvePpdLicense, |
| base::Unretained(this))); |
| |
| // We do not expect there to be any license associated with this |
| // effective_make_and_model, so the response should be empty. |
| const char kEmm2[] = "printer_b_ref"; |
| provider->ResolvePpdLicense( |
| kEmm2, base::BindOnce(&PpdProviderTest::CaptureResolvePpdLicense, |
| base::Unretained(this))); |
| |
| task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(2UL, captured_resolve_ppd_license_.size()); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_license_[0].code); |
| EXPECT_EQ("fake_license", captured_resolve_ppd_license_[0].license); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_license_[1].code); |
| EXPECT_EQ("", captured_resolve_ppd_license_[1].license); |
| } |
| |
| // Verifies that we can extract the Manufacturer and Model selection for a |
| // given effective make and model. |
| TEST_F(PpdProviderTest, ReverseLookup) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| StartFakePpdServer(); |
| std::string ref = "printer_a_ref"; |
| provider->ReverseLookup(ref, |
| base::BindOnce(&PpdProviderTest::CaptureReverseLookup, |
| base::Unretained(this))); |
| // TODO(skau): PpdProvider has a race condition that prevents running these |
| // requests in parallel. |
| task_environment_.RunUntilIdle(); |
| |
| std::string ref_fail = "printer_does_not_exist"; |
| provider->ReverseLookup(ref_fail, |
| base::BindOnce(&PpdProviderTest::CaptureReverseLookup, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(2U, captured_reverse_lookup_.size()); |
| CapturedReverseLookup success_capture = captured_reverse_lookup_[0]; |
| EXPECT_EQ(PpdProvider::SUCCESS, success_capture.code); |
| EXPECT_EQ("manufacturer_a_en", success_capture.manufacturer); |
| EXPECT_EQ("printer_a", success_capture.model); |
| |
| CapturedReverseLookup failed_capture = captured_reverse_lookup_[1]; |
| EXPECT_EQ(PpdProvider::NOT_FOUND, failed_capture.code); |
| } |
| |
| // Verifies that we never attempt to re-download a PPD that we |
| // previously retrieved from the serving root. The Chrome OS Printing |
| // Team plans to keep PPDs immutable inside the serving root, so |
| // PpdProvider should always prefer to retrieve a PPD from the PpdCache |
| // when it's possible to do so. |
| TEST_F(PpdProviderTest, PreferToResolvePpdFromPpdCacheOverServingRoot) { |
| // Explicitly *not* starting a fake server. |
| std::string cached_ppd_contents = |
| "These cached contents are different from what's being served"; |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kOnTestThread, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| Printer::PpdReference ref; |
| ref.effective_make_and_model = "printer_a_ref"; |
| std::string cache_key = PpdProvider::PpdReferenceToCacheKey(ref); |
| |
| // Cache exists, and is just created, so should be fresh. |
| // |
| // PPD basename is taken from value specified in forward index shard |
| // defined in server_contents(). |
| const std::string ppd_basename = "printer_a.ppd"; |
| ppd_cache_->StoreForTesting(PpdProvider::PpdBasenameToCacheKey(ppd_basename), |
| cached_ppd_contents, base::TimeDelta()); |
| ppd_cache_->StoreForTesting(PpdProvider::PpdReferenceToCacheKey(ref), |
| ppd_basename, base::TimeDelta()); |
| task_environment_.RunUntilIdle(); |
| provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| ASSERT_EQ(1UL, captured_resolve_ppd_.size()); |
| |
| // Should get the cached (not served) results back, and not have hit the |
| // network. |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); |
| EXPECT_EQ(cached_ppd_contents, captured_resolve_ppd_[0].ppd_contents); |
| } |
| |
| // For user-provided ppds, we should always use the latest version on |
| // disk if it still exists there. |
| TEST_F(PpdProviderTest, UserPpdAlwaysRefreshedIfAvailable) { |
| base::ScopedTempDir temp_dir; |
| std::string cached_ppd_contents = "Cached Ppd Contents"; |
| std::string disk_ppd_contents = "Updated Ppd Contents"; |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kOnTestThread, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| StartFakePpdServer(); |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath filename = temp_dir.GetPath().Append("my_spiffy.ppd"); |
| |
| Printer::PpdReference ref; |
| ref.user_supplied_ppd_url = |
| base::StringPrintf("file://%s", filename.MaybeAsASCII().c_str()); |
| |
| // Put cached_ppd_contents into the cache. |
| ppd_cache_->StoreForTesting(PpdProvider::PpdReferenceToCacheKey(ref), |
| cached_ppd_contents, base::TimeDelta()); |
| task_environment_.RunUntilIdle(); |
| |
| // Write different contents to disk, so that the cached contents are |
| // now stale. |
| ASSERT_TRUE(base::WriteFile(filename, disk_ppd_contents)); |
| |
| provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| ASSERT_EQ(1UL, captured_resolve_ppd_.size()); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); |
| EXPECT_EQ(disk_ppd_contents, captured_resolve_ppd_[0].ppd_contents); |
| |
| // Check that the cache was also updated with the new contents. |
| PpdCache::FindResult captured_find_result; |
| ppd_cache_->Find(PpdProvider::PpdReferenceToCacheKey(ref), |
| base::BindOnce( |
| [](PpdCache::FindResult* captured_find_result, |
| const PpdCache::FindResult& find_result) { |
| *captured_find_result = find_result; |
| }, |
| &captured_find_result)); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(captured_find_result.success, true); |
| EXPECT_EQ(captured_find_result.contents, disk_ppd_contents); |
| } |
| |
| // Test resolving usb manufacturer when failed to resolve PpdReference. |
| TEST_F(PpdProviderTest, ResolveUsbManufacturer) { |
| auto provider = |
| CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, |
| PropagateLocaleToMetadataManager::kDoPropagate}); |
| StartFakePpdServer(); |
| |
| PrinterSearchData search_data; |
| search_data.discovery_type = PrinterDiscoveryType::kUsb; |
| |
| // Vendor id that exists, nonexistent device id, should get a NOT_FOUND. |
| // Although this is an unsupported printer model, we can still expect to get |
| // the manufacturer name. |
| search_data.usb_vendor_id = 0x03f0; |
| search_data.usb_product_id = 9999; |
| provider->ResolvePpdReference( |
| search_data, base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| |
| // Nonexistent vendor id, should get a NOT_FOUND in the real world, but |
| // the URL interceptor we're using considers all nonexistent files to |
| // be effectively CONNECTION REFUSED, so we just check for non-success |
| // on this one. We should also not be able to get a manufacturer name from a |
| // nonexistent vendor id. |
| search_data.usb_vendor_id = 0x1234; |
| search_data.usb_product_id = 1782; |
| provider->ResolvePpdReference( |
| search_data, base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(static_cast<size_t>(2), captured_resolve_ppd_references_.size()); |
| // Was able to find the printer manufactuer but unable to find the printer |
| // model should result in a NOT_FOUND. |
| EXPECT_EQ(PpdProvider::NOT_FOUND, captured_resolve_ppd_references_[0].code); |
| // Failed to grab the PPD for a USB printer, but should still be able to grab |
| // its manufacturer name. |
| EXPECT_EQ("HP", captured_resolve_ppd_references_[0].usb_manufacturer); |
| |
| // Unable to find the PPD file should result in a failure. |
| EXPECT_FALSE(captured_resolve_ppd_references_[1].code == |
| PpdProvider::SUCCESS); |
| // Unknown vendor id should result in an empty manufacturer string. |
| EXPECT_TRUE(captured_resolve_ppd_references_[1].usb_manufacturer.empty()); |
| } |
| |
| } // namespace |
| } // namespace chromeos |