blob: d0962677224fec039016f1a05335a416fb26e2aa [file] [log] [blame]
rspangler@google.com49fdf182009-10-10 00:57:34 +00001// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <unistd.h>
Darin Petkov41c2fcf2010-08-25 13:14:48 -07006
adlr@google.comc98a7ed2009-12-04 18:54:03 +00007#include <string>
8#include <vector>
Darin Petkov41c2fcf2010-08-25 13:14:48 -07009
Chris Masone790e62e2010-08-12 10:41:18 -070010#include "base/logging.h"
Darin Petkov41c2fcf2010-08-25 13:14:48 -070011#include "base/scoped_ptr.h"
12#include "base/string_util.h"
13#include "glib.h"
14#include "gtest/gtest.h"
rspangler@google.com49fdf182009-10-10 00:57:34 +000015#include "update_engine/libcurl_http_fetcher.h"
16#include "update_engine/mock_http_fetcher.h"
17
adlr@google.comc98a7ed2009-12-04 18:54:03 +000018using std::string;
19using std::vector;
20
rspangler@google.com49fdf182009-10-10 00:57:34 +000021namespace chromeos_update_engine {
22
23namespace {
24// WARNING, if you update this, you must also update test_http_server.py
25const char* const kServerPort = "8080";
26string LocalServerUrlForPath(const string& path) {
27 return string("http://127.0.0.1:") + kServerPort + path;
28}
29}
30
31template <typename T>
32class HttpFetcherTest : public ::testing::Test {
33 public:
34 HttpFetcher* NewLargeFetcher() = 0;
35 HttpFetcher* NewSmallFetcher() = 0;
36 string BigUrl() const = 0;
37 string SmallUrl() const = 0;
adlr@google.comc98a7ed2009-12-04 18:54:03 +000038 bool IsMock() const = 0;
rspangler@google.com49fdf182009-10-10 00:57:34 +000039};
40
41class NullHttpServer {
42 public:
43 NullHttpServer() : started_(true) {}
44 ~NullHttpServer() {}
45 bool started_;
46};
47
48
49template <>
50class HttpFetcherTest<MockHttpFetcher> : public ::testing::Test {
51 public:
52 HttpFetcher* NewLargeFetcher() {
53 vector<char> big_data(1000000);
54 return new MockHttpFetcher(big_data.data(), big_data.size());
55 }
56 HttpFetcher* NewSmallFetcher() {
57 return new MockHttpFetcher("x", 1);
58 }
59 string BigUrl() const {
60 return "unused://unused";
61 }
62 string SmallUrl() const {
63 return "unused://unused";
64 }
adlr@google.comc98a7ed2009-12-04 18:54:03 +000065 bool IsMock() const { return true; }
rspangler@google.com49fdf182009-10-10 00:57:34 +000066 typedef NullHttpServer HttpServer;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070067 void IgnoreServerAborting(HttpServer* server) const {}
rspangler@google.com49fdf182009-10-10 00:57:34 +000068};
69
70class PythonHttpServer {
71 public:
72 PythonHttpServer() {
adlr@google.comc98a7ed2009-12-04 18:54:03 +000073 char *argv[2] = {strdup("./test_http_server"), NULL};
rspangler@google.com49fdf182009-10-10 00:57:34 +000074 GError *err;
75 started_ = false;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070076 validate_quit_ = true;
rspangler@google.com49fdf182009-10-10 00:57:34 +000077 if (!g_spawn_async(NULL,
78 argv,
79 NULL,
80 G_SPAWN_DO_NOT_REAP_CHILD,
81 NULL,
82 NULL,
83 &pid_,
84 &err)) {
85 return;
86 }
87 int rc = 1;
Andrew de los Reyes3270f742010-07-15 22:28:14 -070088 int tries = 10;
89 started_ = true;
rspangler@google.com49fdf182009-10-10 00:57:34 +000090 while (0 != rc) {
Andrew de los Reyes3270f742010-07-15 22:28:14 -070091 LOG(INFO) << "running wget to start";
rspangler@google.com49fdf182009-10-10 00:57:34 +000092 rc = system((string("wget --output-document=/dev/null ") +
93 LocalServerUrlForPath("/test")).c_str());
Andrew de los Reyes3270f742010-07-15 22:28:14 -070094 LOG(INFO) << "done running wget to start";
rspangler@google.com49fdf182009-10-10 00:57:34 +000095 usleep(10 * 1000); // 10 ms
Andrew de los Reyes3270f742010-07-15 22:28:14 -070096 tries--;
97 if (tries == 0) {
98 LOG(ERROR) << "Unable to start server.";
99 started_ = false;
100 break;
101 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000102 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000103 free(argv[0]);
104 return;
105 }
106 ~PythonHttpServer() {
107 if (!started_)
108 return;
109 // request that the server exit itself
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700110 LOG(INFO) << "running wget to exit";
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700111 int rc = system((string("wget -t 1 --output-document=/dev/null ") +
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700112 LocalServerUrlForPath("/quitquitquit")).c_str());
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700113 LOG(INFO) << "done running wget to exit";
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700114 if (validate_quit_)
115 EXPECT_EQ(0, rc);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000116 waitpid(pid_, NULL, 0);
117 }
118 GPid pid_;
119 bool started_;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700120 bool validate_quit_;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000121};
122
123template <>
124class HttpFetcherTest<LibcurlHttpFetcher> : public ::testing::Test {
125 public:
126 HttpFetcher* NewLargeFetcher() {
127 LibcurlHttpFetcher *ret = new LibcurlHttpFetcher;
Darin Petkovb83371f2010-08-17 09:34:49 -0700128 // Speed up test execution.
129 ret->set_idle_seconds(1);
130 ret->set_retry_seconds(1);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000131 return ret;
132 }
133 HttpFetcher* NewSmallFetcher() {
134 return NewLargeFetcher();
135 }
136 string BigUrl() const {
137 return LocalServerUrlForPath("/big");
138 }
139 string SmallUrl() const {
140 return LocalServerUrlForPath("/foo");
141 }
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000142 bool IsMock() const { return false; }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000143 typedef PythonHttpServer HttpServer;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700144 void IgnoreServerAborting(HttpServer* server) const {
145 PythonHttpServer *pyserver = reinterpret_cast<PythonHttpServer*>(server);
146 pyserver->validate_quit_ = false;
147 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000148};
149
150typedef ::testing::Types<LibcurlHttpFetcher, MockHttpFetcher>
151 HttpFetcherTestTypes;
152TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes);
153
154namespace {
155class HttpFetcherTestDelegate : public HttpFetcherDelegate {
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000156 public:
rspangler@google.com49fdf182009-10-10 00:57:34 +0000157 virtual void ReceivedBytes(HttpFetcher* fetcher,
158 const char* bytes, int length) {
159 char str[length + 1];
160 memset(str, 0, length + 1);
161 memcpy(str, bytes, length);
162 }
163 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
Darin Petkovcb466212010-08-26 09:40:11 -0700164 EXPECT_EQ(200, fetcher->http_response_code());
rspangler@google.com49fdf182009-10-10 00:57:34 +0000165 g_main_loop_quit(loop_);
166 }
167 GMainLoop* loop_;
168};
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000169
170struct StartTransferArgs {
171 HttpFetcher *http_fetcher;
172 string url;
173};
174
175gboolean StartTransfer(gpointer data) {
176 StartTransferArgs *args = reinterpret_cast<StartTransferArgs*>(data);
177 args->http_fetcher->BeginTransfer(args->url);
178 return FALSE;
179}
rspangler@google.com49fdf182009-10-10 00:57:34 +0000180} // namespace {}
181
182TYPED_TEST(HttpFetcherTest, SimpleTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700183 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000184 {
185 HttpFetcherTestDelegate delegate;
186 delegate.loop_ = loop;
187 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
188 fetcher->set_delegate(&delegate);
189
190 typename TestFixture::HttpServer server;
191 ASSERT_TRUE(server.started_);
192
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000193 StartTransferArgs start_xfer_args = {fetcher.get(), this->SmallUrl()};
194
195 g_timeout_add(0, StartTransfer, &start_xfer_args);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000196 g_main_loop_run(loop);
197 }
198 g_main_loop_unref(loop);
199}
200
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700201TYPED_TEST(HttpFetcherTest, SimpleBigTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700202 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700203 {
204 HttpFetcherTestDelegate delegate;
205 delegate.loop_ = loop;
206 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
207 fetcher->set_delegate(&delegate);
208
209 typename TestFixture::HttpServer server;
210 ASSERT_TRUE(server.started_);
211
212 StartTransferArgs start_xfer_args = {fetcher.get(), this->BigUrl()};
213
214 g_timeout_add(0, StartTransfer, &start_xfer_args);
215 g_main_loop_run(loop);
216 }
217 g_main_loop_unref(loop);
218}
219
rspangler@google.com49fdf182009-10-10 00:57:34 +0000220namespace {
221class PausingHttpFetcherTestDelegate : public HttpFetcherDelegate {
222 public:
223 virtual void ReceivedBytes(HttpFetcher* fetcher,
224 const char* bytes, int length) {
225 char str[length + 1];
rspangler@google.com49fdf182009-10-10 00:57:34 +0000226 memset(str, 0, length + 1);
227 memcpy(str, bytes, length);
228 CHECK(!paused_);
229 paused_ = true;
230 fetcher->Pause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000231 }
232 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
233 g_main_loop_quit(loop_);
234 }
235 void Unpause() {
236 CHECK(paused_);
237 paused_ = false;
238 fetcher_->Unpause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000239 }
240 bool paused_;
241 HttpFetcher* fetcher_;
242 GMainLoop* loop_;
243};
244
245gboolean UnpausingTimeoutCallback(gpointer data) {
246 PausingHttpFetcherTestDelegate *delegate =
247 reinterpret_cast<PausingHttpFetcherTestDelegate*>(data);
248 if (delegate->paused_)
249 delegate->Unpause();
250 return TRUE;
251}
252} // namespace {}
253
254TYPED_TEST(HttpFetcherTest, PauseTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700255 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000256 {
257 PausingHttpFetcherTestDelegate delegate;
258 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
259 delegate.paused_ = false;
260 delegate.loop_ = loop;
261 delegate.fetcher_ = fetcher.get();
262 fetcher->set_delegate(&delegate);
263
264 typename TestFixture::HttpServer server;
265 ASSERT_TRUE(server.started_);
266 GSource* timeout_source_;
267 timeout_source_ = g_timeout_source_new(0); // ms
268 g_source_set_callback(timeout_source_, UnpausingTimeoutCallback, &delegate,
269 NULL);
270 g_source_attach(timeout_source_, NULL);
271 fetcher->BeginTransfer(this->BigUrl());
272
273 g_main_loop_run(loop);
274 g_source_destroy(timeout_source_);
275 }
276 g_main_loop_unref(loop);
277}
278
279namespace {
280class AbortingHttpFetcherTestDelegate : public HttpFetcherDelegate {
281 public:
282 virtual void ReceivedBytes(HttpFetcher* fetcher,
283 const char* bytes, int length) {}
284 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
285 CHECK(false); // We should never get here
286 g_main_loop_quit(loop_);
287 }
288 void TerminateTransfer() {
289 CHECK(once_);
290 once_ = false;
291 fetcher_->TerminateTransfer();
292 }
293 void EndLoop() {
294 g_main_loop_quit(loop_);
295 }
296 bool once_;
297 HttpFetcher* fetcher_;
298 GMainLoop* loop_;
299};
300
301gboolean AbortingTimeoutCallback(gpointer data) {
302 AbortingHttpFetcherTestDelegate *delegate =
303 reinterpret_cast<AbortingHttpFetcherTestDelegate*>(data);
304 if (delegate->once_) {
305 delegate->TerminateTransfer();
306 return TRUE;
307 } else {
308 delegate->EndLoop();
309 return FALSE;
310 }
311}
312} // namespace {}
313
314TYPED_TEST(HttpFetcherTest, AbortTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700315 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000316 {
317 AbortingHttpFetcherTestDelegate delegate;
318 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
319 delegate.once_ = true;
320 delegate.loop_ = loop;
321 delegate.fetcher_ = fetcher.get();
322 fetcher->set_delegate(&delegate);
323
324 typename TestFixture::HttpServer server;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700325 this->IgnoreServerAborting(&server);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000326 ASSERT_TRUE(server.started_);
327 GSource* timeout_source_;
328 timeout_source_ = g_timeout_source_new(0); // ms
329 g_source_set_callback(timeout_source_, AbortingTimeoutCallback, &delegate,
330 NULL);
331 g_source_attach(timeout_source_, NULL);
332 fetcher->BeginTransfer(this->BigUrl());
333
334 g_main_loop_run(loop);
335 g_source_destroy(timeout_source_);
Darin Petkovcb466212010-08-26 09:40:11 -0700336 EXPECT_EQ(0, fetcher->http_response_code());
rspangler@google.com49fdf182009-10-10 00:57:34 +0000337 }
338 g_main_loop_unref(loop);
339}
340
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000341namespace {
342class FlakyHttpFetcherTestDelegate : public HttpFetcherDelegate {
343 public:
344 virtual void ReceivedBytes(HttpFetcher* fetcher,
345 const char* bytes, int length) {
346 data.append(bytes, length);
347 }
348 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
Andrew de los Reyesfb4ad7d2010-07-19 10:43:46 -0700349 EXPECT_TRUE(successful);
Darin Petkovcb466212010-08-26 09:40:11 -0700350 EXPECT_EQ(206, fetcher->http_response_code());
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000351 g_main_loop_quit(loop_);
352 }
353 string data;
354 GMainLoop* loop_;
355};
356} // namespace {}
357
358TYPED_TEST(HttpFetcherTest, FlakyTest) {
359 if (this->IsMock())
360 return;
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700361 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000362 {
363 FlakyHttpFetcherTestDelegate delegate;
364 delegate.loop_ = loop;
365 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
366 fetcher->set_delegate(&delegate);
367
368 typename TestFixture::HttpServer server;
369 ASSERT_TRUE(server.started_);
370
371 StartTransferArgs start_xfer_args = {
372 fetcher.get(),
373 LocalServerUrlForPath("/flaky")
374 };
375
376 g_timeout_add(0, StartTransfer, &start_xfer_args);
377 g_main_loop_run(loop);
378
379 // verify the data we get back
380 ASSERT_EQ(100000, delegate.data.size());
381 for (int i = 0; i < 100000; i += 10) {
382 // Assert so that we don't flood the screen w/ EXPECT errors on failure.
383 ASSERT_EQ(delegate.data.substr(i, 10), "abcdefghij");
384 }
385 }
386 g_main_loop_unref(loop);
387}
388
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700389namespace {
390class FailureHttpFetcherTestDelegate : public HttpFetcherDelegate {
391 public:
392 FailureHttpFetcherTestDelegate() : loop_(NULL), server_(NULL) {}
393 virtual void ReceivedBytes(HttpFetcher* fetcher,
394 const char* bytes, int length) {
395 if (server_) {
396 LOG(INFO) << "Stopping server";
397 delete server_;
398 LOG(INFO) << "server stopped";
399 server_ = NULL;
400 }
401 }
402 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
403 EXPECT_FALSE(successful);
Darin Petkovcb466212010-08-26 09:40:11 -0700404 EXPECT_EQ(0, fetcher->http_response_code());
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700405 g_main_loop_quit(loop_);
406 }
407 GMainLoop* loop_;
408 PythonHttpServer* server_;
409};
410} // namespace {}
411
412
413TYPED_TEST(HttpFetcherTest, FailureTest) {
414 if (this->IsMock())
415 return;
416 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
417 {
418 FailureHttpFetcherTestDelegate delegate;
419 delegate.loop_ = loop;
420 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
421 fetcher->set_delegate(&delegate);
422
423 StartTransferArgs start_xfer_args = {
424 fetcher.get(),
425 LocalServerUrlForPath(this->SmallUrl())
426 };
427
428 g_timeout_add(0, StartTransfer, &start_xfer_args);
429 g_main_loop_run(loop);
430
431 // Exiting and testing happens in the delegate
432 }
433 g_main_loop_unref(loop);
434}
435
436TYPED_TEST(HttpFetcherTest, ServerDiesTest) {
437 if (this->IsMock())
438 return;
439 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
440 {
441 FailureHttpFetcherTestDelegate delegate;
442 delegate.loop_ = loop;
443 delegate.server_ = new PythonHttpServer;
444 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
445 fetcher->set_delegate(&delegate);
446
447 StartTransferArgs start_xfer_args = {
448 fetcher.get(),
449 LocalServerUrlForPath("/flaky")
450 };
451
452 g_timeout_add(0, StartTransfer, &start_xfer_args);
453 g_main_loop_run(loop);
454
455 // Exiting and testing happens in the delegate
456 }
457 g_main_loop_unref(loop);
458}
459
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700460namespace {
461const int kRedirectCodes[] = { 301, 302, 303, 307 };
462
463class RedirectHttpFetcherTestDelegate : public HttpFetcherDelegate {
464 public:
465 RedirectHttpFetcherTestDelegate(bool expected_successful)
466 : expected_successful_(expected_successful) {}
467 virtual void ReceivedBytes(HttpFetcher* fetcher,
468 const char* bytes, int length) {
469 data.append(bytes, length);
470 }
471 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
472 EXPECT_EQ(expected_successful_, successful);
Darin Petkovcb466212010-08-26 09:40:11 -0700473 if (expected_successful_)
474 EXPECT_EQ(200, fetcher->http_response_code());
475 else {
476 EXPECT_GE(fetcher->http_response_code(), 301);
477 EXPECT_LE(fetcher->http_response_code(), 307);
478 }
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700479 g_main_loop_quit(loop_);
480 }
481 bool expected_successful_;
482 string data;
483 GMainLoop* loop_;
484};
485
486// RedirectTest takes ownership of |http_fetcher|.
487void RedirectTest(bool expected_successful,
488 const string& url,
489 HttpFetcher* http_fetcher) {
490 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
491 RedirectHttpFetcherTestDelegate delegate(expected_successful);
492 delegate.loop_ = loop;
493 scoped_ptr<HttpFetcher> fetcher(http_fetcher);
494 fetcher->set_delegate(&delegate);
495
496 StartTransferArgs start_xfer_args =
497 { fetcher.get(), LocalServerUrlForPath(url) };
498
499 g_timeout_add(0, StartTransfer, &start_xfer_args);
500 g_main_loop_run(loop);
501 if (expected_successful) {
502 // verify the data we get back
503 ASSERT_EQ(1000, delegate.data.size());
504 for (int i = 0; i < 1000; i += 10) {
505 // Assert so that we don't flood the screen w/ EXPECT errors on failure.
506 ASSERT_EQ(delegate.data.substr(i, 10), "abcdefghij");
507 }
508 }
509 g_main_loop_unref(loop);
510}
511} // namespace {}
512
513TYPED_TEST(HttpFetcherTest, SimpleRedirectTest) {
514 if (this->IsMock())
515 return;
516 typename TestFixture::HttpServer server;
517 ASSERT_TRUE(server.started_);
518 for (size_t c = 0; c < arraysize(kRedirectCodes); ++c) {
519 const string url = base::StringPrintf("/redirect/%d/medium",
520 kRedirectCodes[c]);
521 RedirectTest(true, url, this->NewLargeFetcher());
522 }
523}
524
525TYPED_TEST(HttpFetcherTest, MaxRedirectTest) {
526 if (this->IsMock())
527 return;
528 typename TestFixture::HttpServer server;
529 ASSERT_TRUE(server.started_);
530 string url;
531 for (int r = 0; r < LibcurlHttpFetcher::kMaxRedirects; r++) {
532 url += base::StringPrintf("/redirect/%d",
533 kRedirectCodes[r % arraysize(kRedirectCodes)]);
534 }
535 url += "/medium";
536 RedirectTest(true, url, this->NewLargeFetcher());
537}
538
539TYPED_TEST(HttpFetcherTest, BeyondMaxRedirectTest) {
540 if (this->IsMock())
541 return;
542 typename TestFixture::HttpServer server;
543 ASSERT_TRUE(server.started_);
544 string url;
545 for (int r = 0; r < LibcurlHttpFetcher::kMaxRedirects + 1; r++) {
546 url += base::StringPrintf("/redirect/%d",
547 kRedirectCodes[r % arraysize(kRedirectCodes)]);
548 }
549 url += "/medium";
550 RedirectTest(false, url, this->NewLargeFetcher());
551}
552
rspangler@google.com49fdf182009-10-10 00:57:34 +0000553} // namespace chromeos_update_engine