Merge "Add test for minadbd"
This commit is contained in:
@@ -90,12 +90,14 @@ cc_test {
|
||||
|
||||
srcs: [
|
||||
"fuse_adb_provider_test.cpp",
|
||||
"minadbd_services_test.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libminadbd_services",
|
||||
"libfusesideload",
|
||||
"libadbd",
|
||||
"libcrypto",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
|
||||
static int minadbd_socket = -1;
|
||||
static bool rescue_mode = false;
|
||||
static std::string sideload_mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT;
|
||||
|
||||
void SetMinadbdSocketFd(int socket_fd) {
|
||||
minadbd_socket = socket_fd;
|
||||
@@ -59,6 +60,10 @@ void SetMinadbdRescueMode(bool rescue) {
|
||||
rescue_mode = rescue;
|
||||
}
|
||||
|
||||
void SetSideloadMountPoint(const std::string& path) {
|
||||
sideload_mount_point = path;
|
||||
}
|
||||
|
||||
static bool WriteCommandToFd(MinadbdCommands cmd, int fd) {
|
||||
char message[kMinadbdMessageSize];
|
||||
memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix));
|
||||
@@ -109,7 +114,8 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args,
|
||||
}
|
||||
|
||||
auto adb_data_reader = std::make_unique<FuseAdbDataProvider>(sfd, file_size, block_size);
|
||||
if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) {
|
||||
if (int result = run_fuse_sideload(std::move(adb_data_reader), sideload_mount_point.c_str());
|
||||
result != 0) {
|
||||
LOG(ERROR) << "Failed to start fuse";
|
||||
return kMinadbdFuseStartError;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
void SetMinadbdSocketFd(int socket_fd);
|
||||
|
||||
void SetMinadbdRescueMode(bool);
|
||||
|
||||
void SetSideloadMountPoint(const std::string& path);
|
||||
|
||||
213
minadbd/minadbd_services_test.cpp
Normal file
213
minadbd/minadbd_services_test.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <strings.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "fuse_adb_provider.h"
|
||||
#include "fuse_sideload.h"
|
||||
#include "minadbd_services.h"
|
||||
#include "minadbd_types.h"
|
||||
#include "socket.h"
|
||||
|
||||
class MinadbdServicesTest : public ::testing::Test {
|
||||
protected:
|
||||
static constexpr int EXIT_TIME_OUT = 10;
|
||||
|
||||
void SetUp() override {
|
||||
ASSERT_TRUE(
|
||||
android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &minadbd_socket_, &recovery_socket_));
|
||||
SetMinadbdSocketFd(minadbd_socket_);
|
||||
SetSideloadMountPoint(mount_point_.path);
|
||||
|
||||
package_path_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_FILENAME;
|
||||
exit_flag_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_EXIT_FLAG;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
// Umount in case the test fails. Ignore the result.
|
||||
umount(mount_point_.path);
|
||||
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
}
|
||||
|
||||
void ReadAndCheckCommandMessage(int fd, MinadbdCommands expected_command) {
|
||||
std::vector<uint8_t> received(kMinadbdMessageSize, '\0');
|
||||
ASSERT_TRUE(android::base::ReadFully(fd, received.data(), kMinadbdMessageSize));
|
||||
|
||||
std::vector<uint8_t> expected(kMinadbdMessageSize, '\0');
|
||||
memcpy(expected.data(), kMinadbdCommandPrefix, strlen(kMinadbdCommandPrefix));
|
||||
memcpy(expected.data() + strlen(kMinadbdCommandPrefix), &expected_command,
|
||||
sizeof(expected_command));
|
||||
ASSERT_EQ(expected, received);
|
||||
}
|
||||
|
||||
void WaitForFusePath() {
|
||||
constexpr int TIME_OUT = 10;
|
||||
for (int i = 0; i < TIME_OUT; ++i) {
|
||||
struct stat sb;
|
||||
if (stat(package_path_.c_str(), &sb) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (errno == ENOENT) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
FAIL() << "Timed out waiting for the fuse-provided package " << strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void StatExitFlagAndExitProcess(int exit_code) {
|
||||
struct stat sb;
|
||||
if (stat(exit_flag_.c_str(), &sb) != 0) {
|
||||
PLOG(ERROR) << "Failed to stat " << exit_flag_;
|
||||
}
|
||||
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
void WriteMinadbdCommandStatus(MinadbdCommandStatus status) {
|
||||
std::string status_message(kMinadbdMessageSize, '\0');
|
||||
memcpy(status_message.data(), kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix));
|
||||
memcpy(status_message.data() + strlen(kMinadbdStatusPrefix), &status, sizeof(status));
|
||||
ASSERT_TRUE(
|
||||
android::base::WriteFully(recovery_socket_, status_message.data(), kMinadbdMessageSize));
|
||||
}
|
||||
|
||||
void ExecuteCommandAndWaitForExit(const std::string& command) {
|
||||
unique_fd fd = daemon_service_to_fd(command, nullptr);
|
||||
ASSERT_NE(-1, fd);
|
||||
sleep(EXIT_TIME_OUT);
|
||||
}
|
||||
|
||||
android::base::unique_fd minadbd_socket_;
|
||||
android::base::unique_fd recovery_socket_;
|
||||
|
||||
TemporaryDir mount_point_;
|
||||
std::string package_path_;
|
||||
std::string exit_flag_;
|
||||
};
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_wrong_size_argument) {
|
||||
ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:abc:4096"),
|
||||
::testing::ExitedWithCode(kMinadbdPackageSizeError), "");
|
||||
}
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_wrong_block_size) {
|
||||
ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:10:20"),
|
||||
::testing::ExitedWithCode(kMinadbdFuseStartError), "");
|
||||
}
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_broken_minadbd_socket) {
|
||||
SetMinadbdSocketFd(-1);
|
||||
ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"),
|
||||
::testing::ExitedWithCode(kMinadbdSocketIOError), "");
|
||||
}
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_broken_recovery_socket) {
|
||||
recovery_socket_.reset();
|
||||
ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"),
|
||||
::testing::ExitedWithCode(kMinadbdSocketIOError), "");
|
||||
}
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_wrong_command_format) {
|
||||
auto test_body = [&](const std::string& command) {
|
||||
unique_fd fd = daemon_service_to_fd(command, nullptr);
|
||||
ASSERT_NE(-1, fd);
|
||||
WaitForFusePath();
|
||||
ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall);
|
||||
|
||||
struct stat sb;
|
||||
ASSERT_EQ(0, stat(exit_flag_.c_str(), &sb));
|
||||
ASSERT_TRUE(android::base::WriteStringToFd("12345678", recovery_socket_));
|
||||
sleep(EXIT_TIME_OUT);
|
||||
};
|
||||
|
||||
ASSERT_EXIT(test_body("sideload-host:4096:4096"),
|
||||
::testing::ExitedWithCode(kMinadbdMessageFormatError), "");
|
||||
}
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_read_data_from_fuse) {
|
||||
auto test_body = [&]() {
|
||||
std::vector<uint8_t> content(4096, 'a');
|
||||
// Start a new process instead of a thread to read from the package mounted by FUSE. Because
|
||||
// the test may not exit and report failures correctly when the thread blocks by a syscall.
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
WaitForFusePath();
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(package_path_.c_str(), O_RDONLY)));
|
||||
// Do not use assertion here because we want to stat the exit flag and exit the process.
|
||||
// Otherwise the test will wait for the time out instead of failing immediately.
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "Failed to open " << package_path_;
|
||||
StatExitFlagAndExitProcess(1);
|
||||
}
|
||||
std::vector<uint8_t> content_from_fuse(4096);
|
||||
if (!android::base::ReadFully(fd, content_from_fuse.data(), 4096)) {
|
||||
PLOG(ERROR) << "Failed to read from " << package_path_;
|
||||
StatExitFlagAndExitProcess(1);
|
||||
}
|
||||
if (content_from_fuse != content) {
|
||||
LOG(ERROR) << "Content read from fuse doesn't match with the expected value";
|
||||
StatExitFlagAndExitProcess(1);
|
||||
}
|
||||
StatExitFlagAndExitProcess(0);
|
||||
}
|
||||
|
||||
unique_fd fd = daemon_service_to_fd("sideload-host:4096:4096", nullptr);
|
||||
ASSERT_NE(-1, fd);
|
||||
ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall);
|
||||
|
||||
// Mimic the response from adb host.
|
||||
std::string adb_message(8, '\0');
|
||||
ASSERT_TRUE(android::base::ReadFully(fd, adb_message.data(), 8));
|
||||
ASSERT_EQ(android::base::StringPrintf("%08u", 0), adb_message);
|
||||
ASSERT_TRUE(android::base::WriteFully(fd, content.data(), 4096));
|
||||
|
||||
// Check that we read the correct data from fuse.
|
||||
int child_status;
|
||||
waitpid(pid, &child_status, 0);
|
||||
ASSERT_TRUE(WIFEXITED(child_status));
|
||||
ASSERT_EQ(0, WEXITSTATUS(child_status));
|
||||
|
||||
WriteMinadbdCommandStatus(MinadbdCommandStatus::kSuccess);
|
||||
|
||||
// TODO(xunchang) check if adb host-side receives "DONEDONE", there's a race condition between
|
||||
// receiving the message and exit of test body (by detached thread in minadbd service).
|
||||
exit(kMinadbdSuccess);
|
||||
};
|
||||
|
||||
ASSERT_EXIT(test_body(), ::testing::ExitedWithCode(kMinadbdSuccess), "");
|
||||
}
|
||||
Reference in New Issue
Block a user