| // 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 <map> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/logging.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/scoped_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 "chromeos/chromeos_paths.h" |
| #include "chromeos/printing/ppd_cache.h" |
| #include "chromeos/printing/ppd_provider.h" |
| #include "chromeos/printing/printer_configuration.h" |
| #include "net/url_request/test_url_request_interceptor.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| // Name of the fake server we're resolving ppds from. |
| const char kPpdServer[] = "bogus.google.com"; |
| |
| // 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. |
| )"; |
| |
| class PpdProviderTest : public ::testing::Test { |
| public: |
| PpdProviderTest() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::IO), |
| request_context_getter_(new net::TestURLRequestContextGetter( |
| scoped_task_environment_.GetMainThreadTaskRunner())) {} |
| |
| void SetUp() override { |
| ASSERT_TRUE(ppd_cache_temp_dir_.CreateUniqueTempDir()); |
| } |
| |
| void TearDown() override { StopFakePpdServer(); } |
| |
| // Create and return a provider for a test that uses the given |locale|. |
| scoped_refptr<PpdProvider> CreateProvider(const std::string& locale) { |
| auto provider_options = PpdProvider::Options(); |
| provider_options.ppd_server_root = std::string("https://") + kPpdServer; |
| |
| return PpdProvider::Create(locale, request_context_getter_.get(), |
| PpdCache::Create(ppd_cache_temp_dir_.GetPath()), |
| provider_options); |
| } |
| |
| // Create an interceptor that serves a small fileset of ppd server files. |
| void StartFakePpdServer() { |
| ASSERT_TRUE(interceptor_temp_dir_.CreateUniqueTempDir()); |
| interceptor_ = std::make_unique<net::TestURLRequestInterceptor>( |
| "https", kPpdServer, scoped_task_environment_.GetMainThreadTaskRunner(), |
| scoped_task_environment_.GetMainThreadTaskRunner()); |
| // Use brace initialization to express the desired server contents as "url", |
| // "contents" pairs. |
| std::vector<std::pair<std::string, std::string>> server_contents = { |
| {"metadata/locales.json", |
| R"(["en", |
| "es-mx", |
| "en-gb"])"}, |
| {"metadata/index.json", |
| R"([ |
| ["printer_a_ref", "printer_a.ppd"], |
| ["printer_b_ref", "printer_b.ppd"], |
| ["printer_c_ref", "printer_c.ppd"] |
| ])"}, |
| {"metadata/usb-031f.json", |
| R"([ |
| [1592, "Some canonical reference"], |
| [6535, "Some other canonical reference"] |
| ])"}, |
| {"metadata/manufacturers-en.json", |
| R"([ |
| ["manufacturer_a_en", "manufacturer_a.json"], |
| ["manufacturer_b_en", "manufacturer_b.json"] |
| ])"}, |
| {"metadata/manufacturers-en-gb.json", |
| R"([ |
| ["manufacturer_a_en-gb", "manufacturer_a.json"], |
| ["manufacturer_b_en-gb", "manufacturer_b.json"] |
| ])"}, |
| {"metadata/manufacturers-es-mx.json", |
| R"([ |
| ["manufacturer_a_es-mx", "manufacturer_a.json"], |
| ["manufacturer_b_es-mx", "manufacturer_b.json"] |
| ])"}, |
| {"metadata/manufacturer_a.json", |
| R"([ |
| ["printer_a", "printer_a_ref"], |
| ["printer_b", "printer_b_ref"] |
| ])"}, |
| {"metadata/manufacturer_b.json", |
| R"([ |
| ["printer_c", "printer_c_ref"] |
| ])"}, |
| {"metadata_v2/reverse_index-en-01.json", |
| R"([ |
| ["printer_a_ref", "manufacturer_a_en", "printer_a"] |
| ])"}, |
| {"metadata_v2/reverse_index-en-19.json", |
| R"([ |
| ])"}, |
| {"ppds/printer_a.ppd", kCupsFilterPpdContents}, |
| {"ppds/printer_b.ppd", kCupsFilter2PpdContents}, |
| {"ppds/printer_c.ppd", "c"}, |
| {"user_supplied_ppd_directory/user_supplied.ppd", "u"}}; |
| int next_file_num = 0; |
| for (const auto& entry : server_contents) { |
| base::FilePath filename = interceptor_temp_dir_.GetPath().Append( |
| base::StringPrintf("%d.json", next_file_num++)); |
| ASSERT_EQ( |
| base::WriteFile(filename, entry.second.data(), entry.second.size()), |
| static_cast<int>(entry.second.size())) |
| << "Failed to write temp server file"; |
| interceptor_->SetResponse( |
| GURL(base::StringPrintf("https://%s/%s", kPpdServer, |
| entry.first.c_str())), |
| filename); |
| } |
| } |
| |
| // 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() { |
| interceptor_.reset(); |
| scoped_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, |
| const std::vector<std::string>& ppd_filters) { |
| CapturedResolvePpdResults results; |
| results.code = code; |
| results.ppd_contents = ppd_contents; |
| results.ppd_filters = ppd_filters; |
| captured_resolve_ppd_.push_back(results); |
| } |
| |
| // Capture the result of a ResolveUsbIds() call. |
| void CaptureResolvePpdReference(PpdProvider::CallbackResultCode code, |
| const Printer::PpdReference& ref) { |
| captured_resolve_ppd_references_.push_back({code, ref}); |
| } |
| |
| 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) {} |
| |
| protected: |
| // Run a ResolveManufacturers run from the given locale, expect to get |
| // results in expected_used_locale. |
| void RunLocalizationTest(const std::string& browser_locale, |
| const std::string& expected_used_locale) { |
| captured_resolve_manufacturers_.clear(); |
| auto provider = CreateProvider(browser_locale); |
| provider->ResolveManufacturers(base::Bind( |
| &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); |
| scoped_task_environment_.RunUntilIdle(); |
| provider = nullptr; |
| ASSERT_EQ(captured_resolve_manufacturers_.size(), 1UL); |
| EXPECT_EQ(captured_resolve_manufacturers_[0].first, PpdProvider::SUCCESS); |
| |
| const auto& result_vec = captured_resolve_manufacturers_[0].second; |
| |
| // It's sufficient to check for one of the expected locale keys to make sure |
| // we got the right map. |
| EXPECT_FALSE(std::find(result_vec.begin(), result_vec.end(), |
| "manufacturer_a_" + expected_used_locale) == |
| result_vec.end()); |
| } |
| |
| // Environment for task schedulers. |
| base::test::ScopedTaskEnvironment scoped_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<std::string> ppd_filters; |
| }; |
| std::vector<CapturedResolvePpdResults> captured_resolve_ppd_; |
| |
| std::vector<std::pair<PpdProvider::CallbackResultCode, Printer::PpdReference>> |
| captured_resolve_ppd_references_; |
| |
| struct CapturedReverseLookup { |
| PpdProvider::CallbackResultCode code; |
| std::string manufacturer; |
| std::string model; |
| }; |
| std::vector<CapturedReverseLookup> captured_reverse_lookup_; |
| |
| std::unique_ptr<net::TestURLRequestInterceptor> interceptor_; |
| |
| base::ScopedTempDir ppd_cache_temp_dir_; |
| base::ScopedTempDir interceptor_temp_dir_; |
| |
| // Provider to be used in the test. |
| scoped_refptr<PpdProvider> ppd_provider_; |
| |
| // Misc extra stuff needed for the test environment to function. |
| // base::TestMessageLoop loop_; |
| scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |
| }; |
| |
| // Test that we get back manufacturer maps as expected. |
| TEST_F(PpdProviderTest, ManufacturersFetch) { |
| StartFakePpdServer(); |
| auto provider = CreateProvider("en"); |
| // Issue two requests at the same time, both should be resolved properly. |
| provider->ResolveManufacturers(base::Bind( |
| &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); |
| provider->ResolveManufacturers(base::Bind( |
| &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); |
| scoped_task_environment_.RunUntilIdle(); |
| ASSERT_EQ(2UL, captured_resolve_manufacturers_.size()); |
| std::vector<std::string> expected_result( |
| {"manufacturer_a_en", "manufacturer_b_en"}); |
| 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"); |
| // Issue two requests at the same time, both should be resolved properly. |
| provider->ResolveManufacturers(base::Bind( |
| &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); |
| provider->ResolveManufacturers(base::Bind( |
| &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); |
| scoped_task_environment_.RunUntilIdle(); |
| 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()); |
| } |
| |
| // Test that we get things in the requested locale, and that fallbacks are sane. |
| TEST_F(PpdProviderTest, LocalizationAndFallbacks) { |
| StartFakePpdServer(); |
| RunLocalizationTest("en-gb", "en-gb"); |
| RunLocalizationTest("en-blah", "en"); |
| RunLocalizationTest("en-gb-foo", "en-gb"); |
| RunLocalizationTest("es", "es-mx"); |
| RunLocalizationTest("bogus", "en"); |
| } |
| |
| // Tests that mutiples requests for make-and-model resolution can be fulfilled |
| // simultaneously. |
| TEST_F(PpdProviderTest, RepeatedMakeModel) { |
| StartFakePpdServer(); |
| auto provider = CreateProvider("en"); |
| |
| PpdProvider::PrinterSearchData unrecognized_printer; |
| unrecognized_printer.make_and_model = {"Printer Printer"}; |
| |
| PpdProvider::PrinterSearchData recognized_printer; |
| recognized_printer.make_and_model = {"printer_a_ref"}; |
| |
| PpdProvider::PrinterSearchData mixed; |
| mixed.make_and_model = {"printer_a_ref", "Printer Printer"}; |
| |
| // Resolve the same thing repeatedly. |
| provider->ResolvePpdReference( |
| unrecognized_printer, |
| base::Bind(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| provider->ResolvePpdReference( |
| mixed, base::Bind(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| provider->ResolvePpdReference( |
| recognized_printer, |
| base::Bind(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| scoped_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].first); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_references_[1].first); |
| EXPECT_EQ( |
| "printer_a_ref", |
| captured_resolve_ppd_references_[1].second.effective_make_and_model); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_references_[2].first); |
| EXPECT_EQ( |
| "printer_a_ref", |
| captured_resolve_ppd_references_[2].second.effective_make_and_model); |
| } |
| |
| // Test successful and unsuccessful usb resolutions. |
| TEST_F(PpdProviderTest, UsbResolution) { |
| StartFakePpdServer(); |
| auto provider = CreateProvider("en"); |
| |
| PpdProvider::PrinterSearchData search_data; |
| |
| // Should get back "Some canonical reference" |
| search_data.usb_vendor_id = 0x031f; |
| search_data.usb_product_id = 1592; |
| provider->ResolvePpdReference( |
| search_data, base::Bind(&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::Bind(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| |
| // Extant vendor id, nonexistant device id, should get a NOT_FOUND |
| search_data.usb_vendor_id = 0x031f; |
| search_data.usb_product_id = 8162; |
| provider->ResolvePpdReference( |
| search_data, base::Bind(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| |
| // Nonexistant vendor id, should get a NOT_FOUND in the real world, but |
| // the URL interceptor we're using considers all nonexistant files to |
| // be effectively CONNECTION REFUSED, so we just check for non-success |
| // on this one. |
| search_data.usb_vendor_id = 1234; |
| search_data.usb_product_id = 1782; |
| provider->ResolvePpdReference( |
| search_data, base::Bind(&PpdProviderTest::CaptureResolvePpdReference, |
| base::Unretained(this))); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(captured_resolve_ppd_references_.size(), static_cast<size_t>(4)); |
| EXPECT_EQ(captured_resolve_ppd_references_[0].first, PpdProvider::SUCCESS); |
| EXPECT_EQ(captured_resolve_ppd_references_[0].second.effective_make_and_model, |
| "Some canonical reference"); |
| EXPECT_EQ(captured_resolve_ppd_references_[1].first, PpdProvider::SUCCESS); |
| EXPECT_EQ(captured_resolve_ppd_references_[1].second.effective_make_and_model, |
| "Some other canonical reference"); |
| EXPECT_EQ(captured_resolve_ppd_references_[2].first, PpdProvider::NOT_FOUND); |
| EXPECT_FALSE(captured_resolve_ppd_references_[3].first == |
| PpdProvider::SUCCESS); |
| } |
| |
| // For convenience a null ResolveManufacturers callback target. |
| void ResolveManufacturersNop(PpdProvider::CallbackResultCode code, |
| const std::vector<std::string>& v) {} |
| |
| // 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) { |
| StartFakePpdServer(); |
| auto provider = CreateProvider("en"); |
| |
| // Grab the manufacturer list, but don't bother to save it, we know what |
| // should be in it and we check that elsewhere. We just need to run the |
| // resolve to populate the internal PpdProvider structures. |
| provider->ResolveManufacturers(base::Bind(&ResolveManufacturersNop)); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| provider->ResolvePrinters("manufacturer_a_en", |
| base::Bind(&PpdProviderTest::CaptureResolvePrinters, |
| base::Unretained(this))); |
| provider->ResolvePrinters("manufacturer_b_en", |
| base::Bind(&PpdProviderTest::CaptureResolvePrinters, |
| base::Unretained(this))); |
| scoped_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].first); |
| EXPECT_EQ("printer_a_ref", capture0[0].second.effective_make_and_model); |
| |
| EXPECT_EQ("printer_b", capture0[1].first); |
| EXPECT_EQ("printer_b_ref", capture0[1].second.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].first); |
| EXPECT_EQ("printer_c_ref", capture1[0].second.effective_make_and_model); |
| } |
| |
| // Test that if we give a bad reference to ResolvePrinters(), we get an |
| // INTERNAL_ERROR. |
| TEST_F(PpdProviderTest, ResolvePrintersBadReference) { |
| StartFakePpdServer(); |
| auto provider = CreateProvider("en"); |
| provider->ResolveManufacturers(base::Bind(&ResolveManufacturersNop)); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| provider->ResolvePrinters("bogus_doesnt_exist", |
| base::Bind(&PpdProviderTest::CaptureResolvePrinters, |
| base::Unretained(this))); |
| scoped_task_environment_.RunUntilIdle(); |
| ASSERT_EQ(1UL, captured_resolve_printers_.size()); |
| EXPECT_EQ(PpdProvider::INTERNAL_ERROR, captured_resolve_printers_[0].first); |
| } |
| |
| // Test that if the server is unavailable, we get SERVER_ERRORs back out. |
| TEST_F(PpdProviderTest, ResolvePrintersNoServer) { |
| StartFakePpdServer(); |
| auto provider = CreateProvider("en"); |
| provider->ResolveManufacturers(base::Bind(&ResolveManufacturersNop)); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| StopFakePpdServer(); |
| |
| provider->ResolvePrinters("manufacturer_a_en", |
| base::Bind(&PpdProviderTest::CaptureResolvePrinters, |
| base::Unretained(this))); |
| provider->ResolvePrinters("manufacturer_b_en", |
| base::Bind(&PpdProviderTest::CaptureResolvePrinters, |
| base::Unretained(this))); |
| scoped_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) { |
| StartFakePpdServer(); |
| auto provider = CreateProvider("en"); |
| Printer::PpdReference ref; |
| ref.effective_make_and_model = "printer_b_ref"; |
| provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| ref.effective_make_and_model = "printer_c_ref"; |
| provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(2UL, captured_resolve_ppd_.size()); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); |
| EXPECT_EQ(kCupsFilter2PpdContents, captured_resolve_ppd_[0].ppd_contents); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[1].code); |
| EXPECT_EQ("c", captured_resolve_ppd_[1].ppd_contents); |
| } |
| |
| // 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) { |
| StartFakePpdServer(); |
| auto provider = CreateProvider("en"); |
| |
| Printer::PpdReference ref; |
| ref.user_supplied_ppd_url = base::StringPrintf( |
| "https://%s/user_supplied_ppd_directory/user_supplied.ppd", kPpdServer); |
| provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| scoped_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"); |
| 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_EQ(base::WriteFile(filename, user_ppd_contents.data(), |
| user_ppd_contents.size()), |
| static_cast<int>(user_ppd_contents.size())); |
| |
| Printer::PpdReference ref; |
| ref.user_supplied_ppd_url = |
| base::StringPrintf("file://%s", filename.MaybeAsASCII().c_str()); |
| provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| scoped_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"); |
| 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_EQ(base::WriteFile(filename, user_ppd_contents.data(), |
| user_ppd_contents.size()), |
| static_cast<int>(user_ppd_contents.size())); |
| |
| ref.user_supplied_ppd_url = |
| base::StringPrintf("file://%s", filename.MaybeAsASCII().c_str()); |
| provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| scoped_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"); |
| |
| // Re-resolve. |
| provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| scoped_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 the filter extraction code successfully pulls the filters |
| // from the ppds resolved. |
| TEST_F(PpdProviderTest, ExtractPpdFilters) { |
| StartFakePpdServer(); |
| auto provider = CreateProvider("en"); |
| Printer::PpdReference ref; |
| ref.effective_make_and_model = "printer_a_ref"; |
| provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| ref.effective_make_and_model = "printer_b_ref"; |
| provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, |
| base::Unretained(this))); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| ASSERT_EQ(2UL, captured_resolve_ppd_.size()); |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); |
| EXPECT_EQ(kCupsFilterPpdContents, captured_resolve_ppd_[0].ppd_contents); |
| EXPECT_EQ( |
| std::vector<std::string>({"a_different_filter", "filter3", "my_filter"}), |
| captured_resolve_ppd_[0].ppd_filters); |
| |
| EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[1].code); |
| EXPECT_EQ(kCupsFilter2PpdContents, captured_resolve_ppd_[1].ppd_contents); |
| EXPECT_EQ( |
| std::vector<std::string>({"another_real_filter", "the_real_filter"}), |
| captured_resolve_ppd_[1].ppd_filters); |
| } |
| |
| // Verifies that we can extract the Manufacturer and Model selectison for a |
| // given effective make and model. |
| TEST_F(PpdProviderTest, ReverseLookup) { |
| StartFakePpdServer(); |
| auto provider = CreateProvider("en"); |
| std::string ref = "printer_a_ref"; |
| provider->ReverseLookup(ref, |
| base::Bind(&PpdProviderTest::CaptureReverseLookup, |
| base::Unretained(this))); |
| // TODO(skau): PpdProvider has a race condition that prevents running these |
| // requests in parallel. |
| scoped_task_environment_.RunUntilIdle(); |
| |
| std::string ref_fail = "printer_does_not_exist"; |
| provider->ReverseLookup(ref_fail, |
| base::Bind(&PpdProviderTest::CaptureReverseLookup, |
| base::Unretained(this))); |
| scoped_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); |
| } |
| |
| } // namespace |
| } // namespace chromeos |