blob: 92edf69c7f82b88cf4f44e02d9496302499d51cb [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>
6#include <base/scoped_ptr.h>
7#include <glib.h>
8#include <glog/logging.h>
9#include <gtest/gtest.h>
10#include "update_engine/libcurl_http_fetcher.h"
11#include "update_engine/mock_http_fetcher.h"
12
13namespace chromeos_update_engine {
14
15namespace {
16// WARNING, if you update this, you must also update test_http_server.py
17const char* const kServerPort = "8080";
18string LocalServerUrlForPath(const string& path) {
19 return string("http://127.0.0.1:") + kServerPort + path;
20}
21}
22
23template <typename T>
24class HttpFetcherTest : public ::testing::Test {
25 public:
26 HttpFetcher* NewLargeFetcher() = 0;
27 HttpFetcher* NewSmallFetcher() = 0;
28 string BigUrl() const = 0;
29 string SmallUrl() const = 0;
30};
31
32class NullHttpServer {
33 public:
34 NullHttpServer() : started_(true) {}
35 ~NullHttpServer() {}
36 bool started_;
37};
38
39
40template <>
41class HttpFetcherTest<MockHttpFetcher> : public ::testing::Test {
42 public:
43 HttpFetcher* NewLargeFetcher() {
44 vector<char> big_data(1000000);
45 return new MockHttpFetcher(big_data.data(), big_data.size());
46 }
47 HttpFetcher* NewSmallFetcher() {
48 return new MockHttpFetcher("x", 1);
49 }
50 string BigUrl() const {
51 return "unused://unused";
52 }
53 string SmallUrl() const {
54 return "unused://unused";
55 }
56 typedef NullHttpServer HttpServer;
57};
58
59class PythonHttpServer {
60 public:
61 PythonHttpServer() {
62 char *argv[2] = {strdup("./test_http_server.py"), NULL};
63 GError *err;
64 started_ = false;
65 if (!g_spawn_async(NULL,
66 argv,
67 NULL,
68 G_SPAWN_DO_NOT_REAP_CHILD,
69 NULL,
70 NULL,
71 &pid_,
72 &err)) {
73 return;
74 }
75 int rc = 1;
76 while (0 != rc) {
77
78 rc = system((string("wget --output-document=/dev/null ") +
79 LocalServerUrlForPath("/test")).c_str());
80 usleep(10 * 1000); // 10 ms
81 }
82 started_ = true;
83 free(argv[0]);
84 return;
85 }
86 ~PythonHttpServer() {
87 if (!started_)
88 return;
89 // request that the server exit itself
90 system((string("wget --output-document=/dev/null ") +
91 LocalServerUrlForPath("/quitquitquit")).c_str());
92 waitpid(pid_, NULL, 0);
93 }
94 GPid pid_;
95 bool started_;
96};
97
98template <>
99class HttpFetcherTest<LibcurlHttpFetcher> : public ::testing::Test {
100 public:
101 HttpFetcher* NewLargeFetcher() {
102 LibcurlHttpFetcher *ret = new LibcurlHttpFetcher;
103 ret->set_idle_ms(1); // speeds up test execution
104 return ret;
105 }
106 HttpFetcher* NewSmallFetcher() {
107 return NewLargeFetcher();
108 }
109 string BigUrl() const {
110 return LocalServerUrlForPath("/big");
111 }
112 string SmallUrl() const {
113 return LocalServerUrlForPath("/foo");
114 }
115 typedef PythonHttpServer HttpServer;
116};
117
118typedef ::testing::Types<LibcurlHttpFetcher, MockHttpFetcher>
119 HttpFetcherTestTypes;
120TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes);
121
122namespace {
123class HttpFetcherTestDelegate : public HttpFetcherDelegate {
124public:
125 virtual void ReceivedBytes(HttpFetcher* fetcher,
126 const char* bytes, int length) {
127 char str[length + 1];
128 memset(str, 0, length + 1);
129 memcpy(str, bytes, length);
130 }
131 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
132 g_main_loop_quit(loop_);
133 }
134 GMainLoop* loop_;
135};
136} // namespace {}
137
138TYPED_TEST(HttpFetcherTest, SimpleTest) {
139 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
140 {
141 HttpFetcherTestDelegate delegate;
142 delegate.loop_ = loop;
143 scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
144 fetcher->set_delegate(&delegate);
145
146 typename TestFixture::HttpServer server;
147 ASSERT_TRUE(server.started_);
148
149 fetcher->BeginTransfer(this->SmallUrl());
150 g_main_loop_run(loop);
151 }
152 g_main_loop_unref(loop);
153}
154
155namespace {
156class PausingHttpFetcherTestDelegate : public HttpFetcherDelegate {
157 public:
158 virtual void ReceivedBytes(HttpFetcher* fetcher,
159 const char* bytes, int length) {
160 char str[length + 1];
161 LOG(INFO) << "got " << length << " bytes";
162 memset(str, 0, length + 1);
163 memcpy(str, bytes, length);
164 CHECK(!paused_);
165 paused_ = true;
166 fetcher->Pause();
167 LOG(INFO) << "calling pause";
168 }
169 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
170 g_main_loop_quit(loop_);
171 }
172 void Unpause() {
173 CHECK(paused_);
174 paused_ = false;
175 fetcher_->Unpause();
176 LOG(INFO) << "calling unpause";
177 }
178 bool paused_;
179 HttpFetcher* fetcher_;
180 GMainLoop* loop_;
181};
182
183gboolean UnpausingTimeoutCallback(gpointer data) {
184 PausingHttpFetcherTestDelegate *delegate =
185 reinterpret_cast<PausingHttpFetcherTestDelegate*>(data);
186 if (delegate->paused_)
187 delegate->Unpause();
188 return TRUE;
189}
190} // namespace {}
191
192TYPED_TEST(HttpFetcherTest, PauseTest) {
193 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
194 {
195 PausingHttpFetcherTestDelegate delegate;
196 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
197 delegate.paused_ = false;
198 delegate.loop_ = loop;
199 delegate.fetcher_ = fetcher.get();
200 fetcher->set_delegate(&delegate);
201
202 typename TestFixture::HttpServer server;
203 ASSERT_TRUE(server.started_);
204 GSource* timeout_source_;
205 timeout_source_ = g_timeout_source_new(0); // ms
206 g_source_set_callback(timeout_source_, UnpausingTimeoutCallback, &delegate,
207 NULL);
208 g_source_attach(timeout_source_, NULL);
209 fetcher->BeginTransfer(this->BigUrl());
210
211 g_main_loop_run(loop);
212 g_source_destroy(timeout_source_);
213 }
214 g_main_loop_unref(loop);
215}
216
217namespace {
218class AbortingHttpFetcherTestDelegate : public HttpFetcherDelegate {
219 public:
220 virtual void ReceivedBytes(HttpFetcher* fetcher,
221 const char* bytes, int length) {}
222 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
223 CHECK(false); // We should never get here
224 g_main_loop_quit(loop_);
225 }
226 void TerminateTransfer() {
227 CHECK(once_);
228 once_ = false;
229 fetcher_->TerminateTransfer();
230 }
231 void EndLoop() {
232 g_main_loop_quit(loop_);
233 }
234 bool once_;
235 HttpFetcher* fetcher_;
236 GMainLoop* loop_;
237};
238
239gboolean AbortingTimeoutCallback(gpointer data) {
240 AbortingHttpFetcherTestDelegate *delegate =
241 reinterpret_cast<AbortingHttpFetcherTestDelegate*>(data);
242 if (delegate->once_) {
243 delegate->TerminateTransfer();
244 return TRUE;
245 } else {
246 delegate->EndLoop();
247 return FALSE;
248 }
249}
250} // namespace {}
251
252TYPED_TEST(HttpFetcherTest, AbortTest) {
253 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
254 {
255 AbortingHttpFetcherTestDelegate delegate;
256 scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
257 delegate.once_ = true;
258 delegate.loop_ = loop;
259 delegate.fetcher_ = fetcher.get();
260 fetcher->set_delegate(&delegate);
261
262 typename TestFixture::HttpServer server;
263 ASSERT_TRUE(server.started_);
264 GSource* timeout_source_;
265 timeout_source_ = g_timeout_source_new(0); // ms
266 g_source_set_callback(timeout_source_, AbortingTimeoutCallback, &delegate,
267 NULL);
268 g_source_attach(timeout_source_, NULL);
269 fetcher->BeginTransfer(this->BigUrl());
270
271 g_main_loop_run(loop);
272 g_source_destroy(timeout_source_);
273 }
274 g_main_loop_unref(loop);
275}
276
277} // namespace chromeos_update_engine