blob: 8d9c3f83d3f9311eb4022d936219798d3736b1e9 [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;
64};
65
66class PythonHttpServer {
67 public:
68 PythonHttpServer() {
adlr@google.comc98a7ed2009-12-04 18:54:03 +000069 char *argv[2] = {strdup("./test_http_server"), NULL};
rspangler@google.com49fdf182009-10-10 00:57:34 +000070 GError *err;
71 started_ = false;
72 if (!g_spawn_async(NULL,
73 argv,
74 NULL,
75 G_SPAWN_DO_NOT_REAP_CHILD,
76 NULL,
77 NULL,
78 &pid_,
79 &err)) {
80 return;
81 }
82 int rc = 1;
83 while (0 != rc) {
rspangler@google.com49fdf182009-10-10 00:57:34 +000084 rc = system((string("wget --output-document=/dev/null ") +
85 LocalServerUrlForPath("/test")).c_str());
86 usleep(10 * 1000); // 10 ms
87 }
88 started_ = true;
89 free(argv[0]);
90 return;
91 }
92 ~PythonHttpServer() {
93 if (!started_)
94 return;
95 // request that the server exit itself
96 system((string("wget --output-document=/dev/null ") +
97 LocalServerUrlForPath("/quitquitquit")).c_str());
98 waitpid(pid_, NULL, 0);
99 }
100 GPid pid_;
101 bool started_;
102};
103
104template <>
105class HttpFetcherTest<LibcurlHttpFetcher> : public ::testing::Test {
106 public:
107 HttpFetcher* NewLargeFetcher() {
108 LibcurlHttpFetcher *ret = new LibcurlHttpFetcher;
109 ret->set_idle_ms(1); // speeds up test execution
110 return ret;
111 }
112 HttpFetcher* NewSmallFetcher() {
113 return NewLargeFetcher();
114 }
115 string BigUrl() const {
116 return LocalServerUrlForPath("/big");
117 }
118 string SmallUrl() const {
119 return LocalServerUrlForPath("/foo");
120 }
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000121 bool IsMock() const { return false; }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000122 typedef PythonHttpServer HttpServer;
123};
124
125typedef ::testing::Types<LibcurlHttpFetcher, MockHttpFetcher>
126 HttpFetcherTestTypes;
127TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes);
128
129namespace {
130class HttpFetcherTestDelegate : public HttpFetcherDelegate {
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000131 public:
rspangler@google.com49fdf182009-10-10 00:57:34 +0000132 virtual void ReceivedBytes(HttpFetcher* fetcher,
133 const char* bytes, int length) {
134 char str[length + 1];
135 memset(str, 0, length + 1);
136 memcpy(str, bytes, length);
137 }
138 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
139 g_main_loop_quit(loop_);
140 }
141 GMainLoop* loop_;
142};
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000143
144struct StartTransferArgs {
145 HttpFetcher *http_fetcher;
146 string url;
147};
148
149gboolean StartTransfer(gpointer data) {
150 StartTransferArgs *args = reinterpret_cast<StartTransferArgs*>(data);
151 args->http_fetcher->BeginTransfer(args->url);
152 return FALSE;
153}
rspangler@google.com49fdf182009-10-10 00:57:34 +0000154} // namespace {}
155
156TYPED_TEST(HttpFetcherTest, SimpleTest) {
157 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
158 {
159 HttpFetcherTestDelegate delegate;
160 delegate.loop_ = loop;
161 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
162 fetcher->set_delegate(&delegate);
163
164 typename TestFixture::HttpServer server;
165 ASSERT_TRUE(server.started_);
166
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000167 StartTransferArgs start_xfer_args = {fetcher.get(), this->SmallUrl()};
168
169 g_timeout_add(0, StartTransfer, &start_xfer_args);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000170 g_main_loop_run(loop);
171 }
172 g_main_loop_unref(loop);
173}
174
175namespace {
176class PausingHttpFetcherTestDelegate : public HttpFetcherDelegate {
177 public:
178 virtual void ReceivedBytes(HttpFetcher* fetcher,
179 const char* bytes, int length) {
180 char str[length + 1];
rspangler@google.com49fdf182009-10-10 00:57:34 +0000181 memset(str, 0, length + 1);
182 memcpy(str, bytes, length);
183 CHECK(!paused_);
184 paused_ = true;
185 fetcher->Pause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000186 }
187 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
188 g_main_loop_quit(loop_);
189 }
190 void Unpause() {
191 CHECK(paused_);
192 paused_ = false;
193 fetcher_->Unpause();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000194 }
195 bool paused_;
196 HttpFetcher* fetcher_;
197 GMainLoop* loop_;
198};
199
200gboolean UnpausingTimeoutCallback(gpointer data) {
201 PausingHttpFetcherTestDelegate *delegate =
202 reinterpret_cast<PausingHttpFetcherTestDelegate*>(data);
203 if (delegate->paused_)
204 delegate->Unpause();
205 return TRUE;
206}
207} // namespace {}
208
209TYPED_TEST(HttpFetcherTest, PauseTest) {
210 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
211 {
212 PausingHttpFetcherTestDelegate delegate;
213 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
214 delegate.paused_ = false;
215 delegate.loop_ = loop;
216 delegate.fetcher_ = fetcher.get();
217 fetcher->set_delegate(&delegate);
218
219 typename TestFixture::HttpServer server;
220 ASSERT_TRUE(server.started_);
221 GSource* timeout_source_;
222 timeout_source_ = g_timeout_source_new(0); // ms
223 g_source_set_callback(timeout_source_, UnpausingTimeoutCallback, &delegate,
224 NULL);
225 g_source_attach(timeout_source_, NULL);
226 fetcher->BeginTransfer(this->BigUrl());
227
228 g_main_loop_run(loop);
229 g_source_destroy(timeout_source_);
230 }
231 g_main_loop_unref(loop);
232}
233
234namespace {
235class AbortingHttpFetcherTestDelegate : public HttpFetcherDelegate {
236 public:
237 virtual void ReceivedBytes(HttpFetcher* fetcher,
238 const char* bytes, int length) {}
239 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
240 CHECK(false); // We should never get here
241 g_main_loop_quit(loop_);
242 }
243 void TerminateTransfer() {
244 CHECK(once_);
245 once_ = false;
246 fetcher_->TerminateTransfer();
247 }
248 void EndLoop() {
249 g_main_loop_quit(loop_);
250 }
251 bool once_;
252 HttpFetcher* fetcher_;
253 GMainLoop* loop_;
254};
255
256gboolean AbortingTimeoutCallback(gpointer data) {
257 AbortingHttpFetcherTestDelegate *delegate =
258 reinterpret_cast<AbortingHttpFetcherTestDelegate*>(data);
259 if (delegate->once_) {
260 delegate->TerminateTransfer();
261 return TRUE;
262 } else {
263 delegate->EndLoop();
264 return FALSE;
265 }
266}
267} // namespace {}
268
269TYPED_TEST(HttpFetcherTest, AbortTest) {
270 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
271 {
272 AbortingHttpFetcherTestDelegate delegate;
273 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
274 delegate.once_ = true;
275 delegate.loop_ = loop;
276 delegate.fetcher_ = fetcher.get();
277 fetcher->set_delegate(&delegate);
278
279 typename TestFixture::HttpServer server;
280 ASSERT_TRUE(server.started_);
281 GSource* timeout_source_;
282 timeout_source_ = g_timeout_source_new(0); // ms
283 g_source_set_callback(timeout_source_, AbortingTimeoutCallback, &delegate,
284 NULL);
285 g_source_attach(timeout_source_, NULL);
286 fetcher->BeginTransfer(this->BigUrl());
287
288 g_main_loop_run(loop);
289 g_source_destroy(timeout_source_);
290 }
291 g_main_loop_unref(loop);
292}
293
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000294namespace {
295class FlakyHttpFetcherTestDelegate : public HttpFetcherDelegate {
296 public:
297 virtual void ReceivedBytes(HttpFetcher* fetcher,
298 const char* bytes, int length) {
299 data.append(bytes, length);
300 }
301 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
302 g_main_loop_quit(loop_);
303 }
304 string data;
305 GMainLoop* loop_;
306};
307} // namespace {}
308
309TYPED_TEST(HttpFetcherTest, FlakyTest) {
310 if (this->IsMock())
311 return;
312 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
313 {
314 FlakyHttpFetcherTestDelegate delegate;
315 delegate.loop_ = loop;
316 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
317 fetcher->set_delegate(&delegate);
318
319 typename TestFixture::HttpServer server;
320 ASSERT_TRUE(server.started_);
321
322 StartTransferArgs start_xfer_args = {
323 fetcher.get(),
324 LocalServerUrlForPath("/flaky")
325 };
326
327 g_timeout_add(0, StartTransfer, &start_xfer_args);
328 g_main_loop_run(loop);
329
330 // verify the data we get back
331 ASSERT_EQ(100000, delegate.data.size());
332 for (int i = 0; i < 100000; i += 10) {
333 // Assert so that we don't flood the screen w/ EXPECT errors on failure.
334 ASSERT_EQ(delegate.data.substr(i, 10), "abcdefghij");
335 }
336 }
337 g_main_loop_unref(loop);
338}
339
rspangler@google.com49fdf182009-10-10 00:57:34 +0000340} // namespace chromeos_update_engine