blob: a0e620b817f449faa888ccb244882f865b2a0a3b [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;
85 while (0 != rc) {
rspangler@google.com49fdf182009-10-10 00:57:34 +000086 rc = system((string("wget --output-document=/dev/null ") +
87 LocalServerUrlForPath("/test")).c_str());
88 usleep(10 * 1000); // 10 ms
89 }
90 started_ = true;
91 free(argv[0]);
92 return;
93 }
94 ~PythonHttpServer() {
95 if (!started_)
96 return;
97 // request that the server exit itself
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070098 int rc = system((string("wget -t 1 --output-document=/dev/null ") +
99 LocalServerUrlForPath("/quitquitquit")).c_str());
100 if (validate_quit_)
101 EXPECT_EQ(0, rc);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000102 waitpid(pid_, NULL, 0);
103 }
104 GPid pid_;
105 bool started_;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700106 bool validate_quit_;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000107};
108
109template <>
110class HttpFetcherTest<LibcurlHttpFetcher> : public ::testing::Test {
111 public:
112 HttpFetcher* NewLargeFetcher() {
113 LibcurlHttpFetcher *ret = new LibcurlHttpFetcher;
114 ret->set_idle_ms(1); // speeds up test execution
115 return ret;
116 }
117 HttpFetcher* NewSmallFetcher() {
118 return NewLargeFetcher();
119 }
120 string BigUrl() const {
121 return LocalServerUrlForPath("/big");
122 }
123 string SmallUrl() const {
124 return LocalServerUrlForPath("/foo");
125 }
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000126 bool IsMock() const { return false; }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000127 typedef PythonHttpServer HttpServer;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700128 void IgnoreServerAborting(HttpServer* server) const {
129 PythonHttpServer *pyserver = reinterpret_cast<PythonHttpServer*>(server);
130 pyserver->validate_quit_ = false;
131 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000132};
133
134typedef ::testing::Types<LibcurlHttpFetcher, MockHttpFetcher>
135 HttpFetcherTestTypes;
136TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes);
137
138namespace {
139class HttpFetcherTestDelegate : public HttpFetcherDelegate {
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000140 public:
rspangler@google.com49fdf182009-10-10 00:57:34 +0000141 virtual void ReceivedBytes(HttpFetcher* fetcher,
142 const char* bytes, int length) {
143 char str[length + 1];
144 memset(str, 0, length + 1);
145 memcpy(str, bytes, length);
146 }
147 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
148 g_main_loop_quit(loop_);
149 }
150 GMainLoop* loop_;
151};
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000152
153struct StartTransferArgs {
154 HttpFetcher *http_fetcher;
155 string url;
156};
157
158gboolean StartTransfer(gpointer data) {
159 StartTransferArgs *args = reinterpret_cast<StartTransferArgs*>(data);
160 args->http_fetcher->BeginTransfer(args->url);
161 return FALSE;
162}
rspangler@google.com49fdf182009-10-10 00:57:34 +0000163} // namespace {}
164
165TYPED_TEST(HttpFetcherTest, SimpleTest) {
166 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
167 {
168 HttpFetcherTestDelegate delegate;
169 delegate.loop_ = loop;
170 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
171 fetcher->set_delegate(&delegate);
172
173 typename TestFixture::HttpServer server;
174 ASSERT_TRUE(server.started_);
175
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000176 StartTransferArgs start_xfer_args = {fetcher.get(), this->SmallUrl()};
177
178 g_timeout_add(0, StartTransfer, &start_xfer_args);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000179 g_main_loop_run(loop);
180 }
181 g_main_loop_unref(loop);
182}
183
184namespace {
185class PausingHttpFetcherTestDelegate : public HttpFetcherDelegate {
186 public:
187 virtual void ReceivedBytes(HttpFetcher* fetcher,
188 const char* bytes, int length) {
189 char str[length + 1];
rspangler@google.com49fdf182009-10-10 00:57:34 +0000190 memset(str, 0, length + 1);
191 memcpy(str, bytes, length);
192 CHECK(!paused_);
193 paused_ = true;
194 fetcher->Pause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000195 }
196 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
197 g_main_loop_quit(loop_);
198 }
199 void Unpause() {
200 CHECK(paused_);
201 paused_ = false;
202 fetcher_->Unpause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000203 }
204 bool paused_;
205 HttpFetcher* fetcher_;
206 GMainLoop* loop_;
207};
208
209gboolean UnpausingTimeoutCallback(gpointer data) {
210 PausingHttpFetcherTestDelegate *delegate =
211 reinterpret_cast<PausingHttpFetcherTestDelegate*>(data);
212 if (delegate->paused_)
213 delegate->Unpause();
214 return TRUE;
215}
216} // namespace {}
217
218TYPED_TEST(HttpFetcherTest, PauseTest) {
219 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
220 {
221 PausingHttpFetcherTestDelegate delegate;
222 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
223 delegate.paused_ = false;
224 delegate.loop_ = loop;
225 delegate.fetcher_ = fetcher.get();
226 fetcher->set_delegate(&delegate);
227
228 typename TestFixture::HttpServer server;
229 ASSERT_TRUE(server.started_);
230 GSource* timeout_source_;
231 timeout_source_ = g_timeout_source_new(0); // ms
232 g_source_set_callback(timeout_source_, UnpausingTimeoutCallback, &delegate,
233 NULL);
234 g_source_attach(timeout_source_, NULL);
235 fetcher->BeginTransfer(this->BigUrl());
236
237 g_main_loop_run(loop);
238 g_source_destroy(timeout_source_);
239 }
240 g_main_loop_unref(loop);
241}
242
243namespace {
244class AbortingHttpFetcherTestDelegate : public HttpFetcherDelegate {
245 public:
246 virtual void ReceivedBytes(HttpFetcher* fetcher,
247 const char* bytes, int length) {}
248 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
249 CHECK(false); // We should never get here
250 g_main_loop_quit(loop_);
251 }
252 void TerminateTransfer() {
253 CHECK(once_);
254 once_ = false;
255 fetcher_->TerminateTransfer();
256 }
257 void EndLoop() {
258 g_main_loop_quit(loop_);
259 }
260 bool once_;
261 HttpFetcher* fetcher_;
262 GMainLoop* loop_;
263};
264
265gboolean AbortingTimeoutCallback(gpointer data) {
266 AbortingHttpFetcherTestDelegate *delegate =
267 reinterpret_cast<AbortingHttpFetcherTestDelegate*>(data);
268 if (delegate->once_) {
269 delegate->TerminateTransfer();
270 return TRUE;
271 } else {
272 delegate->EndLoop();
273 return FALSE;
274 }
275}
276} // namespace {}
277
278TYPED_TEST(HttpFetcherTest, AbortTest) {
279 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
280 {
281 AbortingHttpFetcherTestDelegate delegate;
282 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
283 delegate.once_ = true;
284 delegate.loop_ = loop;
285 delegate.fetcher_ = fetcher.get();
286 fetcher->set_delegate(&delegate);
287
288 typename TestFixture::HttpServer server;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700289 this->IgnoreServerAborting(&server);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000290 ASSERT_TRUE(server.started_);
291 GSource* timeout_source_;
292 timeout_source_ = g_timeout_source_new(0); // ms
293 g_source_set_callback(timeout_source_, AbortingTimeoutCallback, &delegate,
294 NULL);
295 g_source_attach(timeout_source_, NULL);
296 fetcher->BeginTransfer(this->BigUrl());
297
298 g_main_loop_run(loop);
299 g_source_destroy(timeout_source_);
300 }
301 g_main_loop_unref(loop);
302}
303
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000304namespace {
305class FlakyHttpFetcherTestDelegate : public HttpFetcherDelegate {
306 public:
307 virtual void ReceivedBytes(HttpFetcher* fetcher,
308 const char* bytes, int length) {
309 data.append(bytes, length);
310 }
311 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
312 g_main_loop_quit(loop_);
313 }
314 string data;
315 GMainLoop* loop_;
316};
317} // namespace {}
318
319TYPED_TEST(HttpFetcherTest, FlakyTest) {
320 if (this->IsMock())
321 return;
322 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
323 {
324 FlakyHttpFetcherTestDelegate delegate;
325 delegate.loop_ = loop;
326 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
327 fetcher->set_delegate(&delegate);
328
329 typename TestFixture::HttpServer server;
330 ASSERT_TRUE(server.started_);
331
332 StartTransferArgs start_xfer_args = {
333 fetcher.get(),
334 LocalServerUrlForPath("/flaky")
335 };
336
337 g_timeout_add(0, StartTransfer, &start_xfer_args);
338 g_main_loop_run(loop);
339
340 // verify the data we get back
341 ASSERT_EQ(100000, delegate.data.size());
342 for (int i = 0; i < 100000; i += 10) {
343 // Assert so that we don't flood the screen w/ EXPECT errors on failure.
344 ASSERT_EQ(delegate.data.substr(i, 10), "abcdefghij");
345 }
346 }
347 g_main_loop_unref(loop);
348}
349
rspangler@google.com49fdf182009-10-10 00:57:34 +0000350} // namespace chromeos_update_engine