| Index: src/platform/update_engine/integration_unittest.cc
 | 
| diff --git a/src/platform/update_engine/integration_unittest.cc b/src/platform/update_engine/integration_unittest.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..671240f98126c29bdd636bab2003e7d5cc98e1c0
 | 
| --- /dev/null
 | 
| +++ b/src/platform/update_engine/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
 | 
| 
 |