blob: f7dad8ea7b09fab781f879b2d8cce4ee5c32f3c6 [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) {
164 g_main_loop_quit(loop_);
165 }
166 GMainLoop* loop_;
167};
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000168
169struct StartTransferArgs {
170 HttpFetcher *http_fetcher;
171 string url;
172};
173
174gboolean StartTransfer(gpointer data) {
175 StartTransferArgs *args = reinterpret_cast<StartTransferArgs*>(data);
176 args->http_fetcher->BeginTransfer(args->url);
177 return FALSE;
178}
rspangler@google.com49fdf182009-10-10 00:57:34 +0000179} // namespace {}
180
181TYPED_TEST(HttpFetcherTest, SimpleTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700182 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000183 {
184 HttpFetcherTestDelegate delegate;
185 delegate.loop_ = loop;
186 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
187 fetcher->set_delegate(&delegate);
188
189 typename TestFixture::HttpServer server;
190 ASSERT_TRUE(server.started_);
191
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000192 StartTransferArgs start_xfer_args = {fetcher.get(), this->SmallUrl()};
193
194 g_timeout_add(0, StartTransfer, &start_xfer_args);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000195 g_main_loop_run(loop);
196 }
197 g_main_loop_unref(loop);
198}
199
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700200TYPED_TEST(HttpFetcherTest, SimpleBigTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700201 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700202 {
203 HttpFetcherTestDelegate delegate;
204 delegate.loop_ = loop;
205 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
206 fetcher->set_delegate(&delegate);
207
208 typename TestFixture::HttpServer server;
209 ASSERT_TRUE(server.started_);
210
211 StartTransferArgs start_xfer_args = {fetcher.get(), this->BigUrl()};
212
213 g_timeout_add(0, StartTransfer, &start_xfer_args);
214 g_main_loop_run(loop);
215 }
216 g_main_loop_unref(loop);
217}
218
rspangler@google.com49fdf182009-10-10 00:57:34 +0000219namespace {
220class PausingHttpFetcherTestDelegate : public HttpFetcherDelegate {
221 public:
222 virtual void ReceivedBytes(HttpFetcher* fetcher,
223 const char* bytes, int length) {
224 char str[length + 1];
rspangler@google.com49fdf182009-10-10 00:57:34 +0000225 memset(str, 0, length + 1);
226 memcpy(str, bytes, length);
227 CHECK(!paused_);
228 paused_ = true;
229 fetcher->Pause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000230 }
231 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
232 g_main_loop_quit(loop_);
233 }
234 void Unpause() {
235 CHECK(paused_);
236 paused_ = false;
237 fetcher_->Unpause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000238 }
239 bool paused_;
240 HttpFetcher* fetcher_;
241 GMainLoop* loop_;
242};
243
244gboolean UnpausingTimeoutCallback(gpointer data) {
245 PausingHttpFetcherTestDelegate *delegate =
246 reinterpret_cast<PausingHttpFetcherTestDelegate*>(data);
247 if (delegate->paused_)
248 delegate->Unpause();
249 return TRUE;
250}
251} // namespace {}
252
253TYPED_TEST(HttpFetcherTest, PauseTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700254 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000255 {
256 PausingHttpFetcherTestDelegate delegate;
257 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
258 delegate.paused_ = false;
259 delegate.loop_ = loop;
260 delegate.fetcher_ = fetcher.get();
261 fetcher->set_delegate(&delegate);
262
263 typename TestFixture::HttpServer server;
264 ASSERT_TRUE(server.started_);
265 GSource* timeout_source_;
266 timeout_source_ = g_timeout_source_new(0); // ms
267 g_source_set_callback(timeout_source_, UnpausingTimeoutCallback, &delegate,
268 NULL);
269 g_source_attach(timeout_source_, NULL);
270 fetcher->BeginTransfer(this->BigUrl());
271
272 g_main_loop_run(loop);
273 g_source_destroy(timeout_source_);
274 }
275 g_main_loop_unref(loop);
276}
277
278namespace {
279class AbortingHttpFetcherTestDelegate : public HttpFetcherDelegate {
280 public:
281 virtual void ReceivedBytes(HttpFetcher* fetcher,
282 const char* bytes, int length) {}
283 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
284 CHECK(false); // We should never get here
285 g_main_loop_quit(loop_);
286 }
287 void TerminateTransfer() {
288 CHECK(once_);
289 once_ = false;
290 fetcher_->TerminateTransfer();
291 }
292 void EndLoop() {
293 g_main_loop_quit(loop_);
294 }
295 bool once_;
296 HttpFetcher* fetcher_;
297 GMainLoop* loop_;
298};
299
300gboolean AbortingTimeoutCallback(gpointer data) {
301 AbortingHttpFetcherTestDelegate *delegate =
302 reinterpret_cast<AbortingHttpFetcherTestDelegate*>(data);
303 if (delegate->once_) {
304 delegate->TerminateTransfer();
305 return TRUE;
306 } else {
307 delegate->EndLoop();
308 return FALSE;
309 }
310}
311} // namespace {}
312
313TYPED_TEST(HttpFetcherTest, AbortTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700314 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000315 {
316 AbortingHttpFetcherTestDelegate delegate;
317 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
318 delegate.once_ = true;
319 delegate.loop_ = loop;
320 delegate.fetcher_ = fetcher.get();
321 fetcher->set_delegate(&delegate);
322
323 typename TestFixture::HttpServer server;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700324 this->IgnoreServerAborting(&server);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000325 ASSERT_TRUE(server.started_);
326 GSource* timeout_source_;
327 timeout_source_ = g_timeout_source_new(0); // ms
328 g_source_set_callback(timeout_source_, AbortingTimeoutCallback, &delegate,
329 NULL);
330 g_source_attach(timeout_source_, NULL);
331 fetcher->BeginTransfer(this->BigUrl());
332
333 g_main_loop_run(loop);
334 g_source_destroy(timeout_source_);
335 }
336 g_main_loop_unref(loop);
337}
338
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000339namespace {
340class FlakyHttpFetcherTestDelegate : public HttpFetcherDelegate {
341 public:
342 virtual void ReceivedBytes(HttpFetcher* fetcher,
343 const char* bytes, int length) {
344 data.append(bytes, length);
345 }
346 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
Andrew de los Reyesfb4ad7d2010-07-19 10:43:46 -0700347 EXPECT_TRUE(successful);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000348 g_main_loop_quit(loop_);
349 }
350 string data;
351 GMainLoop* loop_;
352};
353} // namespace {}
354
355TYPED_TEST(HttpFetcherTest, FlakyTest) {
356 if (this->IsMock())
357 return;
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700358 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000359 {
360 FlakyHttpFetcherTestDelegate delegate;
361 delegate.loop_ = loop;
362 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
363 fetcher->set_delegate(&delegate);
364
365 typename TestFixture::HttpServer server;
366 ASSERT_TRUE(server.started_);
367
368 StartTransferArgs start_xfer_args = {
369 fetcher.get(),
370 LocalServerUrlForPath("/flaky")
371 };
372
373 g_timeout_add(0, StartTransfer, &start_xfer_args);
374 g_main_loop_run(loop);
375
376 // verify the data we get back
377 ASSERT_EQ(100000, delegate.data.size());
378 for (int i = 0; i < 100000; i += 10) {
379 // Assert so that we don't flood the screen w/ EXPECT errors on failure.
380 ASSERT_EQ(delegate.data.substr(i, 10), "abcdefghij");
381 }
382 }
383 g_main_loop_unref(loop);
384}
385
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700386namespace {
387class FailureHttpFetcherTestDelegate : public HttpFetcherDelegate {
388 public:
389 FailureHttpFetcherTestDelegate() : loop_(NULL), server_(NULL) {}
390 virtual void ReceivedBytes(HttpFetcher* fetcher,
391 const char* bytes, int length) {
392 if (server_) {
393 LOG(INFO) << "Stopping server";
394 delete server_;
395 LOG(INFO) << "server stopped";
396 server_ = NULL;
397 }
398 }
399 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
400 EXPECT_FALSE(successful);
401 g_main_loop_quit(loop_);
402 }
403 GMainLoop* loop_;
404 PythonHttpServer* server_;
405};
406} // namespace {}
407
408
409TYPED_TEST(HttpFetcherTest, FailureTest) {
410 if (this->IsMock())
411 return;
412 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
413 {
414 FailureHttpFetcherTestDelegate delegate;
415 delegate.loop_ = loop;
416 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
417 fetcher->set_delegate(&delegate);
418
419 StartTransferArgs start_xfer_args = {
420 fetcher.get(),
421 LocalServerUrlForPath(this->SmallUrl())
422 };
423
424 g_timeout_add(0, StartTransfer, &start_xfer_args);
425 g_main_loop_run(loop);
426
427 // Exiting and testing happens in the delegate
428 }
429 g_main_loop_unref(loop);
430}
431
432TYPED_TEST(HttpFetcherTest, ServerDiesTest) {
433 if (this->IsMock())
434 return;
435 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
436 {
437 FailureHttpFetcherTestDelegate delegate;
438 delegate.loop_ = loop;
439 delegate.server_ = new PythonHttpServer;
440 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
441 fetcher->set_delegate(&delegate);
442
443 StartTransferArgs start_xfer_args = {
444 fetcher.get(),
445 LocalServerUrlForPath("/flaky")
446 };
447
448 g_timeout_add(0, StartTransfer, &start_xfer_args);
449 g_main_loop_run(loop);
450
451 // Exiting and testing happens in the delegate
452 }
453 g_main_loop_unref(loop);
454}
455
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700456namespace {
457const int kRedirectCodes[] = { 301, 302, 303, 307 };
458
459class RedirectHttpFetcherTestDelegate : public HttpFetcherDelegate {
460 public:
461 RedirectHttpFetcherTestDelegate(bool expected_successful)
462 : expected_successful_(expected_successful) {}
463 virtual void ReceivedBytes(HttpFetcher* fetcher,
464 const char* bytes, int length) {
465 data.append(bytes, length);
466 }
467 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
468 EXPECT_EQ(expected_successful_, successful);
469 g_main_loop_quit(loop_);
470 }
471 bool expected_successful_;
472 string data;
473 GMainLoop* loop_;
474};
475
476// RedirectTest takes ownership of |http_fetcher|.
477void RedirectTest(bool expected_successful,
478 const string& url,
479 HttpFetcher* http_fetcher) {
480 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
481 RedirectHttpFetcherTestDelegate delegate(expected_successful);
482 delegate.loop_ = loop;
483 scoped_ptr<HttpFetcher> fetcher(http_fetcher);
484 fetcher->set_delegate(&delegate);
485
486 StartTransferArgs start_xfer_args =
487 { fetcher.get(), LocalServerUrlForPath(url) };
488
489 g_timeout_add(0, StartTransfer, &start_xfer_args);
490 g_main_loop_run(loop);
491 if (expected_successful) {
492 // verify the data we get back
493 ASSERT_EQ(1000, delegate.data.size());
494 for (int i = 0; i < 1000; i += 10) {
495 // Assert so that we don't flood the screen w/ EXPECT errors on failure.
496 ASSERT_EQ(delegate.data.substr(i, 10), "abcdefghij");
497 }
498 }
499 g_main_loop_unref(loop);
500}
501} // namespace {}
502
503TYPED_TEST(HttpFetcherTest, SimpleRedirectTest) {
504 if (this->IsMock())
505 return;
506 typename TestFixture::HttpServer server;
507 ASSERT_TRUE(server.started_);
508 for (size_t c = 0; c < arraysize(kRedirectCodes); ++c) {
509 const string url = base::StringPrintf("/redirect/%d/medium",
510 kRedirectCodes[c]);
511 RedirectTest(true, url, this->NewLargeFetcher());
512 }
513}
514
515TYPED_TEST(HttpFetcherTest, MaxRedirectTest) {
516 if (this->IsMock())
517 return;
518 typename TestFixture::HttpServer server;
519 ASSERT_TRUE(server.started_);
520 string url;
521 for (int r = 0; r < LibcurlHttpFetcher::kMaxRedirects; r++) {
522 url += base::StringPrintf("/redirect/%d",
523 kRedirectCodes[r % arraysize(kRedirectCodes)]);
524 }
525 url += "/medium";
526 RedirectTest(true, url, this->NewLargeFetcher());
527}
528
529TYPED_TEST(HttpFetcherTest, BeyondMaxRedirectTest) {
530 if (this->IsMock())
531 return;
532 typename TestFixture::HttpServer server;
533 ASSERT_TRUE(server.started_);
534 string url;
535 for (int r = 0; r < LibcurlHttpFetcher::kMaxRedirects + 1; r++) {
536 url += base::StringPrintf("/redirect/%d",
537 kRedirectCodes[r % arraysize(kRedirectCodes)]);
538 }
539 url += "/medium";
540 RedirectTest(false, url, this->NewLargeFetcher());
541}
542
rspangler@google.com49fdf182009-10-10 00:57:34 +0000543} // namespace chromeos_update_engine