Almost there...



git-svn-id: svn://chrome-svn/chromeos/trunk@24 06c00378-0e64-4dae-be16-12b19f9950a1
diff --git a/download_action_unittest.cc b/download_action_unittest.cc
new file mode 100644
index 0000000..3dfc005
--- /dev/null
+++ b/download_action_unittest.cc
@@ -0,0 +1,264 @@
+// 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 <string>
+#include <vector>
+#include <glib.h>
+#include <gtest/gtest.h>
+#include "update_engine/action_pipe.h"
+#include "update_engine/download_action.h"
+#include "update_engine/mock_http_fetcher.h"
+#include "update_engine/omaha_hash_calculator.h"
+#include "update_engine/test_utils.h"
+
+namespace chromeos_update_engine {
+
+using std::string;
+using std::vector;
+
+class DownloadActionTest : public ::testing::Test { };
+
+namespace {
+class DownloadActionTestProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  DownloadActionTestProcessorDelegate()
+      : loop_(NULL), processing_done_called_(false) {}
+  virtual ~DownloadActionTestProcessorDelegate() {
+    EXPECT_TRUE(processing_done_called_);
+  }
+  virtual void ProcessingDone(const ActionProcessor* processor) {
+    ASSERT_TRUE(loop_);
+    g_main_loop_quit(loop_);
+    vector<char> found_data = ReadFile(path_);
+    ASSERT_EQ(expected_data_.size(), found_data.size());
+    for (unsigned i = 0; i < expected_data_.size(); i++) {
+      EXPECT_EQ(expected_data_[i], found_data[i]);
+    }
+    processing_done_called_ = true;
+  }
+
+  virtual void ActionCompleted(const ActionProcessor* processor,
+                               const AbstractAction* action,
+                               bool success) {
+    // make sure actions always succeed
+    EXPECT_TRUE(success);
+  }
+
+  GMainLoop *loop_;
+  string path_;
+  vector<char> expected_data_;
+  bool processing_done_called_;
+};
+
+struct EntryPointArgs {
+  const vector<char> *data;
+  GMainLoop *loop;
+  ActionProcessor *processor;
+};
+
+gboolean StartProcessorInRunLoop(gpointer data) {
+  ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
+  processor->StartProcessing();
+  return FALSE;
+}
+
+void TestWithData(const vector<char>& data, bool compress) {
+  vector<char> use_data;
+  if (compress) {
+    use_data = GzipCompressData(data);
+  } else {
+    use_data = data;
+  }
+
+  GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+
+  // TODO(adlr): see if we need a different file for build bots
+  const string path("/tmp/DownloadActionTest");
+  // takes ownership of passed in HttpFetcher
+  DownloadAction download_action("", path, 0,
+                                 OmahaHashCalculator::OmahaHashOfData(use_data),
+                                 compress, new MockHttpFetcher(&use_data[0],
+                                 use_data.size()));
+  DownloadActionTestProcessorDelegate delegate;
+  delegate.loop_ = loop;
+  delegate.expected_data_ = data;
+  delegate.path_ = path;
+  ActionProcessor processor;
+  processor.set_delegate(&delegate);
+  processor.EnqueueAction(&download_action);
+
+  g_timeout_add(0, &StartProcessorInRunLoop, &processor);
+  g_main_loop_run(loop);
+  g_main_loop_unref(loop);
+
+  // remove temp file; don't care if there are errors here
+  unlink(path.c_str());
+}
+}  // namespace {}
+
+TEST(DownloadActionTest, SimpleTest) {
+  vector<char> small;
+  const char* foo = "foo";
+  small.insert(small.end(), foo, foo + strlen(foo));
+  TestWithData(small, false);
+  TestWithData(small, true);
+}
+
+TEST(DownloadActionTest, LargeTest) {
+  vector<char> big(5 * kMockHttpFetcherChunkSize);
+  char c = '0';
+  for (unsigned int i = 0; i < big.size(); i++) {
+    big[i] = c;
+    if ('9' == c)
+      c = '0';
+    else
+      c++;
+  }
+  TestWithData(big, false);
+  TestWithData(big, true);
+}
+
+namespace {
+class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  void ProcessingStopped(const ActionProcessor* processor) {
+    ASSERT_TRUE(loop_);
+    g_main_loop_quit(loop_);
+  }
+  GMainLoop *loop_;
+};
+
+gboolean TerminateEarlyTestStarter(gpointer data) {
+  ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
+  processor->StartProcessing();
+  CHECK(processor->IsRunning());
+  processor->StopProcessing();
+  return FALSE;
+}
+
+}  // namespace {}
+
+TEST(DownloadActionTest, TerminateEarlyTest) {
+  GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+
+  vector<char> data(kMockHttpFetcherChunkSize + kMockHttpFetcherChunkSize / 2);
+  memset(&data[0], 0, data.size());
+
+  const string path("/tmp/DownloadActionTest");
+  {
+    // takes ownership of passed in HttpFetcher
+    DownloadAction download_action("", path, 0, "", false,
+        new MockHttpFetcher(&data[0], data.size()));
+    TerminateEarlyTestProcessorDelegate delegate;
+    delegate.loop_ = loop;
+    ActionProcessor processor;
+    processor.set_delegate(&delegate);
+    processor.EnqueueAction(&download_action);
+
+    g_timeout_add(0, &TerminateEarlyTestStarter, &processor);
+    g_main_loop_run(loop);
+    g_main_loop_unref(loop);
+  }
+
+  // 1 or 0 chunks should have come through
+  const off_t resulting_file_size(FileSize(path));
+  if (resulting_file_size != 0)
+    EXPECT_EQ(kMockHttpFetcherChunkSize, resulting_file_size);
+}
+
+class DownloadActionTestAction;
+
+template<>
+class ActionTraits<DownloadActionTestAction> {
+ public:
+  typedef string OutputObjectType;
+  typedef string InputObjectType;
+};
+
+// This is a simple Action class for testing.
+struct DownloadActionTestAction : public Action<DownloadActionTestAction> {
+  DownloadActionTestAction() : did_run_(false) {}
+  typedef string InputObjectType;
+  typedef string OutputObjectType;
+  ActionPipe<string>* in_pipe() { return in_pipe_.get(); }
+  ActionPipe<string>* out_pipe() { return out_pipe_.get(); }
+  ActionProcessor* processor() { return processor_; }
+  void PerformAction() {
+    did_run_ = true;
+    ASSERT_TRUE(HasInputObject());
+    EXPECT_EQ(expected_input_object_, GetInputObject());
+    ASSERT_TRUE(processor());
+    processor()->ActionComplete(this, true);
+  }
+  string Type() const { return "DownloadActionTestAction"; }
+  string expected_input_object_;
+  bool did_run_;
+};
+
+namespace {
+// This class is an ActionProcessorDelegate that simply terminates the
+// run loop when the ActionProcessor has completed processing. It's used
+// only by the test PassObjectOutTest.
+class PassObjectOutTestProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  void ProcessingDone(const ActionProcessor* processor) {
+    ASSERT_TRUE(loop_);
+    g_main_loop_quit(loop_);
+  }
+  GMainLoop *loop_;
+};
+
+gboolean PassObjectOutTestStarter(gpointer data) {
+  ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
+  processor->StartProcessing();
+  return FALSE;
+}
+}
+
+TEST(DownloadActionTest, PassObjectOutTest) {
+  GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+
+  const string path("/tmp/DownloadActionTest");
+
+  // takes ownership of passed in HttpFetcher
+  DownloadAction download_action("", path, 0,
+                                 OmahaHashCalculator::OmahaHashOfString("x"),
+                                 false, new MockHttpFetcher("x", 1));
+
+  DownloadActionTestAction test_action;
+  test_action.expected_input_object_ = path;
+  BondActions(&download_action, &test_action);
+
+  ActionProcessor processor;
+  PassObjectOutTestProcessorDelegate delegate;
+  delegate.loop_ = loop;
+  processor.set_delegate(&delegate);
+  processor.EnqueueAction(&download_action);
+  processor.EnqueueAction(&test_action);
+
+  g_timeout_add(0, &PassObjectOutTestStarter, &processor);
+  g_main_loop_run(loop);
+  g_main_loop_unref(loop);
+
+  EXPECT_EQ(true, test_action.did_run_);
+}
+
+TEST(DownloadActionTest, BadOutFileTest) {
+  GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+
+  const string path("/fake/path/that/cant/be/created/because/of/missing/dirs");
+
+  // takes ownership of passed in HttpFetcher
+  DownloadAction download_action("", path, 0, "", false,
+                                 new MockHttpFetcher("x", 1));
+
+  ActionProcessor processor;
+  processor.EnqueueAction(&download_action);
+  processor.StartProcessing();
+  ASSERT_FALSE(processor.IsRunning());
+
+  g_main_loop_unref(loop);
+}
+
+}  // namespace chromeos_update_engine