blob: 73bf2bffdac85f1e741ce617c947537cdbdb5a1d [file] [log] [blame]
// Copyright 2018 The Chromium 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 "remoting/host/file_transfer/local_file_operations.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/containers/queue.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/test/scoped_path_override.h"
#include "base/test/scoped_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace remoting {
class LocalFileOperationsTest : public testing::Test {
public:
LocalFileOperationsTest();
// testing::Test implementation.
void SetUp() override;
void TearDown() override;
protected:
const base::FilePath kTestFilename =
base::FilePath::FromUTF8Unsafe("test-file.txt");
const base::FilePath kTestFilenameSecondary =
base::FilePath::FromUTF8Unsafe("test-file (1).txt");
const std::string kTestDataOne = "this is the first test string";
const std::string kTestDataTwo = "this is the second test string";
const std::string kTestDataThree = "this is the third test string";
base::FilePath TestDir();
void WriteFile(const base::FilePath& filename,
base::queue<std::string> chunks,
bool close);
void OnFileCreated(
base::queue<std::string> chunks,
bool close,
protocol::FileTransferResult<std::unique_ptr<FileOperations::Writer>>
result);
void OnWriteComplete(base::queue<std::string> remaining_chunks,
bool close,
protocol::FileTransferResult<Monostate> result);
void OnCloseComplete(protocol::FileTransferResult<Monostate> result);
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::ScopedPathOverride scoped_path_override_;
std::unique_ptr<FileOperations> file_operations_;
std::unique_ptr<FileOperations::Writer> file_writer_;
bool operation_completed_ = false;
};
LocalFileOperationsTest::LocalFileOperationsTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT,
base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED),
// Points DIR_USER_DESKTOP at a scoped temporary directory.
scoped_path_override_(base::DIR_USER_DESKTOP),
file_operations_(std::make_unique<LocalFileOperations>()) {}
void LocalFileOperationsTest::SetUp() {}
void LocalFileOperationsTest::TearDown() {}
base::FilePath LocalFileOperationsTest::TestDir() {
base::FilePath result;
EXPECT_TRUE(base::PathService::Get(base::DIR_USER_DESKTOP, &result));
return result;
}
void LocalFileOperationsTest::WriteFile(const base::FilePath& filename,
base::queue<std::string> chunks,
bool close) {
operation_completed_ = false;
file_operations_->WriteFile(
filename,
base::BindOnce(&LocalFileOperationsTest::OnFileCreated,
base::Unretained(this), std::move(chunks), close));
}
void LocalFileOperationsTest::OnFileCreated(
base::queue<std::string> chunks,
bool close,
protocol::FileTransferResult<std::unique_ptr<FileOperations::Writer>>
result) {
OnWriteComplete(std::move(chunks), close,
std::move(result).Map([&](auto writer) {
file_writer_ = std::move(writer);
return kMonostate;
}));
}
void LocalFileOperationsTest::OnWriteComplete(
base::queue<std::string> remaining_chunks,
bool close,
protocol::FileTransferResult<Monostate> result) {
ASSERT_TRUE(result);
if (!remaining_chunks.empty()) {
std::string next_chunk = std::move(remaining_chunks.front());
remaining_chunks.pop();
file_writer_->WriteChunk(
std::move(next_chunk),
base::BindOnce(&LocalFileOperationsTest::OnWriteComplete,
base::Unretained(this), std::move(remaining_chunks),
close));
} else if (close) {
file_writer_->Close(base::BindOnce(
&LocalFileOperationsTest::OnCloseComplete, base::Unretained(this)));
} else {
operation_completed_ = true;
}
}
void LocalFileOperationsTest::OnCloseComplete(
protocol::FileTransferResult<Monostate> result) {
ASSERT_TRUE(result);
operation_completed_ = true;
}
// Verifies that a file consisting of three chunks can be written successfully.
TEST_F(LocalFileOperationsTest, WritesThreeChunks) {
WriteFile(
kTestFilename,
base::queue<std::string>({kTestDataOne, kTestDataTwo, kTestDataThree}),
true /* close */);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(operation_completed_);
std::string actual_file_data;
ASSERT_TRUE(base::ReadFileToString(TestDir().Append(kTestFilename),
&actual_file_data));
ASSERT_EQ(kTestDataOne + kTestDataTwo + kTestDataThree, actual_file_data);
}
// Verifies that LocalFileOperations will write to a file named
// "file (1).txt" if "file.txt" already exists.
TEST_F(LocalFileOperationsTest, RenamesFileIfExists) {
WriteFile(kTestFilename, base::queue<std::string>({kTestDataOne}),
true /* close */);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(operation_completed_);
WriteFile(kTestFilename, base::queue<std::string>({kTestDataTwo}),
true /* close */);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(operation_completed_);
std::string actual_file_data_one;
ASSERT_TRUE(base::ReadFileToString(TestDir().Append(kTestFilename),
&actual_file_data_one));
ASSERT_EQ(kTestDataOne, actual_file_data_one);
std::string actual_file_data_two;
ASSERT_TRUE(base::ReadFileToString(TestDir().Append(kTestFilenameSecondary),
&actual_file_data_two));
ASSERT_EQ(kTestDataTwo, actual_file_data_two);
}
// Verifies that calling Cancel() deletes the temporary file.
TEST_F(LocalFileOperationsTest, CancelDeletesTemp) {
WriteFile(
kTestFilename,
base::queue<std::string>({kTestDataOne, kTestDataTwo, kTestDataThree}),
false /* close */);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(operation_completed_);
file_writer_->Cancel();
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(base::IsDirectoryEmpty(TestDir()));
}
// Verifies that Cancel() can be called while an operation is pending.
TEST_F(LocalFileOperationsTest, CancelsWhileOperationPending) {
WriteFile(kTestFilename, base::queue<std::string>({kTestDataOne}),
false /* close */);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(operation_completed_);
file_writer_->WriteChunk(kTestDataTwo, base::DoNothing());
file_writer_->Cancel();
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(base::IsDirectoryEmpty(TestDir()));
}
// Verifies that dropping an unclosed FileWriter is the same as canceling it.
TEST_F(LocalFileOperationsTest, CancelsWhenDestructed) {
WriteFile(
kTestFilename,
base::queue<std::string>({kTestDataOne, kTestDataTwo, kTestDataThree}),
false /* close */);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(operation_completed_);
file_writer_->WriteChunk(kTestDataTwo, base::DoNothing());
file_writer_.reset();
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(base::IsDirectoryEmpty(TestDir()));
}
} // namespace remoting