AU: Beginnings of delta support

- proto file for delta files; still needs hardlink support

- code to generate a delta update from two directory trees (old, new).

- code to parse delta update

- Actions: postinst-runner, install, bootable flag setter, filesystem
 copier, Omaha response handler, Omaha request preparer,

- misc utility functions, like StringHasSuffix(), templatized Action
 classes to feed/collect an object from another action.

- FilesystemIterator: iterates a directory tree with optional
 exclusion path. Tolerates deleting of files during iteration.

- Subprocess class: support for synchronously or asynchronously
 running an external command. Doesn't pass any env variables.

- Integration test that strings many Actions together and tests using
 actual Omaha/Lorry. Currently only tests full updates.

- New simple HTTP server for unittest that supports fake flaky
 connections.

- Some refactoring.

Review URL: http://codereview.chromium.org/466036


git-svn-id: svn://chrome-svn/chromeos/trunk@334 06c00378-0e64-4dae-be16-12b19f9950a1
diff --git a/http_fetcher_unittest.cc b/http_fetcher_unittest.cc
index f1e9c26..8d9c3f8 100644
--- a/http_fetcher_unittest.cc
+++ b/http_fetcher_unittest.cc
@@ -3,13 +3,18 @@
 // found in the LICENSE file.
 
 #include <unistd.h>
+#include <string>
+#include <vector>
 #include <base/scoped_ptr.h>
 #include <glib.h>
-#include <base/logging.h>
 #include <gtest/gtest.h>
+#include "chromeos/obsolete_logging.h"
 #include "update_engine/libcurl_http_fetcher.h"
 #include "update_engine/mock_http_fetcher.h"
 
+using std::string;
+using std::vector;
+
 namespace chromeos_update_engine {
 
 namespace {
@@ -27,6 +32,7 @@
   HttpFetcher* NewSmallFetcher() = 0;
   string BigUrl() const = 0;
   string SmallUrl() const = 0;
+  bool IsMock() const = 0;
 };
 
 class NullHttpServer {
@@ -53,13 +59,14 @@
   string SmallUrl() const {
     return "unused://unused";
   }
+  bool IsMock() const { return true; }
   typedef NullHttpServer HttpServer;
 };
 
 class PythonHttpServer {
  public:
   PythonHttpServer() {
-    char *argv[2] = {strdup("./test_http_server.py"), NULL};
+    char *argv[2] = {strdup("./test_http_server"), NULL};
     GError *err;
     started_ = false;
     if (!g_spawn_async(NULL,
@@ -74,7 +81,6 @@
     }
     int rc = 1;
     while (0 != rc) {
-      
       rc = system((string("wget --output-document=/dev/null ") +
                    LocalServerUrlForPath("/test")).c_str());
       usleep(10 * 1000);  // 10 ms
@@ -112,6 +118,7 @@
   string SmallUrl() const {
     return LocalServerUrlForPath("/foo");
   }
+  bool IsMock() const { return false; }
   typedef PythonHttpServer HttpServer;
 };
 
@@ -121,7 +128,7 @@
 
 namespace {
 class HttpFetcherTestDelegate : public HttpFetcherDelegate {
-public:
+ public:
   virtual void ReceivedBytes(HttpFetcher* fetcher,
                              const char* bytes, int length) {
     char str[length + 1];
@@ -133,6 +140,17 @@
   }
   GMainLoop* loop_;
 };
+
+struct StartTransferArgs {
+  HttpFetcher *http_fetcher;
+  string url;
+};
+
+gboolean StartTransfer(gpointer data) {
+  StartTransferArgs *args = reinterpret_cast<StartTransferArgs*>(data);
+  args->http_fetcher->BeginTransfer(args->url);
+  return FALSE;
+}
 }  // namespace {}
 
 TYPED_TEST(HttpFetcherTest, SimpleTest) {
@@ -146,7 +164,9 @@
     typename TestFixture::HttpServer server;
     ASSERT_TRUE(server.started_);
 
-    fetcher->BeginTransfer(this->SmallUrl());
+    StartTransferArgs start_xfer_args = {fetcher.get(), this->SmallUrl()};
+
+    g_timeout_add(0, StartTransfer, &start_xfer_args);
     g_main_loop_run(loop);
   }
   g_main_loop_unref(loop);
@@ -158,13 +178,11 @@
   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_);
@@ -173,7 +191,6 @@
     CHECK(paused_);
     paused_ = false;
     fetcher_->Unpause();
-    LOG(INFO) << "calling unpause";
   }
   bool paused_;
   HttpFetcher* fetcher_;
@@ -274,4 +291,50 @@
   g_main_loop_unref(loop);
 }
 
+namespace {
+class FlakyHttpFetcherTestDelegate : public HttpFetcherDelegate {
+ public:
+  virtual void ReceivedBytes(HttpFetcher* fetcher,
+                             const char* bytes, int length) {
+    data.append(bytes, length);
+  }
+  virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
+    g_main_loop_quit(loop_);
+  }
+  string data;
+  GMainLoop* loop_;
+};
+}  // namespace {}
+
+TYPED_TEST(HttpFetcherTest, FlakyTest) {
+  if (this->IsMock())
+    return;
+  GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+  {
+    FlakyHttpFetcherTestDelegate delegate;
+    delegate.loop_ = loop;
+    scoped_ptr<HttpFetcher> fetcher(this->NewSmallFetcher());
+    fetcher->set_delegate(&delegate);
+
+    typename TestFixture::HttpServer server;
+    ASSERT_TRUE(server.started_);
+
+    StartTransferArgs start_xfer_args = {
+      fetcher.get(),
+      LocalServerUrlForPath("/flaky")
+    };
+
+    g_timeout_add(0, StartTransfer, &start_xfer_args);
+    g_main_loop_run(loop);
+
+    // verify the data we get back
+    ASSERT_EQ(100000, delegate.data.size());
+    for (int i = 0; i < 100000; i += 10) {
+      // Assert so that we don't flood the screen w/ EXPECT errors on failure.
+      ASSERT_EQ(delegate.data.substr(i, 10), "abcdefghij");
+    }
+  }
+  g_main_loop_unref(loop);
+}
+
 }  // namespace chromeos_update_engine