Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: components/update_client/update_checker_unittest.cc

Issue 1899043002: Implement ping_freshness for update_client. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@2704
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 "base/bind.h" 5 #include "base/bind.h"
6 #include "base/bind_helpers.h" 6 #include "base/bind_helpers.h"
7 #include "base/files/file_util.h" 7 #include "base/files/file_util.h"
8 #include "base/macros.h" 8 #include "base/macros.h"
9 #include "base/memory/ref_counted.h" 9 #include "base/memory/ref_counted.h"
10 #include "base/memory/scoped_ptr.h" 10 #include "base/memory/scoped_ptr.h"
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 crx_update_item.id = "jebgalgnebhfojomionfpkfelancnnkf"; 158 crx_update_item.id = "jebgalgnebhfojomionfpkfelancnnkf";
159 crx_update_item.component = crx_component; 159 crx_update_item.component = crx_component;
160 160
161 return crx_update_item; 161 return crx_update_item;
162 } 162 }
163 163
164 TEST_F(UpdateCheckerTest, UpdateCheckSuccess) { 164 TEST_F(UpdateCheckerTest, UpdateCheckSuccess) {
165 EXPECT_TRUE(post_interceptor_->ExpectRequest( 165 EXPECT_TRUE(post_interceptor_->ExpectRequest(
166 new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); 166 new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml")));
167 167
168 update_checker_ = UpdateChecker::Create(config_, *metadata_); 168 update_checker_ = UpdateChecker::Create(config_, metadata_.get());
169 169
170 CrxUpdateItem item(BuildCrxUpdateItem()); 170 CrxUpdateItem item(BuildCrxUpdateItem());
171 item.component.ap = "some_ap"; 171 item.component.ap = "some_ap";
172 std::vector<CrxUpdateItem*> items_to_check; 172 std::vector<CrxUpdateItem*> items_to_check;
173 items_to_check.push_back(&item); 173 items_to_check.push_back(&item);
174 174
175 update_checker_->CheckForUpdates( 175 update_checker_->CheckForUpdates(
176 items_to_check, "extra=\"params\"", 176 items_to_check, "extra=\"params\"",
177 base::Bind(&UpdateCheckerTest::UpdateCheckComplete, 177 base::Bind(&UpdateCheckerTest::UpdateCheckComplete,
178 base::Unretained(this))); 178 base::Unretained(this)));
179 179
180 RunThreads(); 180 RunThreads();
181 181
182 EXPECT_EQ(1, post_interceptor_->GetHitCount()) 182 EXPECT_EQ(1, post_interceptor_->GetHitCount())
183 << post_interceptor_->GetRequestsAsString(); 183 << post_interceptor_->GetRequestsAsString();
184 ASSERT_EQ(1, post_interceptor_->GetCount()) 184 ASSERT_EQ(1, post_interceptor_->GetCount())
185 << post_interceptor_->GetRequestsAsString(); 185 << post_interceptor_->GetRequestsAsString();
186 186
187 // Sanity check the request. 187 // Sanity check the request.
188 EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find( 188 EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
189 "request protocol=\"3.0\" extra=\"params\"")); 189 "request protocol=\"3.0\" extra=\"params\""));
190 // The request must not contain any "dlpref" in the default case. 190 // The request must not contain any "dlpref" in the default case.
191 EXPECT_EQ(string::npos, 191 EXPECT_EQ(string::npos,
192 post_interceptor_->GetRequests()[0].find(" dlpref=\"")); 192 post_interceptor_->GetRequests()[0].find(" dlpref=\""));
193 EXPECT_NE( 193 EXPECT_NE(
194 string::npos, 194 string::npos,
195 post_interceptor_->GetRequests()[0].find( 195 post_interceptor_->GetRequests()[0].find(
196 "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\" " 196 "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\" "
197 "brand=\"TEST\" ap=\"some_ap\"><updatecheck /><ping rd=\"-2\" />" 197 "brand=\"TEST\" ap=\"some_ap\"><updatecheck /><ping rd=\"-2\" "));
198 "<packages><package fp=\"fp1\"/></packages></app>")); 198 EXPECT_NE(string::npos,
199 post_interceptor_->GetRequests()[0].find(
200 "<packages><package fp=\"fp1\"/></packages></app>"));
199 201
200 EXPECT_NE(string::npos, 202 EXPECT_NE(string::npos,
201 post_interceptor_->GetRequests()[0].find("<hw physmemory=")); 203 post_interceptor_->GetRequests()[0].find("<hw physmemory="));
202 204
203 // Sanity check the arguments of the callback after parsing. 205 // Sanity check the arguments of the callback after parsing.
204 EXPECT_EQ(0, error_); 206 EXPECT_EQ(0, error_);
205 EXPECT_EQ(1ul, results_.list.size()); 207 EXPECT_EQ(1ul, results_.list.size());
206 EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", 208 EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf",
207 results_.list[0].extension_id.c_str()); 209 results_.list[0].extension_id.c_str());
208 EXPECT_STREQ("1.0", results_.list[0].manifest.version.c_str()); 210 EXPECT_STREQ("1.0", results_.list[0].manifest.version.c_str());
209 } 211 }
210 212
211 // Tests that an invalid "ap" is not serialized. 213 // Tests that an invalid "ap" is not serialized.
212 TEST_F(UpdateCheckerTest, UpdateCheckInvalidAp) { 214 TEST_F(UpdateCheckerTest, UpdateCheckInvalidAp) {
213 EXPECT_TRUE(post_interceptor_->ExpectRequest( 215 EXPECT_TRUE(post_interceptor_->ExpectRequest(
214 new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); 216 new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml")));
215 217
216 update_checker_ = UpdateChecker::Create(config_, *metadata_); 218 update_checker_ = UpdateChecker::Create(config_, metadata_.get());
217 219
218 CrxUpdateItem item(BuildCrxUpdateItem()); 220 CrxUpdateItem item(BuildCrxUpdateItem());
219 item.component.ap = std::string(257, 'a'); // Too long. 221 item.component.ap = std::string(257, 'a'); // Too long.
220 std::vector<CrxUpdateItem*> items_to_check; 222 std::vector<CrxUpdateItem*> items_to_check;
221 items_to_check.push_back(&item); 223 items_to_check.push_back(&item);
222 224
223 update_checker_->CheckForUpdates( 225 update_checker_->CheckForUpdates(
224 items_to_check, "", base::Bind(&UpdateCheckerTest::UpdateCheckComplete, 226 items_to_check, "", base::Bind(&UpdateCheckerTest::UpdateCheckComplete,
225 base::Unretained(this))); 227 base::Unretained(this)));
226 228
227 RunThreads(); 229 RunThreads();
228 230
229 EXPECT_NE( 231 EXPECT_NE(
230 string::npos, 232 string::npos,
231 post_interceptor_->GetRequests()[0].find( 233 post_interceptor_->GetRequests()[0].find(
232 "app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\" " 234 "app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\" "
233 "brand=\"TEST\"><updatecheck /><ping rd=\"-2\" />" 235 "brand=\"TEST\"><updatecheck /><ping rd=\"-2\" "));
234 "<packages><package fp=\"fp1\"/></packages></app>")); 236 EXPECT_NE(string::npos,
237 post_interceptor_->GetRequests()[0].find(
238 "<packages><package fp=\"fp1\"/></packages></app>"));
235 } 239 }
236 240
237 TEST_F(UpdateCheckerTest, UpdateCheckSuccessNoBrand) { 241 TEST_F(UpdateCheckerTest, UpdateCheckSuccessNoBrand) {
238 EXPECT_TRUE(post_interceptor_->ExpectRequest( 242 EXPECT_TRUE(post_interceptor_->ExpectRequest(
239 new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); 243 new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml")));
240 244
241 config_->SetBrand("TOOLONG"); // Sets an invalid brand code. 245 config_->SetBrand("TOOLONG"); // Sets an invalid brand code.
242 update_checker_ = UpdateChecker::Create(config_, *metadata_); 246 update_checker_ = UpdateChecker::Create(config_, metadata_.get());
243 247
244 CrxUpdateItem item(BuildCrxUpdateItem()); 248 CrxUpdateItem item(BuildCrxUpdateItem());
245 std::vector<CrxUpdateItem*> items_to_check; 249 std::vector<CrxUpdateItem*> items_to_check;
246 items_to_check.push_back(&item); 250 items_to_check.push_back(&item);
247 251
248 update_checker_->CheckForUpdates( 252 update_checker_->CheckForUpdates(
249 items_to_check, "", base::Bind(&UpdateCheckerTest::UpdateCheckComplete, 253 items_to_check, "", base::Bind(&UpdateCheckerTest::UpdateCheckComplete,
250 base::Unretained(this))); 254 base::Unretained(this)));
251 255
252 RunThreads(); 256 RunThreads();
253 257
254 EXPECT_NE( 258 EXPECT_NE(
255 string::npos, 259 string::npos,
256 post_interceptor_->GetRequests()[0].find( 260 post_interceptor_->GetRequests()[0].find(
257 "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">" 261 "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">"
258 "<updatecheck /><ping rd=\"-2\" /><packages><package fp=\"fp1\"/>" 262 "<updatecheck /><ping rd=\"-2\" "));
259 "</packages></app>")); 263 EXPECT_NE(string::npos,
264 post_interceptor_->GetRequests()[0].find(
265 "<packages><package fp=\"fp1\"/></packages></app>"));
260 } 266 }
261 267
262 // Simulates a 403 server response error. 268 // Simulates a 403 server response error.
263 TEST_F(UpdateCheckerTest, UpdateCheckError) { 269 TEST_F(UpdateCheckerTest, UpdateCheckError) {
264 EXPECT_TRUE( 270 EXPECT_TRUE(
265 post_interceptor_->ExpectRequest(new PartialMatch("updatecheck"), 403)); 271 post_interceptor_->ExpectRequest(new PartialMatch("updatecheck"), 403));
266 272
267 update_checker_ = UpdateChecker::Create(config_, *metadata_); 273 update_checker_ = UpdateChecker::Create(config_, metadata_.get());
268 274
269 CrxUpdateItem item(BuildCrxUpdateItem()); 275 CrxUpdateItem item(BuildCrxUpdateItem());
270 std::vector<CrxUpdateItem*> items_to_check; 276 std::vector<CrxUpdateItem*> items_to_check;
271 items_to_check.push_back(&item); 277 items_to_check.push_back(&item);
272 278
273 update_checker_->CheckForUpdates( 279 update_checker_->CheckForUpdates(
274 items_to_check, "", base::Bind(&UpdateCheckerTest::UpdateCheckComplete, 280 items_to_check, "", base::Bind(&UpdateCheckerTest::UpdateCheckComplete,
275 base::Unretained(this))); 281 base::Unretained(this)));
276 RunThreads(); 282 RunThreads();
277 283
278 EXPECT_EQ(1, post_interceptor_->GetHitCount()) 284 EXPECT_EQ(1, post_interceptor_->GetHitCount())
279 << post_interceptor_->GetRequestsAsString(); 285 << post_interceptor_->GetRequestsAsString();
280 EXPECT_EQ(1, post_interceptor_->GetCount()) 286 EXPECT_EQ(1, post_interceptor_->GetCount())
281 << post_interceptor_->GetRequestsAsString(); 287 << post_interceptor_->GetRequestsAsString();
282 288
283 EXPECT_EQ(403, error_); 289 EXPECT_EQ(403, error_);
284 EXPECT_EQ(0ul, results_.list.size()); 290 EXPECT_EQ(0ul, results_.list.size());
285 } 291 }
286 292
287 TEST_F(UpdateCheckerTest, UpdateCheckDownloadPreference) { 293 TEST_F(UpdateCheckerTest, UpdateCheckDownloadPreference) {
288 EXPECT_TRUE(post_interceptor_->ExpectRequest( 294 EXPECT_TRUE(post_interceptor_->ExpectRequest(
289 new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); 295 new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml")));
290 296
291 config_->SetDownloadPreference(string("cacheable")); 297 config_->SetDownloadPreference(string("cacheable"));
292 298
293 update_checker_ = UpdateChecker::Create(config_, *metadata_); 299 update_checker_ = UpdateChecker::Create(config_, metadata_.get());
294 300
295 CrxUpdateItem item(BuildCrxUpdateItem()); 301 CrxUpdateItem item(BuildCrxUpdateItem());
296 std::vector<CrxUpdateItem*> items_to_check; 302 std::vector<CrxUpdateItem*> items_to_check;
297 items_to_check.push_back(&item); 303 items_to_check.push_back(&item);
298 304
299 update_checker_->CheckForUpdates( 305 update_checker_->CheckForUpdates(
300 items_to_check, "extra=\"params\"", 306 items_to_check, "extra=\"params\"",
301 base::Bind(&UpdateCheckerTest::UpdateCheckComplete, 307 base::Bind(&UpdateCheckerTest::UpdateCheckComplete,
302 base::Unretained(this))); 308 base::Unretained(this)));
303 309
304 RunThreads(); 310 RunThreads();
305 311
306 // The request must contain dlpref="cacheable". 312 // The request must contain dlpref="cacheable".
307 EXPECT_NE(string::npos, 313 EXPECT_NE(string::npos,
308 post_interceptor_->GetRequests()[0].find(" dlpref=\"cacheable\"")); 314 post_interceptor_->GetRequests()[0].find(" dlpref=\"cacheable\""));
309 } 315 }
310 316
311 // This test is checking that an update check signed with CUP fails, since there 317 // This test is checking that an update check signed with CUP fails, since there
312 // is currently no entity that can respond with a valid signed response. 318 // is currently no entity that can respond with a valid signed response.
313 // A proper CUP test requires network mocks, which are not available now. 319 // A proper CUP test requires network mocks, which are not available now.
314 TEST_F(UpdateCheckerTest, UpdateCheckCupError) { 320 TEST_F(UpdateCheckerTest, UpdateCheckCupError) {
315 EXPECT_TRUE(post_interceptor_->ExpectRequest( 321 EXPECT_TRUE(post_interceptor_->ExpectRequest(
316 new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); 322 new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml")));
317 323
318 config_->SetUseCupSigning(true); 324 config_->SetUseCupSigning(true);
319 update_checker_ = UpdateChecker::Create(config_, *metadata_); 325 update_checker_ = UpdateChecker::Create(config_, metadata_.get());
320 326
321 CrxUpdateItem item(BuildCrxUpdateItem()); 327 CrxUpdateItem item(BuildCrxUpdateItem());
322 std::vector<CrxUpdateItem*> items_to_check; 328 std::vector<CrxUpdateItem*> items_to_check;
323 items_to_check.push_back(&item); 329 items_to_check.push_back(&item);
324 330
325 update_checker_->CheckForUpdates( 331 update_checker_->CheckForUpdates(
326 items_to_check, "", base::Bind(&UpdateCheckerTest::UpdateCheckComplete, 332 items_to_check, "", base::Bind(&UpdateCheckerTest::UpdateCheckComplete,
327 base::Unretained(this))); 333 base::Unretained(this)));
328 334
329 RunThreads(); 335 RunThreads();
330 336
331 EXPECT_EQ(1, post_interceptor_->GetHitCount()) 337 EXPECT_EQ(1, post_interceptor_->GetHitCount())
332 << post_interceptor_->GetRequestsAsString(); 338 << post_interceptor_->GetRequestsAsString();
333 ASSERT_EQ(1, post_interceptor_->GetCount()) 339 ASSERT_EQ(1, post_interceptor_->GetCount())
334 << post_interceptor_->GetRequestsAsString(); 340 << post_interceptor_->GetRequestsAsString();
335 341
336 // Sanity check the request. 342 // Sanity check the request.
337 EXPECT_NE( 343 EXPECT_NE(
338 string::npos, 344 string::npos,
339 post_interceptor_->GetRequests()[0].find( 345 post_interceptor_->GetRequests()[0].find(
340 "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\" " 346 "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\" "
341 "brand=\"TEST\"><updatecheck /><ping rd=\"-2\" />" 347 "brand=\"TEST\"><updatecheck /><ping rd=\"-2\" "));
342 "<packages><package fp=\"fp1\"/></packages></app>")); 348 EXPECT_NE(string::npos,
349 post_interceptor_->GetRequests()[0].find(
350 "<packages><package fp=\"fp1\"/></packages></app>"));
343 351
344 // Expect an error since the response is not trusted. 352 // Expect an error since the response is not trusted.
345 EXPECT_EQ(-10000, error_); 353 EXPECT_EQ(-10000, error_);
346 EXPECT_EQ(0ul, results_.list.size()); 354 EXPECT_EQ(0ul, results_.list.size());
347 } 355 }
348 356
349 // Tests that the UpdateCheckers will not make an update check for a 357 // Tests that the UpdateCheckers will not make an update check for a
350 // component that requires encryption when the update check URL is unsecure. 358 // component that requires encryption when the update check URL is unsecure.
351 TEST_F(UpdateCheckerTest, UpdateCheckRequiresEncryptionError) { 359 TEST_F(UpdateCheckerTest, UpdateCheckRequiresEncryptionError) {
352 config_->SetUpdateCheckUrl(GURL("http:\\foo\bar")); 360 config_->SetUpdateCheckUrl(GURL("http:\\foo\bar"));
353 361
354 update_checker_ = UpdateChecker::Create(config_, *metadata_); 362 update_checker_ = UpdateChecker::Create(config_, metadata_.get());
355 363
356 CrxUpdateItem item(BuildCrxUpdateItem()); 364 CrxUpdateItem item(BuildCrxUpdateItem());
357 item.component.requires_network_encryption = true; 365 item.component.requires_network_encryption = true;
358 std::vector<CrxUpdateItem*> items_to_check; 366 std::vector<CrxUpdateItem*> items_to_check;
359 items_to_check.push_back(&item); 367 items_to_check.push_back(&item);
360 368
361 update_checker_->CheckForUpdates( 369 update_checker_->CheckForUpdates(
362 items_to_check, "", base::Bind(&UpdateCheckerTest::UpdateCheckComplete, 370 items_to_check, "", base::Bind(&UpdateCheckerTest::UpdateCheckComplete,
363 base::Unretained(this))); 371 base::Unretained(this)));
364 RunThreads(); 372 RunThreads();
365 373
366 EXPECT_EQ(-1, error_); 374 EXPECT_EQ(-1, error_);
367 EXPECT_EQ(0u, results_.list.size()); 375 EXPECT_EQ(0u, results_.list.size());
368 } 376 }
369 377
370 // Tests that the PersistedData will get correctly update and reserialize 378 // Tests that the PersistedData will get correctly update and reserialize
371 // the elapsed_days value. 379 // the elapsed_days value.
372 TEST_F(UpdateCheckerTest, UpdateCheckDateLastRollCall) { 380 TEST_F(UpdateCheckerTest, UpdateCheckDateLastRollCall) {
373 EXPECT_TRUE(post_interceptor_->ExpectRequest( 381 EXPECT_TRUE(post_interceptor_->ExpectRequest(
374 new PartialMatch("updatecheck"), test_file("updatecheck_reply_4.xml"))); 382 new PartialMatch("updatecheck"), test_file("updatecheck_reply_4.xml")));
375 EXPECT_TRUE(post_interceptor_->ExpectRequest( 383 EXPECT_TRUE(post_interceptor_->ExpectRequest(
376 new PartialMatch("updatecheck"), test_file("updatecheck_reply_4.xml"))); 384 new PartialMatch("updatecheck"), test_file("updatecheck_reply_4.xml")));
377 385
378 update_checker_ = UpdateChecker::Create(config_, *metadata_); 386 update_checker_ = UpdateChecker::Create(config_, metadata_.get());
379 387
380 CrxUpdateItem item(BuildCrxUpdateItem()); 388 CrxUpdateItem item(BuildCrxUpdateItem());
381 std::vector<CrxUpdateItem*> items_to_check; 389 std::vector<CrxUpdateItem*> items_to_check;
382 items_to_check.push_back(&item); 390 items_to_check.push_back(&item);
383 391
384 // Do two update-checks. 392 // Do two update-checks.
385 update_checker_->CheckForUpdates( 393 update_checker_->CheckForUpdates(
386 items_to_check, "extra=\"params\"", 394 items_to_check, "extra=\"params\"",
387 base::Bind(&UpdateCheckerTest::UpdateCheckComplete, 395 base::Bind(&UpdateCheckerTest::UpdateCheckComplete,
388 base::Unretained(this))); 396 base::Unretained(this)));
389 RunThreads(); 397 RunThreads();
390 update_checker_ = UpdateChecker::Create(config_, *metadata_); 398 update_checker_ = UpdateChecker::Create(config_, metadata_.get());
391 update_checker_->CheckForUpdates( 399 update_checker_->CheckForUpdates(
392 items_to_check, "extra=\"params\"", 400 items_to_check, "extra=\"params\"",
393 base::Bind(&UpdateCheckerTest::UpdateCheckComplete, 401 base::Bind(&UpdateCheckerTest::UpdateCheckComplete,
394 base::Unretained(this))); 402 base::Unretained(this)));
395 RunThreads(); 403 RunThreads();
396 404
397 EXPECT_EQ(2, post_interceptor_->GetHitCount()) 405 EXPECT_EQ(2, post_interceptor_->GetHitCount())
398 << post_interceptor_->GetRequestsAsString(); 406 << post_interceptor_->GetRequestsAsString();
399 ASSERT_EQ(2, post_interceptor_->GetCount()) 407 ASSERT_EQ(2, post_interceptor_->GetCount())
400 << post_interceptor_->GetRequestsAsString(); 408 << post_interceptor_->GetRequestsAsString();
401 EXPECT_NE(string::npos, 409 EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find(
402 post_interceptor_->GetRequests()[0].find("<ping rd=\"-2\" />")); 410 "<ping rd=\"-2\" ping_freshness="));
403 EXPECT_NE(string::npos, 411 EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find(
404 post_interceptor_->GetRequests()[1].find("<ping rd=\"3383\" />")); 412 "<ping rd=\"3383\" ping_freshness="));
405 } 413 }
406 414
407 } // namespace update_client 415 } // namespace update_client
OLDNEW
« no previous file with comments | « components/update_client/update_checker.cc ('k') | components/update_client/update_client_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698