blob: 16816e50441e6e52e75d4a206c629419731be668 [file]
/*
* Copyright (C) 2018 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 <fstream>
#include <iostream>
#include <gtest/gtest.h>
#include "tools/base/deploy/installer/apk_archive.h"
#include "tools/base/deploy/installer/command_cmd.h"
#include "tools/base/deploy/installer/executor/executor_impl.h"
#include "tools/base/deploy/installer/patch_applier.h"
#include "tools/base/deploy/proto/deploy.pb.h"
using namespace deploy;
// Friend test to get around private scope of ApkArchive private functions.
class deploy::ApkArchiveTester {
public:
ApkArchiveTester(const ApkArchive& archive) : archive_(archive) {}
ApkArchive::Location GetCDLocation() noexcept {
return archive_.GetCDLocation();
}
ApkArchive::Location GetSignatureLocation(size_t start) noexcept {
return archive_.GetSignatureLocation(start);
}
private:
ApkArchive archive_;
};
class GetProcessInfoExecutor : public Executor {
public:
GetProcessInfoExecutor(const std::string& file_path)
: file_path_(file_path) {}
bool Run(const std::string& executable_path,
const std::vector<std::string>& args, std::string* output,
std::string* error) const {
std::ifstream file(file_path_);
std::string line;
while (std::getline(file, line)) {
output->append(line);
output->append("\n");
}
std::cout << *output << std::endl;
return true;
}
bool ForkAndExec(const std::string& executable_path,
const std::vector<std::string>& parameters,
int* child_stdin_fd, int* child_stdout_fd,
int* child_stderr_fd, int* fork_pid) const {
return false;
}
bool ForkAndExecWithStdinFd(const std::string& executable_path,
const std::vector<std::string>& parameters,
int stdin_fd, int* child_stdout_fd,
int* child_stderr_fd, int* fork_pid) const {
return false;
}
private:
const std::string file_path_;
};
class InstallerTest : public ::testing::Test {};
TEST_F(InstallerTest, TestGetProcessInfo) {
Executor* exec = new GetProcessInfoExecutor(
"tools/base/deploy/installer/tests/data/dumpOutput.txt");
Workspace workspace("fakeversion", exec);
CmdCommand cmd(workspace);
std::vector<ProcessRecord> records;
ASSERT_TRUE(cmd.GetProcessInfo("com.noah.clr", &records));
ASSERT_EQ(2, records.size());
ASSERT_EQ("com.noah.clr:wow", records[0].process_name);
ASSERT_EQ(false, records[0].crashing);
ASSERT_EQ(true, records[0].not_responding);
ASSERT_EQ("com.noah.clr", records[1].process_name);
ASSERT_EQ(true, records[1].crashing);
ASSERT_EQ(false, records[1].not_responding);
}
TEST_F(InstallerTest, TestArchiveParser) {
ApkArchive archive(
"tools/base/deploy/installer/tests/data/app/my.fake.app/sample.apk");
ApkArchiveTester archiveTester(archive);
ApkArchive::Location cdLoc = archiveTester.GetCDLocation();
EXPECT_TRUE(cdLoc.valid);
ASSERT_EQ(cdLoc.offset, 2044145);
ASSERT_EQ(cdLoc.size, 49390);
// Check that block can be retrieved
ApkArchive::Location sigLoc =
archiveTester.GetSignatureLocation(cdLoc.offset);
EXPECT_TRUE(sigLoc.valid);
ASSERT_EQ(sigLoc.offset, 2040049);
ASSERT_EQ(sigLoc.size, 4088);
}
TEST_F(InstallerTest, TestFileNoOpPatching) {
proto::PatchInstruction patchInstruction;
patchInstruction.set_src_absolute_path(
"tools/base/deploy/installer/tests/data/patchTest.txt");
int32_t instructions[0] = {};
patchInstruction.set_instructions(reinterpret_cast<char*>(instructions), 0);
int32_t patches[0] = {};
patchInstruction.set_patches(reinterpret_cast<char*>(patches), 0);
patchInstruction.set_dst_filesize(3);
int pipeBuffer[2];
pipe(pipeBuffer);
PatchApplier patchApplier;
bool patchSucceeded =
patchApplier.ApplyPatchToFD(patchInstruction, pipeBuffer[1]);
EXPECT_TRUE(patchSucceeded == true);
close(pipeBuffer[1]);
char patchedContent[3] = {'z', 'z', 'z'};
read(pipeBuffer[0], patchedContent, 3);
close(pipeBuffer[0]);
EXPECT_TRUE(patchedContent[0] == 'c');
EXPECT_TRUE(patchedContent[1] == 'b');
EXPECT_TRUE(patchedContent[2] == 'a');
}
TEST_F(InstallerTest, TestFilePatchingDirtyBeginning) {
proto::PatchInstruction patchInstruction;
patchInstruction.set_src_absolute_path(
"tools/base/deploy/installer/tests/data/patchTest.txt");
// Patch index 0 with 1 byte from patch payload.
int32_t instructions[2] = {0, 1};
patchInstruction.set_instructions(reinterpret_cast<char*>(instructions), 8);
const char* patches = "a";
patchInstruction.set_patches(patches, 1);
patchInstruction.set_dst_filesize(3);
int pipeBuffer[2];
pipe(pipeBuffer);
PatchApplier patchApplier;
patchApplier.ApplyPatchToFD(patchInstruction, pipeBuffer[1]);
close(pipeBuffer[1]);
char patchedContent[3] = {'z', 'z', 'z'};
read(pipeBuffer[0], patchedContent, 3);
close(pipeBuffer[0]);
EXPECT_TRUE(patchedContent[0] == 'a');
EXPECT_TRUE(patchedContent[1] == 'b');
EXPECT_TRUE(patchedContent[2] == 'a');
}
TEST_F(InstallerTest, TestFilePatching) {
proto::PatchInstruction patchInstruction;
patchInstruction.set_src_absolute_path(
"tools/base/deploy/installer/tests/data/patchTest.txt");
// Patch index 0 with 1 byte from patch payload.
// Patch index 2 with 1 byte from patch payload.
int32_t instructions[4] = {0, 1, 2, 1};
patchInstruction.set_instructions(reinterpret_cast<char*>(instructions), 16);
const char* patches = "ac";
patchInstruction.set_patches(patches, 2);
patchInstruction.set_dst_filesize(3);
int pipeBuffer[2];
pipe(pipeBuffer);
PatchApplier patchApplier;
patchApplier.ApplyPatchToFD(patchInstruction, pipeBuffer[1]);
close(pipeBuffer[1]);
char patchedContent[3] = {'z', 'z', 'z'};
read(pipeBuffer[0], patchedContent, 3);
close(pipeBuffer[0]);
EXPECT_TRUE(patchedContent[0] == 'a');
EXPECT_TRUE(patchedContent[1] == 'b');
EXPECT_TRUE(patchedContent[2] == 'c');
}