Remove ComputeSourceHash mode in FileSystemVerification action.
This mode was used to calculate the source partition hash before download
the payload, and we will verify it against the hash in the payload.
Now that we are using per-operation source hash, this mode is no longer
needed.
Test: ./update_engine_unittests
Test: cros_workon_make update_engine --test
Bug: 26972259
Change-Id: Ie30a38cfd9f94e4efe02dfc8664e6785018261f6
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 1c6a862..de121ed 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -775,17 +775,6 @@
partitions_.push_back(std::move(kern_part));
}
- // TODO(deymo): Remove this block of code once we switched to optional
- // source partition verification. This list of partitions in the InstallPlan
- // is initialized with the expected hashes in the payload major version 1,
- // so we need to check those now if already set. See b/23182225.
- if (!install_plan_->partitions.empty()) {
- if (!VerifySourcePartitions()) {
- *error = ErrorCode::kDownloadStateInitializationError;
- return false;
- }
- }
-
// Fill in the InstallPlan::partitions based on the partitions from the
// payload.
install_plan_->partitions.clear();
@@ -1631,80 +1620,6 @@
return ErrorCode::kSuccess;
}
-namespace {
-void LogVerifyError(const string& type,
- const string& device,
- uint64_t size,
- const string& local_hash,
- const string& expected_hash) {
- LOG(ERROR) << "This is a server-side error due to "
- << "mismatched delta update image!";
- LOG(ERROR) << "The delta I've been given contains a " << type << " delta "
- << "update that must be applied over a " << type << " with "
- << "a specific checksum, but the " << type << " we're starting "
- << "with doesn't have that checksum! This means that "
- << "the delta I've been given doesn't match my existing "
- << "system. The " << type << " partition I have has hash: "
- << local_hash << " but the update expected me to have "
- << expected_hash << " .";
- LOG(INFO) << "To get the checksum of the " << type << " partition run this"
- "command: dd if=" << device << " bs=1M count=" << size
- << " iflag=count_bytes 2>/dev/null | openssl dgst -sha256 -binary "
- "| openssl base64";
- LOG(INFO) << "To get the checksum of partitions in a bin file, "
- << "run: .../src/scripts/sha256_partitions.sh .../file.bin";
-}
-
-string StringForHashBytes(const void* bytes, size_t size) {
- return brillo::data_encoding::Base64Encode(bytes, size);
-}
-} // namespace
-
-bool DeltaPerformer::VerifySourcePartitions() {
- LOG(INFO) << "Verifying source partitions.";
- CHECK(manifest_valid_);
- CHECK(install_plan_);
- if (install_plan_->partitions.size() != partitions_.size()) {
- DLOG(ERROR) << "The list of partitions in the InstallPlan doesn't match the "
- "list received in the payload. The InstallPlan has "
- << install_plan_->partitions.size()
- << " partitions while the payload has " << partitions_.size()
- << " partitions.";
- return false;
- }
- for (size_t i = 0; i < partitions_.size(); ++i) {
- if (partitions_[i].partition_name() != install_plan_->partitions[i].name) {
- DLOG(ERROR) << "The InstallPlan's partition " << i << " is \""
- << install_plan_->partitions[i].name
- << "\" but the payload expects it to be \""
- << partitions_[i].partition_name()
- << "\". This is an error in the DeltaPerformer setup.";
- return false;
- }
- if (!partitions_[i].has_old_partition_info())
- continue;
- const PartitionInfo& info = partitions_[i].old_partition_info();
- const InstallPlan::Partition& plan_part = install_plan_->partitions[i];
- bool valid =
- !plan_part.source_hash.empty() &&
- plan_part.source_hash.size() == info.hash().size() &&
- memcmp(plan_part.source_hash.data(),
- info.hash().data(),
- plan_part.source_hash.size()) == 0;
- if (!valid) {
- LogVerifyError(partitions_[i].partition_name(),
- plan_part.source_path,
- info.hash().size(),
- StringForHashBytes(plan_part.source_hash.data(),
- plan_part.source_hash.size()),
- StringForHashBytes(info.hash().data(),
- info.hash().size()));
- return false;
- }
- }
- return true;
-}
-
void DeltaPerformer::DiscardBuffer(bool do_advance_offset,
size_t signed_hash_buffer_size) {
// Update the buffer offset.
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 03f30b2..4da631f 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -217,12 +217,6 @@
// Update overall progress metrics, log as necessary.
void UpdateOverallProgress(bool force_log, const char* message_prefix);
- // Verifies that the expected source partition hashes (if present) match the
- // hashes for the current partitions. Returns true if there are no expected
- // hashes in the payload (e.g., if it's a new-style full update) or if the
- // hashes match; returns false otherwise.
- bool VerifySourcePartitions();
-
// Returns true if enough of the delta file has been passed via Write()
// to be able to perform a given install operation.
bool CanPerformInstallOperation(const InstallOperation& operation);
diff --git a/payload_consumer/filesystem_verifier_action.cc b/payload_consumer/filesystem_verifier_action.cc
index 4d7d3dc..94af2bb 100644
--- a/payload_consumer/filesystem_verifier_action.cc
+++ b/payload_consumer/filesystem_verifier_action.cc
@@ -26,6 +26,7 @@
#include <string>
#include <base/bind.h>
+#include <brillo/data_encoding.h>
#include <brillo/streams/file_stream.h>
#include "update_engine/common/boot_control_interface.h"
@@ -39,13 +40,15 @@
namespace {
const off_t kReadFileBufferSize = 128 * 1024;
+
+string StringForHashBytes(const brillo::Blob& hash) {
+ return brillo::data_encoding::Base64Encode(hash.data(), hash.size());
+}
} // namespace
FilesystemVerifierAction::FilesystemVerifierAction(
- const BootControlInterface* boot_control,
- VerifierMode verifier_mode)
- : verifier_mode_(verifier_mode),
- boot_control_(boot_control) {}
+ const BootControlInterface* boot_control)
+ : boot_control_(boot_control) {}
void FilesystemVerifierAction::PerformAction() {
// Will tell the ActionProcessor we've failed if we return.
@@ -92,27 +95,31 @@
void FilesystemVerifierAction::StartPartitionHashing() {
if (partition_index_ == install_plan_.partitions.size()) {
- // We never called this action with kVerifySourceHash directly, if we are in
- // this mode, it means the target partition verification has failed, so we
- // should set the error code to reflect the error in target.
- if (verifier_mode_ == VerifierMode::kVerifySourceHash)
- Cleanup(ErrorCode::kNewRootfsVerificationError);
- else
- Cleanup(ErrorCode::kSuccess);
+ switch (verifier_step_) {
+ case VerifierStep::kVerifySourceHash:
+ // The action will skip kVerifySourceHash step if target partition hash
+ // matches, if we are in this step, it means target hash does not match,
+ // and now that the source hash matches, we should set the error code to
+ // reflect the error in target partition.
+ Cleanup(ErrorCode::kNewRootfsVerificationError);
+ break;
+ case VerifierStep::kVerifyTargetHash:
+ Cleanup(ErrorCode::kSuccess);
+ break;
+ }
return;
}
InstallPlan::Partition& partition =
install_plan_.partitions[partition_index_];
string part_path;
- switch (verifier_mode_) {
- case VerifierMode::kComputeSourceHash:
- case VerifierMode::kVerifySourceHash:
+ switch (verifier_step_) {
+ case VerifierStep::kVerifySourceHash:
boot_control_->GetPartitionDevice(
partition.name, install_plan_.source_slot, &part_path);
remaining_size_ = partition.source_size;
break;
- case VerifierMode::kVerifyTargetHash:
+ case VerifierStep::kVerifyTargetHash:
boot_control_->GetPartitionDevice(
partition.name, install_plan_.target_slot, &part_path);
remaining_size_ = partition.target_size;
@@ -211,32 +218,45 @@
install_plan_.partitions[partition_index_];
LOG(INFO) << "Hash of " << partition.name << ": " << hasher_->hash();
- switch (verifier_mode_) {
- case VerifierMode::kComputeSourceHash:
- partition.source_hash = hasher_->raw_hash();
- partition_index_++;
- break;
- case VerifierMode::kVerifyTargetHash:
+ switch (verifier_step_) {
+ case VerifierStep::kVerifyTargetHash:
if (partition.target_hash != hasher_->raw_hash()) {
LOG(ERROR) << "New '" << partition.name
<< "' partition verification failed.";
- if (DeltaPerformer::kSupportedMinorPayloadVersion <
- kOpSrcHashMinorPayloadVersion)
- return Cleanup(ErrorCode::kNewRootfsVerificationError);
- // If we support per-operation source hash, then we skipped source
- // filesystem verification, now that the target partition does not
- // match, we need to switch to kVerifySourceHash mode to check if it's
- // because the source partition does not match either.
- verifier_mode_ = VerifierMode::kVerifySourceHash;
+ // If we have not verified source partition yet, now that the target
+ // partition does not match, we need to switch to kVerifySourceHash step
+ // to check if it's because the source partition does not match either.
+ verifier_step_ = VerifierStep::kVerifySourceHash;
partition_index_ = 0;
} else {
partition_index_++;
}
break;
- case VerifierMode::kVerifySourceHash:
+ case VerifierStep::kVerifySourceHash:
if (partition.source_hash != hasher_->raw_hash()) {
LOG(ERROR) << "Old '" << partition.name
<< "' partition verification failed.";
+ LOG(ERROR) << "This is a server-side error due to mismatched delta"
+ << " update image!";
+ LOG(ERROR) << "The delta I've been given contains a " << partition.name
+ << " delta update that must be applied over a "
+ << partition.name << " with a specific checksum, but the "
+ << partition.name
+ << " we're starting with doesn't have that checksum! This"
+ " means that the delta I've been given doesn't match my"
+ " existing system. The "
+ << partition.name << " partition I have has hash: "
+ << StringForHashBytes(hasher_->raw_hash())
+ << " but the update expected me to have "
+ << StringForHashBytes(partition.source_hash) << " .";
+ LOG(INFO) << "To get the checksum of the " << partition.name
+ << " partition run this command: dd if="
+ << partition.source_path
+ << " bs=1M count=" << partition.source_size
+ << " iflag=count_bytes 2>/dev/null | openssl dgst -sha256 "
+ "-binary | openssl base64";
+ LOG(INFO) << "To get the checksum of partitions in a bin file, "
+ << "run: .../src/scripts/sha256_partitions.sh .../file.bin";
return Cleanup(ErrorCode::kDownloadStateInitializationError);
}
partition_index_++;
diff --git a/payload_consumer/filesystem_verifier_action.h b/payload_consumer/filesystem_verifier_action.h
index 94f1b4e..6039e02 100644
--- a/payload_consumer/filesystem_verifier_action.h
+++ b/payload_consumer/filesystem_verifier_action.h
@@ -30,28 +30,27 @@
#include "update_engine/common/hash_calculator.h"
#include "update_engine/payload_consumer/install_plan.h"
-// This action will hash all the partitions of a single slot involved in the
-// update (either source or target slot). The hashes are then either stored in
-// the InstallPlan (for source partitions) or verified against it (for target
-// partitions).
+// This action will hash all the partitions of the target slot involved in the
+// update. The hashes are then verified against the ones in the InstallPlan.
+// If the target hash does not match, the action will fail. In case of failure,
+// the error code will depend on whether the source slot hashes are provided and
+// match.
namespace chromeos_update_engine {
-// The mode we are running the FilesystemVerifier on. On kComputeSourceHash mode
-// it computes the source_hash of all the partitions in the InstallPlan, based
-// on the already populated source_size values. On kVerifyTargetHash it computes
-// the hash on the target partitions based on the already populated size and
-// verifies it matches the one in the target_hash in the InstallPlan.
-enum class VerifierMode {
- kComputeSourceHash,
+// The step FilesystemVerifier is on. On kVerifyTargetHash it computes the hash
+// on the target partitions based on the already populated size and verifies it
+// matches the one in the target_hash in the InstallPlan.
+// If the hash matches, then we skip the kVerifySourceHash step, otherwise we
+// need to check if the source is the root cause of the mismatch.
+enum class VerifierStep {
kVerifyTargetHash,
kVerifySourceHash,
};
class FilesystemVerifierAction : public InstallPlanAction {
public:
- FilesystemVerifierAction(const BootControlInterface* boot_control,
- VerifierMode verifier_mode);
+ explicit FilesystemVerifierAction(const BootControlInterface* boot_control);
void PerformAction() override;
void TerminateProcessing() override;
@@ -72,7 +71,7 @@
RunAsRootDetermineFilesystemSizeTest);
// Starts the hashing of the current partition. If there aren't any partitions
- // remaining to be hashed, if finishes the action.
+ // remaining to be hashed, it finishes the action.
void StartPartitionHashing();
// Schedules the asynchronous read of the filesystem.
@@ -93,7 +92,7 @@
void Cleanup(ErrorCode code);
// The type of the partition that we are verifying.
- VerifierMode verifier_mode_;
+ VerifierStep verifier_step_ = VerifierStep::kVerifyTargetHash;
// The BootControlInterface used to get the partitions based on the slots.
const BootControlInterface* const boot_control_;
diff --git a/payload_consumer/filesystem_verifier_action_unittest.cc b/payload_consumer/filesystem_verifier_action_unittest.cc
index fdc94d9..ad224bc 100644
--- a/payload_consumer/filesystem_verifier_action_unittest.cc
+++ b/payload_consumer/filesystem_verifier_action_unittest.cc
@@ -56,9 +56,7 @@
}
// Returns true iff test has completed successfully.
- bool DoTest(bool terminate_early,
- bool hash_fail,
- VerifierMode verifier_mode);
+ bool DoTest(bool terminate_early, bool hash_fail);
brillo::FakeMessageLoop loop_{nullptr};
FakeBootControl fake_boot_control_;
@@ -115,23 +113,8 @@
}
}
-// TODO(garnold) Temporarily disabling this test, see chromium-os:31082 for
-// details; still trying to track down the root cause for these rare write
-// failures and whether or not they are due to the test setup or an inherent
-// issue with the chroot environment, library versions we use, etc.
-TEST_F(FilesystemVerifierActionTest, DISABLED_RunAsRootSimpleTest) {
- ASSERT_EQ(0U, getuid());
- bool test = DoTest(false, false, VerifierMode::kComputeSourceHash);
- EXPECT_TRUE(test);
- if (!test)
- return;
- test = DoTest(false, false, VerifierMode::kVerifyTargetHash);
- EXPECT_TRUE(test);
-}
-
bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
- bool hash_fail,
- VerifierMode verifier_mode) {
+ bool hash_fail) {
string a_loop_file;
if (!(utils::MakeTempFile("a_loop_file.XXXXXX", &a_loop_file, nullptr))) {
@@ -170,15 +153,13 @@
install_plan.target_slot = 1;
InstallPlan::Partition part;
part.name = "part";
- if (verifier_mode == VerifierMode::kVerifyTargetHash) {
- part.target_size = kLoopFileSize - (hash_fail ? 1 : 0);
- part.target_path = a_dev;
- fake_boot_control_.SetPartitionDevice(
- part.name, install_plan.target_slot, a_dev);
- if (!HashCalculator::RawHashOfData(a_loop_data, &part.target_hash)) {
- ADD_FAILURE();
- success = false;
- }
+ part.target_size = kLoopFileSize - (hash_fail ? 1 : 0);
+ part.target_path = a_dev;
+ fake_boot_control_.SetPartitionDevice(
+ part.name, install_plan.target_slot, a_dev);
+ if (!HashCalculator::RawHashOfData(a_loop_data, &part.target_hash)) {
+ ADD_FAILURE();
+ success = false;
}
part.source_size = kLoopFileSize;
part.source_path = a_dev;
@@ -193,7 +174,7 @@
ActionProcessor processor;
ObjectFeederAction<InstallPlan> feeder_action;
- FilesystemVerifierAction copier_action(&fake_boot_control_, verifier_mode);
+ FilesystemVerifierAction copier_action(&fake_boot_control_);
ObjectCollectorAction<InstallPlan> collector_action;
BondActions(&feeder_action, &copier_action);
@@ -265,8 +246,7 @@
processor.set_delegate(&delegate);
- FilesystemVerifierAction copier_action(&fake_boot_control_,
- VerifierMode::kVerifyTargetHash);
+ FilesystemVerifierAction copier_action(&fake_boot_control_);
ObjectCollectorAction<InstallPlan> collector_action;
BondActions(&copier_action, &collector_action);
@@ -294,8 +274,7 @@
install_plan.partitions = {part};
feeder_action.set_obj(install_plan);
- FilesystemVerifierAction verifier_action(&fake_boot_control_,
- VerifierMode::kVerifyTargetHash);
+ FilesystemVerifierAction verifier_action(&fake_boot_control_);
ObjectCollectorAction<InstallPlan> collector_action;
BondActions(&verifier_action, &collector_action);
@@ -311,18 +290,17 @@
TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashTest) {
ASSERT_EQ(0U, getuid());
- EXPECT_TRUE(DoTest(false, false, VerifierMode::kVerifyTargetHash));
- EXPECT_TRUE(DoTest(false, false, VerifierMode::kComputeSourceHash));
+ EXPECT_TRUE(DoTest(false, false));
}
TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) {
ASSERT_EQ(0U, getuid());
- EXPECT_TRUE(DoTest(false, true, VerifierMode::kVerifyTargetHash));
+ EXPECT_TRUE(DoTest(false, true));
}
TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) {
ASSERT_EQ(0U, getuid());
- EXPECT_TRUE(DoTest(true, false, VerifierMode::kVerifyTargetHash));
+ EXPECT_TRUE(DoTest(true, false));
// TerminateEarlyTest may leak some null callbacks from the Stream class.
while (loop_.RunOnce(false)) {}
}
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index 454dd78..f9240c7 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -67,15 +67,11 @@
// The vector below is used for partition verification. The flow is:
//
- // 1. FilesystemVerifierAction computes and fills in the source partition
- // hash based on the guessed source size for delta major version 1 updates.
+ // 1. DownloadAction fills in the expected source and target partition sizes
+ // and hashes based on the manifest.
//
- // 2. DownloadAction verifies the source partition sizes and hashes against
- // the expected values transmitted in the update manifest. It fills in the
- // expected target partition sizes and hashes based on the manifest.
- //
- // 3. FilesystemVerifierAction computes and verifies the applied partition
- // sizes and hashes against the expected values in target_partition_hashes.
+ // 2. FilesystemVerifierAction computes and verifies the partition sizes and
+ // hashes against the expected values.
struct Partition {
bool operator==(const Partition& that) const;
diff --git a/update_attempter.cc b/update_attempter.cc
index 81f2ab0..751eb52 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -610,9 +610,6 @@
false));
shared_ptr<OmahaResponseHandlerAction> response_handler_action(
new OmahaResponseHandlerAction(system_state_));
- shared_ptr<FilesystemVerifierAction> src_filesystem_verifier_action(
- new FilesystemVerifierAction(system_state_->boot_control(),
- VerifierMode::kComputeSourceHash));
shared_ptr<OmahaRequestAction> download_started_action(
new OmahaRequestAction(system_state_,
@@ -640,9 +637,8 @@
new LibcurlHttpFetcher(GetProxyResolver(),
system_state_->hardware())),
false));
- shared_ptr<FilesystemVerifierAction> dst_filesystem_verifier_action(
- new FilesystemVerifierAction(system_state_->boot_control(),
- VerifierMode::kVerifyTargetHash));
+ shared_ptr<FilesystemVerifierAction> filesystem_verifier_action(
+ new FilesystemVerifierAction(system_state_->boot_control()));
shared_ptr<OmahaRequestAction> update_complete_action(
new OmahaRequestAction(
system_state_,
@@ -658,25 +654,20 @@
actions_.push_back(shared_ptr<AbstractAction>(update_check_action));
actions_.push_back(shared_ptr<AbstractAction>(response_handler_action));
- actions_.push_back(shared_ptr<AbstractAction>(
- src_filesystem_verifier_action));
actions_.push_back(shared_ptr<AbstractAction>(download_started_action));
actions_.push_back(shared_ptr<AbstractAction>(download_action));
actions_.push_back(shared_ptr<AbstractAction>(download_finished_action));
- actions_.push_back(shared_ptr<AbstractAction>(
- dst_filesystem_verifier_action));
+ actions_.push_back(shared_ptr<AbstractAction>(filesystem_verifier_action));
// Bond them together. We have to use the leaf-types when calling
// BondActions().
BondActions(update_check_action.get(),
response_handler_action.get());
BondActions(response_handler_action.get(),
- src_filesystem_verifier_action.get());
- BondActions(src_filesystem_verifier_action.get(),
download_action.get());
BondActions(download_action.get(),
- dst_filesystem_verifier_action.get());
- BuildPostInstallActions(dst_filesystem_verifier_action.get());
+ filesystem_verifier_action.get());
+ BuildPostInstallActions(filesystem_verifier_action.get());
actions_.push_back(shared_ptr<AbstractAction>(update_complete_action));
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index e42f569..9f46536 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -399,9 +399,8 @@
hardware_,
nullptr, // system_state, not used.
new MultiRangeHttpFetcher(download_fetcher))); // passes ownership
- shared_ptr<FilesystemVerifierAction> dst_filesystem_verifier_action(
- new FilesystemVerifierAction(boot_control_,
- VerifierMode::kVerifyTargetHash));
+ shared_ptr<FilesystemVerifierAction> filesystem_verifier_action(
+ new FilesystemVerifierAction(boot_control_));
shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
new PostinstallRunnerAction(boot_control_));
@@ -411,15 +410,14 @@
actions_.push_back(shared_ptr<AbstractAction>(install_plan_action));
actions_.push_back(shared_ptr<AbstractAction>(download_action));
- actions_.push_back(
- shared_ptr<AbstractAction>(dst_filesystem_verifier_action));
+ actions_.push_back(shared_ptr<AbstractAction>(filesystem_verifier_action));
actions_.push_back(shared_ptr<AbstractAction>(postinstall_runner_action));
// Bond them together. We have to use the leaf-types when calling
// BondActions().
BondActions(install_plan_action.get(), download_action.get());
- BondActions(download_action.get(), dst_filesystem_verifier_action.get());
- BondActions(dst_filesystem_verifier_action.get(),
+ BondActions(download_action.get(), filesystem_verifier_action.get());
+ BondActions(filesystem_verifier_action.get(),
postinstall_runner_action.get());
// Enqueue the actions.
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 35bd206..d8b4318 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -286,7 +286,7 @@
GetErrorCodeForAction(&omaha_response_handler_action,
ErrorCode::kError));
FilesystemVerifierAction filesystem_verifier_action(
- fake_system_state_.boot_control(), VerifierMode::kVerifyTargetHash);
+ fake_system_state_.boot_control());
EXPECT_EQ(ErrorCode::kFilesystemVerifierError,
GetErrorCodeForAction(&filesystem_verifier_action,
ErrorCode::kError));
@@ -377,7 +377,6 @@
const string kUpdateActionTypes[] = { // NOLINT(runtime/string)
OmahaRequestAction::StaticType(),
OmahaResponseHandlerAction::StaticType(),
- FilesystemVerifierAction::StaticType(),
OmahaRequestAction::StaticType(),
DownloadAction::StaticType(),
OmahaRequestAction::StaticType(),
@@ -429,10 +428,10 @@
}
EXPECT_EQ(attempter_.response_handler_action_.get(),
attempter_.actions_[1].get());
- AbstractAction* action_4 = attempter_.actions_[4].get();
- ASSERT_NE(nullptr, action_4);
- ASSERT_EQ(DownloadAction::StaticType(), action_4->Type());
- DownloadAction* download_action = static_cast<DownloadAction*>(action_4);
+ AbstractAction* action_3 = attempter_.actions_[3].get();
+ ASSERT_NE(nullptr, action_3);
+ ASSERT_EQ(DownloadAction::StaticType(), action_3->Type());
+ DownloadAction* download_action = static_cast<DownloadAction*>(action_3);
EXPECT_EQ(&attempter_, download_action->delegate());
EXPECT_EQ(UpdateStatus::CHECKING_FOR_UPDATE, attempter_.status());
loop_.BreakLoop();