Missed new files in last commit

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


git-svn-id: svn://chrome-svn/chromeos/trunk@336 06c00378-0e64-4dae-be16-12b19f9950a1
diff --git a/integration_unittest.cc b/integration_unittest.cc
new file mode 100644
index 0000000..671240f
--- /dev/null
+++ b/integration_unittest.cc
@@ -0,0 +1,189 @@
+// 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 <pthread.h>
+#include <gtest/gtest.h>
+#include "update_engine/download_action.h"
+#include "update_engine/install_action.h"
+#include "update_engine/libcurl_http_fetcher.h"
+#include "update_engine/mock_http_fetcher.h"
+#include "update_engine/omaha_request_prep_action.h"
+#include "update_engine/omaha_response_handler_action.h"
+#include "update_engine/postinstall_runner_action.h"
+#include "update_engine/set_bootable_flag_action.h"
+#include "update_engine/test_utils.h"
+#include "update_engine/update_check_action.h"
+#include "update_engine/utils.h"
+
+// The tests here integrate many Action objects together. This test that
+// the objects work well together, whereas most other tests focus on a single
+// action at a time.
+
+namespace chromeos_update_engine {
+
+using std::string;
+using std::vector;
+
+class IntegrationTest : public ::testing::Test { };
+
+namespace {
+const char* kTestDir = "/tmp/update_engine-integration-test";
+
+class IntegrationTestProcessorDelegate : public ActionProcessorDelegate {
+ public:
+  IntegrationTestProcessorDelegate()
+      : loop_(NULL), processing_done_called_(false) {}
+  virtual ~IntegrationTestProcessorDelegate() {
+    EXPECT_TRUE(processing_done_called_);
+  }
+  virtual void ProcessingDone(const ActionProcessor* processor) {
+    processing_done_called_ = true;
+    g_main_loop_quit(loop_);
+  }
+
+  virtual void ActionCompleted(ActionProcessor* processor,
+                               AbstractAction* action,
+                               bool success) {
+    // make sure actions always succeed
+    EXPECT_TRUE(success);
+
+    // Swap in the device path for PostinstallRunnerAction with a loop device
+    if (action->Type() == InstallAction::StaticType()) {
+      InstallAction* install_action = static_cast<InstallAction*>(action);
+      old_dev_ = install_action->GetOutputObject();
+      string dev = GetUnusedLoopDevice();
+      string cmd = string("losetup ") + dev + " " + kTestDir + "/dev2";
+      EXPECT_EQ(0, system(cmd.c_str()));
+      install_action->SetOutputObject(dev);
+    } else if (action->Type() == PostinstallRunnerAction::StaticType()) {
+      PostinstallRunnerAction* postinstall_runner_action =
+          static_cast<PostinstallRunnerAction*>(action);
+      string dev = postinstall_runner_action->GetOutputObject();
+      EXPECT_EQ(0, system((string("losetup -d ") + dev).c_str()));
+      postinstall_runner_action->SetOutputObject(old_dev_);
+      old_dev_ = "";
+    }
+  }
+
+  void set_loop(GMainLoop* loop) {
+    loop_ = loop;
+  }
+
+ private:
+  GMainLoop *loop_;
+  bool processing_done_called_;
+
+  // We have to change the dev for the PostinstallRunnerAction action.
+  // Before that runs, we store the device here, and after it runs, we
+  // restore it.
+  // This is because we use a file, rather than a device, to install into,
+  // but the PostinstallRunnerAction requires a real device. We set up a
+  // loop device pointing to the file when necessary.
+  string old_dev_;
+};
+
+gboolean TestStarter(gpointer data) {
+  ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
+  processor->StartProcessing();
+  return FALSE;
+}
+
+}  // namespace {}
+
+TEST(IntegrationTest, DISABLED_RunAsRootFullInstallTest) {
+  ASSERT_EQ(0, getuid());
+  GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+
+  ActionProcessor processor;
+  IntegrationTestProcessorDelegate delegate;
+  delegate.set_loop(loop);
+  processor.set_delegate(&delegate);
+
+  // Actions:
+  OmahaRequestPrepAction request_prep_action(false);
+  UpdateCheckAction update_check_action(new LibcurlHttpFetcher);
+  OmahaResponseHandlerAction response_handler_action;
+  DownloadAction download_action(new LibcurlHttpFetcher);
+  InstallAction install_action;
+  PostinstallRunnerAction postinstall_runner_action;
+  SetBootableFlagAction set_bootable_flag_action;
+
+  // Enqueue the actions
+  processor.EnqueueAction(&request_prep_action);
+  processor.EnqueueAction(&update_check_action);
+  processor.EnqueueAction(&response_handler_action);
+  processor.EnqueueAction(&download_action);
+  processor.EnqueueAction(&install_action);
+  processor.EnqueueAction(&postinstall_runner_action);
+  processor.EnqueueAction(&set_bootable_flag_action);
+
+  // Bond them together
+  BondActions(&request_prep_action, &update_check_action);
+  BondActions(&update_check_action, &response_handler_action);
+  BondActions(&response_handler_action, &download_action);
+  BondActions(&download_action, &install_action);
+  BondActions(&install_action, &postinstall_runner_action);
+  BondActions(&postinstall_runner_action, &set_bootable_flag_action);
+
+  // Set up filesystem to trick some of the actions
+  ASSERT_EQ(0, System(string("rm -rf ") + kTestDir));
+  ASSERT_EQ(0, system("rm -f /tmp/update_engine_test_postinst_out.txt"));
+  ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
+  ASSERT_TRUE(WriteFileString(string(kTestDir) + "/etc/lsb-release",
+                              "GOOGLE_RELEASE=0.2.0.0\n"
+                              "GOOGLE_TRACK=unittest-track"));
+  ASSERT_EQ(0, System(string("touch ") + kTestDir + "/dev1"));
+  ASSERT_EQ(0, System(string("touch ") + kTestDir + "/dev2"));
+  ASSERT_TRUE(WriteFileVector(string(kTestDir) + "/dev", GenerateSampleMbr()));
+
+  request_prep_action.set_root(kTestDir);
+  response_handler_action.set_boot_device(string(kTestDir) + "/dev1");
+
+  // Run the actions
+  g_timeout_add(0, &TestStarter, &processor);
+  g_main_loop_run(loop);
+  g_main_loop_unref(loop);
+
+  // Verify the results
+  struct stat stbuf;
+  ASSERT_EQ(0, lstat((string(kTestDir) + "/dev1").c_str(), &stbuf));
+  EXPECT_EQ(0, stbuf.st_size);
+  EXPECT_TRUE(S_ISREG(stbuf.st_mode));
+  ASSERT_EQ(0, lstat((string(kTestDir) + "/dev2").c_str(), &stbuf));
+  EXPECT_EQ(996147200, stbuf.st_size);
+  EXPECT_TRUE(S_ISREG(stbuf.st_mode));
+  ASSERT_EQ(0, lstat((string(kTestDir) + "/dev").c_str(), &stbuf));
+  ASSERT_EQ(512, stbuf.st_size);
+  EXPECT_TRUE(S_ISREG(stbuf.st_mode));
+  vector<char> new_mbr;
+  EXPECT_TRUE(utils::ReadFile((string(kTestDir) + "/dev").c_str(), &new_mbr));
+
+  // Check bootable flag in MBR
+  for (int i = 0; i < 4; i++) {
+    char expected_flag = '\0';
+    if (i == 1)
+      expected_flag = 0x80;
+    EXPECT_EQ(expected_flag, new_mbr[446 + 16 * i]);
+  }
+  // Check MBR signature
+  EXPECT_EQ(static_cast<char>(0x55), new_mbr[510]);
+  EXPECT_EQ(static_cast<char>(0xaa), new_mbr[511]);
+
+  ASSERT_EQ(0, lstat("/tmp/update_engine_test_postinst_out.txt", &stbuf));
+  EXPECT_TRUE(S_ISREG(stbuf.st_mode));
+  string file_data;
+  EXPECT_TRUE(utils::ReadFileToString(
+      "/tmp/update_engine_test_postinst_out.txt",
+      &file_data));
+  EXPECT_EQ("POSTINST_DONE\n", file_data);
+
+  // cleanup
+  ASSERT_EQ(0, System(string("rm -rf ") + kTestDir));
+  ASSERT_EQ(0, system("rm -f /tmp/update_engine_test_postinst_out.txt"));
+}
+
+}  // namespace chromeos_update_engine