blob: 101736e284c41ec0b50aa4030ff073e3b640704d [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/public/cpp/system/file_data_source.h"
#include <algorithm>
#include <vector>
#include "base/containers/span.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/string_view_util.h"
#include "mojo/public/c/system/types.h"
#include "mojo/public/cpp/system/data_pipe_producer.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/content_uri_utils.h"
#include "base/test/android/content_uri_test_utils.h"
#endif
namespace mojo {
namespace {
class FileDataSourceCppTest : public testing::Test {
public:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
base::FilePath path = temp_dir_.GetPath().AppendASCII("testfile");
ASSERT_TRUE(base::WriteFile(path, "123456"));
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_EQ(file.GetLength(), 6);
file_data_source_ = std::make_unique<FileDataSource>(std::move(file));
EXPECT_EQ(file_data_source_->GetLength(), 6u);
// Create FileDataSource for a file-backed, and in-memory content-URI.
#if BUILDFLAG(IS_ANDROID)
base::FilePath content_uri_file =
*base::test::android::GetContentUriFromCacheDirFilePath(path);
base::File::Info info;
ASSERT_TRUE(GetFileInfo(content_uri_file, &info));
ASSERT_EQ(info.size, 6);
cu_file_data_source_ = std::make_unique<FileDataSource>(base::File(
content_uri_file, base::File::FLAG_OPEN | base::File::FLAG_READ));
EXPECT_EQ(cu_file_data_source_->GetLength(), 6u);
base::FilePath content_uri_in_memory =
*base::test::android::GetInMemoryContentUriFromCacheDirFilePath(path);
ASSERT_TRUE(GetFileInfo(content_uri_in_memory, &info));
ASSERT_EQ(info.size, 6);
cu_memory_data_source_ = std::make_unique<FileDataSource>(base::File(
content_uri_in_memory, base::File::FLAG_OPEN | base::File::FLAG_READ));
EXPECT_EQ(cu_memory_data_source_->GetLength(), 6u);
#endif
}
protected:
base::ScopedTempDir temp_dir_;
std::unique_ptr<FileDataSource> file_data_source_;
#if BUILDFLAG(IS_ANDROID)
std::unique_ptr<FileDataSource> cu_file_data_source_;
std::unique_ptr<FileDataSource> cu_memory_data_source_;
#endif
};
TEST_F(FileDataSourceCppTest, ReadAll) {
std::vector<char> buf(6, 'x');
file_data_source_->SetRange(0, 6u);
auto result = file_data_source_->Read(0, buf);
EXPECT_EQ(result.result, MOJO_RESULT_OK);
EXPECT_EQ(result.bytes_read, 6u);
EXPECT_EQ(base::as_string_view(buf), "123456");
#if BUILDFLAG(IS_ANDROID)
// File-backed content-URIs should read all ok.
std::ranges::fill(buf, 'x');
cu_file_data_source_->SetRange(0, 6u);
result = cu_file_data_source_->Read(0, buf);
EXPECT_EQ(result.result, MOJO_RESULT_OK);
EXPECT_EQ(result.bytes_read, 6u);
EXPECT_EQ(base::as_string_view(buf), "123456");
// In-memory content-URIs should fail on pread().
std::ranges::fill(buf, 'x');
cu_memory_data_source_->SetRange(0, 6u);
result = cu_memory_data_source_->Read(0, buf);
EXPECT_EQ(result.result, MOJO_RESULT_UNKNOWN);
EXPECT_EQ(result.bytes_read, 0u);
EXPECT_EQ(base::as_string_view(buf), "xxxxxx");
#endif
}
TEST_F(FileDataSourceCppTest, ReadInChunks) {
std::vector<char> buf(3, 'x');
file_data_source_->SetRange(0, 6);
auto result = file_data_source_->Read(0, buf);
EXPECT_EQ(result.result, MOJO_RESULT_OK);
EXPECT_EQ(result.bytes_read, 3u);
EXPECT_EQ(base::as_string_view(buf), "123");
result = file_data_source_->Read(3, buf);
EXPECT_EQ(result.result, MOJO_RESULT_OK);
EXPECT_EQ(result.bytes_read, 3u);
EXPECT_EQ(base::as_string_view(buf), "456");
#if BUILDFLAG(IS_ANDROID)
// File-backed content-URIs should read chunks ok.
cu_file_data_source_->SetRange(0, 6);
result = cu_file_data_source_->Read(0, buf);
EXPECT_EQ(result.result, MOJO_RESULT_OK);
EXPECT_EQ(result.bytes_read, 3u);
EXPECT_EQ(base::as_string_view(buf), "123");
result = cu_file_data_source_->Read(3, buf);
EXPECT_EQ(result.result, MOJO_RESULT_OK);
EXPECT_EQ(result.bytes_read, 3u);
EXPECT_EQ(base::as_string_view(buf), "456");
// In-memory content-URIs should fail.
std::ranges::fill(buf, 'x');
cu_memory_data_source_->SetRange(0, 6);
result = cu_memory_data_source_->Read(0, buf);
EXPECT_EQ(result.result, MOJO_RESULT_UNKNOWN);
EXPECT_EQ(result.bytes_read, 0u);
EXPECT_EQ(base::as_string_view(buf), "xxx");
#endif
}
TEST_F(FileDataSourceCppTest, SetRangeInvalid) {
std::vector<char> buf(6, 'x');
file_data_source_->SetRange(2, 1);
EXPECT_EQ(file_data_source_->GetLength(), 0u);
auto result = file_data_source_->Read(0, buf);
EXPECT_EQ(result.result, MOJO_RESULT_INVALID_ARGUMENT);
EXPECT_EQ(result.bytes_read, 0u);
EXPECT_EQ(base::as_string_view(buf), "xxxxxx");
}
TEST_F(FileDataSourceCppTest, ReadRange) {
std::vector<char> buf(6, 'x');
file_data_source_->SetRange(2, 4);
EXPECT_EQ(file_data_source_->GetLength(), 2u);
auto result = file_data_source_->Read(0, buf);
EXPECT_EQ(result.result, MOJO_RESULT_OK);
EXPECT_EQ(result.bytes_read, 2u);
EXPECT_EQ(base::as_string_view(buf), "34xxxx");
#if BUILDFLAG(IS_ANDROID)
// File-backed content-URIs should read range ok.
std::ranges::fill(buf, 'x');
cu_file_data_source_->SetRange(2, 4);
EXPECT_EQ(cu_file_data_source_->GetLength(), 2u);
result = cu_file_data_source_->Read(0, buf);
EXPECT_EQ(result.result, MOJO_RESULT_OK);
EXPECT_EQ(result.bytes_read, 2u);
EXPECT_EQ(base::as_string_view(buf), "34xxxx");
// In-memory content-URIs with no_seek should fail.
std::ranges::fill(buf, 'x');
cu_memory_data_source_->SetRange(2, 4);
EXPECT_EQ(cu_memory_data_source_->GetLength(), 2u);
result = cu_memory_data_source_->Read(2, buf);
EXPECT_EQ(result.result, MOJO_RESULT_UNKNOWN);
EXPECT_EQ(result.bytes_read, 0u);
EXPECT_EQ(base::as_string_view(buf), "xxxxxx");
#endif
}
TEST_F(FileDataSourceCppTest, ReadFromOffset) {
std::vector<char> buf(6, 'x');
auto result = file_data_source_->Read(2, buf);
EXPECT_EQ(result.result, MOJO_RESULT_OK);
EXPECT_EQ(result.bytes_read, 4u);
EXPECT_EQ(base::as_string_view(buf), "3456xx");
#if BUILDFLAG(IS_ANDROID)
// File-backed content-URIs should read from offset ok.
std::ranges::fill(buf, 'x');
result = cu_file_data_source_->Read(2, buf);
EXPECT_EQ(result.result, MOJO_RESULT_OK);
EXPECT_EQ(result.bytes_read, 4u);
EXPECT_EQ(base::as_string_view(buf), "3456xx");
// In-memory content-URIs should fail since the don't support pread().
std::ranges::fill(buf, 'x');
result = cu_memory_data_source_->Read(2, buf);
EXPECT_EQ(result.result, MOJO_RESULT_UNKNOWN);
EXPECT_EQ(result.bytes_read, 0u);
EXPECT_EQ(base::as_string_view(buf), "xxxxxx");
#endif
}
} // namespace
} // namespace mojo