577 lines
22 KiB
C++
577 lines
22 KiB
C++
//
|
|
// Copyright (C) 2015 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
#include "update_engine/payload_generator/ab_generator.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <random>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "update_engine/common/hash_calculator.h"
|
|
#include "update_engine/common/test_utils.h"
|
|
#include "update_engine/common/utils.h"
|
|
#include "update_engine/payload_generator/annotated_operation.h"
|
|
#include "update_engine/payload_generator/delta_diff_generator.h"
|
|
#include "update_engine/payload_generator/extent_ranges.h"
|
|
#include "update_engine/payload_generator/extent_utils.h"
|
|
#include "update_engine/payload_generator/xz.h"
|
|
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
namespace chromeos_update_engine {
|
|
|
|
namespace {
|
|
|
|
bool ExtentEquals(const Extent& ext,
|
|
uint64_t start_block,
|
|
uint64_t num_blocks) {
|
|
return ext.start_block() == start_block && ext.num_blocks() == num_blocks;
|
|
}
|
|
|
|
// Tests splitting of a REPLACE/REPLACE_XZ operation.
|
|
void TestSplitReplaceOrReplaceXzOperation(InstallOperation::Type orig_type,
|
|
bool compressible) {
|
|
const size_t op_ex1_start_block = 2;
|
|
const size_t op_ex1_num_blocks = 2;
|
|
const size_t op_ex2_start_block = 6;
|
|
const size_t op_ex2_num_blocks = 1;
|
|
const size_t part_num_blocks = 7;
|
|
|
|
// Create the target partition data.
|
|
const size_t part_size = part_num_blocks * kBlockSize;
|
|
brillo::Blob part_data;
|
|
if (compressible) {
|
|
part_data.resize(part_size);
|
|
test_utils::FillWithData(&part_data);
|
|
} else {
|
|
std::mt19937 gen(12345);
|
|
std::uniform_int_distribution<uint8_t> dis(0, 255);
|
|
for (uint32_t i = 0; i < part_size; i++)
|
|
part_data.push_back(dis(gen));
|
|
}
|
|
ASSERT_EQ(part_size, part_data.size());
|
|
ScopedTempFile part_file("SplitReplaceOrReplaceXzTest_part.XXXXXX");
|
|
ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
|
|
|
|
// Create original operation and blob data.
|
|
const size_t op_ex1_offset = op_ex1_start_block * kBlockSize;
|
|
const size_t op_ex1_size = op_ex1_num_blocks * kBlockSize;
|
|
const size_t op_ex2_offset = op_ex2_start_block * kBlockSize;
|
|
const size_t op_ex2_size = op_ex2_num_blocks * kBlockSize;
|
|
InstallOperation op;
|
|
op.set_type(orig_type);
|
|
*(op.add_dst_extents()) =
|
|
ExtentForRange(op_ex1_start_block, op_ex1_num_blocks);
|
|
*(op.add_dst_extents()) =
|
|
ExtentForRange(op_ex2_start_block, op_ex2_num_blocks);
|
|
|
|
brillo::Blob op_data;
|
|
op_data.insert(op_data.end(),
|
|
part_data.begin() + op_ex1_offset,
|
|
part_data.begin() + op_ex1_offset + op_ex1_size);
|
|
op_data.insert(op_data.end(),
|
|
part_data.begin() + op_ex2_offset,
|
|
part_data.begin() + op_ex2_offset + op_ex2_size);
|
|
brillo::Blob op_blob;
|
|
if (orig_type == InstallOperation::REPLACE) {
|
|
op_blob = op_data;
|
|
} else {
|
|
ASSERT_TRUE(XzCompress(op_data, &op_blob));
|
|
}
|
|
op.set_data_offset(0);
|
|
op.set_data_length(op_blob.size());
|
|
|
|
AnnotatedOperation aop;
|
|
aop.op = op;
|
|
aop.name = "SplitTestOp";
|
|
|
|
// Create the data file.
|
|
ScopedTempFile data_file("SplitReplaceOrReplaceXzTest_data.XXXXXX");
|
|
EXPECT_TRUE(test_utils::WriteFileVector(data_file.path(), op_blob));
|
|
int data_fd = open(data_file.path().c_str(), O_RDWR, 000);
|
|
EXPECT_GE(data_fd, 0);
|
|
ScopedFdCloser data_fd_closer(&data_fd);
|
|
off_t data_file_size = op_blob.size();
|
|
BlobFileWriter blob_file(data_fd, &data_file_size);
|
|
|
|
// Split the operation.
|
|
vector<AnnotatedOperation> result_ops;
|
|
PayloadVersion version(kBrilloMajorPayloadVersion,
|
|
kSourceMinorPayloadVersion);
|
|
ASSERT_TRUE(ABGenerator::SplitAReplaceOp(
|
|
version, aop, part_file.path(), &result_ops, &blob_file));
|
|
|
|
// Check the result.
|
|
InstallOperation::Type expected_type =
|
|
compressible ? InstallOperation::REPLACE_XZ : InstallOperation::REPLACE;
|
|
|
|
ASSERT_EQ(2U, result_ops.size());
|
|
|
|
EXPECT_EQ("SplitTestOp:0", result_ops[0].name);
|
|
InstallOperation first_op = result_ops[0].op;
|
|
EXPECT_EQ(expected_type, first_op.type());
|
|
EXPECT_FALSE(first_op.has_src_length());
|
|
EXPECT_FALSE(first_op.has_dst_length());
|
|
EXPECT_EQ(1, first_op.dst_extents().size());
|
|
EXPECT_TRUE(ExtentEquals(
|
|
first_op.dst_extents(0), op_ex1_start_block, op_ex1_num_blocks));
|
|
// Obtain the expected blob.
|
|
brillo::Blob first_expected_data(
|
|
part_data.begin() + op_ex1_offset,
|
|
part_data.begin() + op_ex1_offset + op_ex1_size);
|
|
brillo::Blob first_expected_blob;
|
|
if (compressible) {
|
|
ASSERT_TRUE(XzCompress(first_expected_data, &first_expected_blob));
|
|
} else {
|
|
first_expected_blob = first_expected_data;
|
|
}
|
|
EXPECT_EQ(first_expected_blob.size(), first_op.data_length());
|
|
// Check that the actual blob matches what's expected.
|
|
brillo::Blob first_data_blob(first_op.data_length());
|
|
ssize_t bytes_read;
|
|
ASSERT_TRUE(utils::PReadAll(data_fd,
|
|
first_data_blob.data(),
|
|
first_op.data_length(),
|
|
first_op.data_offset(),
|
|
&bytes_read));
|
|
ASSERT_EQ(bytes_read, static_cast<ssize_t>(first_op.data_length()));
|
|
EXPECT_EQ(first_expected_blob, first_data_blob);
|
|
|
|
EXPECT_EQ("SplitTestOp:1", result_ops[1].name);
|
|
InstallOperation second_op = result_ops[1].op;
|
|
EXPECT_EQ(expected_type, second_op.type());
|
|
EXPECT_FALSE(second_op.has_src_length());
|
|
EXPECT_FALSE(second_op.has_dst_length());
|
|
EXPECT_EQ(1, second_op.dst_extents().size());
|
|
EXPECT_TRUE(ExtentEquals(
|
|
second_op.dst_extents(0), op_ex2_start_block, op_ex2_num_blocks));
|
|
// Obtain the expected blob.
|
|
brillo::Blob second_expected_data(
|
|
part_data.begin() + op_ex2_offset,
|
|
part_data.begin() + op_ex2_offset + op_ex2_size);
|
|
brillo::Blob second_expected_blob;
|
|
if (compressible) {
|
|
ASSERT_TRUE(XzCompress(second_expected_data, &second_expected_blob));
|
|
} else {
|
|
second_expected_blob = second_expected_data;
|
|
}
|
|
EXPECT_EQ(second_expected_blob.size(), second_op.data_length());
|
|
// Check that the actual blob matches what's expected.
|
|
brillo::Blob second_data_blob(second_op.data_length());
|
|
ASSERT_TRUE(utils::PReadAll(data_fd,
|
|
second_data_blob.data(),
|
|
second_op.data_length(),
|
|
second_op.data_offset(),
|
|
&bytes_read));
|
|
ASSERT_EQ(bytes_read, static_cast<ssize_t>(second_op.data_length()));
|
|
EXPECT_EQ(second_expected_blob, second_data_blob);
|
|
|
|
// Check relative layout of data blobs.
|
|
EXPECT_EQ(first_op.data_offset() + first_op.data_length(),
|
|
second_op.data_offset());
|
|
EXPECT_EQ(second_op.data_offset() + second_op.data_length(),
|
|
static_cast<uint64_t>(data_file_size));
|
|
// If we split a REPLACE into multiple ones, ensure reuse of preexisting blob.
|
|
if (!compressible && orig_type == InstallOperation::REPLACE) {
|
|
EXPECT_EQ(0U, first_op.data_offset());
|
|
}
|
|
}
|
|
|
|
// Tests merging of REPLACE/REPLACE_XZ operations.
|
|
void TestMergeReplaceOrReplaceXzOperations(InstallOperation::Type orig_type,
|
|
bool compressible) {
|
|
const size_t first_op_num_blocks = 1;
|
|
const size_t second_op_num_blocks = 2;
|
|
const size_t total_op_num_blocks = first_op_num_blocks + second_op_num_blocks;
|
|
const size_t part_num_blocks = total_op_num_blocks + 2;
|
|
|
|
// Create the target partition data.
|
|
const size_t part_size = part_num_blocks * kBlockSize;
|
|
brillo::Blob part_data;
|
|
if (compressible) {
|
|
part_data.resize(part_size);
|
|
test_utils::FillWithData(&part_data);
|
|
} else {
|
|
std::mt19937 gen(12345);
|
|
std::uniform_int_distribution<uint8_t> dis(0, 255);
|
|
for (uint32_t i = 0; i < part_size; i++)
|
|
part_data.push_back(dis(gen));
|
|
}
|
|
ASSERT_EQ(part_size, part_data.size());
|
|
ScopedTempFile part_file("MergeReplaceOrReplaceXzTest_part.XXXXXX");
|
|
ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
|
|
|
|
// Create original operations and blob data.
|
|
vector<AnnotatedOperation> aops;
|
|
brillo::Blob blob_data;
|
|
const size_t total_op_size = total_op_num_blocks * kBlockSize;
|
|
|
|
InstallOperation first_op;
|
|
first_op.set_type(orig_type);
|
|
const size_t first_op_size = first_op_num_blocks * kBlockSize;
|
|
*(first_op.add_dst_extents()) = ExtentForRange(0, first_op_num_blocks);
|
|
brillo::Blob first_op_data(part_data.begin(),
|
|
part_data.begin() + first_op_size);
|
|
brillo::Blob first_op_blob;
|
|
if (orig_type == InstallOperation::REPLACE) {
|
|
first_op_blob = first_op_data;
|
|
} else {
|
|
ASSERT_TRUE(XzCompress(first_op_data, &first_op_blob));
|
|
}
|
|
first_op.set_data_offset(0);
|
|
first_op.set_data_length(first_op_blob.size());
|
|
blob_data.insert(blob_data.end(), first_op_blob.begin(), first_op_blob.end());
|
|
AnnotatedOperation first_aop;
|
|
first_aop.op = first_op;
|
|
first_aop.name = "first";
|
|
aops.push_back(first_aop);
|
|
|
|
InstallOperation second_op;
|
|
second_op.set_type(orig_type);
|
|
*(second_op.add_dst_extents()) =
|
|
ExtentForRange(first_op_num_blocks, second_op_num_blocks);
|
|
brillo::Blob second_op_data(part_data.begin() + first_op_size,
|
|
part_data.begin() + total_op_size);
|
|
brillo::Blob second_op_blob;
|
|
if (orig_type == InstallOperation::REPLACE) {
|
|
second_op_blob = second_op_data;
|
|
} else {
|
|
ASSERT_TRUE(XzCompress(second_op_data, &second_op_blob));
|
|
}
|
|
second_op.set_data_offset(first_op_blob.size());
|
|
second_op.set_data_length(second_op_blob.size());
|
|
blob_data.insert(
|
|
blob_data.end(), second_op_blob.begin(), second_op_blob.end());
|
|
AnnotatedOperation second_aop;
|
|
second_aop.op = second_op;
|
|
second_aop.name = "second";
|
|
aops.push_back(second_aop);
|
|
|
|
// Create the data file.
|
|
ScopedTempFile data_file("MergeReplaceOrReplaceXzTest_data.XXXXXX");
|
|
EXPECT_TRUE(test_utils::WriteFileVector(data_file.path(), blob_data));
|
|
int data_fd = open(data_file.path().c_str(), O_RDWR, 000);
|
|
EXPECT_GE(data_fd, 0);
|
|
ScopedFdCloser data_fd_closer(&data_fd);
|
|
off_t data_file_size = blob_data.size();
|
|
BlobFileWriter blob_file(data_fd, &data_file_size);
|
|
|
|
// Merge the operations.
|
|
PayloadVersion version(kBrilloMajorPayloadVersion,
|
|
kSourceMinorPayloadVersion);
|
|
EXPECT_TRUE(ABGenerator::MergeOperations(
|
|
&aops, version, 5, part_file.path(), &blob_file));
|
|
|
|
// Check the result.
|
|
InstallOperation::Type expected_op_type =
|
|
compressible ? InstallOperation::REPLACE_XZ : InstallOperation::REPLACE;
|
|
EXPECT_EQ(1U, aops.size());
|
|
InstallOperation new_op = aops[0].op;
|
|
EXPECT_EQ(expected_op_type, new_op.type());
|
|
EXPECT_FALSE(new_op.has_src_length());
|
|
EXPECT_FALSE(new_op.has_dst_length());
|
|
EXPECT_EQ(1, new_op.dst_extents().size());
|
|
EXPECT_TRUE(ExtentEquals(new_op.dst_extents(0), 0, total_op_num_blocks));
|
|
EXPECT_EQ("first,second", aops[0].name);
|
|
|
|
// Check to see if the blob pointed to in the new extent has what we expect.
|
|
brillo::Blob expected_data(part_data.begin(),
|
|
part_data.begin() + total_op_size);
|
|
brillo::Blob expected_blob;
|
|
if (compressible) {
|
|
ASSERT_TRUE(XzCompress(expected_data, &expected_blob));
|
|
} else {
|
|
expected_blob = expected_data;
|
|
}
|
|
ASSERT_EQ(expected_blob.size(), new_op.data_length());
|
|
ASSERT_EQ(blob_data.size() + expected_blob.size(),
|
|
static_cast<size_t>(data_file_size));
|
|
brillo::Blob new_op_blob(new_op.data_length());
|
|
ssize_t bytes_read;
|
|
ASSERT_TRUE(utils::PReadAll(data_fd,
|
|
new_op_blob.data(),
|
|
new_op.data_length(),
|
|
new_op.data_offset(),
|
|
&bytes_read));
|
|
ASSERT_EQ(static_cast<ssize_t>(new_op.data_length()), bytes_read);
|
|
EXPECT_EQ(expected_blob, new_op_blob);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
class ABGeneratorTest : public ::testing::Test {};
|
|
|
|
TEST_F(ABGeneratorTest, SplitSourceCopyTest) {
|
|
InstallOperation op;
|
|
op.set_type(InstallOperation::SOURCE_COPY);
|
|
*(op.add_src_extents()) = ExtentForRange(2, 3);
|
|
*(op.add_src_extents()) = ExtentForRange(6, 1);
|
|
*(op.add_src_extents()) = ExtentForRange(8, 4);
|
|
*(op.add_dst_extents()) = ExtentForRange(10, 2);
|
|
*(op.add_dst_extents()) = ExtentForRange(14, 3);
|
|
*(op.add_dst_extents()) = ExtentForRange(18, 3);
|
|
|
|
AnnotatedOperation aop;
|
|
aop.op = op;
|
|
aop.name = "SplitSourceCopyTestOp";
|
|
vector<AnnotatedOperation> result_ops;
|
|
EXPECT_TRUE(ABGenerator::SplitSourceCopy(aop, &result_ops));
|
|
EXPECT_EQ(3U, result_ops.size());
|
|
|
|
EXPECT_EQ("SplitSourceCopyTestOp:0", result_ops[0].name);
|
|
InstallOperation first_op = result_ops[0].op;
|
|
EXPECT_EQ(InstallOperation::SOURCE_COPY, first_op.type());
|
|
EXPECT_FALSE(first_op.has_src_length());
|
|
EXPECT_EQ(1, first_op.src_extents().size());
|
|
EXPECT_EQ(2U, first_op.src_extents(0).start_block());
|
|
EXPECT_EQ(2U, first_op.src_extents(0).num_blocks());
|
|
EXPECT_FALSE(first_op.has_dst_length());
|
|
EXPECT_EQ(1, first_op.dst_extents().size());
|
|
EXPECT_EQ(10U, first_op.dst_extents(0).start_block());
|
|
EXPECT_EQ(2U, first_op.dst_extents(0).num_blocks());
|
|
|
|
EXPECT_EQ("SplitSourceCopyTestOp:1", result_ops[1].name);
|
|
InstallOperation second_op = result_ops[1].op;
|
|
EXPECT_EQ(InstallOperation::SOURCE_COPY, second_op.type());
|
|
EXPECT_FALSE(second_op.has_src_length());
|
|
EXPECT_EQ(3, second_op.src_extents().size());
|
|
EXPECT_EQ(4U, second_op.src_extents(0).start_block());
|
|
EXPECT_EQ(1U, second_op.src_extents(0).num_blocks());
|
|
EXPECT_EQ(6U, second_op.src_extents(1).start_block());
|
|
EXPECT_EQ(1U, second_op.src_extents(1).num_blocks());
|
|
EXPECT_EQ(8U, second_op.src_extents(2).start_block());
|
|
EXPECT_EQ(1U, second_op.src_extents(2).num_blocks());
|
|
EXPECT_FALSE(second_op.has_dst_length());
|
|
EXPECT_EQ(1, second_op.dst_extents().size());
|
|
EXPECT_EQ(14U, second_op.dst_extents(0).start_block());
|
|
EXPECT_EQ(3U, second_op.dst_extents(0).num_blocks());
|
|
|
|
EXPECT_EQ("SplitSourceCopyTestOp:2", result_ops[2].name);
|
|
InstallOperation third_op = result_ops[2].op;
|
|
EXPECT_EQ(InstallOperation::SOURCE_COPY, third_op.type());
|
|
EXPECT_FALSE(third_op.has_src_length());
|
|
EXPECT_EQ(1, third_op.src_extents().size());
|
|
EXPECT_EQ(9U, third_op.src_extents(0).start_block());
|
|
EXPECT_EQ(3U, third_op.src_extents(0).num_blocks());
|
|
EXPECT_FALSE(third_op.has_dst_length());
|
|
EXPECT_EQ(1, third_op.dst_extents().size());
|
|
EXPECT_EQ(18U, third_op.dst_extents(0).start_block());
|
|
EXPECT_EQ(3U, third_op.dst_extents(0).num_blocks());
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, SplitReplaceTest) {
|
|
TestSplitReplaceOrReplaceXzOperation(InstallOperation::REPLACE, false);
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, SplitReplaceIntoReplaceXzTest) {
|
|
TestSplitReplaceOrReplaceXzOperation(InstallOperation::REPLACE, true);
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, SplitReplaceXzTest) {
|
|
TestSplitReplaceOrReplaceXzOperation(InstallOperation::REPLACE_XZ, true);
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, SplitReplaceXzIntoReplaceTest) {
|
|
TestSplitReplaceOrReplaceXzOperation(InstallOperation::REPLACE_XZ, false);
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, SortOperationsByDestinationTest) {
|
|
vector<AnnotatedOperation> aops;
|
|
// One operation with multiple destination extents.
|
|
InstallOperation first_op;
|
|
*(first_op.add_dst_extents()) = ExtentForRange(6, 1);
|
|
*(first_op.add_dst_extents()) = ExtentForRange(10, 2);
|
|
AnnotatedOperation first_aop;
|
|
first_aop.op = first_op;
|
|
first_aop.name = "first";
|
|
aops.push_back(first_aop);
|
|
|
|
// One with no destination extent. Should end up at the end of the vector.
|
|
InstallOperation second_op;
|
|
AnnotatedOperation second_aop;
|
|
second_aop.op = second_op;
|
|
second_aop.name = "second";
|
|
aops.push_back(second_aop);
|
|
|
|
// One with one destination extent.
|
|
InstallOperation third_op;
|
|
*(third_op.add_dst_extents()) = ExtentForRange(3, 2);
|
|
AnnotatedOperation third_aop;
|
|
third_aop.op = third_op;
|
|
third_aop.name = "third";
|
|
aops.push_back(third_aop);
|
|
|
|
ABGenerator::SortOperationsByDestination(&aops);
|
|
EXPECT_EQ(3U, aops.size());
|
|
EXPECT_EQ(third_aop.name, aops[0].name);
|
|
EXPECT_EQ(first_aop.name, aops[1].name);
|
|
EXPECT_EQ(second_aop.name, aops[2].name);
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, MergeSourceCopyOperationsTest) {
|
|
vector<AnnotatedOperation> aops;
|
|
InstallOperation first_op;
|
|
first_op.set_type(InstallOperation::SOURCE_COPY);
|
|
*(first_op.add_src_extents()) = ExtentForRange(1, 1);
|
|
*(first_op.add_dst_extents()) = ExtentForRange(6, 1);
|
|
AnnotatedOperation first_aop;
|
|
first_aop.op = first_op;
|
|
first_aop.name = "1";
|
|
aops.push_back(first_aop);
|
|
|
|
InstallOperation second_op;
|
|
second_op.set_type(InstallOperation::SOURCE_COPY);
|
|
*(second_op.add_src_extents()) = ExtentForRange(2, 2);
|
|
*(second_op.add_src_extents()) = ExtentForRange(8, 2);
|
|
*(second_op.add_dst_extents()) = ExtentForRange(7, 3);
|
|
*(second_op.add_dst_extents()) = ExtentForRange(11, 1);
|
|
AnnotatedOperation second_aop;
|
|
second_aop.op = second_op;
|
|
second_aop.name = "2";
|
|
aops.push_back(second_aop);
|
|
|
|
InstallOperation third_op;
|
|
third_op.set_type(InstallOperation::SOURCE_COPY);
|
|
*(third_op.add_src_extents()) = ExtentForRange(11, 1);
|
|
*(third_op.add_dst_extents()) = ExtentForRange(12, 1);
|
|
AnnotatedOperation third_aop;
|
|
third_aop.op = third_op;
|
|
third_aop.name = "3";
|
|
aops.push_back(third_aop);
|
|
|
|
BlobFileWriter blob_file(0, nullptr);
|
|
PayloadVersion version(kBrilloMajorPayloadVersion,
|
|
kSourceMinorPayloadVersion);
|
|
EXPECT_TRUE(ABGenerator::MergeOperations(&aops, version, 5, "", &blob_file));
|
|
|
|
EXPECT_EQ(1U, aops.size());
|
|
InstallOperation first_result_op = aops[0].op;
|
|
EXPECT_EQ(InstallOperation::SOURCE_COPY, first_result_op.type());
|
|
EXPECT_FALSE(first_result_op.has_src_length());
|
|
EXPECT_EQ(3, first_result_op.src_extents().size());
|
|
EXPECT_TRUE(ExtentEquals(first_result_op.src_extents(0), 1, 3));
|
|
EXPECT_TRUE(ExtentEquals(first_result_op.src_extents(1), 8, 2));
|
|
EXPECT_TRUE(ExtentEquals(first_result_op.src_extents(2), 11, 1));
|
|
EXPECT_FALSE(first_result_op.has_dst_length());
|
|
EXPECT_EQ(2, first_result_op.dst_extents().size());
|
|
EXPECT_TRUE(ExtentEquals(first_result_op.dst_extents(0), 6, 4));
|
|
EXPECT_TRUE(ExtentEquals(first_result_op.dst_extents(1), 11, 2));
|
|
EXPECT_EQ(aops[0].name, "1,2,3");
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, MergeReplaceOperationsTest) {
|
|
TestMergeReplaceOrReplaceXzOperations(InstallOperation::REPLACE, false);
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, MergeReplaceOperationsToReplaceXzTest) {
|
|
TestMergeReplaceOrReplaceXzOperations(InstallOperation::REPLACE, true);
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, MergeReplaceXzOperationsTest) {
|
|
TestMergeReplaceOrReplaceXzOperations(InstallOperation::REPLACE_XZ, true);
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, MergeReplaceXzOperationsToReplaceTest) {
|
|
TestMergeReplaceOrReplaceXzOperations(InstallOperation::REPLACE_XZ, false);
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, NoMergeOperationsTest) {
|
|
// Test to make sure we don't merge operations that shouldn't be merged.
|
|
vector<AnnotatedOperation> aops;
|
|
InstallOperation first_op;
|
|
first_op.set_type(InstallOperation::ZERO);
|
|
*(first_op.add_dst_extents()) = ExtentForRange(0, 1);
|
|
AnnotatedOperation first_aop;
|
|
first_aop.op = first_op;
|
|
aops.push_back(first_aop);
|
|
|
|
// Should merge with first, except op types don't match...
|
|
InstallOperation second_op;
|
|
second_op.set_type(InstallOperation::REPLACE);
|
|
*(second_op.add_dst_extents()) = ExtentForRange(1, 2);
|
|
second_op.set_data_length(2 * kBlockSize);
|
|
AnnotatedOperation second_aop;
|
|
second_aop.op = second_op;
|
|
aops.push_back(second_aop);
|
|
|
|
// Should merge with second, except it would exceed chunk size...
|
|
InstallOperation third_op;
|
|
third_op.set_type(InstallOperation::REPLACE);
|
|
*(third_op.add_dst_extents()) = ExtentForRange(3, 3);
|
|
third_op.set_data_length(3 * kBlockSize);
|
|
AnnotatedOperation third_aop;
|
|
third_aop.op = third_op;
|
|
aops.push_back(third_aop);
|
|
|
|
// Should merge with third, except they aren't contiguous...
|
|
InstallOperation fourth_op;
|
|
fourth_op.set_type(InstallOperation::REPLACE);
|
|
*(fourth_op.add_dst_extents()) = ExtentForRange(7, 2);
|
|
fourth_op.set_data_length(2 * kBlockSize);
|
|
AnnotatedOperation fourth_aop;
|
|
fourth_aop.op = fourth_op;
|
|
aops.push_back(fourth_aop);
|
|
|
|
BlobFileWriter blob_file(0, nullptr);
|
|
PayloadVersion version(kBrilloMajorPayloadVersion,
|
|
kSourceMinorPayloadVersion);
|
|
EXPECT_TRUE(ABGenerator::MergeOperations(&aops, version, 4, "", &blob_file));
|
|
|
|
// No operations were merged, the number of ops is the same.
|
|
EXPECT_EQ(4U, aops.size());
|
|
}
|
|
|
|
TEST_F(ABGeneratorTest, AddSourceHashTest) {
|
|
vector<AnnotatedOperation> aops;
|
|
InstallOperation first_op;
|
|
first_op.set_type(InstallOperation::SOURCE_COPY);
|
|
first_op.set_src_length(kBlockSize);
|
|
*(first_op.add_src_extents()) = ExtentForRange(0, 1);
|
|
AnnotatedOperation first_aop;
|
|
first_aop.op = first_op;
|
|
aops.push_back(first_aop);
|
|
|
|
InstallOperation second_op;
|
|
second_op.set_type(InstallOperation::REPLACE);
|
|
AnnotatedOperation second_aop;
|
|
second_aop.op = second_op;
|
|
aops.push_back(second_aop);
|
|
|
|
ScopedTempFile src_part_file("AddSourceHashTest_src_part.XXXXXX");
|
|
brillo::Blob src_data(kBlockSize);
|
|
test_utils::FillWithData(&src_data);
|
|
ASSERT_TRUE(test_utils::WriteFileVector(src_part_file.path(), src_data));
|
|
|
|
EXPECT_TRUE(ABGenerator::AddSourceHash(&aops, src_part_file.path()));
|
|
|
|
EXPECT_TRUE(aops[0].op.has_src_sha256_hash());
|
|
EXPECT_FALSE(aops[1].op.has_src_sha256_hash());
|
|
brillo::Blob expected_hash;
|
|
EXPECT_TRUE(HashCalculator::RawHashOfData(src_data, &expected_hash));
|
|
brillo::Blob result_hash(aops[0].op.src_sha256_hash().begin(),
|
|
aops[0].op.src_sha256_hash().end());
|
|
EXPECT_EQ(expected_hash, result_hash);
|
|
}
|
|
|
|
} // namespace chromeos_update_engine
|