blob: c20d810acafab25fe35277385a7be696ad0712bb [file] [log] [blame]
// Copyright (c) 2010 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_action.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/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,
ActionExitCode code) {
processing_done_called_ = true;
g_main_loop_quit(loop_);
}
virtual void ActionCompleted(ActionProcessor* processor,
AbstractAction* action,
ActionExitCode code) {
// make sure actions always succeed
EXPECT_EQ(kActionCodeSuccess, code);
// 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);
OmahaRequestAction update_check_action(NULL, 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_EQ(0, system((string("mkdir -p ") + kTestDir +
utils::kStatefulPartition +
"/etc").c_str()));
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