blob: 54154014deb323a8c64adf03dfcc0aeb9d7c0b23 [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "arc/vm/mojo_proxy/proxy_file_system.h"
#include <unistd.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <base/bind.h>
#include <base/bind_helpers.h>
#include <base/files/file_path.h>
#include <base/files/scoped_file.h>
#include <base/files/scoped_temp_dir.h>
#include <base/posix/eintr_wrapper.h>
#include <base/synchronization/waitable_event.h>
#include <base/test/task_environment.h>
#include <base/threading/thread.h>
#include <gtest/gtest.h>
namespace arc {
namespace {
constexpr int64_t kHandle = 123;
constexpr char kTestData[] = "abcdefghijklmnopqrstuvwxyz";
class ProxyFileSystemTest : public testing::Test,
public ProxyFileSystem::Delegate {
public:
ProxyFileSystemTest() = default;
~ProxyFileSystemTest() override = default;
ProxyFileSystemTest(const ProxyFileSystemTest&) = delete;
ProxyFileSystemTest& operator=(const ProxyFileSystemTest&) = delete;
void SetUp() override {
ASSERT_TRUE(mount_dir_.CreateUniqueTempDir());
ASSERT_TRUE(delegate_thread_.Start());
base::Thread::Options options(base::MessagePumpType::IO, 0);
ASSERT_TRUE(file_system_thread_.StartWithOptions(options));
file_system_ = std::make_unique<ProxyFileSystem>(
this, delegate_thread_.task_runner(), mount_dir_.GetPath());
bool result = false;
file_system_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce([](ProxyFileSystem* file_system,
bool* result) { *result = file_system->Init(); },
file_system_.get(), &result));
file_system_thread_.FlushForTesting();
ASSERT_TRUE(result);
}
// ProxyFileSystem::Delegate overridess:
void Pread(int64_t handle,
uint64_t count,
uint64_t offset,
PreadCallback callback) override {
if (handle == kHandle) {
const std::string data(kTestData);
offset = std::min(static_cast<uint64_t>(data.size()), offset);
count = std::min(static_cast<uint64_t>(data.size()) - offset, count);
std::move(callback).Run(0, data.substr(offset, count));
} else {
std::move(callback).Run(EBADF, std::string());
}
}
void Pwrite(int64_t handle,
std::string blob,
uint64_t offset,
PwriteCallback callback) override {
if (handle == kHandle) {
data_written_.resize(std::max(data_written_.size(),
static_cast<size_t>(offset + blob.size())));
data_written_.replace(offset, blob.size(), blob);
std::move(callback).Run(0, blob.size());
} else {
std::move(callback).Run(EBADF, 0);
}
}
void Close(int64_t handle) override {
EXPECT_FALSE(close_was_called_.IsSignaled());
EXPECT_EQ(kHandle, handle);
close_was_called_.Signal();
}
void Fstat(int64_t handle, FstatCallback callback) override {
if (handle == kHandle) {
std::move(callback).Run(0, sizeof(kTestData));
} else {
std::move(callback).Run(EBADF, 0);
}
}
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY,
base::test::TaskEnvironment::MainThreadType::IO};
// Mount point for ProxyFileSystem.
base::ScopedTempDir mount_dir_;
base::Thread delegate_thread_{"FileSystemDelegate"};
base::Thread file_system_thread_{"FileSystem"};
// ProxyFileSystem to be tested.
std::unique_ptr<ProxyFileSystem> file_system_;
base::WaitableEvent close_was_called_{
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED};
std::string data_written_;
};
// On ARM devices, unit tests run without necessary capabilities in QEMU.
#if defined(ARCH_CPU_ARM_FAMILY)
#define MAYBE_RegularFileReadTest DISABLED_RegularFileReadTest
#define MAYBE_RegularFileWriteTest DISABLED_RegularFileWriteTest
#define MAYBE_RegularFileReadWriteTest DISABLED_RegularFileReadWriteTest
#else
#define MAYBE_RegularFileReadTest RegularFileReadTest
#define MAYBE_RegularFileWriteTest RegularFileWriteTest
#define MAYBE_RegularFileReadWriteTest RegularFileReadWriteTest
#endif
TEST_F(ProxyFileSystemTest, MAYBE_RegularFileReadTest) {
base::ScopedFD fd = file_system_->RegisterHandle(kHandle, O_RDONLY);
char buf[10];
ASSERT_EQ(sizeof(buf), HANDLE_EINTR(read(fd.get(), buf, sizeof(buf))));
EXPECT_EQ("abcdefghij", std::string(buf, sizeof(buf)));
ASSERT_EQ(sizeof(buf), HANDLE_EINTR(read(fd.get(), buf, sizeof(buf))));
EXPECT_EQ("klmnopqrst", std::string(buf, sizeof(buf)));
ASSERT_EQ(6, HANDLE_EINTR(read(fd.get(), buf, sizeof(buf))));
EXPECT_EQ("uvwxyz", std::string(buf, 6));
// Make sure EOF.
ASSERT_EQ(0, HANDLE_EINTR(read(fd.get(), buf, sizeof(buf))));
// Close the file descriptor.
fd.reset();
close_was_called_.Wait();
}
TEST_F(ProxyFileSystemTest, MAYBE_RegularFileWriteTest) {
base::ScopedFD fd = file_system_->RegisterHandle(kHandle, O_WRONLY);
ASSERT_EQ(10, HANDLE_EINTR(write(fd.get(), kTestData, 10)));
ASSERT_EQ(10, HANDLE_EINTR(write(fd.get(), kTestData + 10, 10)));
ASSERT_EQ(6, HANDLE_EINTR(write(fd.get(), kTestData + 20, 6)));
EXPECT_EQ(kTestData, data_written_);
// Close the file descriptor.
fd.reset();
close_was_called_.Wait();
}
TEST_F(ProxyFileSystemTest, MAYBE_RegularFileReadWriteTest) {
base::ScopedFD fd = file_system_->RegisterHandle(kHandle, O_RDWR);
ASSERT_EQ(26, HANDLE_EINTR(pwrite(fd.get(), kTestData, 26, 0)));
EXPECT_EQ(kTestData, data_written_);
char buf[26];
ASSERT_EQ(sizeof(buf), HANDLE_EINTR(pread(fd.get(), buf, sizeof(buf), 0)));
EXPECT_EQ(kTestData, std::string(buf, sizeof(buf)));
// Close the file descriptor.
fd.reset();
close_was_called_.Wait();
}
} // namespace
} // namespace arc