Almost there...
git-svn-id: svn://chrome-svn/chromeos/trunk@24 06c00378-0e64-4dae-be16-12b19f9950a1
diff --git a/http_fetcher_unittest.cc b/http_fetcher_unittest.cc
new file mode 100644
index 0000000..92edf69
--- /dev/null
+++ b/http_fetcher_unittest.cc
@@ -0,0 +1,277 @@
+// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <unistd.h>
+#include <base/scoped_ptr.h>
+#include <glib.h>
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+#include "update_engine/libcurl_http_fetcher.h"
+#include "update_engine/mock_http_fetcher.h"
+
+namespace chromeos_update_engine {
+
+namespace {
+// WARNING, if you update this, you must also update test_http_server.py
+const char* const kServerPort = "8080";
+string LocalServerUrlForPath(const string& path) {
+ return string("http://127.0.0.1:") + kServerPort + path;
+}
+}
+
+template <typename T>
+class HttpFetcherTest : public ::testing::Test {
+ public:
+ HttpFetcher* NewLargeFetcher() = 0;
+ HttpFetcher* NewSmallFetcher() = 0;
+ string BigUrl() const = 0;
+ string SmallUrl() const = 0;
+};
+
+class NullHttpServer {
+ public:
+ NullHttpServer() : started_(true) {}
+ ~NullHttpServer() {}
+ bool started_;
+};
+
+
+template <>
+class HttpFetcherTest<MockHttpFetcher> : public ::testing::Test {
+ public:
+ HttpFetcher* NewLargeFetcher() {
+ vector<char> big_data(1000000);
+ return new MockHttpFetcher(big_data.data(), big_data.size());
+ }
+ HttpFetcher* NewSmallFetcher() {
+ return new MockHttpFetcher("x", 1);
+ }
+ string BigUrl() const {
+ return "unused://unused";
+ }
+ string SmallUrl() const {
+ return "unused://unused";
+ }
+ typedef NullHttpServer HttpServer;
+};
+
+class PythonHttpServer {
+ public:
+ PythonHttpServer() {
+ char *argv[2] = {strdup("./test_http_server.py"), NULL};
+ GError *err;
+ started_ = false;
+ if (!g_spawn_async(NULL,
+ argv,
+ NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL,
+ NULL,
+ &pid_,
+ &err)) {
+ return;
+ }
+ int rc = 1;
+ while (0 != rc) {
+
+ rc = system((string("wget --output-document=/dev/null ") +
+ LocalServerUrlForPath("/test")).c_str());
+ usleep(10 * 1000); // 10 ms
+ }
+ started_ = true;
+ free(argv[0]);
+ return;
+ }
+ ~PythonHttpServer() {
+ if (!started_)
+ return;
+ // request that the server exit itself
+ system((string("wget --output-document=/dev/null ") +
+ LocalServerUrlForPath("/quitquitquit")).c_str());
+ waitpid(pid_, NULL, 0);
+ }
+ GPid pid_;
+ bool started_;
+};
+
+template <>
+class HttpFetcherTest<LibcurlHttpFetcher> : public ::testing::Test {
+ public:
+ HttpFetcher* NewLargeFetcher() {
+ LibcurlHttpFetcher *ret = new LibcurlHttpFetcher;
+ ret->set_idle_ms(1); // speeds up test execution
+ return ret;
+ }
+ HttpFetcher* NewSmallFetcher() {
+ return NewLargeFetcher();
+ }
+ string BigUrl() const {
+ return LocalServerUrlForPath("/big");
+ }
+ string SmallUrl() const {
+ return LocalServerUrlForPath("/foo");
+ }
+ typedef PythonHttpServer HttpServer;
+};
+
+typedef ::testing::Types<LibcurlHttpFetcher, MockHttpFetcher>
+ HttpFetcherTestTypes;
+TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes);
+
+namespace {
+class HttpFetcherTestDelegate : public HttpFetcherDelegate {
+public:
+ virtual void ReceivedBytes(HttpFetcher* fetcher,
+ const char* bytes, int length) {
+ char str[length + 1];
+ memset(str, 0, length + 1);
+ memcpy(str, bytes, length);
+ }
+ virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
+ g_main_loop_quit(loop_);
+ }
+ GMainLoop* loop_;
+};
+} // namespace {}
+
+TYPED_TEST(HttpFetcherTest, SimpleTest) {
+ GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+ {
+ HttpFetcherTestDelegate delegate;
+ delegate.loop_ = loop;
+ scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
+ fetcher->set_delegate(&delegate);
+
+ typename TestFixture::HttpServer server;
+ ASSERT_TRUE(server.started_);
+
+ fetcher->BeginTransfer(this->SmallUrl());
+ g_main_loop_run(loop);
+ }
+ g_main_loop_unref(loop);
+}
+
+namespace {
+class PausingHttpFetcherTestDelegate : public HttpFetcherDelegate {
+ public:
+ virtual void ReceivedBytes(HttpFetcher* fetcher,
+ const char* bytes, int length) {
+ char str[length + 1];
+ LOG(INFO) << "got " << length << " bytes";
+ memset(str, 0, length + 1);
+ memcpy(str, bytes, length);
+ CHECK(!paused_);
+ paused_ = true;
+ fetcher->Pause();
+ LOG(INFO) << "calling pause";
+ }
+ virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
+ g_main_loop_quit(loop_);
+ }
+ void Unpause() {
+ CHECK(paused_);
+ paused_ = false;
+ fetcher_->Unpause();
+ LOG(INFO) << "calling unpause";
+ }
+ bool paused_;
+ HttpFetcher* fetcher_;
+ GMainLoop* loop_;
+};
+
+gboolean UnpausingTimeoutCallback(gpointer data) {
+ PausingHttpFetcherTestDelegate *delegate =
+ reinterpret_cast<PausingHttpFetcherTestDelegate*>(data);
+ if (delegate->paused_)
+ delegate->Unpause();
+ return TRUE;
+}
+} // namespace {}
+
+TYPED_TEST(HttpFetcherTest, PauseTest) {
+ GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+ {
+ PausingHttpFetcherTestDelegate delegate;
+ scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
+ delegate.paused_ = false;
+ delegate.loop_ = loop;
+ delegate.fetcher_ = fetcher.get();
+ fetcher->set_delegate(&delegate);
+
+ typename TestFixture::HttpServer server;
+ ASSERT_TRUE(server.started_);
+ GSource* timeout_source_;
+ timeout_source_ = g_timeout_source_new(0); // ms
+ g_source_set_callback(timeout_source_, UnpausingTimeoutCallback, &delegate,
+ NULL);
+ g_source_attach(timeout_source_, NULL);
+ fetcher->BeginTransfer(this->BigUrl());
+
+ g_main_loop_run(loop);
+ g_source_destroy(timeout_source_);
+ }
+ g_main_loop_unref(loop);
+}
+
+namespace {
+class AbortingHttpFetcherTestDelegate : public HttpFetcherDelegate {
+ public:
+ virtual void ReceivedBytes(HttpFetcher* fetcher,
+ const char* bytes, int length) {}
+ virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
+ CHECK(false); // We should never get here
+ g_main_loop_quit(loop_);
+ }
+ void TerminateTransfer() {
+ CHECK(once_);
+ once_ = false;
+ fetcher_->TerminateTransfer();
+ }
+ void EndLoop() {
+ g_main_loop_quit(loop_);
+ }
+ bool once_;
+ HttpFetcher* fetcher_;
+ GMainLoop* loop_;
+};
+
+gboolean AbortingTimeoutCallback(gpointer data) {
+ AbortingHttpFetcherTestDelegate *delegate =
+ reinterpret_cast<AbortingHttpFetcherTestDelegate*>(data);
+ if (delegate->once_) {
+ delegate->TerminateTransfer();
+ return TRUE;
+ } else {
+ delegate->EndLoop();
+ return FALSE;
+ }
+}
+} // namespace {}
+
+TYPED_TEST(HttpFetcherTest, AbortTest) {
+ GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+ {
+ AbortingHttpFetcherTestDelegate delegate;
+ scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
+ delegate.once_ = true;
+ delegate.loop_ = loop;
+ delegate.fetcher_ = fetcher.get();
+ fetcher->set_delegate(&delegate);
+
+ typename TestFixture::HttpServer server;
+ ASSERT_TRUE(server.started_);
+ GSource* timeout_source_;
+ timeout_source_ = g_timeout_source_new(0); // ms
+ g_source_set_callback(timeout_source_, AbortingTimeoutCallback, &delegate,
+ NULL);
+ g_source_attach(timeout_source_, NULL);
+ fetcher->BeginTransfer(this->BigUrl());
+
+ g_main_loop_run(loop);
+ g_source_destroy(timeout_source_);
+ }
+ g_main_loop_unref(loop);
+}
+
+} // namespace chromeos_update_engine