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/SConstruct b/SConstruct
index fa03602..64406e4 100644
--- a/SConstruct
+++ b/SConstruct
@@ -2,41 +2,137 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+# Protobuffer compilation
+""" Inputs:
+ target: list of targets to compile to
+ source: list of sources to compile
+ env: the scons environment in which we are compiling
+ Outputs:
+ target: the list of targets we'll emit
+ source: the list of sources we'll compile"""
+def ProtocolBufferEmitter(target, source, env):
+ output = str(source[0])
+ output = output[0:output.rfind('.proto')]
+ target = [
+ output + '.pb.cc',
+ output + '.pb.h',
+ ]
+ return target, source
+
+""" Inputs:
+ source: list of sources to process
+ target: list of targets to generate
+ env: scons environment in which we are working
+ for_signature: unused
+ Outputs: a list of commands to execute to generate the targets from
+ the sources."""
+def ProtocolBufferGenerator(source, target, env, for_signature):
+ commands = [
+ '/usr/bin/protoc '
+ ' --proto_path . ${SOURCES} --cpp_out .']
+ return commands
+
+proto_builder = Builder(generator = ProtocolBufferGenerator,
+ emitter = ProtocolBufferEmitter,
+ single_source = 1,
+ suffix = '.pb.cc')
+
env = Environment()
-env['CCFLAGS'] = '-g -fno-exceptions -Wall -Werror -D_FILE_OFFSET_BITS=64 ' + \
- '-I/usr/include/libxml2'
-env['LIBS'] = Split('curl gtest ssl xml2 z')
-env['CPPPATH'] = ['..']
+env['CCFLAGS'] = ' '.join("""-g
+ -fno-exceptions
+ -Wall
+ -Werror
+ -Wclobbered
+ -Wempty-body
+ -Wignored-qualifiers
+ -Wmissing-field-initializers
+ -Wsign-compare
+ -Wtype-limits
+ -Wuninitialized
+ -D_FILE_OFFSET_BITS=64
+ -I/usr/include/libxml2""".split());
+
+env['LIBS'] = Split("""base
+ curl
+ gflags
+ glib-2.0
+ gtest
+ gthread-2.0
+ libpcrecpp
+ protobuf
+ pthread
+ ssl
+ xml2
+ z""")
+env['CPPPATH'] = ['..', '../../third_party/chrome/files', '../../common']
+env['LIBPATH'] = ['../../third_party/chrome']
+env['BUILDERS']['ProtocolBuffer'] = proto_builder
env.ParseConfig('pkg-config --cflags --libs glib-2.0')
+env.ProtocolBuffer('update_metadata.pb.cc', 'update_metadata.proto')
if ARGUMENTS.get('debug', 0):
env['CCFLAGS'] += ' -fprofile-arcs -ftest-coverage'
env['LIBS'] += ['gcov']
+
+
sources = Split("""action_processor.cc
decompressing_file_writer.cc
+ delta_diff_parser.cc
download_action.cc
+ filesystem_copier_action.cc
+ filesystem_iterator.cc
+ file_writer.cc
+ gzip.cc
+ install_action.cc
libcurl_http_fetcher.cc
omaha_hash_calculator.cc
- update_check_action.cc""")
+ omaha_request_prep_action.cc
+ omaha_response_handler_action.cc
+ postinstall_runner_action.cc
+ set_bootable_flag_action.cc
+ subprocess.cc
+ update_check_action.cc
+ update_metadata.pb.cc
+ utils.cc""")
main = ['main.cc']
unittest_sources = Split("""action_unittest.cc
action_pipe_unittest.cc
action_processor_unittest.cc
decompressing_file_writer_unittest.cc
+ delta_diff_generator_unittest.cc
download_action_unittest.cc
file_writer_unittest.cc
+ filesystem_copier_action_unittest.cc
+ filesystem_iterator_unittest.cc
+ gzip_unittest.cc
http_fetcher_unittest.cc
+ install_action_unittest.cc
+ integration_unittest.cc
mock_http_fetcher.cc
omaha_hash_calculator_unittest.cc
+ omaha_request_prep_action_unittest.cc
+ omaha_response_handler_action_unittest.cc
+ postinstall_runner_action_unittest.cc
+ set_bootable_flag_action_unittest.cc
+ subprocess_unittest.cc
test_utils.cc
- update_check_action_unittest.cc""")
+ update_check_action_unittest.cc
+ utils_unittest.cc""")
unittest_main = ['testrunner.cc']
+delta_generator_sources = Split("""delta_diff_generator.cc""")
+
env.Program('update_engine', sources + main)
unittest_cmd = env.Program('update_engine_unittests',
- sources + unittest_sources + unittest_main)
+ sources + delta_generator_sources +
+ unittest_sources + unittest_main)
Clean(unittest_cmd, Glob('*.gcda') + Glob('*.gcno') + Glob('*.gcov') +
Split('html app.info'))
+
+delta_generator_cmd = env.Program('delta_generator',
+ sources + delta_generator_sources + main)
+
+http_server_cmd = env.Program('test_http_server', 'test_http_server.cc')
diff --git a/action.h b/action.h
index 15f72df..af6deee 100644
--- a/action.h
+++ b/action.h
@@ -2,17 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_ACTION_H__
-#define UPDATE_ENGINE_ACTION_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_ACTION_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_ACTION_H__
#include <stdio.h>
#include <tr1/memory>
#include <iostream>
-
#include "base/basictypes.h"
-#include <base/logging.h>
-
+#include "chromeos/obsolete_logging.h"
#include "update_engine/action_processor.h"
+#include "update_engine/action_pipe.h"
// The structure of these classes (Action, ActionPipe, ActionProcessor, etc.)
// is based on the KSAction* classes from the Google Update Engine code at
@@ -157,7 +156,7 @@
out_pipe) {
out_pipe_ = out_pipe;
}
- protected:
+
// Returns true iff there is an associated input pipe. If there's an input
// pipe, there's an input object, but it may have been constructed with the
// default ctor if the previous action didn't call SetOutputObject().
@@ -184,6 +183,13 @@
out_pipe_->set_contents(out_obj);
}
+ // Returns a reference to the object sitting in the output pipe.
+ const typename ActionTraits<SubClass>::OutputObjectType& GetOutputObject() {
+ CHECK(HasOutputPipe());
+ return out_pipe_->contents();
+ }
+
+protected:
// We use a shared_ptr to the pipe. shared_ptr objects destroy what they
// point to when the last such shared_ptr object dies. We consider the
// Actions on either end of a pipe to "own" the pipe. When the last Action
@@ -196,4 +202,4 @@
}; // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_ACTION_H__
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_ACTION_H__
diff --git a/action_pipe.h b/action_pipe.h
index 8ece015..509fbee 100644
--- a/action_pipe.h
+++ b/action_pipe.h
@@ -2,16 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_PIPE_H__
-#define UPDATE_ENGINE_PIPE_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_PIPE_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_PIPE_H__
#include <stdio.h>
#include <iostream>
#include <map>
#include <string>
#include <tr1/memory>
-#include <base/logging.h>
#include "base/basictypes.h"
+#include "chromeos/obsolete_logging.h"
// The structure of these classes (Action, ActionPipe, ActionProcessor, etc.)
// is based on the KSAction* classes from the Google Update Engine code at
@@ -91,4 +91,4 @@
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_PIPE_H__
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_PIPE_H__
diff --git a/action_processor.cc b/action_processor.cc
index 623e3d2..21a88f1 100644
--- a/action_processor.cc
+++ b/action_processor.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "update_engine/action_processor.h"
+#include "chromeos/obsolete_logging.h"
#include "update_engine/action.h"
namespace chromeos_update_engine {
@@ -49,7 +50,7 @@
delegate_->ProcessingStopped(this);
}
-void ActionProcessor::ActionComplete(const AbstractAction* actionptr,
+void ActionProcessor::ActionComplete(AbstractAction* actionptr,
bool success) {
CHECK_EQ(actionptr, current_action_);
if (delegate_)
@@ -60,6 +61,14 @@
if (actions_.empty()) {
LOG(INFO) << "ActionProcessor::ActionComplete: finished last action of"
" type " << old_type;
+ } else if (!success) {
+ LOG(INFO) << "ActionProcessor::ActionComplete: " << old_type
+ << " action failed. Aborting processing.";
+ actions_.clear();
+ }
+ if (actions_.empty()) {
+ LOG(INFO) << "ActionProcessor::ActionComplete: finished last action of"
+ " type " << old_type;
if (delegate_) {
delegate_->ProcessingDone(this);
}
diff --git a/action_processor.h b/action_processor.h
index d478477..938a81d 100644
--- a/action_processor.h
+++ b/action_processor.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_ACTION_PROCESSOR_H__
-#define UPDATE_ENGINE_ACTION_PROCESSOR_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_ACTION_PROCESSOR_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_ACTION_PROCESSOR_H__
#include <deque>
@@ -57,7 +57,7 @@
}
// Called by an action to notify processor that it's done. Caller passes self.
- void ActionComplete(const AbstractAction* actionptr, bool success);
+ void ActionComplete(AbstractAction* actionptr, bool success);
private:
// Actions that have not yet begun processing, in the order in which
@@ -87,11 +87,11 @@
// Called whenever an action has finished processing, either successfully
// or otherwise.
- virtual void ActionCompleted(const ActionProcessor* processor,
- const AbstractAction* action,
+ virtual void ActionCompleted(ActionProcessor* processor,
+ AbstractAction* action,
bool success) {}
};
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_ACTION_PROCESSOR_H__
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_ACTION_PROCESSOR_H__
diff --git a/action_processor_unittest.cc b/action_processor_unittest.cc
index 2a80bc3..6130585 100644
--- a/action_processor_unittest.cc
+++ b/action_processor_unittest.cc
@@ -70,8 +70,8 @@
EXPECT_FALSE(processing_stopped_called_);
processing_stopped_called_ = true;
}
- virtual void ActionCompleted(const ActionProcessor* processor,
- const AbstractAction* action,
+ virtual void ActionCompleted(ActionProcessor* processor,
+ AbstractAction* action,
bool success) {
EXPECT_EQ(processor_, processor);
EXPECT_FALSE(action_completed_called_);
diff --git a/decompressing_file_writer.cc b/decompressing_file_writer.cc
index 15bd4fd..96db010 100644
--- a/decompressing_file_writer.cc
+++ b/decompressing_file_writer.cc
@@ -37,7 +37,10 @@
// so not all data we get fed to us this time will necessarily
// be written out this time (in decompressed form).
- CHECK_EQ(0, stream_.avail_in);
+ if (stream_.avail_in) {
+ LOG(ERROR) << "Have data already. Bailing";
+ return -1;
+ }
char buf[1024];
buffer_.reserve(count);
diff --git a/decompressing_file_writer.h b/decompressing_file_writer.h
index 975ca3c..6f12e87 100644
--- a/decompressing_file_writer.h
+++ b/decompressing_file_writer.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_DECOMPRESSING_FILE_WRITER_H__
-#define UPDATE_ENGINE_DECOMPRESSING_FILE_WRITER_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_DECOMPRESSING_FILE_WRITER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_DECOMPRESSING_FILE_WRITER_H__
#include <zlib.h>
#include "base/basictypes.h"
@@ -57,4 +57,4 @@
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_DECOMPRESSING_FILE_WRITER_H__
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_DECOMPRESSING_FILE_WRITER_H__
diff --git a/download_action.cc b/download_action.cc
index 3a039d1..833f806 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -2,23 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <glib.h>
-
-#include "update_engine/action_pipe.h"
#include "update_engine/download_action.h"
+#include <errno.h>
+#include <algorithm>
+#include <glib.h>
+#include "update_engine/action_pipe.h"
+
+using std::min;
namespace chromeos_update_engine {
-DownloadAction::DownloadAction(const std::string& url,
- const std::string& output_path,
- off_t size, const std::string& hash,
- const bool should_decompress,
- HttpFetcher* http_fetcher)
- : size_(size),
- url_(url),
- output_path_(output_path),
- hash_(hash),
- should_decompress_(should_decompress),
+DownloadAction::DownloadAction(HttpFetcher* http_fetcher)
+ : size_(0),
+ should_decompress_(false),
writer_(NULL),
http_fetcher_(http_fetcher) {}
@@ -29,14 +25,24 @@
CHECK(!writer_);
direct_file_writer_.reset(new DirectFileWriter);
+ // Get the InstallPlan and read it
+ CHECK(HasInputObject());
+ InstallPlan install_plan(GetInputObject());
+
+ should_decompress_ = install_plan.is_full_update;
+ url_ = install_plan.download_url;
+ output_path_ = install_plan.download_path;
+ hash_ = install_plan.download_hash;
+ install_plan.Dump();
+
if (should_decompress_) {
decompressing_file_writer_.reset(
new GzipDecompressingFileWriter(direct_file_writer_.get()));
writer_ = decompressing_file_writer_.get();
+ output_path_ = install_plan.install_path;
} else {
writer_ = direct_file_writer_.get();
}
-
int rc = writer_->Open(output_path_.c_str(),
O_TRUNC | O_WRONLY | O_CREAT | O_LARGEFILE, 0644);
if (rc < 0) {
@@ -58,35 +64,29 @@
void DownloadAction::ReceivedBytes(HttpFetcher *fetcher,
const char* bytes,
int length) {
- int bytes_written = 0;
- do {
- CHECK_GT(length, bytes_written);
- int rc = writer_->Write(bytes + bytes_written, length - bytes_written);
- // TODO(adlr): handle write error
- CHECK_GE(rc, 0);
- bytes_written += rc;
- } while (length != bytes_written);
+ int rc = writer_->Write(bytes, length);
+ TEST_AND_RETURN(rc >= 0);
omaha_hash_calculator_.Update(bytes, length);
}
void DownloadAction::TransferComplete(HttpFetcher *fetcher, bool successful) {
if (writer_) {
- CHECK_EQ(0, writer_->Close()) << errno;
+ CHECK_EQ(writer_->Close(), 0) << errno;
writer_ = NULL;
}
if (successful) {
// Make sure hash is correct
omaha_hash_calculator_.Finalize();
if (omaha_hash_calculator_.hash() != hash_) {
- LOG(INFO) << "Download of " << url_ << " failed. Expect hash "
- << hash_ << " but got hash " << omaha_hash_calculator_.hash();
+ LOG(ERROR) << "Download of " << url_ << " failed. Expect hash "
+ << hash_ << " but got hash " << omaha_hash_calculator_.hash();
successful = false;
}
}
// Write the path to the output pipe if we're successful
if (successful && HasOutputPipe())
- SetOutputObject(output_path_);
+ SetOutputObject(GetInputObject());
processor_->ActionComplete(this, successful);
}
diff --git a/download_action.h b/download_action.h
index 9abbdcf..e6939e1 100644
--- a/download_action.h
+++ b/download_action.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_DOWNLOAD_ACTION_H__
-#define UPDATE_ENGINE_DOWNLOAD_ACTION_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_DOWNLOAD_ACTION_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_DOWNLOAD_ACTION_H__
#include <sys/types.h>
#include <sys/stat.h>
@@ -19,11 +19,11 @@
#include "update_engine/decompressing_file_writer.h"
#include "update_engine/file_writer.h"
#include "update_engine/http_fetcher.h"
+#include "update_engine/install_plan.h"
#include "update_engine/omaha_hash_calculator.h"
// The Download Action downloads a requested url to a specified path on disk.
-// It takes no input object, but if successful, passes the output path
-// to the output pipe.
+// The url and output path are determined by the InstallPlan passed in.
using std::map;
using std::string;
@@ -36,10 +36,9 @@
template<>
class ActionTraits<DownloadAction> {
public:
- // Does not take an object for input
- typedef NoneType InputObjectType;
- // On success, puts the output path on output
- typedef std::string OutputObjectType;
+ // Takes and returns an InstallPlan
+ typedef InstallPlan InputObjectType;
+ typedef InstallPlan OutputObjectType;
};
class DownloadAction : public Action<DownloadAction>,
@@ -47,11 +46,8 @@
public:
// Takes ownership of the passed in HttpFetcher. Useful for testing.
// A good calling pattern is:
- // DownloadAction(..., new WhateverHttpFetcher);
- DownloadAction(const std::string& url, const std::string& output_path,
- off_t size, const std::string& hash,
- const bool should_decompress,
- HttpFetcher* http_fetcher);
+ // DownloadAction(new WhateverHttpFetcher);
+ DownloadAction(HttpFetcher* http_fetcher);
virtual ~DownloadAction();
typedef ActionTraits<DownloadAction>::InputObjectType InputObjectType;
typedef ActionTraits<DownloadAction>::OutputObjectType OutputObjectType;
@@ -59,7 +55,8 @@
void TerminateProcessing();
// Debugging/logging
- std::string Type() const { return "DownloadAction"; }
+ static std::string StaticType() { return "DownloadAction"; }
+ std::string Type() const { return StaticType(); }
// Delegate methods (see http_fetcher.h)
virtual void ReceivedBytes(HttpFetcher *fetcher,
@@ -71,17 +68,17 @@
const size_t size_;
// URL to download
- const std::string url_;
+ std::string url_;
// Path to save URL to
- const std::string output_path_;
+ std::string output_path_;
// Expected hash of the file. The hash must match for this action to
// succeed.
- const std::string hash_;
+ std::string hash_;
// Whether the caller requested that we decompress the downloaded data.
- const bool should_decompress_;
+ bool should_decompress_;
// The FileWriter that downloaded data should be written to. It will
// either point to *decompressing_file_writer_ or *direct_file_writer_.
@@ -107,4 +104,4 @@
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_DOWNLOAD_ACTION_H__
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_DOWNLOAD_ACTION_H__
diff --git a/download_action_unittest.cc b/download_action_unittest.cc
index 3dfc005..4276dc9 100644
--- a/download_action_unittest.cc
+++ b/download_action_unittest.cc
@@ -11,6 +11,7 @@
#include "update_engine/mock_http_fetcher.h"
#include "update_engine/omaha_hash_calculator.h"
#include "update_engine/test_utils.h"
+#include "update_engine/utils.h"
namespace chromeos_update_engine {
@@ -30,7 +31,8 @@
virtual void ProcessingDone(const ActionProcessor* processor) {
ASSERT_TRUE(loop_);
g_main_loop_quit(loop_);
- vector<char> found_data = ReadFile(path_);
+ vector<char> found_data;
+ ASSERT_TRUE(utils::ReadFile(path_, &found_data));
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]);
@@ -38,8 +40,8 @@
processing_done_called_ = true;
}
- virtual void ActionCompleted(const ActionProcessor* processor,
- const AbstractAction* action,
+ virtual void ActionCompleted(ActionProcessor* processor,
+ AbstractAction* action,
bool success) {
// make sure actions always succeed
EXPECT_TRUE(success);
@@ -76,16 +78,22 @@
// 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()));
+ InstallPlan install_plan(compress, "",
+ OmahaHashCalculator::OmahaHashOfData(use_data),
+ compress ? "" : path, compress ? path : "");
+ ObjectFeederAction<InstallPlan> feeder_action;
+ feeder_action.set_obj(install_plan);
+ DownloadAction download_action(new MockHttpFetcher(&use_data[0],
+ use_data.size()));
+ BondActions(&feeder_action, &download_action);
+
DownloadActionTestProcessorDelegate delegate;
delegate.loop_ = loop;
delegate.expected_data_ = data;
delegate.path_ = path;
ActionProcessor processor;
processor.set_delegate(&delegate);
+ processor.EnqueueAction(&feeder_action);
processor.EnqueueAction(&download_action);
g_timeout_add(0, &StartProcessorInRunLoop, &processor);
@@ -148,13 +156,17 @@
const string path("/tmp/DownloadActionTest");
{
// takes ownership of passed in HttpFetcher
- DownloadAction download_action("", path, 0, "", false,
- new MockHttpFetcher(&data[0], data.size()));
+ ObjectFeederAction<InstallPlan> feeder_action;
+ InstallPlan install_plan(false, "", "", path, "");
+ feeder_action.set_obj(install_plan);
+ DownloadAction download_action(new MockHttpFetcher(&data[0], data.size()));
TerminateEarlyTestProcessorDelegate delegate;
delegate.loop_ = loop;
ActionProcessor processor;
processor.set_delegate(&delegate);
+ processor.EnqueueAction(&feeder_action);
processor.EnqueueAction(&download_action);
+ BondActions(&feeder_action, &download_action);
g_timeout_add(0, &TerminateEarlyTestStarter, &processor);
g_main_loop_run(loop);
@@ -172,27 +184,27 @@
template<>
class ActionTraits<DownloadActionTestAction> {
public:
- typedef string OutputObjectType;
- typedef string InputObjectType;
+ typedef InstallPlan OutputObjectType;
+ typedef InstallPlan 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(); }
+ typedef InstallPlan InputObjectType;
+ typedef InstallPlan OutputObjectType;
+ ActionPipe<InstallPlan>* in_pipe() { return in_pipe_.get(); }
+ ActionPipe<InstallPlan>* out_pipe() { return out_pipe_.get(); }
ActionProcessor* processor() { return processor_; }
void PerformAction() {
did_run_ = true;
ASSERT_TRUE(HasInputObject());
- EXPECT_EQ(expected_input_object_, GetInputObject());
+ EXPECT_TRUE(expected_input_object_ == GetInputObject());
ASSERT_TRUE(processor());
processor()->ActionComplete(this, true);
}
string Type() const { return "DownloadActionTestAction"; }
- string expected_input_object_;
+ InstallPlan expected_input_object_;
bool did_run_;
};
@@ -222,18 +234,23 @@
const string path("/tmp/DownloadActionTest");
// takes ownership of passed in HttpFetcher
- DownloadAction download_action("", path, 0,
- OmahaHashCalculator::OmahaHashOfString("x"),
- false, new MockHttpFetcher("x", 1));
+ InstallPlan install_plan(false, "",
+ OmahaHashCalculator::OmahaHashOfString("x"), path,
+ "");
+ ObjectFeederAction<InstallPlan> feeder_action;
+ feeder_action.set_obj(install_plan);
+ DownloadAction download_action(new MockHttpFetcher("x", 1));
DownloadActionTestAction test_action;
- test_action.expected_input_object_ = path;
+ test_action.expected_input_object_ = install_plan;
+ BondActions(&feeder_action, &download_action);
BondActions(&download_action, &test_action);
ActionProcessor processor;
PassObjectOutTestProcessorDelegate delegate;
delegate.loop_ = loop;
processor.set_delegate(&delegate);
+ processor.EnqueueAction(&feeder_action);
processor.EnqueueAction(&download_action);
processor.EnqueueAction(&test_action);
@@ -250,10 +267,14 @@
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));
+ InstallPlan install_plan(false, "", "", path, "");
+ ObjectFeederAction<InstallPlan> feeder_action;
+ feeder_action.set_obj(install_plan);
+ DownloadAction download_action(new MockHttpFetcher("x", 1));
+ BondActions(&feeder_action, &download_action);
ActionProcessor processor;
+ processor.EnqueueAction(&feeder_action);
processor.EnqueueAction(&download_action);
processor.StartProcessing();
ASSERT_FALSE(processor.IsRunning());
diff --git a/file_writer.h b/file_writer.h
index 3b44a26..c506c01 100644
--- a/file_writer.h
+++ b/file_writer.h
@@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_FILE_WRITER_H__
-#define UPDATE_ENGINE_FILE_WRITER_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_FILE_WRITER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_FILE_WRITER_H__
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
-#include "base/logging.h"
+#include "chromeos/obsolete_logging.h"
+#include "update_engine/utils.h"
// FileWriter is a class that is used to (synchronously, for now) write to
// a file. This file is a thin wrapper around open/write/close system calls,
@@ -41,39 +42,29 @@
DirectFileWriter() : fd_(-1) {}
virtual ~DirectFileWriter() {}
- virtual int Open(const char* path, int flags, mode_t mode) {
- CHECK_EQ(-1, fd_);
- fd_ = open(path, flags, mode);
- if (fd_ < 0)
- return -errno;
- return 0;
- }
+ virtual int Open(const char* path, int flags, mode_t mode);
+ virtual int Write(const void* bytes, size_t count);
+ virtual int Close();
- virtual int Write(const void* bytes, size_t count) {
- CHECK_GE(fd_, 0);
- ssize_t rc = write(fd_, bytes, count);
- if (rc < 0)
- return -errno;
- return rc;
- }
-
- virtual int Close() {
- CHECK_GE(fd_, 0);
- int rc = close(fd_);
-
- // This can be any negative number that's not -1. This way, this FileWriter
- // won't be used again for another file.
- fd_ = -2;
-
- if (rc < 0)
- return -errno;
- return rc;
- }
+ int fd() const { return fd_; }
private:
int fd_;
};
+class ScopedFileWriterCloser {
+ public:
+ explicit ScopedFileWriterCloser(FileWriter* writer) : writer_(writer) {}
+ ~ScopedFileWriterCloser() {
+ int err = writer_->Close();
+ if (err)
+ LOG(ERROR) << "FileWriter::Close failed: "
+ << utils::ErrnoNumberAsString(-err);
+ }
+ private:
+ FileWriter* writer_;
+};
+
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_FILE_WRITER_H__
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_FILE_WRITER_H__
diff --git a/file_writer_unittest.cc b/file_writer_unittest.cc
index ed8bcaa..6e494ec 100644
--- a/file_writer_unittest.cc
+++ b/file_writer_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <string>
@@ -9,6 +10,7 @@
#include <gtest/gtest.h>
#include "update_engine/file_writer.h"
#include "update_engine/test_utils.h"
+#include "update_engine/utils.h"
using std::string;
using std::vector;
@@ -24,7 +26,8 @@
O_CREAT | O_LARGEFILE | O_TRUNC | O_WRONLY,
0644));
ASSERT_EQ(4, file_writer.Write("test", 4));
- vector<char> actual_data = ReadFile(path);
+ vector<char> actual_data;
+ EXPECT_TRUE(utils::ReadFile(path, &actual_data));
EXPECT_FALSE(memcmp("test", &actual_data[0], actual_data.size()));
EXPECT_EQ(0, file_writer.Close());
diff --git a/gen_coverage_html.sh b/gen_coverage_html.sh
index 7ccc5d8..1d3d586 100755
--- a/gen_coverage_html.sh
+++ b/gen_coverage_html.sh
@@ -6,10 +6,11 @@
set -ex
-scons debug=1 -c
-scons debug=1
+scons debug=1 -j 2
lcov -d . --zerocounters
-./update_engine_unittests
+./update_engine_unittests --gtest_filter='-*.RunAsRoot*:*.Fakeroot*'
+fakeroot ./update_engine_unittests --gtest_filter='*.Fakeroot*'
+sudo ./update_engine_unittests --gtest_filter='*.RunAsRoot*'
lcov --directory . --capture --output-file app.info
# some versions of genhtml support the --no-function-coverage argument,
@@ -19,3 +20,4 @@
# is tested, but it shows only 50% function coverage b/c it thinks we didn't
# test the prod version.
genhtml --no-function-coverage -o html ./app.info || genhtml -o html ./app.info
+./local_coverage_rate.sh
diff --git a/http_fetcher.h b/http_fetcher.h
index 6e26906..5cd6f08 100644
--- a/http_fetcher.h
+++ b/http_fetcher.h
@@ -2,10 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_HTTP_FETCHER_H__
-#define UPDATE_ENGINE_HTTP_FETCHER_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_HTTP_FETCHER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_HTTP_FETCHER_H__
#include <string>
+#include <vector>
#include <glib.h>
#include "base/basictypes.h"
@@ -22,7 +23,7 @@
class HttpFetcher {
public:
- HttpFetcher() : delegate_(NULL) {}
+ HttpFetcher() : post_data_set_(false), delegate_(NULL) {}
virtual ~HttpFetcher() {}
void set_delegate(HttpFetcherDelegate* delegate) {
delegate_ = delegate;
@@ -87,4 +88,4 @@
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_HTTP_FETCHER_H__
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_HTTP_FETCHER_H__
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
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index 624e6a9..9ed0c64 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -2,8 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/logging.h"
#include "update_engine/libcurl_http_fetcher.h"
+#include <algorithm>
+#include "chromeos/obsolete_logging.h"
+
+using std::max;
+using std::make_pair;
// This is a concrete implementation of HttpFetcher that uses libcurl to do the
// http work.
@@ -14,8 +18,7 @@
CleanUp();
}
-// Begins the transfer, which must not have already been started.
-void LibcurlHttpFetcher::BeginTransfer(const std::string& url) {
+void LibcurlHttpFetcher::ResumeTransfer(const std::string& url) {
CHECK(!transfer_in_progress_);
url_ = url;
curl_multi_handle_ = curl_multi_init();
@@ -25,22 +28,40 @@
CHECK(curl_handle_);
if (post_data_set_) {
- CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_POST, 1));
- CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDS,
- &post_data_[0]));
- CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDSIZE,
- post_data_.size()));
+ CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POST, 1), CURLE_OK);
+ CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDS,
+ &post_data_[0]),
+ CURLE_OK);
+ CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDSIZE,
+ post_data_.size()),
+ CURLE_OK);
}
- CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this));
- CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_WRITEFUNCTION,
- StaticLibcurlWrite));
- CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_URL, url_.c_str()));
- CHECK_EQ(CURLM_OK, curl_multi_add_handle(curl_multi_handle_, curl_handle_));
+ if (bytes_downloaded_ > 0) {
+ // Resume from where we left off
+ resume_offset_ = bytes_downloaded_;
+ CHECK_EQ(curl_easy_setopt(curl_handle_,
+ CURLOPT_RESUME_FROM_LARGE,
+ bytes_downloaded_), CURLE_OK);
+ }
+
+ CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this), CURLE_OK);
+ CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEFUNCTION,
+ StaticLibcurlWrite), CURLE_OK);
+ CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_URL, url_.c_str()), CURLE_OK);
+ CHECK_EQ(curl_multi_add_handle(curl_multi_handle_, curl_handle_), CURLM_OK);
transfer_in_progress_ = true;
CurlPerformOnce();
}
+// Begins the transfer, which must not have already been started.
+void LibcurlHttpFetcher::BeginTransfer(const std::string& url) {
+ transfer_size_ = -1;
+ bytes_downloaded_ = 0;
+ resume_offset_ = 0;
+ ResumeTransfer(url);
+}
+
void LibcurlHttpFetcher::TerminateTransfer() {
CleanUp();
}
@@ -59,8 +80,14 @@
if (0 == running_handles) {
// we're done!
CleanUp();
- if (delegate_)
- delegate_->TransferComplete(this, true); // success
+
+ if ((transfer_size_ >= 0) && (bytes_downloaded_ < transfer_size_)) {
+ ResumeTransfer(url_);
+ } else {
+ if (delegate_) {
+ delegate_->TransferComplete(this, true); // success
+ }
+ }
} else {
// set up callback
SetupMainloopSources();
@@ -68,6 +95,17 @@
}
size_t LibcurlHttpFetcher::LibcurlWrite(void *ptr, size_t size, size_t nmemb) {
+ {
+ double transfer_size_double;
+ CHECK_EQ(curl_easy_getinfo(curl_handle_,
+ CURLINFO_CONTENT_LENGTH_DOWNLOAD,
+ &transfer_size_double), CURLE_OK);
+ off_t new_transfer_size = static_cast<off_t>(transfer_size_double);
+ if (new_transfer_size > 0) {
+ transfer_size_ = resume_offset_ + new_transfer_size;
+ }
+ }
+ bytes_downloaded_ += size * nmemb;
if (delegate_)
delegate_->ReceivedBytes(this, reinterpret_cast<char*>(ptr), size * nmemb);
return size * nmemb;
@@ -76,13 +114,13 @@
void LibcurlHttpFetcher::Pause() {
CHECK(curl_handle_);
CHECK(transfer_in_progress_);
- CHECK_EQ(CURLE_OK, curl_easy_pause(curl_handle_, CURLPAUSE_ALL));
+ CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_ALL), CURLE_OK);
}
void LibcurlHttpFetcher::Unpause() {
CHECK(curl_handle_);
CHECK(transfer_in_progress_);
- CHECK_EQ(CURLE_OK, curl_easy_pause(curl_handle_, CURLPAUSE_CONT));
+ CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_CONT), CURLE_OK);
}
// This method sets up callbacks with the glib main loop.
@@ -99,8 +137,8 @@
// Ask libcurl for the set of file descriptors we should track on its
// behalf.
- CHECK_EQ(CURLM_OK, curl_multi_fdset(curl_multi_handle_, &fd_read, &fd_write,
- &fd_exec, &fd_max));
+ CHECK_EQ(curl_multi_fdset(curl_multi_handle_, &fd_read, &fd_write,
+ &fd_exec, &fd_max), CURLM_OK);
// We should iterate through all file descriptors up to libcurl's fd_max or
// the highest one we're tracking, whichever is larger
@@ -113,7 +151,7 @@
// in io_channels_ as there are fds that we're tracking.
for (int i = 0; i <= fd_max; i++) {
if (!(FD_ISSET(i, &fd_read) || FD_ISSET(i, &fd_write) ||
- FD_ISSET(i, &fd_exec))) {
+ FD_ISSET(i, &fd_exec))) {
// if we have an outstanding io_channel, remove it
if (io_channels_.find(i) != io_channels_.end()) {
g_source_remove(io_channels_[i].second);
@@ -139,7 +177,7 @@
// Wet up a timeout callback for libcurl
long ms = 0;
- CHECK_EQ(CURLM_OK, curl_multi_timeout(curl_multi_handle_, &ms));
+ CHECK_EQ(curl_multi_timeout(curl_multi_handle_, &ms), CURLM_OK);
if (ms < 0) {
// From http://curl.haxx.se/libcurl/c/curl_multi_timeout.html:
// if libcurl returns a -1 timeout here, it just means that libcurl
@@ -210,14 +248,14 @@
if (curl_handle_) {
if (curl_multi_handle_) {
- CHECK_EQ(CURLM_OK,
- curl_multi_remove_handle(curl_multi_handle_, curl_handle_));
+ CHECK_EQ(curl_multi_remove_handle(curl_multi_handle_, curl_handle_),
+ CURLM_OK);
}
curl_easy_cleanup(curl_handle_);
curl_handle_ = NULL;
}
if (curl_multi_handle_) {
- CHECK_EQ(CURLM_OK, curl_multi_cleanup(curl_multi_handle_));
+ CHECK_EQ(curl_multi_cleanup(curl_multi_handle_), CURLM_OK);
curl_multi_handle_ = NULL;
}
transfer_in_progress_ = false;
diff --git a/libcurl_http_fetcher.h b/libcurl_http_fetcher.h
index 433feeb..6f32183 100644
--- a/libcurl_http_fetcher.h
+++ b/libcurl_http_fetcher.h
@@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__
-#define UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__
#include <map>
#include <string>
#include <curl/curl.h>
#include <glib.h>
#include "base/basictypes.h"
-#include "base/logging.h"
+#include "chromeos/obsolete_logging.h"
#include "update_engine/http_fetcher.h"
// This is a concrete implementation of HttpFetcher that uses libcurl to do the
@@ -54,6 +54,10 @@
idle_ms_ = ms;
}
private:
+ // Resumes a transfer where it left off. This will use the
+ // HTTP Range: header to make a new connection from where the last
+ // left off.
+ virtual void ResumeTransfer(const std::string& url);
// These two methods are for glib main loop callbacks. They are called
// when either a file descriptor is ready for work or when a timer
@@ -109,10 +113,20 @@
bool transfer_in_progress_;
+ // The transfer size. -1 if not known.
+ off_t transfer_size_;
+
+ // How many bytes have been downloaded and sent to the delegate.
+ off_t bytes_downloaded_;
+
+ // If we resumed an earlier transfer, data offset that we used for the
+ // new connection. 0 otherwise.
+ off_t resume_offset_;
+
long idle_ms_;
DISALLOW_COPY_AND_ASSIGN(LibcurlHttpFetcher);
};
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__
diff --git a/main.cc b/main.cc
index 412020b..bbb6401 100644
--- a/main.cc
+++ b/main.cc
@@ -2,34 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <stdlib.h>
-#include <stdio.h>
-#include <iostream>
#include <glib.h>
-#include <base/logging.h>
-#include <libxml/parser.h>
-#include <libxml/xpath.h>
-#include <libxml/xpathInternals.h>
-
-// This code runs inside the main loop
-gboolean EntryPoint(gpointer data) {
- GMainLoop *loop = reinterpret_cast<GMainLoop*>(data);
- LOG(INFO) << "Chrome OS Update Engine beginning";
- g_main_loop_quit(loop);
- return FALSE;
-}
+#include "update_engine/subprocess.h"
int main(int argc, char** argv) {
- xmlDocPtr doc = xmlNewDoc((const xmlChar*)"1.0");
- CHECK(doc);
- CHECK_EQ(argc, 2);
- printf("enc: [%s]\n", xmlEncodeEntitiesReentrant(doc, (const xmlChar*)argv[1]));
- return 0;
-
-
- GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
- g_timeout_add(0, &EntryPoint, loop);
- g_main_loop_run(loop);
+ g_thread_init(NULL);
+ chromeos_update_engine::Subprocess::Init();
return 0;
}
diff --git a/mock_file_writer.h b/mock_file_writer.h
index e6d1612..7cf0fd5 100644
--- a/mock_file_writer.h
+++ b/mock_file_writer.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_MOCK_FILE_WRITER_H__
-#define UPDATE_ENGINE_MOCK_FILE_WRITER_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_MOCK_FILE_WRITER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_MOCK_FILE_WRITER_H__
#include "base/basictypes.h"
#include "update_engine/file_writer.h"
@@ -56,4 +56,4 @@
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_MOCK_FILE_WRITER_H__
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_MOCK_FILE_WRITER_H__
diff --git a/mock_http_fetcher.cc b/mock_http_fetcher.cc
index 6459351..abeff7a 100644
--- a/mock_http_fetcher.cc
+++ b/mock_http_fetcher.cc
@@ -2,11 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/logging.h"
#include "update_engine/mock_http_fetcher.h"
+#include <algorithm>
+#include "chromeos/obsolete_logging.h"
// This is a mac implementation of HttpFetcher which is useful for testing.
+using std::min;
+
namespace chromeos_update_engine {
MockHttpFetcher::~MockHttpFetcher() {
diff --git a/mock_http_fetcher.h b/mock_http_fetcher.h
index 68cb731..2efd258 100644
--- a/mock_http_fetcher.h
+++ b/mock_http_fetcher.h
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_MOCK_HTTP_FETCHER_H__
-#define UPDATE_ENGINE_MOCK_HTTP_FETCHER_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_MOCK_HTTP_FETCHER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_MOCK_HTTP_FETCHER_H__
#include <vector>
#include <glib.h>
-#include <base/logging.h>
+#include "chromeos/obsolete_logging.h"
#include "update_engine/http_fetcher.h"
// This is a mock implementation of HttpFetcher which is useful for testing.
@@ -54,7 +54,7 @@
const std::vector<char>& post_data() const {
return post_data_;
}
-
+
private:
// Sends data to the delegate and sets up a glib timeout callback if needed.
// There must be a delegate and there must be data to send. If there is
@@ -91,4 +91,4 @@
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_MOCK_HTTP_FETCHER_H__
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_MOCK_HTTP_FETCHER_H__
diff --git a/omaha_hash_calculator.cc b/omaha_hash_calculator.cc
index 2ef0f97..93b3d9e 100644
--- a/omaha_hash_calculator.cc
+++ b/omaha_hash_calculator.cc
@@ -5,13 +5,13 @@
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
-#include "base/logging.h"
+#include "chromeos/obsolete_logging.h"
#include "update_engine/omaha_hash_calculator.h"
namespace chromeos_update_engine {
OmahaHashCalculator::OmahaHashCalculator() {
- CHECK_EQ(1, SHA1_Init(&ctx_));
+ CHECK_EQ(SHA1_Init(&ctx_), 1);
}
// Update is called with all of the data that should be hashed in order.
@@ -20,7 +20,7 @@
CHECK(hash_.empty()) << "Can't Update after hash is finalized";
COMPILE_ASSERT(sizeof(size_t) <= sizeof(unsigned long),
length_param_may_be_truncated_in_SHA1_Update);
- CHECK_EQ(1, SHA1_Update(&ctx_, data, length));
+ CHECK_EQ(SHA1_Update(&ctx_, data, length), 1);
}
// Call Finalize() when all data has been passed in. This mostly just
@@ -28,7 +28,7 @@
void OmahaHashCalculator::Finalize() {
CHECK(hash_.empty()) << "Don't call Finalize() twice";
unsigned char md[SHA_DIGEST_LENGTH];
- CHECK_EQ(1, SHA1_Final(md, &ctx_));
+ CHECK_EQ(SHA1_Final(md, &ctx_), 1);
// Convert md to base64 encoding and store it in hash_
BIO *b64 = BIO_new(BIO_f_base64());
@@ -36,8 +36,8 @@
BIO *bmem = BIO_new(BIO_s_mem());
CHECK(bmem);
b64 = BIO_push(b64, bmem);
- CHECK_EQ(sizeof(md), BIO_write(b64, md, sizeof(md)));
- CHECK_EQ(1, BIO_flush(b64));
+ CHECK_EQ(BIO_write(b64, md, sizeof(md)), sizeof(md));
+ CHECK_EQ(BIO_flush(b64), 1);
BUF_MEM *bptr = NULL;
BIO_get_mem_ptr(b64, &bptr);
diff --git a/omaha_hash_calculator.h b/omaha_hash_calculator.h
index 28602a3..c96f75e 100644
--- a/omaha_hash_calculator.h
+++ b/omaha_hash_calculator.h
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_OMAHA_HASH_CALCULATOR_H__
-#define UPDATE_ENGINE_OMAHA_HASH_CALCULATOR_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_OMAHA_HASH_CALCULATOR_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_OMAHA_HASH_CALCULATOR_H__
#include "base/basictypes.h"
#include <string>
+#include <vector>
#include <openssl/sha.h>
// Omaha uses base64 encoded SHA-1 as the hash. This class provides a simple
@@ -53,4 +54,4 @@
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_OMAHA_HASH_CALCULATOR_H__
\ No newline at end of file
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_OMAHA_HASH_CALCULATOR_H__
\ No newline at end of file
diff --git a/test_utils.cc b/test_utils.cc
index 6255119..6346bf3 100644
--- a/test_utils.cc
+++ b/test_utils.cc
@@ -2,72 +2,273 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "update_engine/test_utils.h"
#include <sys/stat.h>
#include <sys/types.h>
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <set>
#include <string>
#include <vector>
-#include "update_engine/test_utils.h"
-#include "base/logging.h"
+#include "base/string_util.h"
+#include "chromeos/obsolete_logging.h"
#include "update_engine/file_writer.h"
+#include "update_engine/filesystem_iterator.h"
+#include "update_engine/utils.h"
+using std::set;
using std::string;
using std::vector;
namespace chromeos_update_engine {
-vector<char> ReadFile(const string& path) {
- vector<char> ret;
- FILE* fp = fopen(path.c_str(), "r");
- if (!fp)
- return ret;
- const size_t kChunkSize(1024);
- size_t read_size;
- do {
- char buf[kChunkSize];
- read_size = fread(buf, 1, kChunkSize, fp);
- ret.insert(ret.end(), buf, buf + read_size);
- } while (read_size == kChunkSize);
- fclose(fp);
- return ret;
+bool WriteFile(const char* path, const char* data, int data_len) {
+ DirectFileWriter writer;
+ TEST_AND_RETURN_FALSE_ERRNO(0 == writer.Open(path,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ 0666));
+ ScopedFileWriterCloser closer(&writer);
+ TEST_AND_RETURN_FALSE_ERRNO(data_len == writer.Write(data, data_len));
+ return true;
}
-bool WriteFile(const std::string& path, const std::vector<char>& data) {
- DirectFileWriter writer;
- if (0 != writer.Open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644)) {
- return false;
- }
- if (static_cast<int>(data.size()) != writer.Write(&data[0], data.size())) {
- writer.Close();
- return false;
- }
- writer.Close();
- return true;
+bool WriteFileVector(const std::string& path, const std::vector<char>& data) {
+ return WriteFile(path.c_str(), &data[0], data.size());
+}
+
+bool WriteFileString(const std::string& path, const std::string& data) {
+ return WriteFile(path.c_str(), data.data(), data.size());
}
off_t FileSize(const string& path) {
struct stat stbuf;
int rc = stat(path.c_str(), &stbuf);
- CHECK_EQ(0, rc);
+ CHECK_EQ(rc, 0);
if (rc < 0)
return rc;
return stbuf.st_size;
}
+std::string Readlink(const std::string& path) {
+ vector<char> buf(PATH_MAX + 1);
+ ssize_t r = readlink(path.c_str(), &buf[0], buf.size());
+ if (r < 0)
+ return "";
+ CHECK_LT(r, static_cast<ssize_t>(buf.size()));
+ buf.resize(r);
+ string ret;
+ ret.insert(ret.begin(), buf.begin(), buf.end());
+ return ret;
+}
+
std::vector<char> GzipCompressData(const std::vector<char>& data) {
const char fname[] = "/tmp/GzipCompressDataTemp";
- if (!WriteFile(fname, data)) {
+ if (!WriteFileVector(fname, data)) {
system((string("rm ") + fname).c_str());
return vector<char>();
}
system((string("cat ") + fname + "|gzip>" + fname + ".gz").c_str());
system((string("rm ") + fname).c_str());
- vector<char> ret = ReadFile(string(fname) + ".gz");
+ vector<char> ret;
+ EXPECT_TRUE(utils::ReadFile(string(fname) + ".gz", &ret));
system((string("rm ") + fname + ".gz").c_str());
return ret;
}
+vector<char> GenerateSampleMbr() {
+ // This is the actual MBR from my dev machine. Partition 1 (the first)
+ // is currently marked bootable
+ char mbr[512] = {
+ 0xeb, 0x48, 0x90, 0x10, 0x8e, 0xd0, 0xbc, 0x00,
+ 0xb0, 0xb8, 0x00, 0x00, 0x8e, 0xd8, 0x8e, 0xc0,
+ 0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9,
+ 0x00, 0x02, 0xf3, 0xa4, 0xea, 0x21, 0x06, 0x00,
+ 0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b,
+ 0x83, 0xc6, 0x10, 0x81, 0xfe, 0xfe, 0x07, 0x75,
+ 0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb,
+ 0x00, 0x7c, 0xb2, 0x80, 0x8a, 0x74, 0x03, 0x02,
+ 0xff, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0xfa, 0x90, 0x90, 0xf6, 0xc2, 0x80,
+ 0x75, 0x02, 0xb2, 0x80, 0xea, 0x59, 0x7c, 0x00,
+ 0x00, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xd0, 0xbc,
+ 0x00, 0x20, 0xfb, 0xa0, 0x40, 0x7c, 0x3c, 0xff,
+ 0x74, 0x02, 0x88, 0xc2, 0x52, 0xbe, 0x7f, 0x7d,
+ 0xe8, 0x34, 0x01, 0xf6, 0xc2, 0x80, 0x74, 0x54,
+ 0xb4, 0x41, 0xbb, 0xaa, 0x55, 0xcd, 0x13, 0x5a,
+ 0x52, 0x72, 0x49, 0x81, 0xfb, 0x55, 0xaa, 0x75,
+ 0x43, 0xa0, 0x41, 0x7c, 0x84, 0xc0, 0x75, 0x05,
+ 0x83, 0xe1, 0x01, 0x74, 0x37, 0x66, 0x8b, 0x4c,
+ 0x10, 0xbe, 0x05, 0x7c, 0xc6, 0x44, 0xff, 0x01,
+ 0x66, 0x8b, 0x1e, 0x44, 0x7c, 0xc7, 0x04, 0x10,
+ 0x00, 0xc7, 0x44, 0x02, 0x01, 0x00, 0x66, 0x89,
+ 0x5c, 0x08, 0xc7, 0x44, 0x06, 0x00, 0x70, 0x66,
+ 0x31, 0xc0, 0x89, 0x44, 0x04, 0x66, 0x89, 0x44,
+ 0x0c, 0xb4, 0x42, 0xcd, 0x13, 0x72, 0x05, 0xbb,
+ 0x00, 0x70, 0xeb, 0x7d, 0xb4, 0x08, 0xcd, 0x13,
+ 0x73, 0x0a, 0xf6, 0xc2, 0x80, 0x0f, 0x84, 0xea,
+ 0x00, 0xe9, 0x8d, 0x00, 0xbe, 0x05, 0x7c, 0xc6,
+ 0x44, 0xff, 0x00, 0x66, 0x31, 0xc0, 0x88, 0xf0,
+ 0x40, 0x66, 0x89, 0x44, 0x04, 0x31, 0xd2, 0x88,
+ 0xca, 0xc1, 0xe2, 0x02, 0x88, 0xe8, 0x88, 0xf4,
+ 0x40, 0x89, 0x44, 0x08, 0x31, 0xc0, 0x88, 0xd0,
+ 0xc0, 0xe8, 0x02, 0x66, 0x89, 0x04, 0x66, 0xa1,
+ 0x44, 0x7c, 0x66, 0x31, 0xd2, 0x66, 0xf7, 0x34,
+ 0x88, 0x54, 0x0a, 0x66, 0x31, 0xd2, 0x66, 0xf7,
+ 0x74, 0x04, 0x88, 0x54, 0x0b, 0x89, 0x44, 0x0c,
+ 0x3b, 0x44, 0x08, 0x7d, 0x3c, 0x8a, 0x54, 0x0d,
+ 0xc0, 0xe2, 0x06, 0x8a, 0x4c, 0x0a, 0xfe, 0xc1,
+ 0x08, 0xd1, 0x8a, 0x6c, 0x0c, 0x5a, 0x8a, 0x74,
+ 0x0b, 0xbb, 0x00, 0x70, 0x8e, 0xc3, 0x31, 0xdb,
+ 0xb8, 0x01, 0x02, 0xcd, 0x13, 0x72, 0x2a, 0x8c,
+ 0xc3, 0x8e, 0x06, 0x48, 0x7c, 0x60, 0x1e, 0xb9,
+ 0x00, 0x01, 0x8e, 0xdb, 0x31, 0xf6, 0x31, 0xff,
+ 0xfc, 0xf3, 0xa5, 0x1f, 0x61, 0xff, 0x26, 0x42,
+ 0x7c, 0xbe, 0x85, 0x7d, 0xe8, 0x40, 0x00, 0xeb,
+ 0x0e, 0xbe, 0x8a, 0x7d, 0xe8, 0x38, 0x00, 0xeb,
+ 0x06, 0xbe, 0x94, 0x7d, 0xe8, 0x30, 0x00, 0xbe,
+ 0x99, 0x7d, 0xe8, 0x2a, 0x00, 0xeb, 0xfe, 0x47,
+ 0x52, 0x55, 0x42, 0x20, 0x00, 0x47, 0x65, 0x6f,
+ 0x6d, 0x00, 0x48, 0x61, 0x72, 0x64, 0x20, 0x44,
+ 0x69, 0x73, 0x6b, 0x00, 0x52, 0x65, 0x61, 0x64,
+ 0x00, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x00,
+ 0xbb, 0x01, 0x00, 0xb4, 0x0e, 0xcd, 0x10, 0xac,
+ 0x3c, 0x00, 0x75, 0xf4, 0xc3, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x50, 0xc1, 0x04, 0x00, 0x00, 0x00, 0x80, 0x01,
+ 0x01, 0x00, 0x83, 0xfe, 0xff, 0xff, 0x3f, 0x00,
+ 0x00, 0x00, 0x09, 0x7f, 0x32, 0x06, 0x00, 0xfe,
+ 0xff, 0xff, 0x05, 0xfe, 0xff, 0xff, 0x48, 0x7f,
+ 0x32, 0x06, 0x79, 0x59, 0x2d, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
+ };
+ vector<char> ret;
+ ret.insert(ret.begin(), mbr, mbr + sizeof(mbr));
+ return ret;
+}
+
+std::string GetUnusedLoopDevice() {
+ // get a loop device we can use for the install device
+ FILE* find_dev_cmd = popen("losetup -f", "r");
+ CHECK(find_dev_cmd);
+
+ string ret;
+ char dev[100] = {0};
+ size_t r;
+ while ((r = fread(dev, 1, sizeof(dev - 1), find_dev_cmd)) > 0) {
+ EXPECT_LT(r, sizeof(dev));
+ ret.insert(ret.end(), dev, dev + r);
+ }
+ EXPECT_EQ(r, 0);
+ EXPECT_TRUE(feof(find_dev_cmd));
+ fclose(find_dev_cmd);
+
+ // strip trailing \n on dev
+ if (*ret.rbegin() == '\n')
+ ret.resize(ret.size() - 1);
+
+ return ret;
+}
+
+bool ExpectVectorsEq(const vector<char>& a, const vector<char>& b) {
+ EXPECT_EQ(a.size(), b.size());
+ if (a.size() != b.size())
+ return false;
+ for (unsigned int i = 0; i < a.size(); i++) {
+ EXPECT_EQ(a[i], b[i]) << "offset: " << i;
+ }
+ return true;
+}
+
+void CreateExtImageAtPath(const string& path, vector<string>* out_paths) {
+ // create 10MiB sparse file
+ const char* const mount_path = kMountPath.c_str();
+ EXPECT_EQ(0, System(StringPrintf("dd if=/dev/zero of=%s"
+ " seek=10485759 bs=1 count=1",
+ path.c_str())));
+ EXPECT_EQ(0, System(StringPrintf("mkfs.ext3 -F %s", path.c_str())));
+ EXPECT_EQ(0, System(StringPrintf("mkdir -p %s", mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("mount -o loop %s %s", path.c_str(),
+ mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("echo hi > %s/hi", mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("echo hello > %s/hello", mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir", mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir/empty_dir", mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir/mnt", mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("echo T > %s/some_dir/test", mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("mkfifo %s/some_dir/fifo", mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("mknod %s/cdev c 2 3", mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("ln -s /some/target %s/sym", mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("ln %s/some_dir/test %s/testlink",
+ mount_path, mount_path)));
+ EXPECT_EQ(0, System(StringPrintf("umount %s", mount_path)));
+
+ if (out_paths) {
+ out_paths->clear();
+ out_paths->push_back("");
+ out_paths->push_back("/hi");
+ out_paths->push_back("/hello");
+ out_paths->push_back("/some_dir");
+ out_paths->push_back("/some_dir/empty_dir");
+ out_paths->push_back("/some_dir/mnt");
+ out_paths->push_back("/some_dir/test");
+ out_paths->push_back("/some_dir/fifo");
+ out_paths->push_back("/cdev");
+ out_paths->push_back("/testlink");
+ out_paths->push_back("/sym");
+ out_paths->push_back("/lost+found");
+ }
+}
+
+void VerifyAllPaths(const string& parent, set<string> expected_paths) {
+ FilesystemIterator iter(parent, set<string>());
+ ino_t test_ino = 0;
+ ino_t testlink_ino = 0;
+ while (!iter.IsEnd()) {
+ string path = iter.GetFullPath();
+ EXPECT_TRUE(expected_paths.find(path) != expected_paths.end()) << path;
+ EXPECT_EQ(1, expected_paths.erase(path));
+ if (utils::StringHasSuffix(path, "/hi") ||
+ utils::StringHasSuffix(path, "/hello") ||
+ utils::StringHasSuffix(path, "/test") ||
+ utils::StringHasSuffix(path, "/testlink")) {
+ EXPECT_TRUE(S_ISREG(iter.GetStat().st_mode));
+ if (utils::StringHasSuffix(path, "/test"))
+ test_ino = iter.GetStat().st_ino;
+ else if (utils::StringHasSuffix(path, "/testlink"))
+ testlink_ino = iter.GetStat().st_ino;
+ } else if (utils::StringHasSuffix(path, "/some_dir") ||
+ utils::StringHasSuffix(path, "/empty_dir") ||
+ utils::StringHasSuffix(path, "/mnt") ||
+ utils::StringHasSuffix(path, "/lost+found") ||
+ parent == path) {
+ EXPECT_TRUE(S_ISDIR(iter.GetStat().st_mode));
+ } else if (utils::StringHasSuffix(path, "/fifo")) {
+ EXPECT_TRUE(S_ISFIFO(iter.GetStat().st_mode));
+ } else if (utils::StringHasSuffix(path, "/cdev")) {
+ EXPECT_TRUE(S_ISCHR(iter.GetStat().st_mode));
+ } else if (utils::StringHasSuffix(path, "/sym")) {
+ EXPECT_TRUE(S_ISLNK(iter.GetStat().st_mode));
+ } else {
+ LOG(INFO) << "got non hardcoded path: " << path;
+ }
+ iter.Increment();
+ }
+ EXPECT_EQ(testlink_ino, test_ino);
+ EXPECT_NE(0, test_ino);
+ EXPECT_FALSE(iter.IsErr());
+ EXPECT_TRUE(expected_paths.empty());
+ if (!expected_paths.empty()) {
+ for (set<string>::const_iterator it = expected_paths.begin();
+ it != expected_paths.end(); ++it) {
+ LOG(INFO) << "extra path: " << *it;
+ }
+ }
+}
+
} // namespace chromeos_update_engine
diff --git a/test_utils.h b/test_utils.h
index 0c12e13..642158b 100644
--- a/test_utils.h
+++ b/test_utils.h
@@ -2,32 +2,178 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_TEST_UTILS_H__
-#define UPDATE_ENGINE_TEST_UTILS_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_TEST_UTILS_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_TEST_UTILS_H__
-#include <vector>
+#include <set>
#include <string>
+#include <vector>
+#include <gtest/gtest.h>
+#include "update_engine/action.h"
// These are some handy functions for unittests.
namespace chromeos_update_engine {
-// Returns the entire contents of the file at path. If the file doesn't
-// exist or error occurrs, an empty vector is returned.
-std::vector<char> ReadFile(const std::string& path);
-
// Writes the data passed to path. The file at path will be overwritten if it
// exists. Returns true on success, false otherwise.
-bool WriteFile(const std::string& path, const std::vector<char>& data);
+bool WriteFile(const char* path, const char* data, int data_len);
+bool WriteFileVector(const std::string& path, const std::vector<char>& data);
+bool WriteFileString(const std::string& path, const std::string& data);
// Returns the size of the file at path. If the file doesn't exist or some
// error occurrs, -1 is returned.
off_t FileSize(const std::string& path);
+// Reads a symlink from disk. Returns empty string on failure.
+std::string Readlink(const std::string& path);
+
// Gzip compresses the data passed using the gzip command line program.
// Returns compressed data back.
std::vector<char> GzipCompressData(const std::vector<char>& data);
+// Gives back a 512-bytes length array that contains an MBR with
+// the first partition is marked bootable.
+std::vector<char> GenerateSampleMbr();
+
+std::string GetUnusedLoopDevice();
+
+// Returns true iff a == b
+bool ExpectVectorsEq(const std::vector<char>& a, const std::vector<char>& b);
+
+inline int System(const std::string& cmd) {
+ return system(cmd.c_str());
+}
+
+namespace {
+// 300 byte pseudo-random string. Not null terminated.
+// This does not gzip compress well.
+const unsigned char kRandomString[] = {
+ 0xf2, 0xb7, 0x55, 0x92, 0xea, 0xa6, 0xc9, 0x57,
+ 0xe0, 0xf8, 0xeb, 0x34, 0x93, 0xd9, 0xc4, 0x8f,
+ 0xcb, 0x20, 0xfa, 0x37, 0x4b, 0x40, 0xcf, 0xdc,
+ 0xa5, 0x08, 0x70, 0x89, 0x79, 0x35, 0xe2, 0x3d,
+ 0x56, 0xa4, 0x75, 0x73, 0xa3, 0x6d, 0xd1, 0xd5,
+ 0x26, 0xbb, 0x9c, 0x60, 0xbd, 0x2f, 0x5a, 0xfa,
+ 0xb7, 0xd4, 0x3a, 0x50, 0xa7, 0x6b, 0x3e, 0xfd,
+ 0x61, 0x2b, 0x3a, 0x31, 0x30, 0x13, 0x33, 0x53,
+ 0xdb, 0xd0, 0x32, 0x71, 0x5c, 0x39, 0xed, 0xda,
+ 0xb4, 0x84, 0xca, 0xbc, 0xbd, 0x78, 0x1c, 0x0c,
+ 0xd8, 0x0b, 0x41, 0xe8, 0xe1, 0xe0, 0x41, 0xad,
+ 0x03, 0x12, 0xd3, 0x3d, 0xb8, 0x75, 0x9b, 0xe6,
+ 0xd9, 0x01, 0xd0, 0x87, 0xf4, 0x36, 0xfa, 0xa7,
+ 0x0a, 0xfa, 0xc5, 0x87, 0x65, 0xab, 0x9a, 0x7b,
+ 0xeb, 0x58, 0x23, 0xf0, 0xa8, 0x0a, 0xf2, 0x33,
+ 0x3a, 0xe2, 0xe3, 0x35, 0x74, 0x95, 0xdd, 0x3c,
+ 0x59, 0x5a, 0xd9, 0x52, 0x3a, 0x3c, 0xac, 0xe5,
+ 0x15, 0x87, 0x6d, 0x82, 0xbc, 0xf8, 0x7d, 0xbe,
+ 0xca, 0xd3, 0x2c, 0xd6, 0xec, 0x38, 0xeb, 0xe4,
+ 0x53, 0xb0, 0x4c, 0x3f, 0x39, 0x29, 0xf7, 0xa4,
+ 0x73, 0xa8, 0xcb, 0x32, 0x50, 0x05, 0x8c, 0x1c,
+ 0x1c, 0xca, 0xc9, 0x76, 0x0b, 0x8f, 0x6b, 0x57,
+ 0x1f, 0x24, 0x2b, 0xba, 0x82, 0xba, 0xed, 0x58,
+ 0xd8, 0xbf, 0xec, 0x06, 0x64, 0x52, 0x6a, 0x3f,
+ 0xe4, 0xad, 0xce, 0x84, 0xb4, 0x27, 0x55, 0x14,
+ 0xe3, 0x75, 0x59, 0x73, 0x71, 0x51, 0xea, 0xe8,
+ 0xcc, 0xda, 0x4f, 0x09, 0xaf, 0xa4, 0xbc, 0x0e,
+ 0xa6, 0x1f, 0xe2, 0x3a, 0xf8, 0x96, 0x7d, 0x30,
+ 0x23, 0xc5, 0x12, 0xb5, 0xd8, 0x73, 0x6b, 0x71,
+ 0xab, 0xf1, 0xd7, 0x43, 0x58, 0xa7, 0xc9, 0xf0,
+ 0xe4, 0x85, 0x1c, 0xd6, 0x92, 0x50, 0x2c, 0x98,
+ 0x36, 0xfe, 0x87, 0xaf, 0x43, 0x8f, 0x8f, 0xf5,
+ 0x88, 0x48, 0x18, 0x42, 0xcf, 0x42, 0xc1, 0xa8,
+ 0xe8, 0x05, 0x08, 0xa1, 0x45, 0x70, 0x5b, 0x8c,
+ 0x39, 0x28, 0xab, 0xe9, 0x6b, 0x51, 0xd2, 0xcb,
+ 0x30, 0x04, 0xea, 0x7d, 0x2f, 0x6e, 0x6c, 0x3b,
+ 0x5f, 0x82, 0xd9, 0x5b, 0x89, 0x37, 0x65, 0x65,
+ 0xbe, 0x9f, 0xa3, 0x5d
+};
+
+const string kMountPath("/tmp/UpdateEngineTests_mnt");
+} // namespace {}
+
+// Creates an ext image with some files in it. The paths creates are
+// returned in out_paths.
+void CreateExtImageAtPath(const std::string& path,
+ std::vector<std::string>* out_paths);
+
+// Verifies that for each path in paths, it exists in the filesystem under
+// parent. Also, verifies that no additional paths are present under parent.
+// Also tests properties of various files created by CreateExtImageAtPath().
+// Intentionally copies expected_paths.
+void VerifyAllPaths(const std::string& parent,
+ std::set<std::string> expected_paths);
+
+// Useful actions for test
+
+class NoneType;
+
+template<typename T>
+class ObjectFeederAction;
+
+template<typename T>
+class ActionTraits<ObjectFeederAction<T> > {
+ public:
+ typedef T OutputObjectType;
+ typedef NoneType InputObjectType;
+};
+
+// This is a simple Action class for testing. It feeds an object into
+// another action.
+template<typename T>
+struct ObjectFeederAction : public Action<ObjectFeederAction<T> > {
+ public:
+ typedef NoneType InputObjectType;
+ typedef T OutputObjectType;
+ void PerformAction() {
+ LOG(INFO) << "feeder running!";
+ CHECK(this->processor_);
+ if (this->HasOutputPipe()) {
+ this->SetOutputObject(out_obj_);
+ }
+ this->processor_->ActionComplete(this, true);
+ }
+ static std::string StaticType() { return "ObjectFeederAction"; }
+ std::string Type() const { return StaticType(); }
+ void set_obj(const T& out_obj) {
+ out_obj_ = out_obj;
+ }
+ private:
+ T out_obj_;
+};
+
+template<typename T>
+class ObjectCollectorAction;
+
+template<typename T>
+class ActionTraits<ObjectCollectorAction<T> > {
+ public:
+ typedef NoneType OutputObjectType;
+ typedef T InputObjectType;
+};
+
+// This is a simple Action class for testing. It receives an object from
+// another action.
+template<typename T>
+struct ObjectCollectorAction : public Action<ObjectCollectorAction<T> > {
+ public:
+ typedef T InputObjectType;
+ typedef NoneType OutputObjectType;
+ void PerformAction() {
+ LOG(INFO) << "collector running!";
+ ASSERT_TRUE(this->processor_);
+ if (this->HasInputObject()) {
+ object_ = this->GetInputObject();
+ }
+ this->processor_->ActionComplete(this, true);
+ }
+ static std::string StaticType() { return "ObjectCollectorAction"; }
+ std::string Type() const { return StaticType(); }
+ const T& object() const { return object_; }
+ private:
+ T object_;
+};
+
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_TEST_UTILS_H__
\ No newline at end of file
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_TEST_UTILS_H__
\ No newline at end of file
diff --git a/testrunner.cc b/testrunner.cc
index 8931902..0a74480 100644
--- a/testrunner.cc
+++ b/testrunner.cc
@@ -4,9 +4,14 @@
// based on pam_google_testrunner.cc
+#include <glib.h>
#include <gtest/gtest.h>
+#include "update_engine/subprocess.h"
+
int main(int argc, char **argv) {
+ g_thread_init(NULL);
+ chromeos_update_engine::Subprocess::Init();
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
diff --git a/update_check_action.cc b/update_check_action.cc
index a4942a6..a9d303c 100644
--- a/update_check_action.cc
+++ b/update_check_action.cc
@@ -9,14 +9,16 @@
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
+#include "chromeos/obsolete_logging.h"
#include "update_engine/action_pipe.h"
+#include "update_engine/utils.h"
using std::string;
namespace chromeos_update_engine {
const char* const UpdateCheckParams::kAppId(
- "87efface-864d-49a5-9bb3-4b050a7c227a");
+ "{87efface-864d-49a5-9bb3-4b050a7c227a}");
const char* const UpdateCheckParams::kOsPlatform("Chrome OS");
const char* const UpdateCheckParams::kOsVersion("Indy");
@@ -91,13 +93,14 @@
return string(reinterpret_cast<const char *>(str.get()));
}
-UpdateCheckAction::UpdateCheckAction(const UpdateCheckParams& params,
- HttpFetcher* http_fetcher)
- : params_(params), http_fetcher_(http_fetcher) {}
+UpdateCheckAction::UpdateCheckAction(HttpFetcher* http_fetcher)
+ : http_fetcher_(http_fetcher) {}
UpdateCheckAction::~UpdateCheckAction() {}
void UpdateCheckAction::PerformAction() {
+ CHECK(HasInputObject());
+ params_ = GetInputObject();
http_fetcher_->set_delegate(this);
string request_post(FormatRequest(params_));
http_fetcher_->SetPostData(request_post.data(), request_post.size());
@@ -118,26 +121,6 @@
}
namespace {
-// A little object to call ActionComplete on the ActionProcessor when
-// it's destructed.
-class ScopedActionCompleter {
- public:
- explicit ScopedActionCompleter(ActionProcessor* processor,
- AbstractAction* action)
- : processor_(processor), action_(action), success_(false) {}
- ~ScopedActionCompleter() {
- processor_->ActionComplete(action_, success_);
- }
- void set_success(bool success) {
- success_ = success;
- }
- private:
- ActionProcessor* processor_;
- AbstractAction* action_;
- bool success_;
- DISALLOW_COPY_AND_ASSIGN(ScopedActionCompleter);
-};
-
// If non-NULL response, caller is responsible for calling xmlXPathFreeObject()
// on the returned object.
// This code is roughly based on the libxml tutorial at:
diff --git a/update_check_action.h b/update_check_action.h
index cf6d512..402943b 100644
--- a/update_check_action.h
+++ b/update_check_action.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UPDATE_ENGINE_UPDATE_CHECK_ACTION_H__
-#define UPDATE_ENGINE_UPDATE_CHECK_ACTION_H__
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_CHECK_ACTION_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_CHECK_ACTION_H__
#include <sys/types.h>
#include <sys/stat.h>
@@ -93,8 +93,8 @@
template<>
class ActionTraits<UpdateCheckAction> {
public:
- // Does not take an object for input
- typedef NoneType InputObjectType;
+ // Takes parameters on the input pipe
+ typedef UpdateCheckParams InputObjectType;
// On success, puts the output path on output
typedef UpdateCheckResponse OutputObjectType;
};
@@ -108,8 +108,7 @@
// Takes ownership of the passed in HttpFetcher. Useful for testing.
// A good calling pattern is:
// UpdateCheckAction(..., new WhateverHttpFetcher);
- UpdateCheckAction(const UpdateCheckParams& params,
- HttpFetcher* http_fetcher);
+ UpdateCheckAction(HttpFetcher* http_fetcher);
virtual ~UpdateCheckAction();
typedef ActionTraits<UpdateCheckAction>::InputObjectType InputObjectType;
typedef ActionTraits<UpdateCheckAction>::OutputObjectType OutputObjectType;
@@ -117,7 +116,8 @@
void TerminateProcessing();
// Debugging/logging
- std::string Type() const { return "UpdateCheckAction"; }
+ static std::string StaticType() { return "UpdateCheckAction"; }
+ std::string Type() const { return StaticType(); }
// Delegate methods (see http_fetcher.h)
virtual void ReceivedBytes(HttpFetcher *fetcher,
@@ -139,4 +139,4 @@
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_UPDATE_CHECK_ACTION_H__
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_CHECK_ACTION_H__
diff --git a/update_check_action_unittest.cc b/update_check_action_unittest.cc
index 9b8436c..723fc22 100644
--- a/update_check_action_unittest.cc
+++ b/update_check_action_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include <string>
+#include <vector>
#include <glib.h>
#include <gtest/gtest.h>
#include "update_engine/action_pipe.h"
@@ -11,9 +12,10 @@
#include "update_engine/omaha_hash_calculator.h"
#include "update_engine/test_utils.h"
-namespace chromeos_update_engine {
-
using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
class UpdateCheckActionTest : public ::testing::Test { };
@@ -35,8 +37,9 @@
const string& needsadmin,
const string& size) {
return string("<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
- "appid=\"") + app_id + "\" status=\"ok\"><ping "
+ "xmlns=\"http://www.google.com/update2/response\" "
+ "protocol=\"2.0\"><app "
+ "appid=\"") + app_id + "\" status=\"ok\"><ping "
"status=\"ok\"/><updatecheck DisplayVersion=\"" + display_version + "\" "
"MoreInfo=\"" + more_info_url + "\" Prompt=\"" + prompt + "\" "
"codebase=\"" + codebase + "\" "
@@ -56,11 +59,11 @@
g_main_loop_quit(loop_);
}
- virtual void ActionCompleted(const ActionProcessor* processor,
- const AbstractAction* action,
+ virtual void ActionCompleted(ActionProcessor* processor,
+ AbstractAction* action,
bool success) {
// make sure actions always succeed
- if (action->Type() == "UpdateCheckAction")
+ if (action->Type() == UpdateCheckAction::StaticType())
EXPECT_EQ(expected_success_, success);
else
EXPECT_TRUE(success);
@@ -102,7 +105,10 @@
CHECK(false);
}
// Debugging/logging
- std::string Type() const { return "OutputObjectCollectorAction"; }
+ static std::string StaticType() {
+ return "OutputObjectCollectorAction";
+ }
+ std::string Type() const { return StaticType(); }
bool has_input_object_;
UpdateCheckResponse update_check_response_;
};
@@ -119,17 +125,20 @@
GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
MockHttpFetcher *fetcher = new MockHttpFetcher(http_response.data(),
http_response.size());
-
- UpdateCheckAction action(params, fetcher); // takes ownership of fetcher
+ ObjectFeederAction<UpdateCheckParams> feeder_action;
+ UpdateCheckAction action(fetcher); // takes ownership of fetcher
UpdateCheckActionTestProcessorDelegate delegate;
delegate.loop_ = loop;
delegate.expected_success_ = expected_success;
ActionProcessor processor;
+ feeder_action.set_obj(params);
processor.set_delegate(&delegate);
+ processor.EnqueueAction(&feeder_action);
processor.EnqueueAction(&action);
OutputObjectCollectorAction collector_action;
+ BondActions(&feeder_action, &action);
BondActions(&action, &collector_action);
processor.EnqueueAction(&collector_action);
@@ -211,14 +220,17 @@
GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
- UpdateCheckAction action(params,
- new MockHttpFetcher(http_response.data(),
+ ObjectFeederAction<UpdateCheckParams> feeder_action;
+ feeder_action.set_obj(params);
+ UpdateCheckAction action(new MockHttpFetcher(http_response.data(),
http_response.size()));
UpdateCheckActionTestProcessorDelegate delegate;
delegate.loop_ = loop;
ActionProcessor processor;
processor.set_delegate(&delegate);
+ processor.EnqueueAction(&feeder_action);
processor.EnqueueAction(&action);
+ BondActions(&feeder_action, &action);
g_timeout_add(0, &StartProcessorInRunLoop, &processor);
g_main_loop_run(loop);
@@ -327,18 +339,23 @@
"unittest_track");
UpdateCheckResponse response;
ASSERT_TRUE(TestUpdateCheckAction(params,
- string("<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" "
- "protocol=\"2.0\"><app appid=\"") +
- UpdateCheckParams::kAppId + "\" status=\"ok\"><ping "
- "status=\"ok\"/><updatecheck DisplayVersion=\"1.2.3.4\" "
- "Prompt=\"false\" "
- "codebase=\"http://code/base\" "
- "hash=\"HASH1234=\" needsadmin=\"true\" "
- "size=\"123\" status=\"ok\"/></app></gupdate>",
- true,
- &response,
- NULL));
+ string("<?xml version=\"1.0\" "
+ "encoding=\"UTF-8\"?><gupdate "
+ "xmlns=\"http://www.google.com/"
+ "update2/response\" "
+ "protocol=\"2.0\"><app appid=\"") +
+ UpdateCheckParams::kAppId
+ + "\" status=\"ok\"><ping "
+ "status=\"ok\"/><updatecheck "
+ "DisplayVersion=\"1.2.3.4\" "
+ "Prompt=\"false\" "
+ "codebase=\"http://code/base\" "
+ "hash=\"HASH1234=\" needsadmin=\"true\" "
+ "size=\"123\" "
+ "status=\"ok\"/></app></gupdate>",
+ true,
+ &response,
+ NULL));
EXPECT_TRUE(response.update_exists);
EXPECT_EQ("1.2.3.4", response.display_version);
EXPECT_EQ("http://code/base", response.codebase);
@@ -381,14 +398,17 @@
string http_response("doesn't matter");
GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
- UpdateCheckAction action(params,
- new MockHttpFetcher(http_response.data(),
+ ObjectFeederAction<UpdateCheckParams> feeder_action;
+ feeder_action.set_obj(params);
+ UpdateCheckAction action(new MockHttpFetcher(http_response.data(),
http_response.size()));
TerminateEarlyTestProcessorDelegate delegate;
delegate.loop_ = loop;
ActionProcessor processor;
processor.set_delegate(&delegate);
+ processor.EnqueueAction(&feeder_action);
processor.EnqueueAction(&action);
+ BondActions(&feeder_action, &action);
g_timeout_add(0, &TerminateTransferTestStarter, &processor);
g_main_loop_run(loop);