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