| OLD | NEW | 
|    1 // Copyright 2016 The Chromium Authors. All rights reserved. |    1 // Copyright 2016 The Chromium Authors. All rights reserved. | 
|    2 // Use of this source code is governed by a BSD-style license that can be |    2 // Use of this source code is governed by a BSD-style license that can be | 
|    3 // found in the LICENSE file. |    3 // found in the LICENSE file. | 
|    4  |    4  | 
 |    5 #include <map> | 
 |    6 #include <utility> | 
 |    7  | 
|    5 #include "base/bind.h" |    8 #include "base/bind.h" | 
 |    9 #include "base/bind_helpers.h" | 
|    6 #include "base/files/file_util.h" |   10 #include "base/files/file_util.h" | 
|    7 #include "base/files/scoped_temp_dir.h" |   11 #include "base/files/scoped_temp_dir.h" | 
|    8 #include "base/logging.h" |   12 #include "base/logging.h" | 
|    9 #include "base/message_loop/message_loop.h" |   13 #include "base/message_loop/message_loop.h" | 
|   10 #include "base/run_loop.h" |   14 #include "base/run_loop.h" | 
|   11 #include "base/single_thread_task_runner.h" |   15 #include "base/single_thread_task_runner.h" | 
|   12 #include "base/strings/stringprintf.h" |   16 #include "base/strings/stringprintf.h" | 
 |   17 #include "base/test/test_message_loop.h" | 
 |   18 #include "base/threading/sequenced_task_runner_handle.h" | 
|   13 #include "base/threading/thread_task_runner_handle.h" |   19 #include "base/threading/thread_task_runner_handle.h" | 
|   14 #include "chromeos/chromeos_paths.h" |   20 #include "chromeos/chromeos_paths.h" | 
|   15 #include "chromeos/printing/ppd_cache.h" |   21 #include "chromeos/printing/ppd_cache.h" | 
|   16 #include "chromeos/printing/ppd_provider.h" |   22 #include "chromeos/printing/ppd_provider.h" | 
|   17 #include "net/url_request/test_url_request_interceptor.h" |   23 #include "net/url_request/test_url_request_interceptor.h" | 
|   18 #include "net/url_request/url_request_test_util.h" |   24 #include "net/url_request/url_request_test_util.h" | 
|   19 #include "testing/gtest/include/gtest/gtest.h" |   25 #include "testing/gtest/include/gtest/gtest.h" | 
|   20  |   26  | 
|   21 namespace chromeos { |   27 namespace chromeos { | 
|   22 namespace printing { |   28 namespace printing { | 
 |   29  | 
|   23 namespace { |   30 namespace { | 
|   24  |   31  | 
|   25 const char kTestQuirksServer[] = "bogusserver.bogus.com"; |   32 // Name of the fake server we're resolving ppds from. | 
|   26 const char kTestAPIKey[] = "BOGUSAPIKEY"; |   33 const char kPpdServer[] = "bogus.google.com"; | 
|   27 const char kLocalPpdUrl[] = "/some/path"; |  | 
|   28 const char kTestManufacturer[] = "Bogus Printer Corp"; |  | 
|   29 const char kTestModel[] = "MegaPrint 9000"; |  | 
|   30 // The compressedPPD contents here comes from running the command |  | 
|   31 // echo -n "This is the quirks ppd" | base64 |  | 
|   32 const char kQuirksResponse[] = |  | 
|   33     "{\n" |  | 
|   34     "  \"compressedPpd\": \"VGhpcyBpcyB0aGUgcXVpcmtzIHBwZA==\",\n" |  | 
|   35     "  \"lastUpdatedTime\": \"1\"\n" |  | 
|   36     "}\n"; |  | 
|   37 const char kQuirksPpd[] = "This is the quirks ppd"; |  | 
|   38  |  | 
|   39 // A well-formatted response for a list of ppds from quirks server.  This |  | 
|   40 // corresponds to the AvailablePrintersMap returned by QuirksPrinters() below. |  | 
|   41 const char kQuirksListResponse[] = |  | 
|   42     "{\n" |  | 
|   43     "  \"manufacturers\": [\n" |  | 
|   44     "    {\n" |  | 
|   45     "      \"manufacturer\": \"manu_a\",\n" |  | 
|   46     "      \"models\": [\n" |  | 
|   47     "         \"model_1\",\n" |  | 
|   48     "         \"model_2\"\n" |  | 
|   49     "      ]\n" |  | 
|   50     "    },\n" |  | 
|   51     "    {\n" |  | 
|   52     "      \"manufacturer\": \"manu_b\",\n" |  | 
|   53     "      \"models\": [\n" |  | 
|   54     "         \"model_3\",\n" |  | 
|   55     "         \"model_1\"\n" |  | 
|   56     "      ]\n" |  | 
|   57     "    }\n" |  | 
|   58     "  ]\n" |  | 
|   59     "}\n"; |  | 
|   60  |  | 
|   61 // Return an AvailablePrintersMap that matches what's in kQuirksListResponse. |  | 
|   62 PpdProvider::AvailablePrintersMap QuirksPrinters() { |  | 
|   63   return {{"manu_a", {"model_1", "model_2"}}, |  | 
|   64           {"manu_b", {"model_3", "model_1"}}}; |  | 
|   65 } |  | 
|   66  |   34  | 
|   67 class PpdProviderTest : public ::testing::Test { |   35 class PpdProviderTest : public ::testing::Test { | 
|   68  public: |   36  public: | 
|   69   PpdProviderTest() |   37   PpdProviderTest() | 
|   70       : loop_(base::MessageLoop::TYPE_IO), |   38       : loop_(base::MessageLoop::TYPE_IO), | 
|   71         request_context_getter_( |   39         request_context_getter_(new net::TestURLRequestContextGetter( | 
|   72             new net::TestURLRequestContextGetter(loop_.task_runner().get())) {} |   40             base::MessageLoop::current()->task_runner())) {} | 
|   73  |   41  | 
|   74   void SetUp() override { |   42   void SetUp() override { | 
|   75     ASSERT_TRUE(ppd_cache_temp_dir_.CreateUniqueTempDir()); |   43     ASSERT_TRUE(ppd_cache_temp_dir_.CreateUniqueTempDir()); | 
 |   44   } | 
 |   45  | 
 |   46   void TearDown() override { StopFakePpdServer(); } | 
 |   47  | 
 |   48   // Create and return a provider for a test that uses the given |locale|. | 
 |   49   scoped_refptr<PpdProvider> CreateProvider(const std::string& locale) { | 
|   76     auto provider_options = PpdProvider::Options(); |   50     auto provider_options = PpdProvider::Options(); | 
|   77     provider_options.quirks_server = kTestQuirksServer; |   51     provider_options.ppd_server_root = std::string("https://") + kPpdServer; | 
|   78     ppd_provider_ = PpdProvider::Create( |   52  | 
|   79         kTestAPIKey, request_context_getter_.get(), loop_.task_runner().get(), |   53     return PpdProvider::Create( | 
|   80         PpdCache::Create(ppd_cache_temp_dir_.GetPath()), provider_options); |   54         locale, request_context_getter_.get(), | 
|   81   } |   55         PpdCache::Create(ppd_cache_temp_dir_.GetPath(), | 
 |   56                          base::MessageLoop::current()->task_runner()), | 
 |   57         loop_.task_runner().get(), provider_options); | 
 |   58   } | 
 |   59  | 
 |   60   // Create an interceptor that serves a small fileset of ppd server files. | 
 |   61   void StartFakePpdServer() { | 
 |   62     ASSERT_TRUE(interceptor_temp_dir_.CreateUniqueTempDir()); | 
 |   63     interceptor_ = base::MakeUnique<net::TestURLRequestInterceptor>( | 
 |   64         "https", kPpdServer, base::ThreadTaskRunnerHandle::Get(), | 
 |   65         base::ThreadTaskRunnerHandle::Get()); | 
 |   66     // Use brace initialization to express the desired server contents as "url", | 
 |   67     // "contents" pairs. | 
 |   68     std::vector<std::pair<std::string, std::string>> server_contents = { | 
 |   69         {"metadata/locales.json", | 
 |   70          R"(["en", | 
 |   71              "es-mx", | 
 |   72              "en-gb"])"}, | 
 |   73         {"metadata/index.json", | 
 |   74          R"([ | 
 |   75              ["printer_a_ref", "printer_a.ppd"], | 
 |   76              ["printer_b_ref", "printer_b.ppd"], | 
 |   77              ["printer_c_ref", "printer_c.ppd"] | 
 |   78             ])"}, | 
 |   79         {"metadata/manufacturers-en.json", | 
 |   80          R"([ | 
 |   81             ["manufacturer_a_en", "manufacturer_a.json"], | 
 |   82             ["manufacturer_b_en", "manufacturer_b.json"] | 
 |   83             ])"}, | 
 |   84         {"metadata/manufacturers-en-gb.json", | 
 |   85          R"([ | 
 |   86             ["manufacturer_a_en-gb", "manufacturer_a.json"], | 
 |   87             ["manufacturer_b_en-gb", "manufacturer_b.json"] | 
 |   88             ])"}, | 
 |   89         {"metadata/manufacturers-es-mx.json", | 
 |   90          R"([ | 
 |   91             ["manufacturer_a_es-mx", "manufacturer_a.json"], | 
 |   92             ["manufacturer_b_es-mx", "manufacturer_b.json"] | 
 |   93             ])"}, | 
 |   94         {"metadata/manufacturer_a.json", | 
 |   95          R"([ | 
 |   96             ["printer_a", "printer_a_ref"], | 
 |   97             ["printer_b", "printer_b_ref"] | 
 |   98             ])"}, | 
 |   99         {"metadata/manufacturer_b.json", | 
 |  100          R"([ | 
 |  101             ["printer_c", "printer_c_ref"] | 
 |  102             ])"}, | 
 |  103         {"ppds/printer_a.ppd", "a"}, | 
 |  104         {"ppds/printer_b.ppd", "b"}, | 
 |  105         {"ppds/printer_c.ppd", "c"}, | 
 |  106         {"user_supplied_ppd_directory/user_supplied.ppd", "u"}}; | 
 |  107     int next_file_num = 0; | 
 |  108     for (const auto& entry : server_contents) { | 
 |  109       base::FilePath filename = interceptor_temp_dir_.GetPath().Append( | 
 |  110           base::StringPrintf("%d.json", next_file_num++)); | 
 |  111       ASSERT_EQ( | 
 |  112           base::WriteFile(filename, entry.second.data(), entry.second.size()), | 
 |  113           static_cast<int>(entry.second.size())) | 
 |  114           << "Failed to write temp server file"; | 
 |  115       interceptor_->SetResponse( | 
 |  116           GURL(base::StringPrintf("https://%s/%s", kPpdServer, | 
 |  117                                   entry.first.c_str())), | 
 |  118           filename); | 
 |  119     } | 
 |  120   } | 
 |  121  | 
 |  122   // Interceptor posts a *task* during destruction that actually unregisters | 
 |  123   // things.  So we have to run the message loop post-interceptor-destruction to | 
 |  124   // actually unregister the URLs, otherwise they won't *actually* be | 
 |  125   // unregistered until the next time we invoke the message loop.  Which may be | 
 |  126   // in the middle of the next test. | 
 |  127   // | 
 |  128   // Note this is harmless to call if we haven't started a fake ppd server. | 
 |  129   void StopFakePpdServer() { | 
 |  130     interceptor_.reset(); | 
 |  131     base::RunLoop().RunUntilIdle(); | 
 |  132   } | 
 |  133  | 
 |  134   // Capture the result of a ResolveManufacturers() call. | 
 |  135   void CaptureResolveManufacturers(PpdProvider::CallbackResultCode code, | 
 |  136                                    const std::vector<std::string>& data) { | 
 |  137     captured_resolve_manufacturers_.push_back({code, data}); | 
 |  138   } | 
 |  139  | 
 |  140   // Capture the result of a ResolvePrinters() call. | 
 |  141   void CaptureResolvePrinters(PpdProvider::CallbackResultCode code, | 
 |  142                               const std::vector<std::string>& data) { | 
 |  143     captured_resolve_printers_.push_back({code, data}); | 
 |  144   } | 
 |  145  | 
 |  146   // Capture the result of a ResolvePpd() call. | 
 |  147   void CaptureResolvePpd(PpdProvider::CallbackResultCode code, | 
 |  148                          const std::string& contents) { | 
 |  149     captured_resolve_ppd_.push_back({code, contents}); | 
 |  150   } | 
 |  151  | 
 |  152   // Discard the result of a ResolvePpd() call. | 
 |  153   void DiscardResolvePpd(PpdProvider::CallbackResultCode code, | 
 |  154                          const std::string& contents) {} | 
|   82  |  155  | 
|   83  protected: |  156  protected: | 
 |  157   // Run a ResolveManufacturers run from the given locale, expect to get | 
 |  158   // results in expected_used_locale. | 
 |  159   void RunLocalizationTest(const std::string& browser_locale, | 
 |  160                            const std::string& expected_used_locale) { | 
 |  161     captured_resolve_manufacturers_.clear(); | 
 |  162     auto provider = CreateProvider(browser_locale); | 
 |  163     provider->ResolveManufacturers(base::Bind( | 
 |  164         &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); | 
 |  165     base::RunLoop().RunUntilIdle(); | 
 |  166     provider = nullptr; | 
 |  167     ASSERT_EQ(captured_resolve_manufacturers_.size(), 1UL); | 
 |  168     EXPECT_EQ(captured_resolve_manufacturers_[0].first, PpdProvider::SUCCESS); | 
 |  169  | 
 |  170     const auto& result_vec = captured_resolve_manufacturers_[0].second; | 
 |  171  | 
 |  172     // It's sufficient to check for one of the expected locale keys to make sure | 
 |  173     // we got the right map. | 
 |  174     EXPECT_FALSE(std::find(result_vec.begin(), result_vec.end(), | 
 |  175                            "manufacturer_a_" + expected_used_locale) == | 
 |  176                  result_vec.end()); | 
 |  177   } | 
 |  178  | 
 |  179   // Drain tasks both on the loop we use for network/disk activity and the | 
 |  180   // top-level loop that we're using in the test itself.  Unfortunately, even | 
 |  181   // thought the TestURLRequestContextGetter tells the url fetcher to run on the | 
 |  182   // current message loop, some deep backend processes can get put into other | 
 |  183   // loops, which means we can't just trust RunLoop::RunUntilIdle() to drain | 
 |  184   // outstanding work. | 
 |  185   void Drain(const PpdProvider& provider) { | 
 |  186     do { | 
 |  187       base::RunLoop().RunUntilIdle(); | 
 |  188     } while (!provider.Idle()); | 
 |  189   } | 
 |  190  | 
 |  191   // Message loop that runs on the current thread. | 
 |  192   base::TestMessageLoop loop_; | 
 |  193  | 
 |  194   std::vector< | 
 |  195       std::pair<PpdProvider::CallbackResultCode, std::vector<std::string>>> | 
 |  196       captured_resolve_manufacturers_; | 
 |  197  | 
 |  198   std::vector< | 
 |  199       std::pair<PpdProvider::CallbackResultCode, std::vector<std::string>>> | 
 |  200       captured_resolve_printers_; | 
 |  201  | 
 |  202   std::vector<std::pair<PpdProvider::CallbackResultCode, std::string>> | 
 |  203       captured_resolve_ppd_; | 
 |  204  | 
 |  205   std::unique_ptr<net::TestURLRequestInterceptor> interceptor_; | 
 |  206  | 
|   84   base::ScopedTempDir ppd_cache_temp_dir_; |  207   base::ScopedTempDir ppd_cache_temp_dir_; | 
 |  208   base::ScopedTempDir interceptor_temp_dir_; | 
|   85  |  209  | 
|   86   // Provider to be used in the test. |  210   // Provider to be used in the test. | 
|   87   std::unique_ptr<PpdProvider> ppd_provider_; |  211   scoped_refptr<PpdProvider> ppd_provider_; | 
|   88  |  212  | 
|   89   // Misc extra stuff needed for the test environment to function. |  213   // Misc extra stuff needed for the test environment to function. | 
|   90   base::MessageLoop loop_; |  214   //  base::TestMessageLoop loop_; | 
|   91   scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |  215   scoped_refptr<net::URLRequestContextGetter> request_context_getter_; | 
|   92 }; |  216 }; | 
|   93  |  217  | 
|   94 // Struct that just captures the callback result for a PpdProvider lookup and |  218 // Test that we get back manufacturer maps as expected. | 
|   95 // saves it for inspection by the test. |  219 TEST_F(PpdProviderTest, ManufacturersFetch) { | 
|   96 struct CapturedResolveResult { |  220   StartFakePpdServer(); | 
|   97   bool initialized = false; |  221   auto provider = CreateProvider("en"); | 
|   98   PpdProvider::CallbackResultCode result; |  222   // Issue two requests at the same time, both should be resolved properly. | 
|   99   base::FilePath file; |  223   provider->ResolveManufacturers(base::Bind( | 
|  100 }; |  224       &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); | 
|  101  |  225   provider->ResolveManufacturers(base::Bind( | 
|  102 // Callback for saving a resolve callback. |  226       &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); | 
|  103 void CaptureResolveResultCallback(CapturedResolveResult* capture, |  227   Drain(*provider); | 
|  104                                   PpdProvider::CallbackResultCode result, |  228   ASSERT_EQ(2UL, captured_resolve_manufacturers_.size()); | 
|  105                                   base::FilePath file) { |  229   std::vector<std::string> expected_result( | 
|  106   capture->initialized = true; |  230       {"manufacturer_a_en", "manufacturer_b_en"}); | 
|  107   capture->result = result; |  231   EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_manufacturers_[0].first); | 
|  108   capture->file = file; |  232   EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_manufacturers_[1].first); | 
|  109 } |  233   EXPECT_TRUE(captured_resolve_manufacturers_[0].second == expected_result); | 
|  110  |  234   EXPECT_TRUE(captured_resolve_manufacturers_[1].second == expected_result); | 
|  111 // For a resolve result that should end up successful, check that it is |  235 } | 
|  112 // successful and the contents are expected_contents. |  236  | 
|  113 void CheckResolveSuccessful(const CapturedResolveResult& captured, |  237 // Test that we get a reasonable error when we have no server to contact.  Tis | 
|  114                             const std::string& expected_contents) { |  238 // is almost exactly the same as the above test, we just don't bring up the fake | 
|  115   ASSERT_TRUE(captured.initialized); |  239 // server first. | 
|  116   EXPECT_EQ(PpdProvider::SUCCESS, captured.result); |  240 TEST_F(PpdProviderTest, ManufacturersFetchNoServer) { | 
|  117  |  241   auto provider = CreateProvider("en"); | 
|  118   std::string contents; |  242   // Issue two requests at the same time, both should be resolved properly. | 
|  119   ASSERT_TRUE(base::ReadFileToString(captured.file, &contents)); |  243   provider->ResolveManufacturers(base::Bind( | 
|  120   EXPECT_EQ(expected_contents, contents); |  244       &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); | 
|  121 } |  245   provider->ResolveManufacturers(base::Bind( | 
|  122  |  246       &PpdProviderTest::CaptureResolveManufacturers, base::Unretained(this))); | 
|  123 // Resolve a PPD via the quirks server. |  247   Drain(*provider); | 
|  124 TEST_F(PpdProviderTest, QuirksServerResolve) { |  248   ASSERT_EQ(2UL, captured_resolve_manufacturers_.size()); | 
 |  249   EXPECT_EQ(PpdProvider::SERVER_ERROR, | 
 |  250             captured_resolve_manufacturers_[0].first); | 
 |  251   EXPECT_EQ(PpdProvider::SERVER_ERROR, | 
 |  252             captured_resolve_manufacturers_[1].first); | 
 |  253   EXPECT_TRUE(captured_resolve_manufacturers_[0].second.empty()); | 
 |  254   EXPECT_TRUE(captured_resolve_manufacturers_[1].second.empty()); | 
 |  255 } | 
 |  256  | 
 |  257 // Test that we get things in the requested locale, and that fallbacks are sane. | 
 |  258 TEST_F(PpdProviderTest, LocalizationAndFallbacks) { | 
 |  259   StartFakePpdServer(); | 
 |  260   RunLocalizationTest("en-gb", "en-gb"); | 
 |  261   RunLocalizationTest("en-blah", "en"); | 
 |  262   RunLocalizationTest("en-gb-foo", "en-gb"); | 
 |  263   RunLocalizationTest("es", "es-mx"); | 
 |  264   RunLocalizationTest("bogus", "en"); | 
 |  265 } | 
 |  266  | 
 |  267 // For convenience a null ResolveManufacturers callback target. | 
 |  268 void ResolveManufacturersNop(PpdProvider::CallbackResultCode code, | 
 |  269                              const std::vector<std::string>& v) {} | 
 |  270  | 
 |  271 // Test basic ResolvePrinters() functionality.  At the same time, make | 
 |  272 // sure we can get the PpdReference for each of the resolved printers. | 
 |  273 TEST_F(PpdProviderTest, ResolvePrinters) { | 
 |  274   StartFakePpdServer(); | 
 |  275   auto provider = CreateProvider("en"); | 
 |  276  | 
 |  277   // Grab the manufacturer list, but don't bother to save it, we know what | 
 |  278   // should be in it and we check that elsewhere.  We just need to run the | 
 |  279   // resolve to populate the internal PpdProvider structures. | 
 |  280   provider->ResolveManufacturers(base::Bind(&ResolveManufacturersNop)); | 
 |  281   Drain(*provider); | 
 |  282  | 
 |  283   provider->ResolvePrinters("manufacturer_a_en", | 
 |  284                             base::Bind(&PpdProviderTest::CaptureResolvePrinters, | 
 |  285                                        base::Unretained(this))); | 
 |  286   provider->ResolvePrinters("manufacturer_b_en", | 
 |  287                             base::Bind(&PpdProviderTest::CaptureResolvePrinters, | 
 |  288                                        base::Unretained(this))); | 
 |  289   Drain(*provider); | 
 |  290   ASSERT_EQ(2UL, captured_resolve_printers_.size()); | 
 |  291   EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_printers_[0].first); | 
 |  292   EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_printers_[1].first); | 
 |  293   EXPECT_EQ(2UL, captured_resolve_printers_[0].second.size()); | 
 |  294   EXPECT_EQ(std::vector<std::string>({"printer_a", "printer_b"}), | 
 |  295             captured_resolve_printers_[0].second); | 
 |  296   EXPECT_EQ(std::vector<std::string>({"printer_c"}), | 
 |  297             captured_resolve_printers_[1].second); | 
 |  298  | 
 |  299   // We have manufacturers and models, we should be able to get a ppd out of | 
 |  300   // this. | 
 |  301   Printer::PpdReference ref; | 
 |  302   ASSERT_TRUE( | 
 |  303       provider->GetPpdReference("manufacturer_a_en", "printer_b", &ref)); | 
 |  304 } | 
 |  305  | 
 |  306 // Test that if we give a bad reference to ResolvePrinters(), we get an | 
 |  307 // INTERNAL_ERROR. | 
 |  308 TEST_F(PpdProviderTest, ResolvePrintersBadReference) { | 
 |  309   StartFakePpdServer(); | 
 |  310   auto provider = CreateProvider("en"); | 
 |  311   provider->ResolveManufacturers(base::Bind(&ResolveManufacturersNop)); | 
 |  312   Drain(*provider); | 
 |  313  | 
 |  314   provider->ResolvePrinters("bogus_doesnt_exist", | 
 |  315                             base::Bind(&PpdProviderTest::CaptureResolvePrinters, | 
 |  316                                        base::Unretained(this))); | 
 |  317   Drain(*provider); | 
 |  318   ASSERT_EQ(1UL, captured_resolve_printers_.size()); | 
 |  319   EXPECT_EQ(PpdProvider::INTERNAL_ERROR, captured_resolve_printers_[0].first); | 
 |  320 } | 
 |  321  | 
 |  322 // Test that if the server is unavailable, we get SERVER_ERRORs back out. | 
 |  323 TEST_F(PpdProviderTest, ResolvePrintersNoServer) { | 
 |  324   StartFakePpdServer(); | 
 |  325   auto provider = CreateProvider("en"); | 
 |  326   provider->ResolveManufacturers(base::Bind(&ResolveManufacturersNop)); | 
 |  327   Drain(*provider); | 
 |  328  | 
 |  329   StopFakePpdServer(); | 
 |  330  | 
 |  331   provider->ResolvePrinters("manufacturer_a_en", | 
 |  332                             base::Bind(&PpdProviderTest::CaptureResolvePrinters, | 
 |  333                                        base::Unretained(this))); | 
 |  334   provider->ResolvePrinters("manufacturer_b_en", | 
 |  335                             base::Bind(&PpdProviderTest::CaptureResolvePrinters, | 
 |  336                                        base::Unretained(this))); | 
 |  337   Drain(*provider); | 
 |  338   ASSERT_EQ(2UL, captured_resolve_printers_.size()); | 
 |  339   EXPECT_EQ(PpdProvider::SERVER_ERROR, captured_resolve_printers_[0].first); | 
 |  340   EXPECT_EQ(PpdProvider::SERVER_ERROR, captured_resolve_printers_[1].first); | 
 |  341 } | 
 |  342  | 
 |  343 // Test a successful ppd resolution from an effective_make_and_model reference. | 
 |  344 TEST_F(PpdProviderTest, ResolveServerKeyPpd) { | 
 |  345   StartFakePpdServer(); | 
 |  346   auto provider = CreateProvider("en"); | 
 |  347   Printer::PpdReference ref; | 
 |  348   ref.effective_make_and_model = "printer_b_ref"; | 
 |  349   provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, | 
 |  350                                        base::Unretained(this))); | 
 |  351   ref.effective_make_and_model = "printer_c_ref"; | 
 |  352   provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, | 
 |  353                                        base::Unretained(this))); | 
 |  354   Drain(*provider); | 
 |  355  | 
 |  356   ASSERT_EQ(2UL, captured_resolve_ppd_.size()); | 
 |  357   EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].first); | 
 |  358   EXPECT_EQ("b", captured_resolve_ppd_[0].second); | 
 |  359   EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[1].first); | 
 |  360   EXPECT_EQ("c", captured_resolve_ppd_[1].second); | 
 |  361 } | 
 |  362  | 
 |  363 // Test that we *don't* resolve a ppd URL over non-file schemes.  It's not clear | 
 |  364 // whether we'll want to do this in the long term, but for now this is | 
 |  365 // disallowed because we're not sure we completely understand the security | 
 |  366 // implications. | 
 |  367 TEST_F(PpdProviderTest, ResolveUserSuppliedUrlPpdFromNetworkFails) { | 
 |  368   StartFakePpdServer(); | 
 |  369   auto provider = CreateProvider("en"); | 
 |  370  | 
 |  371   Printer::PpdReference ref; | 
 |  372   ref.user_supplied_ppd_url = base::StringPrintf( | 
 |  373       "https://%s/user_supplied_ppd_directory/user_supplied.ppd", kPpdServer); | 
 |  374   provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, | 
 |  375                                        base::Unretained(this))); | 
 |  376   Drain(*provider); | 
 |  377  | 
 |  378   ASSERT_EQ(1UL, captured_resolve_ppd_.size()); | 
 |  379   EXPECT_EQ(PpdProvider::INTERNAL_ERROR, captured_resolve_ppd_[0].first); | 
 |  380   EXPECT_TRUE(captured_resolve_ppd_[0].second.empty()); | 
 |  381 } | 
 |  382  | 
 |  383 // Test a successful ppd resolution from a user_supplied_url field when | 
 |  384 // reading from a file.  Note we shouldn't need the server to be up | 
 |  385 // to do this successfully, as we should be able to do this offline. | 
 |  386 TEST_F(PpdProviderTest, ResolveUserSuppliedUrlPpdFromFile) { | 
 |  387   auto provider = CreateProvider("en"); | 
|  125   base::ScopedTempDir temp_dir; |  388   base::ScopedTempDir temp_dir; | 
|  126   ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |  389   ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 
|  127  |  390   base::FilePath filename = temp_dir.GetPath().Append("my_spiffy.ppd"); | 
|  128   Printer::PpdReference ppd_reference; |  391  | 
|  129   ppd_reference.effective_manufacturer = kTestManufacturer; |  392   std::string user_ppd_contents = "Woohoo"; | 
|  130   ppd_reference.effective_model = kTestModel; |  393  | 
|  131  |  394   ASSERT_EQ(base::WriteFile(filename, user_ppd_contents.data(), | 
|  132   { |  395                             user_ppd_contents.size()), | 
|  133     net::TestURLRequestInterceptor interceptor( |  396             static_cast<int>(user_ppd_contents.size())); | 
|  134         "https", kTestQuirksServer, base::ThreadTaskRunnerHandle::Get(), |  397  | 
|  135         base::ThreadTaskRunnerHandle::Get()); |  398   Printer::PpdReference ref; | 
|  136  |  399   ref.user_supplied_ppd_url = | 
|  137     GURL expected_url(base::StringPrintf( |  400       base::StringPrintf("file://%s", filename.MaybeAsASCII().c_str()); | 
|  138         "https://%s/v2/printer/manufacturers/%s/models/%s?key=%s", |  401   provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, | 
|  139         kTestQuirksServer, kTestManufacturer, kTestModel, kTestAPIKey)); |  402                                        base::Unretained(this))); | 
|  140  |  403   Drain(*provider); | 
|  141     base::FilePath contents_path = temp_dir.GetPath().Append("response"); |  404  | 
|  142     std::string contents = kQuirksResponse; |  405   ASSERT_EQ(1UL, captured_resolve_ppd_.size()); | 
|  143     int bytes_written = |  406   EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].first); | 
|  144         base::WriteFile(contents_path, contents.data(), contents.size()); |  407   EXPECT_EQ(user_ppd_contents, captured_resolve_ppd_[0].second); | 
|  145     ASSERT_EQ(bytes_written, static_cast<int>(contents.size())); |  408 } | 
|  146  |  409  | 
|  147     interceptor.SetResponse(expected_url, contents_path); |  410 // Test that we cache ppd resolutions when we fetch them and that we can resolve | 
|  148  |  411 // from the cache without the server available. | 
|  149     CapturedResolveResult captured; |  412 TEST_F(PpdProviderTest, ResolvedPpdsGetCached) { | 
|  150     ppd_provider_->Resolve(ppd_reference, |  413   auto provider = CreateProvider("en"); | 
|  151                            base::Bind(CaptureResolveResultCallback, &captured)); |  414   std::string user_ppd_contents = "Woohoo"; | 
|  152     base::RunLoop().RunUntilIdle(); |  415   Printer::PpdReference ref; | 
|  153     CheckResolveSuccessful(captured, kQuirksPpd); |  | 
|  154   } |  | 
|  155  |  | 
|  156   // Now that the interceptor is out of scope, re-run the query.  We should |  | 
|  157   // hit in the cache, and thus *not* re-run the query. |  | 
|  158   CapturedResolveResult captured; |  | 
|  159   ppd_provider_->Resolve(ppd_reference, |  | 
|  160                          base::Bind(CaptureResolveResultCallback, &captured)); |  | 
|  161   base::RunLoop().RunUntilIdle(); |  | 
|  162   CheckResolveSuccessful(captured, kQuirksPpd); |  | 
|  163 } |  | 
|  164  |  | 
|  165 // Test storage and retrieval of PPDs that are added manually.  Even though we |  | 
|  166 // supply a manufacturer and model, we should *not* hit the network for this |  | 
|  167 // resolution since we should find the stored version already cached. |  | 
|  168 TEST_F(PpdProviderTest, LocalResolve) { |  | 
|  169   Printer::PpdReference ppd_reference; |  | 
|  170   ppd_reference.user_supplied_ppd_url = kLocalPpdUrl; |  | 
|  171   ppd_reference.effective_manufacturer = kTestManufacturer; |  | 
|  172   ppd_reference.effective_model = kTestModel; |  | 
|  173  |  | 
|  174   // Initially, should not resolve. |  | 
|  175   { |  | 
|  176     CapturedResolveResult captured; |  | 
|  177     ppd_provider_->Resolve(ppd_reference, |  | 
|  178                            base::Bind(CaptureResolveResultCallback, &captured)); |  | 
|  179     base::RunLoop().RunUntilIdle(); |  | 
|  180     EXPECT_TRUE(captured.initialized); |  | 
|  181     EXPECT_EQ(PpdProvider::NOT_FOUND, captured.result); |  | 
|  182   } |  | 
|  183  |  | 
|  184   // Store a local ppd. |  | 
|  185   const std::string kLocalPpdContents("My local ppd contents"); |  | 
|  186   { |  416   { | 
|  187     base::ScopedTempDir temp_dir; |  417     base::ScopedTempDir temp_dir; | 
|  188     ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |  418     ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 
|  189  |  419     base::FilePath filename = temp_dir.GetPath().Append("my_spiffy.ppd"); | 
|  190     base::FilePath local_ppd_path = temp_dir.GetPath().Append("local_ppd"); |  420  | 
|  191     ASSERT_EQ(base::WriteFile(local_ppd_path, kLocalPpdContents.data(), |  421     ASSERT_EQ(base::WriteFile(filename, user_ppd_contents.data(), | 
|  192                               kLocalPpdContents.size()), |  422                               user_ppd_contents.size()), | 
|  193               static_cast<int>(kLocalPpdContents.size())); |  423               static_cast<int>(user_ppd_contents.size())); | 
|  194     ASSERT_TRUE(ppd_provider_->CachePpd(ppd_reference, local_ppd_path)); |  424  | 
|  195   } |  425     ref.user_supplied_ppd_url = | 
|  196   // temp_dir should now be deleted, which helps make sure we actually latched a |  426         base::StringPrintf("file://%s", filename.MaybeAsASCII().c_str()); | 
|  197   // copy, not a reference. |  427     provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, | 
|  198  |  428                                          base::Unretained(this))); | 
|  199   // Retry the resove, should get the PPD back now. |  429     Drain(*provider); | 
|  200   { |  430  | 
|  201     CapturedResolveResult captured; |  431     ASSERT_EQ(1UL, captured_resolve_ppd_.size()); | 
|  202  |  432     EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].first); | 
|  203     ppd_provider_->Resolve(ppd_reference, |  433     EXPECT_EQ(user_ppd_contents, captured_resolve_ppd_[0].second); | 
|  204                            base::Bind(CaptureResolveResultCallback, &captured)); |  434   } | 
|  205     base::RunLoop().RunUntilIdle(); |  435   // ScopedTempDir goes out of scope, so the source file should now be | 
|  206     CheckResolveSuccessful(captured, kLocalPpdContents); |  436   // deleted.  But if we resolve again, we should hit the cache and | 
|  207   } |  437   // still be successful. | 
|  208 } |  438  | 
|  209  |  439   captured_resolve_ppd_.clear(); | 
|  210 // Run a query for the list of available printers. |  440  | 
|  211 TEST_F(PpdProviderTest, QueryAvailable) { |  441   // Recreate the provider to make sure we don't have any memory caches which | 
|  212   base::ScopedTempDir temp_dir; |  442   // would mask problems with disk persistence. | 
|  213   ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |  443   provider = CreateProvider("en"); | 
|  214   PpdProvider::CallbackResultCode result_code; |  444  | 
|  215   PpdProvider::AvailablePrintersMap available_printers; |  445   // Re-resolve. | 
|  216  |  446   provider->ResolvePpd(ref, base::Bind(&PpdProviderTest::CaptureResolvePpd, | 
|  217   // Define a callback that sets the above variables with the callback results. |  447                                        base::Unretained(this))); | 
|  218   // This would be cleaner with capture groups, but Bind disallows them in |  448   Drain(*provider); | 
|  219   // lambdas. |  449  | 
|  220   PpdProvider::QueryAvailableCallback query_callback = base::Bind( |  450   ASSERT_EQ(1UL, captured_resolve_ppd_.size()); | 
|  221       [](PpdProvider::CallbackResultCode* code_out, |  451   EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].first); | 
|  222          PpdProvider::AvailablePrintersMap* query_result_out, |  452   EXPECT_EQ(user_ppd_contents, captured_resolve_ppd_[0].second); | 
|  223          PpdProvider::CallbackResultCode code, |  | 
|  224          const PpdProvider::AvailablePrintersMap& query_result) { |  | 
|  225         *code_out = code; |  | 
|  226         *query_result_out = query_result; |  | 
|  227       }, |  | 
|  228       &result_code, &available_printers); |  | 
|  229  |  | 
|  230   { |  | 
|  231     net::TestURLRequestInterceptor interceptor( |  | 
|  232         "https", kTestQuirksServer, base::ThreadTaskRunnerHandle::Get(), |  | 
|  233         base::ThreadTaskRunnerHandle::Get()); |  | 
|  234  |  | 
|  235     GURL expected_url(base::StringPrintf("https://%s/v2/printer/list?key=%s", |  | 
|  236                                          kTestQuirksServer, kTestAPIKey)); |  | 
|  237  |  | 
|  238     base::FilePath contents_path = temp_dir.GetPath().Append("response"); |  | 
|  239     std::string contents = kQuirksListResponse; |  | 
|  240     int bytes_written = base::WriteFile(contents_path, kQuirksListResponse, |  | 
|  241                                         strlen(kQuirksListResponse)); |  | 
|  242     ASSERT_EQ(static_cast<int>(strlen(kQuirksListResponse)), bytes_written); |  | 
|  243  |  | 
|  244     interceptor.SetResponse(expected_url, contents_path); |  | 
|  245  |  | 
|  246     CapturedResolveResult captured; |  | 
|  247     ppd_provider_->QueryAvailable(query_callback); |  | 
|  248     base::RunLoop().RunUntilIdle(); |  | 
|  249     EXPECT_EQ(PpdProvider::SUCCESS, result_code); |  | 
|  250     EXPECT_EQ(QuirksPrinters(), available_printers); |  | 
|  251   } |  | 
|  252  |  | 
|  253   // Now that the interceptor is out of scope, re-run the query.  We should |  | 
|  254   // hit in the cache, and thus *not* re-run the query.  Reset the capture |  | 
|  255   // variables first. |  | 
|  256   result_code = PpdProvider::SERVER_ERROR; |  | 
|  257   available_printers.clear(); |  | 
|  258   ppd_provider_->QueryAvailable(query_callback); |  | 
|  259   base::RunLoop().RunUntilIdle(); |  | 
|  260   EXPECT_EQ(PpdProvider::SUCCESS, result_code); |  | 
|  261   EXPECT_EQ(QuirksPrinters(), available_printers); |  | 
|  262 } |  453 } | 
|  263  |  454  | 
|  264 }  // namespace |  455 }  // namespace | 
|  265 }  // namespace printing |  456 }  // namespace printing | 
|  266 }  // namespace chromeos |  457 }  // namespace chromeos | 
| OLD | NEW |