blob: 17e359e844454579976a7c7f994ee0680fcf7da5 [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>
adlr@google.comc98a7ed2009-12-04 18:54:03 +00006#include <string>
7#include <vector>
rspangler@google.com49fdf182009-10-10 00:57:34 +00008#include <base/scoped_ptr.h>
9#include <glib.h>
rspangler@google.com49fdf182009-10-10 00:57:34 +000010#include <gtest/gtest.h>
Chris Masone790e62e2010-08-12 10:41:18 -070011#include "base/logging.h"
rspangler@google.com49fdf182009-10-10 00:57:34 +000012#include "update_engine/libcurl_http_fetcher.h"
13#include "update_engine/mock_http_fetcher.h"
14
adlr@google.comc98a7ed2009-12-04 18:54:03 +000015using std::string;
16using std::vector;
17
rspangler@google.com49fdf182009-10-10 00:57:34 +000018namespace chromeos_update_engine {
19
20namespace {
21// WARNING, if you update this, you must also update test_http_server.py
22const char* const kServerPort = "8080";
23string LocalServerUrlForPath(const string& path) {
24 return string("http://127.0.0.1:") + kServerPort + path;
25}
26}
27
28template <typename T>
29class HttpFetcherTest : public ::testing::Test {
30 public:
31 HttpFetcher* NewLargeFetcher() = 0;
32 HttpFetcher* NewSmallFetcher() = 0;
33 string BigUrl() const = 0;
34 string SmallUrl() const = 0;
adlr@google.comc98a7ed2009-12-04 18:54:03 +000035 bool IsMock() const = 0;
rspangler@google.com49fdf182009-10-10 00:57:34 +000036};
37
38class NullHttpServer {
39 public:
40 NullHttpServer() : started_(true) {}
41 ~NullHttpServer() {}
42 bool started_;
43};
44
45
46template <>
47class HttpFetcherTest<MockHttpFetcher> : public ::testing::Test {
48 public:
49 HttpFetcher* NewLargeFetcher() {
50 vector<char> big_data(1000000);
51 return new MockHttpFetcher(big_data.data(), big_data.size());
52 }
53 HttpFetcher* NewSmallFetcher() {
54 return new MockHttpFetcher("x", 1);
55 }
56 string BigUrl() const {
57 return "unused://unused";
58 }
59 string SmallUrl() const {
60 return "unused://unused";
61 }
adlr@google.comc98a7ed2009-12-04 18:54:03 +000062 bool IsMock() const { return true; }
rspangler@google.com49fdf182009-10-10 00:57:34 +000063 typedef NullHttpServer HttpServer;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070064 void IgnoreServerAborting(HttpServer* server) const {}
rspangler@google.com49fdf182009-10-10 00:57:34 +000065};
66
67class PythonHttpServer {
68 public:
69 PythonHttpServer() {
adlr@google.comc98a7ed2009-12-04 18:54:03 +000070 char *argv[2] = {strdup("./test_http_server"), NULL};
rspangler@google.com49fdf182009-10-10 00:57:34 +000071 GError *err;
72 started_ = false;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070073 validate_quit_ = true;
rspangler@google.com49fdf182009-10-10 00:57:34 +000074 if (!g_spawn_async(NULL,
75 argv,
76 NULL,
77 G_SPAWN_DO_NOT_REAP_CHILD,
78 NULL,
79 NULL,
80 &pid_,
81 &err)) {
82 return;
83 }
84 int rc = 1;
Andrew de los Reyes3270f742010-07-15 22:28:14 -070085 int tries = 10;
86 started_ = true;
rspangler@google.com49fdf182009-10-10 00:57:34 +000087 while (0 != rc) {
Andrew de los Reyes3270f742010-07-15 22:28:14 -070088 LOG(INFO) << "running wget to start";
rspangler@google.com49fdf182009-10-10 00:57:34 +000089 rc = system((string("wget --output-document=/dev/null ") +
90 LocalServerUrlForPath("/test")).c_str());
Andrew de los Reyes3270f742010-07-15 22:28:14 -070091 LOG(INFO) << "done running wget to start";
rspangler@google.com49fdf182009-10-10 00:57:34 +000092 usleep(10 * 1000); // 10 ms
Andrew de los Reyes3270f742010-07-15 22:28:14 -070093 tries--;
94 if (tries == 0) {
95 LOG(ERROR) << "Unable to start server.";
96 started_ = false;
97 break;
98 }
rspangler@google.com49fdf182009-10-10 00:57:34 +000099 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000100 free(argv[0]);
101 return;
102 }
103 ~PythonHttpServer() {
104 if (!started_)
105 return;
106 // request that the server exit itself
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700107 LOG(INFO) << "running wget to exit";
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700108 int rc = system((string("wget -t 1 --output-document=/dev/null ") +
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700109 LocalServerUrlForPath("/quitquitquit")).c_str());
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700110 LOG(INFO) << "done running wget to exit";
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700111 if (validate_quit_)
112 EXPECT_EQ(0, rc);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000113 waitpid(pid_, NULL, 0);
114 }
115 GPid pid_;
116 bool started_;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700117 bool validate_quit_;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000118};
119
120template <>
121class HttpFetcherTest<LibcurlHttpFetcher> : public ::testing::Test {
122 public:
123 HttpFetcher* NewLargeFetcher() {
124 LibcurlHttpFetcher *ret = new LibcurlHttpFetcher;
Darin Petkovb83371f2010-08-17 09:34:49 -0700125 // Speed up test execution.
126 ret->set_idle_seconds(1);
127 ret->set_retry_seconds(1);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000128 return ret;
129 }
130 HttpFetcher* NewSmallFetcher() {
131 return NewLargeFetcher();
132 }
133 string BigUrl() const {
134 return LocalServerUrlForPath("/big");
135 }
136 string SmallUrl() const {
137 return LocalServerUrlForPath("/foo");
138 }
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000139 bool IsMock() const { return false; }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000140 typedef PythonHttpServer HttpServer;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700141 void IgnoreServerAborting(HttpServer* server) const {
142 PythonHttpServer *pyserver = reinterpret_cast<PythonHttpServer*>(server);
143 pyserver->validate_quit_ = false;
144 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000145};
146
147typedef ::testing::Types<LibcurlHttpFetcher, MockHttpFetcher>
148 HttpFetcherTestTypes;
149TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes);
150
151namespace {
152class HttpFetcherTestDelegate : public HttpFetcherDelegate {
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000153 public:
rspangler@google.com49fdf182009-10-10 00:57:34 +0000154 virtual void ReceivedBytes(HttpFetcher* fetcher,
155 const char* bytes, int length) {
156 char str[length + 1];
157 memset(str, 0, length + 1);
158 memcpy(str, bytes, length);
159 }
160 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
161 g_main_loop_quit(loop_);
162 }
163 GMainLoop* loop_;
164};
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000165
166struct StartTransferArgs {
167 HttpFetcher *http_fetcher;
168 string url;
169};
170
171gboolean StartTransfer(gpointer data) {
172 StartTransferArgs *args = reinterpret_cast<StartTransferArgs*>(data);
173 args->http_fetcher->BeginTransfer(args->url);
174 return FALSE;
175}
rspangler@google.com49fdf182009-10-10 00:57:34 +0000176} // namespace {}
177
178TYPED_TEST(HttpFetcherTest, SimpleTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700179 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000180 {
181 HttpFetcherTestDelegate delegate;
182 delegate.loop_ = loop;
183 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
184 fetcher->set_delegate(&delegate);
185
186 typename TestFixture::HttpServer server;
187 ASSERT_TRUE(server.started_);
188
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000189 StartTransferArgs start_xfer_args = {fetcher.get(), this->SmallUrl()};
190
191 g_timeout_add(0, StartTransfer, &start_xfer_args);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000192 g_main_loop_run(loop);
193 }
194 g_main_loop_unref(loop);
195}
196
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700197TYPED_TEST(HttpFetcherTest, SimpleBigTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700198 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700199 {
200 HttpFetcherTestDelegate delegate;
201 delegate.loop_ = loop;
202 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
203 fetcher->set_delegate(&delegate);
204
205 typename TestFixture::HttpServer server;
206 ASSERT_TRUE(server.started_);
207
208 StartTransferArgs start_xfer_args = {fetcher.get(), this->BigUrl()};
209
210 g_timeout_add(0, StartTransfer, &start_xfer_args);
211 g_main_loop_run(loop);
212 }
213 g_main_loop_unref(loop);
214}
215
rspangler@google.com49fdf182009-10-10 00:57:34 +0000216namespace {
217class PausingHttpFetcherTestDelegate : public HttpFetcherDelegate {
218 public:
219 virtual void ReceivedBytes(HttpFetcher* fetcher,
220 const char* bytes, int length) {
221 char str[length + 1];
rspangler@google.com49fdf182009-10-10 00:57:34 +0000222 memset(str, 0, length + 1);
223 memcpy(str, bytes, length);
224 CHECK(!paused_);
225 paused_ = true;
226 fetcher->Pause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000227 }
228 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
229 g_main_loop_quit(loop_);
230 }
231 void Unpause() {
232 CHECK(paused_);
233 paused_ = false;
234 fetcher_->Unpause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000235 }
236 bool paused_;
237 HttpFetcher* fetcher_;
238 GMainLoop* loop_;
239};
240
241gboolean UnpausingTimeoutCallback(gpointer data) {
242 PausingHttpFetcherTestDelegate *delegate =
243 reinterpret_cast<PausingHttpFetcherTestDelegate*>(data);
244 if (delegate->paused_)
245 delegate->Unpause();
246 return TRUE;
247}
248} // namespace {}
249
250TYPED_TEST(HttpFetcherTest, PauseTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700251 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000252 {
253 PausingHttpFetcherTestDelegate delegate;
254 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
255 delegate.paused_ = false;
256 delegate.loop_ = loop;
257 delegate.fetcher_ = fetcher.get();
258 fetcher->set_delegate(&delegate);
259
260 typename TestFixture::HttpServer server;
261 ASSERT_TRUE(server.started_);
262 GSource* timeout_source_;
263 timeout_source_ = g_timeout_source_new(0); // ms
264 g_source_set_callback(timeout_source_, UnpausingTimeoutCallback, &delegate,
265 NULL);
266 g_source_attach(timeout_source_, NULL);
267 fetcher->BeginTransfer(this->BigUrl());
268
269 g_main_loop_run(loop);
270 g_source_destroy(timeout_source_);
271 }
272 g_main_loop_unref(loop);
273}
274
275namespace {
276class AbortingHttpFetcherTestDelegate : public HttpFetcherDelegate {
277 public:
278 virtual void ReceivedBytes(HttpFetcher* fetcher,
279 const char* bytes, int length) {}
280 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
281 CHECK(false); // We should never get here
282 g_main_loop_quit(loop_);
283 }
284 void TerminateTransfer() {
285 CHECK(once_);
286 once_ = false;
287 fetcher_->TerminateTransfer();
288 }
289 void EndLoop() {
290 g_main_loop_quit(loop_);
291 }
292 bool once_;
293 HttpFetcher* fetcher_;
294 GMainLoop* loop_;
295};
296
297gboolean AbortingTimeoutCallback(gpointer data) {
298 AbortingHttpFetcherTestDelegate *delegate =
299 reinterpret_cast<AbortingHttpFetcherTestDelegate*>(data);
300 if (delegate->once_) {
301 delegate->TerminateTransfer();
302 return TRUE;
303 } else {
304 delegate->EndLoop();
305 return FALSE;
306 }
307}
308} // namespace {}
309
310TYPED_TEST(HttpFetcherTest, AbortTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700311 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000312 {
313 AbortingHttpFetcherTestDelegate delegate;
314 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
315 delegate.once_ = true;
316 delegate.loop_ = loop;
317 delegate.fetcher_ = fetcher.get();
318 fetcher->set_delegate(&delegate);
319
320 typename TestFixture::HttpServer server;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700321 this->IgnoreServerAborting(&server);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000322 ASSERT_TRUE(server.started_);
323 GSource* timeout_source_;
324 timeout_source_ = g_timeout_source_new(0); // ms
325 g_source_set_callback(timeout_source_, AbortingTimeoutCallback, &delegate,
326 NULL);
327 g_source_attach(timeout_source_, NULL);
328 fetcher->BeginTransfer(this->BigUrl());
329
330 g_main_loop_run(loop);
331 g_source_destroy(timeout_source_);
332 }
333 g_main_loop_unref(loop);
334}
335
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000336namespace {
337class FlakyHttpFetcherTestDelegate : public HttpFetcherDelegate {
338 public:
339 virtual void ReceivedBytes(HttpFetcher* fetcher,
340 const char* bytes, int length) {
341 data.append(bytes, length);
342 }
343 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
Andrew de los Reyesfb4ad7d2010-07-19 10:43:46 -0700344 EXPECT_TRUE(successful);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000345 g_main_loop_quit(loop_);
346 }
347 string data;
348 GMainLoop* loop_;
349};
350} // namespace {}
351
352TYPED_TEST(HttpFetcherTest, FlakyTest) {
353 if (this->IsMock())
354 return;
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700355 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000356 {
357 FlakyHttpFetcherTestDelegate delegate;
358 delegate.loop_ = loop;
359 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
360 fetcher->set_delegate(&delegate);
361
362 typename TestFixture::HttpServer server;
363 ASSERT_TRUE(server.started_);
364
365 StartTransferArgs start_xfer_args = {
366 fetcher.get(),
367 LocalServerUrlForPath("/flaky")
368 };
369
370 g_timeout_add(0, StartTransfer, &start_xfer_args);
371 g_main_loop_run(loop);
372
373 // verify the data we get back
374 ASSERT_EQ(100000, delegate.data.size());
375 for (int i = 0; i < 100000; i += 10) {
376 // Assert so that we don't flood the screen w/ EXPECT errors on failure.
377 ASSERT_EQ(delegate.data.substr(i, 10), "abcdefghij");
378 }
379 }
380 g_main_loop_unref(loop);
381}
382
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700383namespace {
384class FailureHttpFetcherTestDelegate : public HttpFetcherDelegate {
385 public:
386 FailureHttpFetcherTestDelegate() : loop_(NULL), server_(NULL) {}
387 virtual void ReceivedBytes(HttpFetcher* fetcher,
388 const char* bytes, int length) {
389 if (server_) {
390 LOG(INFO) << "Stopping server";
391 delete server_;
392 LOG(INFO) << "server stopped";
393 server_ = NULL;
394 }
395 }
396 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
397 EXPECT_FALSE(successful);
398 g_main_loop_quit(loop_);
399 }
400 GMainLoop* loop_;
401 PythonHttpServer* server_;
402};
403} // namespace {}
404
405
406TYPED_TEST(HttpFetcherTest, FailureTest) {
407 if (this->IsMock())
408 return;
409 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
410 {
411 FailureHttpFetcherTestDelegate delegate;
412 delegate.loop_ = loop;
413 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
414 fetcher->set_delegate(&delegate);
415
416 StartTransferArgs start_xfer_args = {
417 fetcher.get(),
418 LocalServerUrlForPath(this->SmallUrl())
419 };
420
421 g_timeout_add(0, StartTransfer, &start_xfer_args);
422 g_main_loop_run(loop);
423
424 // Exiting and testing happens in the delegate
425 }
426 g_main_loop_unref(loop);
427}
428
429TYPED_TEST(HttpFetcherTest, ServerDiesTest) {
430 if (this->IsMock())
431 return;
432 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
433 {
434 FailureHttpFetcherTestDelegate delegate;
435 delegate.loop_ = loop;
436 delegate.server_ = new PythonHttpServer;
437 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
438 fetcher->set_delegate(&delegate);
439
440 StartTransferArgs start_xfer_args = {
441 fetcher.get(),
442 LocalServerUrlForPath("/flaky")
443 };
444
445 g_timeout_add(0, StartTransfer, &start_xfer_args);
446 g_main_loop_run(loop);
447
448 // Exiting and testing happens in the delegate
449 }
450 g_main_loop_unref(loop);
451}
452
rspangler@google.com49fdf182009-10-10 00:57:34 +0000453} // namespace chromeos_update_engine