4a01f36d21
Most of the tests in component/ are in fact unit tests. And it doesn't look practically beneficial to distinguish between the two: - They have the same test setup; - We always run both (recovery_unit_test and recovery_component_test) at the same time; - Breaking any of them would be equally bad. This CL merges the tests in recovery_component_test into recovery_unit_test to save the effort to maintain both. Test: Run recovery_unit_test on marlin (via `adb sync data`). Test: `atest recovery_unit_test` Change-Id: I93ff32e7219cd83425a4bcfe5613978a8dd48d75
198 lines
6.4 KiB
C++
198 lines
6.4 KiB
C++
/*
|
|
* Copyright (C) 2016 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 agree 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/logging.h>
|
|
#include <android-base/strings.h>
|
|
#include <bsdiff/bsdiff.h>
|
|
#include <gtest/gtest.h>
|
|
#include <openssl/sha.h>
|
|
|
|
#include "applypatch/applypatch_modes.h"
|
|
#include "common/test_constants.h"
|
|
#include "otautil/paths.h"
|
|
#include "otautil/print_sha1.h"
|
|
#include "otautil/sysutil.h"
|
|
|
|
using namespace std::string_literals;
|
|
|
|
// Loads a given partition and returns a string of form "EMMC:name:size:hash".
|
|
static std::string GetEmmcTargetString(const std::string& filename,
|
|
const std::string& display_name = "") {
|
|
std::string data;
|
|
if (!android::base::ReadFileToString(filename, &data)) {
|
|
PLOG(ERROR) << "Failed to read " << filename;
|
|
return {};
|
|
}
|
|
|
|
uint8_t digest[SHA_DIGEST_LENGTH];
|
|
SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest);
|
|
|
|
return "EMMC:"s + (display_name.empty() ? filename : display_name) + ":" +
|
|
std::to_string(data.size()) + ":" + print_sha1(digest);
|
|
}
|
|
|
|
class ApplyPatchModesTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
source = GetEmmcTargetString(from_testdata_base("boot.img"));
|
|
ASSERT_FALSE(source.empty());
|
|
|
|
std::string recovery_file = from_testdata_base("recovery.img");
|
|
recovery = GetEmmcTargetString(recovery_file);
|
|
ASSERT_FALSE(recovery.empty());
|
|
|
|
ASSERT_TRUE(android::base::WriteStringToFile("", patched_file_.path));
|
|
target = GetEmmcTargetString(recovery_file, patched_file_.path);
|
|
ASSERT_FALSE(target.empty());
|
|
|
|
Paths::Get().set_cache_temp_source(cache_source_.path);
|
|
}
|
|
|
|
std::string source;
|
|
std::string target;
|
|
std::string recovery;
|
|
|
|
private:
|
|
TemporaryFile cache_source_;
|
|
TemporaryFile patched_file_;
|
|
};
|
|
|
|
static int InvokeApplyPatchModes(const std::vector<std::string>& args) {
|
|
auto args_to_call = StringVectorToNullTerminatedArray(args);
|
|
return applypatch_modes(args_to_call.size() - 1, args_to_call.data());
|
|
}
|
|
|
|
static void VerifyPatchedTarget(const std::string& target) {
|
|
std::vector<std::string> pieces = android::base::Split(target, ":");
|
|
ASSERT_EQ(4, pieces.size());
|
|
ASSERT_EQ("EMMC", pieces[0]);
|
|
|
|
std::string patched_emmc = GetEmmcTargetString(pieces[1]);
|
|
ASSERT_FALSE(patched_emmc.empty());
|
|
ASSERT_EQ(target, patched_emmc);
|
|
}
|
|
|
|
TEST_F(ApplyPatchModesTest, InvalidArgs) {
|
|
// At least two args (including the filename).
|
|
ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch" }));
|
|
|
|
// Unrecognized args.
|
|
ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch", "-x" }));
|
|
}
|
|
|
|
TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) {
|
|
std::vector<std::string> args{
|
|
"applypatch",
|
|
"--bonus",
|
|
from_testdata_base("bonus.file"),
|
|
"--patch",
|
|
from_testdata_base("recovery-from-boot.p"),
|
|
"--target",
|
|
target,
|
|
"--source",
|
|
source,
|
|
};
|
|
ASSERT_EQ(0, InvokeApplyPatchModes(args));
|
|
VerifyPatchedTarget(target);
|
|
}
|
|
|
|
// Tests patching an eMMC target without a separate bonus file (i.e. recovery-from-boot patch has
|
|
// everything).
|
|
TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithoutBonusFile) {
|
|
std::vector<std::string> args{
|
|
"applypatch", "--patch", from_testdata_base("recovery-from-boot-with-bonus.p"),
|
|
"--target", target, "--source",
|
|
source,
|
|
};
|
|
|
|
ASSERT_EQ(0, InvokeApplyPatchModes(args));
|
|
VerifyPatchedTarget(target);
|
|
}
|
|
|
|
// Ensures that applypatch works with a bsdiff based recovery-from-boot.p.
|
|
TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithBsdiffPatch) {
|
|
// Generate the bsdiff patch of recovery-from-boot.p.
|
|
std::string src_content;
|
|
ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("boot.img"), &src_content));
|
|
|
|
std::string tgt_content;
|
|
ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("recovery.img"), &tgt_content));
|
|
|
|
TemporaryFile patch_file;
|
|
ASSERT_EQ(0,
|
|
bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), src_content.size(),
|
|
reinterpret_cast<const uint8_t*>(tgt_content.data()), tgt_content.size(),
|
|
patch_file.path, nullptr));
|
|
|
|
std::vector<std::string> args{
|
|
"applypatch", "--patch", patch_file.path, "--target", target, "--source", source,
|
|
};
|
|
ASSERT_EQ(0, InvokeApplyPatchModes(args));
|
|
VerifyPatchedTarget(target);
|
|
}
|
|
|
|
TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) {
|
|
// Invalid bonus file.
|
|
std::vector<std::string> args{
|
|
"applypatch", "--bonus", "/doesntexist", "--patch", from_testdata_base("recovery-from-boot.p"),
|
|
"--target", target, "--source", source,
|
|
};
|
|
ASSERT_NE(0, InvokeApplyPatchModes(args));
|
|
|
|
// With bonus file, but missing args.
|
|
ASSERT_NE(0,
|
|
InvokeApplyPatchModes({ "applypatch", "--bonus", from_testdata_base("bonus.file") }));
|
|
}
|
|
|
|
TEST_F(ApplyPatchModesTest, FlashMode) {
|
|
std::vector<std::string> args{
|
|
"applypatch", "--flash", from_testdata_base("recovery.img"), "--target", target,
|
|
};
|
|
ASSERT_EQ(0, InvokeApplyPatchModes(args));
|
|
VerifyPatchedTarget(target);
|
|
}
|
|
|
|
TEST_F(ApplyPatchModesTest, FlashModeInvalidArgs) {
|
|
std::vector<std::string> args{
|
|
"applypatch", "--bonus", from_testdata_base("bonus.file"), "--flash", source,
|
|
"--target", target,
|
|
};
|
|
ASSERT_NE(0, InvokeApplyPatchModes(args));
|
|
}
|
|
|
|
TEST_F(ApplyPatchModesTest, CheckMode) {
|
|
ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--check", recovery }));
|
|
ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--check", source }));
|
|
}
|
|
|
|
TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) {
|
|
ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch", "--check" }));
|
|
}
|
|
|
|
TEST_F(ApplyPatchModesTest, CheckModeNonEmmcTarget) {
|
|
ASSERT_NE(0, InvokeApplyPatchModes({ "applypatch", "--check", from_testdata_base("boot.img") }));
|
|
}
|
|
|
|
TEST_F(ApplyPatchModesTest, ShowLicenses) {
|
|
ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--license" }));
|
|
}
|