blob: 0a38c5c1aaf1bb98503c9ce4bb9d4b802d9cf06d [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>
adlr@google.comc98a7ed2009-12-04 18:54:03 +000011#include "chromeos/obsolete_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;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700125 ret->set_idle_ms(1000); // speeds up test execution
rspangler@google.com49fdf182009-10-10 00:57:34 +0000126 return ret;
127 }
128 HttpFetcher* NewSmallFetcher() {
129 return NewLargeFetcher();
130 }
131 string BigUrl() const {
132 return LocalServerUrlForPath("/big");
133 }
134 string SmallUrl() const {
135 return LocalServerUrlForPath("/foo");
136 }
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000137 bool IsMock() const { return false; }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000138 typedef PythonHttpServer HttpServer;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700139 void IgnoreServerAborting(HttpServer* server) const {
140 PythonHttpServer *pyserver = reinterpret_cast<PythonHttpServer*>(server);
141 pyserver->validate_quit_ = false;
142 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000143};
144
145typedef ::testing::Types<LibcurlHttpFetcher, MockHttpFetcher>
146 HttpFetcherTestTypes;
147TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes);
148
149namespace {
150class HttpFetcherTestDelegate : public HttpFetcherDelegate {
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000151 public:
rspangler@google.com49fdf182009-10-10 00:57:34 +0000152 virtual void ReceivedBytes(HttpFetcher* fetcher,
153 const char* bytes, int length) {
154 char str[length + 1];
155 memset(str, 0, length + 1);
156 memcpy(str, bytes, length);
157 }
158 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
159 g_main_loop_quit(loop_);
160 }
161 GMainLoop* loop_;
162};
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000163
164struct StartTransferArgs {
165 HttpFetcher *http_fetcher;
166 string url;
167};
168
169gboolean StartTransfer(gpointer data) {
170 StartTransferArgs *args = reinterpret_cast<StartTransferArgs*>(data);
171 args->http_fetcher->BeginTransfer(args->url);
172 return FALSE;
173}
rspangler@google.com49fdf182009-10-10 00:57:34 +0000174} // namespace {}
175
176TYPED_TEST(HttpFetcherTest, SimpleTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700177 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000178 {
179 HttpFetcherTestDelegate delegate;
180 delegate.loop_ = loop;
181 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
182 fetcher->set_delegate(&delegate);
183
184 typename TestFixture::HttpServer server;
185 ASSERT_TRUE(server.started_);
186
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000187 StartTransferArgs start_xfer_args = {fetcher.get(), this->SmallUrl()};
188
189 g_timeout_add(0, StartTransfer, &start_xfer_args);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000190 g_main_loop_run(loop);
191 }
192 g_main_loop_unref(loop);
193}
194
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700195TYPED_TEST(HttpFetcherTest, SimpleBigTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700196 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700197 {
198 HttpFetcherTestDelegate delegate;
199 delegate.loop_ = loop;
200 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
201 fetcher->set_delegate(&delegate);
202
203 typename TestFixture::HttpServer server;
204 ASSERT_TRUE(server.started_);
205
206 StartTransferArgs start_xfer_args = {fetcher.get(), this->BigUrl()};
207
208 g_timeout_add(0, StartTransfer, &start_xfer_args);
209 g_main_loop_run(loop);
210 }
211 g_main_loop_unref(loop);
212}
213
rspangler@google.com49fdf182009-10-10 00:57:34 +0000214namespace {
215class PausingHttpFetcherTestDelegate : public HttpFetcherDelegate {
216 public:
217 virtual void ReceivedBytes(HttpFetcher* fetcher,
218 const char* bytes, int length) {
219 char str[length + 1];
rspangler@google.com49fdf182009-10-10 00:57:34 +0000220 memset(str, 0, length + 1);
221 memcpy(str, bytes, length);
222 CHECK(!paused_);
223 paused_ = true;
224 fetcher->Pause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000225 }
226 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
227 g_main_loop_quit(loop_);
228 }
229 void Unpause() {
230 CHECK(paused_);
231 paused_ = false;
232 fetcher_->Unpause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000233 }
234 bool paused_;
235 HttpFetcher* fetcher_;
236 GMainLoop* loop_;
237};
238
239gboolean UnpausingTimeoutCallback(gpointer data) {
240 PausingHttpFetcherTestDelegate *delegate =
241 reinterpret_cast<PausingHttpFetcherTestDelegate*>(data);
242 if (delegate->paused_)
243 delegate->Unpause();
244 return TRUE;
245}
246} // namespace {}
247
248TYPED_TEST(HttpFetcherTest, PauseTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700249 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000250 {
251 PausingHttpFetcherTestDelegate delegate;
252 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
253 delegate.paused_ = false;
254 delegate.loop_ = loop;
255 delegate.fetcher_ = fetcher.get();
256 fetcher->set_delegate(&delegate);
257
258 typename TestFixture::HttpServer server;
259 ASSERT_TRUE(server.started_);
260 GSource* timeout_source_;
261 timeout_source_ = g_timeout_source_new(0); // ms
262 g_source_set_callback(timeout_source_, UnpausingTimeoutCallback, &delegate,
263 NULL);
264 g_source_attach(timeout_source_, NULL);
265 fetcher->BeginTransfer(this->BigUrl());
266
267 g_main_loop_run(loop);
268 g_source_destroy(timeout_source_);
269 }
270 g_main_loop_unref(loop);
271}
272
273namespace {
274class AbortingHttpFetcherTestDelegate : public HttpFetcherDelegate {
275 public:
276 virtual void ReceivedBytes(HttpFetcher* fetcher,
277 const char* bytes, int length) {}
278 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
279 CHECK(false); // We should never get here
280 g_main_loop_quit(loop_);
281 }
282 void TerminateTransfer() {
283 CHECK(once_);
284 once_ = false;
285 fetcher_->TerminateTransfer();
286 }
287 void EndLoop() {
288 g_main_loop_quit(loop_);
289 }
290 bool once_;
291 HttpFetcher* fetcher_;
292 GMainLoop* loop_;
293};
294
295gboolean AbortingTimeoutCallback(gpointer data) {
296 AbortingHttpFetcherTestDelegate *delegate =
297 reinterpret_cast<AbortingHttpFetcherTestDelegate*>(data);
298 if (delegate->once_) {
299 delegate->TerminateTransfer();
300 return TRUE;
301 } else {
302 delegate->EndLoop();
303 return FALSE;
304 }
305}
306} // namespace {}
307
308TYPED_TEST(HttpFetcherTest, AbortTest) {
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700309 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000310 {
311 AbortingHttpFetcherTestDelegate delegate;
312 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
313 delegate.once_ = true;
314 delegate.loop_ = loop;
315 delegate.fetcher_ = fetcher.get();
316 fetcher->set_delegate(&delegate);
317
318 typename TestFixture::HttpServer server;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700319 this->IgnoreServerAborting(&server);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000320 ASSERT_TRUE(server.started_);
321 GSource* timeout_source_;
322 timeout_source_ = g_timeout_source_new(0); // ms
323 g_source_set_callback(timeout_source_, AbortingTimeoutCallback, &delegate,
324 NULL);
325 g_source_attach(timeout_source_, NULL);
326 fetcher->BeginTransfer(this->BigUrl());
327
328 g_main_loop_run(loop);
329 g_source_destroy(timeout_source_);
330 }
331 g_main_loop_unref(loop);
332}
333
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000334namespace {
335class FlakyHttpFetcherTestDelegate : public HttpFetcherDelegate {
336 public:
337 virtual void ReceivedBytes(HttpFetcher* fetcher,
338 const char* bytes, int length) {
339 data.append(bytes, length);
340 }
341 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
Andrew de los Reyesfb4ad7d2010-07-19 10:43:46 -0700342 EXPECT_TRUE(successful);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000343 g_main_loop_quit(loop_);
344 }
345 string data;
346 GMainLoop* loop_;
347};
348} // namespace {}
349
350TYPED_TEST(HttpFetcherTest, FlakyTest) {
351 if (this->IsMock())
352 return;
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700353 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000354 {
355 FlakyHttpFetcherTestDelegate delegate;
356 delegate.loop_ = loop;
357 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
358 fetcher->set_delegate(&delegate);
359
360 typename TestFixture::HttpServer server;
361 ASSERT_TRUE(server.started_);
362
363 StartTransferArgs start_xfer_args = {
364 fetcher.get(),
365 LocalServerUrlForPath("/flaky")
366 };
367
368 g_timeout_add(0, StartTransfer, &start_xfer_args);
369 g_main_loop_run(loop);
370
371 // verify the data we get back
372 ASSERT_EQ(100000, delegate.data.size());
373 for (int i = 0; i < 100000; i += 10) {
374 // Assert so that we don't flood the screen w/ EXPECT errors on failure.
375 ASSERT_EQ(delegate.data.substr(i, 10), "abcdefghij");
376 }
377 }
378 g_main_loop_unref(loop);
379}
380
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700381namespace {
382class FailureHttpFetcherTestDelegate : public HttpFetcherDelegate {
383 public:
384 FailureHttpFetcherTestDelegate() : loop_(NULL), server_(NULL) {}
385 virtual void ReceivedBytes(HttpFetcher* fetcher,
386 const char* bytes, int length) {
387 if (server_) {
388 LOG(INFO) << "Stopping server";
389 delete server_;
390 LOG(INFO) << "server stopped";
391 server_ = NULL;
392 }
393 }
394 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
395 EXPECT_FALSE(successful);
396 g_main_loop_quit(loop_);
397 }
398 GMainLoop* loop_;
399 PythonHttpServer* server_;
400};
401} // namespace {}
402
403
404TYPED_TEST(HttpFetcherTest, FailureTest) {
405 if (this->IsMock())
406 return;
407 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
408 {
409 FailureHttpFetcherTestDelegate delegate;
410 delegate.loop_ = loop;
411 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
412 fetcher->set_delegate(&delegate);
413
414 StartTransferArgs start_xfer_args = {
415 fetcher.get(),
416 LocalServerUrlForPath(this->SmallUrl())
417 };
418
419 g_timeout_add(0, StartTransfer, &start_xfer_args);
420 g_main_loop_run(loop);
421
422 // Exiting and testing happens in the delegate
423 }
424 g_main_loop_unref(loop);
425}
426
427TYPED_TEST(HttpFetcherTest, ServerDiesTest) {
428 if (this->IsMock())
429 return;
430 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
431 {
432 FailureHttpFetcherTestDelegate delegate;
433 delegate.loop_ = loop;
434 delegate.server_ = new PythonHttpServer;
435 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
436 fetcher->set_delegate(&delegate);
437
438 StartTransferArgs start_xfer_args = {
439 fetcher.get(),
440 LocalServerUrlForPath("/flaky")
441 };
442
443 g_timeout_add(0, StartTransfer, &start_xfer_args);
444 g_main_loop_run(loop);
445
446 // Exiting and testing happens in the delegate
447 }
448 g_main_loop_unref(loop);
449}
450
rspangler@google.com49fdf182009-10-10 00:57:34 +0000451} // namespace chromeos_update_engine