blob: fa52f3cf402e7dceb12bd401a1b4d6f000b31ae2 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 <memory>
#include <string>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include "arrow/filesystem/filesystem.h"
#include "arrow/filesystem/mockfs.h"
#include "arrow/filesystem/path-util.h"
#include "arrow/filesystem/test-util.h"
#include "arrow/io/interfaces.h"
#include "arrow/testing/gtest_util.h"
namespace arrow {
namespace fs {
namespace internal {
void AssertPartsEqual(const std::vector<std::string>& parts,
const std::vector<std::string>& expected) {
ASSERT_EQ(parts, expected);
}
void AssertPairEqual(const std::pair<std::string, std::string>& pair,
const std::pair<std::string, std::string>& expected) {
ASSERT_EQ(pair, expected);
}
TEST(FileStats, BaseName) {
auto st = FileStats();
ASSERT_EQ(st.base_name(), "");
st.set_path("foo");
ASSERT_EQ(st.base_name(), "foo");
st.set_path("foo/bar/baz.qux");
ASSERT_EQ(st.base_name(), "baz.qux");
}
TEST(PathUtil, SplitAbstractPath) {
std::vector<std::string> parts;
parts = SplitAbstractPath("");
AssertPartsEqual(parts, {});
parts = SplitAbstractPath("abc");
AssertPartsEqual(parts, {"abc"});
parts = SplitAbstractPath("abc/def.ghi");
AssertPartsEqual(parts, {"abc", "def.ghi"});
parts = SplitAbstractPath("abc/def/ghi");
AssertPartsEqual(parts, {"abc", "def", "ghi"});
parts = SplitAbstractPath("abc\\def\\ghi");
AssertPartsEqual(parts, {"abc\\def\\ghi"});
// Trailing slash
parts = SplitAbstractPath("abc/");
AssertPartsEqual(parts, {"abc"});
parts = SplitAbstractPath("abc/def.ghi/");
AssertPartsEqual(parts, {"abc", "def.ghi"});
parts = SplitAbstractPath("abc/def.ghi\\");
AssertPartsEqual(parts, {"abc", "def.ghi\\"});
// Leading slash
parts = SplitAbstractPath("/");
AssertPartsEqual(parts, {});
parts = SplitAbstractPath("/abc");
AssertPartsEqual(parts, {"abc"});
parts = SplitAbstractPath("/abc/def.ghi");
AssertPartsEqual(parts, {"abc", "def.ghi"});
parts = SplitAbstractPath("/abc/def.ghi/");
AssertPartsEqual(parts, {"abc", "def.ghi"});
}
TEST(PathUtil, GetAbstractPathParent) {
std::pair<std::string, std::string> pair;
pair = GetAbstractPathParent("");
AssertPairEqual(pair, {"", ""});
pair = GetAbstractPathParent("abc");
AssertPairEqual(pair, {"", "abc"});
pair = GetAbstractPathParent("abc/def/ghi");
AssertPairEqual(pair, {"abc/def", "ghi"});
pair = GetAbstractPathParent("abc/def\\ghi");
AssertPairEqual(pair, {"abc", "def\\ghi"});
}
TEST(PathUtil, ValidateAbstractPathParts) {
ASSERT_OK(ValidateAbstractPathParts({}));
ASSERT_OK(ValidateAbstractPathParts({"abc"}));
ASSERT_OK(ValidateAbstractPathParts({"abc", "def"}));
ASSERT_OK(ValidateAbstractPathParts({"abc", "def.ghi"}));
ASSERT_OK(ValidateAbstractPathParts({"abc", "def\\ghi"}));
// Empty path component
ASSERT_RAISES(Invalid, ValidateAbstractPathParts({""}));
ASSERT_RAISES(Invalid, ValidateAbstractPathParts({"abc", "", "def"}));
// Separator in component
ASSERT_RAISES(Invalid, ValidateAbstractPathParts({"/"}));
ASSERT_RAISES(Invalid, ValidateAbstractPathParts({"abc/def"}));
}
TEST(PathUtil, ConcatAbstractPath) {
ASSERT_EQ("abc", ConcatAbstractPath("", "abc"));
ASSERT_EQ("abc/def", ConcatAbstractPath("abc", "def"));
ASSERT_EQ("abc/def/ghi", ConcatAbstractPath("abc/def", "ghi"));
ASSERT_EQ("abc/def", ConcatAbstractPath("abc/", "def"));
ASSERT_EQ("abc/def/ghi", ConcatAbstractPath("abc/def/", "ghi"));
ASSERT_EQ("/abc", ConcatAbstractPath("/", "abc"));
ASSERT_EQ("/abc/def", ConcatAbstractPath("/abc", "def"));
ASSERT_EQ("/abc/def", ConcatAbstractPath("/abc/", "def"));
}
TEST(PathUtil, JoinAbstractPath) {
std::vector<std::string> parts = {"abc", "def", "ghi", "jkl"};
ASSERT_EQ("abc/def/ghi/jkl", JoinAbstractPath(parts.begin(), parts.end()));
ASSERT_EQ("def/ghi", JoinAbstractPath(parts.begin() + 1, parts.begin() + 3));
ASSERT_EQ("", JoinAbstractPath(parts.begin(), parts.begin()));
}
TEST(PathUtil, EnsureTrailingSlash) {
ASSERT_EQ("", EnsureTrailingSlash(""));
ASSERT_EQ("/", EnsureTrailingSlash("/"));
ASSERT_EQ("abc/", EnsureTrailingSlash("abc"));
ASSERT_EQ("abc/", EnsureTrailingSlash("abc/"));
ASSERT_EQ("/abc/", EnsureTrailingSlash("/abc"));
ASSERT_EQ("/abc/", EnsureTrailingSlash("/abc/"));
}
////////////////////////////////////////////////////////////////////////////
// Generic MockFileSystem tests
class TestMockFSGeneric : public ::testing::Test, public GenericFileSystemTest {
public:
void SetUp() override {
time_ = TimePoint(TimePoint::duration(42));
fs_ = std::make_shared<MockFileSystem>(time_);
}
protected:
std::shared_ptr<FileSystem> GetEmptyFileSystem() override { return fs_; }
TimePoint time_;
std::shared_ptr<MockFileSystem> fs_;
};
GENERIC_FS_TEST_FUNCTIONS(TestMockFSGeneric);
////////////////////////////////////////////////////////////////////////////
// Concrete MockFileSystem tests
class TestMockFS : public ::testing::Test {
public:
void SetUp() override {
time_ = TimePoint(TimePoint::duration(42));
fs_ = std::make_shared<MockFileSystem>(time_);
}
Status WriteString(io::OutputStream* stream, const std::string& s) {
return stream->Write(s.data(), static_cast<int64_t>(s.length()));
}
void CheckDirs(const std::vector<DirInfo>& expected) {
ASSERT_EQ(fs_->AllDirs(), expected);
}
void CheckDirPaths(const std::vector<std::string>& expected) {
std::vector<DirInfo> infos;
infos.reserve(expected.size());
for (const auto& s : expected) {
infos.push_back({s, time_});
}
ASSERT_EQ(fs_->AllDirs(), infos);
}
void CheckFiles(const std::vector<FileInfo>& expected) {
ASSERT_EQ(fs_->AllFiles(), expected);
}
void CreateFile(const std::string& path, const std::string& data) {
::arrow::fs::CreateFile(fs_.get(), path, data);
}
protected:
TimePoint time_;
std::shared_ptr<MockFileSystem> fs_;
};
TEST_F(TestMockFS, Empty) {
CheckDirs({});
CheckFiles({});
}
TEST_F(TestMockFS, CreateDir) {
ASSERT_OK(fs_->CreateDir("AB"));
ASSERT_OK(fs_->CreateDir("AB/CD/EF")); // Recursive
// Non-recursive, parent doesn't exist
ASSERT_RAISES(IOError, fs_->CreateDir("AB/GH/IJ", false /* recursive */));
ASSERT_OK(fs_->CreateDir("AB/GH", false /* recursive */));
ASSERT_OK(fs_->CreateDir("AB/GH/IJ", false /* recursive */));
// Idempotency
ASSERT_OK(fs_->CreateDir("AB/GH/IJ", false /* recursive */));
ASSERT_OK(fs_->CreateDir("XY"));
CheckDirs({{"AB", time_},
{"AB/CD", time_},
{"AB/CD/EF", time_},
{"AB/GH", time_},
{"AB/GH/IJ", time_},
{"XY", time_}});
CheckFiles({});
}
TEST_F(TestMockFS, DeleteDir) {
ASSERT_OK(fs_->CreateDir("AB/CD/EF"));
ASSERT_OK(fs_->CreateDir("AB/GH/IJ"));
ASSERT_OK(fs_->DeleteDir("AB/CD"));
ASSERT_OK(fs_->DeleteDir("AB/GH/IJ"));
CheckDirs({{"AB", time_}, {"AB/GH", time_}});
CheckFiles({});
ASSERT_RAISES(IOError, fs_->DeleteDir("AB/CD"));
ASSERT_OK(fs_->DeleteDir("AB"));
CheckDirs({});
CheckFiles({});
}
TEST_F(TestMockFS, DeleteFile) {
ASSERT_OK(fs_->CreateDir("AB"));
CreateFile("AB/cd", "data");
CheckDirs({{"AB", time_}});
CheckFiles({{"AB/cd", time_, "data"}});
ASSERT_OK(fs_->DeleteFile("AB/cd"));
CheckDirs({{"AB", time_}});
CheckFiles({});
CreateFile("ab", "data");
CheckDirs({{"AB", time_}});
CheckFiles({{"ab", time_, "data"}});
ASSERT_OK(fs_->DeleteFile("ab"));
CheckDirs({{"AB", time_}});
CheckFiles({});
}
TEST_F(TestMockFS, GetTargetStatsSingle) {
ASSERT_OK(fs_->CreateDir("AB/CD"));
CreateFile("AB/CD/ef", "some data");
FileStats st;
ASSERT_OK(fs_->GetTargetStats("AB", &st));
AssertFileStats(st, "AB", FileType::Directory, time_);
ASSERT_EQ(st.base_name(), "AB");
ASSERT_OK(fs_->GetTargetStats("AB/CD/ef", &st));
AssertFileStats(st, "AB/CD/ef", FileType::File, time_, 9);
ASSERT_EQ(st.base_name(), "ef");
// Invalid path
ASSERT_RAISES(Invalid, fs_->GetTargetStats("//foo//bar//baz//", &st));
}
TEST_F(TestMockFS, GetTargetStatsVector) {
ASSERT_OK(fs_->CreateDir("AB/CD"));
CreateFile("AB/CD/ef", "some data");
std::vector<FileStats> stats;
ASSERT_OK(
fs_->GetTargetStats({"AB", "AB/CD", "AB/zz", "zz", "XX/zz", "AB/CD/ef"}, &stats));
ASSERT_EQ(stats.size(), 6);
AssertFileStats(stats[0], "AB", FileType::Directory, time_);
AssertFileStats(stats[1], "AB/CD", FileType::Directory, time_);
AssertFileStats(stats[2], "AB/zz", FileType::NonExistent);
AssertFileStats(stats[3], "zz", FileType::NonExistent);
AssertFileStats(stats[4], "XX/zz", FileType::NonExistent);
AssertFileStats(stats[5], "AB/CD/ef", FileType::File, time_, 9);
// Invalid path
ASSERT_RAISES(Invalid,
fs_->GetTargetStats({"AB", "AB/CD", "//foo//bar//baz//"}, &stats));
}
TEST_F(TestMockFS, GetTargetStatsSelector) {
ASSERT_OK(fs_->CreateDir("AB/CD"));
CreateFile("ab", "data");
Selector s;
s.base_dir = "";
std::vector<FileStats> stats;
ASSERT_OK(fs_->GetTargetStats(s, &stats));
ASSERT_EQ(stats.size(), 2);
AssertFileStats(stats[0], "AB", FileType::Directory, time_);
AssertFileStats(stats[1], "ab", FileType::File, time_, 4);
s.recursive = true;
ASSERT_OK(fs_->GetTargetStats(s, &stats));
ASSERT_EQ(stats.size(), 3);
AssertFileStats(stats[0], "AB", FileType::Directory, time_);
AssertFileStats(stats[1], "AB/CD", FileType::Directory, time_);
AssertFileStats(stats[2], "ab", FileType::File, time_, 4);
// Invalid path
s.base_dir = "//foo//bar//baz//";
ASSERT_RAISES(Invalid, fs_->GetTargetStats(s, &stats));
}
TEST_F(TestMockFS, OpenOutputStream) {
std::shared_ptr<io::OutputStream> stream;
ASSERT_OK(fs_->OpenOutputStream("ab", &stream));
ASSERT_OK(stream->Close());
CheckDirs({});
CheckFiles({{"ab", time_, ""}});
}
TEST_F(TestMockFS, OpenAppendStream) {
std::shared_ptr<io::OutputStream> stream;
ASSERT_OK(fs_->OpenAppendStream("ab", &stream));
ASSERT_OK(WriteString(stream.get(), "some "));
ASSERT_OK(stream->Close());
ASSERT_OK(fs_->OpenAppendStream("ab", &stream));
ASSERT_OK(WriteString(stream.get(), "data"));
ASSERT_OK(stream->Close());
CheckDirs({});
CheckFiles({{"ab", time_, "some data"}});
}
////////////////////////////////////////////////////////////////////////////
// Concrete SubTreeFileSystem tests
class TestSubTreeFileSystem : public TestMockFS {
public:
void SetUp() override {
TestMockFS::SetUp();
ASSERT_OK(fs_->CreateDir("sub/tree"));
subfs_ = std::make_shared<SubTreeFileSystem>("sub/tree", fs_);
}
void CreateFile(const std::string& path, const std::string& data) {
::arrow::fs::CreateFile(subfs_.get(), path, data);
}
protected:
std::shared_ptr<SubTreeFileSystem> subfs_;
};
TEST_F(TestSubTreeFileSystem, CreateDir) {
ASSERT_OK(subfs_->CreateDir("AB"));
ASSERT_OK(subfs_->CreateDir("AB/CD/EF")); // Recursive
// Non-recursive, parent doesn't exist
ASSERT_RAISES(IOError, subfs_->CreateDir("AB/GH/IJ", false /* recursive */));
ASSERT_OK(subfs_->CreateDir("AB/GH", false /* recursive */));
ASSERT_OK(subfs_->CreateDir("AB/GH/IJ", false /* recursive */));
// Can't create root dir
ASSERT_RAISES(IOError, subfs_->CreateDir(""));
CheckDirs({{"sub", time_},
{"sub/tree", time_},
{"sub/tree/AB", time_},
{"sub/tree/AB/CD", time_},
{"sub/tree/AB/CD/EF", time_},
{"sub/tree/AB/GH", time_},
{"sub/tree/AB/GH/IJ", time_}});
CheckFiles({});
}
TEST_F(TestSubTreeFileSystem, DeleteDir) {
ASSERT_OK(subfs_->CreateDir("AB/CD/EF"));
ASSERT_OK(subfs_->CreateDir("AB/GH/IJ"));
ASSERT_OK(subfs_->DeleteDir("AB/CD"));
ASSERT_OK(subfs_->DeleteDir("AB/GH/IJ"));
CheckDirs({{"sub", time_},
{"sub/tree", time_},
{"sub/tree/AB", time_},
{"sub/tree/AB/GH", time_}});
CheckFiles({});
ASSERT_RAISES(IOError, subfs_->DeleteDir("AB/CD"));
ASSERT_OK(subfs_->DeleteDir("AB"));
CheckDirs({{"sub", time_}, {"sub/tree", time_}});
CheckFiles({});
// Can't delete root dir
ASSERT_RAISES(IOError, subfs_->DeleteDir(""));
CheckDirs({{"sub", time_}, {"sub/tree", time_}});
CheckFiles({});
}
TEST_F(TestSubTreeFileSystem, DeleteFile) {
ASSERT_OK(subfs_->CreateDir("AB"));
CreateFile("ab", "");
CheckFiles({{"sub/tree/ab", time_, ""}});
ASSERT_OK(subfs_->DeleteFile("ab"));
CheckFiles({});
CreateFile("AB/cd", "");
CheckFiles({{"sub/tree/AB/cd", time_, ""}});
ASSERT_OK(subfs_->DeleteFile("AB/cd"));
CheckFiles({});
ASSERT_RAISES(IOError, subfs_->DeleteFile("non-existent"));
ASSERT_RAISES(IOError, subfs_->DeleteFile(""));
}
TEST_F(TestSubTreeFileSystem, MoveFile) {
CreateFile("ab", "");
CheckFiles({{"sub/tree/ab", time_, ""}});
ASSERT_OK(subfs_->Move("ab", "cd"));
CheckFiles({{"sub/tree/cd", time_, ""}});
ASSERT_OK(subfs_->CreateDir("AB"));
ASSERT_OK(subfs_->Move("cd", "AB/ef"));
CheckFiles({{"sub/tree/AB/ef", time_, ""}});
ASSERT_RAISES(IOError, subfs_->Move("AB/ef", ""));
ASSERT_RAISES(IOError, subfs_->Move("", "xxx"));
CheckFiles({{"sub/tree/AB/ef", time_, ""}});
CheckDirs({{"sub", time_}, {"sub/tree", time_}, {"sub/tree/AB", time_}});
}
TEST_F(TestSubTreeFileSystem, MoveDir) {
ASSERT_OK(subfs_->CreateDir("AB/CD/EF"));
ASSERT_OK(subfs_->Move("AB/CD", "GH"));
CheckDirs({{"sub", time_},
{"sub/tree", time_},
{"sub/tree/AB", time_},
{"sub/tree/GH", time_},
{"sub/tree/GH/EF", time_}});
ASSERT_RAISES(IOError, subfs_->Move("AB", ""));
}
TEST_F(TestSubTreeFileSystem, CopyFile) {
CreateFile("ab", "data");
CheckFiles({{"sub/tree/ab", time_, "data"}});
ASSERT_OK(subfs_->CopyFile("ab", "cd"));
CheckFiles({{"sub/tree/ab", time_, "data"}, {"sub/tree/cd", time_, "data"}});
ASSERT_OK(subfs_->CreateDir("AB"));
ASSERT_OK(subfs_->CopyFile("cd", "AB/ef"));
CheckFiles({{"sub/tree/AB/ef", time_, "data"},
{"sub/tree/ab", time_, "data"},
{"sub/tree/cd", time_, "data"}});
ASSERT_RAISES(IOError, subfs_->CopyFile("ab", ""));
ASSERT_RAISES(IOError, subfs_->CopyFile("", "xxx"));
CheckFiles({{"sub/tree/AB/ef", time_, "data"},
{"sub/tree/ab", time_, "data"},
{"sub/tree/cd", time_, "data"}});
}
TEST_F(TestSubTreeFileSystem, OpenInputStream) {
std::shared_ptr<io::InputStream> stream;
std::shared_ptr<Buffer> buffer;
CreateFile("ab", "data");
ASSERT_OK(subfs_->OpenInputStream("ab", &stream));
ASSERT_OK(stream->Read(4, &buffer));
AssertBufferEqual(*buffer, "data");
ASSERT_OK(stream->Close());
ASSERT_RAISES(IOError, subfs_->OpenInputStream("non-existent", &stream));
ASSERT_RAISES(IOError, subfs_->OpenInputStream("", &stream));
}
TEST_F(TestSubTreeFileSystem, OpenInputFile) {
std::shared_ptr<io::RandomAccessFile> stream;
std::shared_ptr<Buffer> buffer;
CreateFile("ab", "some data");
ASSERT_OK(subfs_->OpenInputFile("ab", &stream));
ASSERT_OK(stream->ReadAt(5, 4, &buffer));
AssertBufferEqual(*buffer, "data");
ASSERT_OK(stream->Close());
ASSERT_RAISES(IOError, subfs_->OpenInputFile("non-existent", &stream));
ASSERT_RAISES(IOError, subfs_->OpenInputFile("", &stream));
}
TEST_F(TestSubTreeFileSystem, OpenOutputStream) {
std::shared_ptr<io::OutputStream> stream;
ASSERT_OK(subfs_->OpenOutputStream("ab", &stream));
ASSERT_OK(stream->Write("data"));
ASSERT_OK(stream->Close());
CheckFiles({{"sub/tree/ab", time_, "data"}});
ASSERT_OK(subfs_->CreateDir("AB"));
ASSERT_OK(subfs_->OpenOutputStream("AB/cd", &stream));
ASSERT_OK(stream->Write("other"));
ASSERT_OK(stream->Close());
CheckFiles({{"sub/tree/AB/cd", time_, "other"}, {"sub/tree/ab", time_, "data"}});
ASSERT_RAISES(IOError, subfs_->OpenOutputStream("non-existent/xxx", &stream));
ASSERT_RAISES(IOError, subfs_->OpenOutputStream("AB", &stream));
ASSERT_RAISES(IOError, subfs_->OpenOutputStream("", &stream));
CheckFiles({{"sub/tree/AB/cd", time_, "other"}, {"sub/tree/ab", time_, "data"}});
}
TEST_F(TestSubTreeFileSystem, OpenAppendStream) {
std::shared_ptr<io::OutputStream> stream;
ASSERT_OK(subfs_->OpenAppendStream("ab", &stream));
ASSERT_OK(stream->Write("some"));
ASSERT_OK(stream->Close());
CheckFiles({{"sub/tree/ab", time_, "some"}});
ASSERT_OK(subfs_->OpenAppendStream("ab", &stream));
ASSERT_OK(stream->Write(" data"));
ASSERT_OK(stream->Close());
CheckFiles({{"sub/tree/ab", time_, "some data"}});
}
TEST_F(TestSubTreeFileSystem, GetTargetStatsSingle) {
FileStats st;
ASSERT_OK(subfs_->CreateDir("AB/CD"));
ASSERT_OK(subfs_->GetTargetStats("AB", &st));
AssertFileStats(st, "AB", FileType::Directory, time_);
ASSERT_OK(subfs_->GetTargetStats("AB/CD", &st));
AssertFileStats(st, "AB/CD", FileType::Directory, time_);
CreateFile("ab", "data");
ASSERT_OK(subfs_->GetTargetStats("ab", &st));
AssertFileStats(st, "ab", FileType::File, time_, 4);
ASSERT_OK(subfs_->GetTargetStats("non-existent", &st));
AssertFileStats(st, "non-existent", FileType::NonExistent);
}
TEST_F(TestSubTreeFileSystem, GetTargetStatsVector) {
std::vector<FileStats> stats;
ASSERT_OK(subfs_->CreateDir("AB/CD"));
CreateFile("ab", "data");
CreateFile("AB/cd", "other data");
ASSERT_OK(subfs_->GetTargetStats({"ab", "AB", "AB/cd", "non-existent"}, &stats));
ASSERT_EQ(stats.size(), 4);
AssertFileStats(stats[0], "ab", FileType::File, time_, 4);
AssertFileStats(stats[1], "AB", FileType::Directory, time_);
AssertFileStats(stats[2], "AB/cd", FileType::File, time_, 10);
AssertFileStats(stats[3], "non-existent", FileType::NonExistent);
}
TEST_F(TestSubTreeFileSystem, GetTargetStatsSelector) {
std::vector<FileStats> stats;
Selector selector;
ASSERT_OK(subfs_->CreateDir("AB/CD"));
CreateFile("ab", "data");
CreateFile("AB/cd", "data2");
CreateFile("AB/CD/ef", "data34");
selector.base_dir = "AB";
selector.recursive = false;
ASSERT_OK(subfs_->GetTargetStats(selector, &stats));
ASSERT_EQ(stats.size(), 2);
AssertFileStats(stats[0], "AB/CD", FileType::Directory, time_);
AssertFileStats(stats[1], "AB/cd", FileType::File, time_, 5);
selector.recursive = true;
ASSERT_OK(subfs_->GetTargetStats(selector, &stats));
ASSERT_EQ(stats.size(), 3);
AssertFileStats(stats[0], "AB/CD", FileType::Directory, time_);
AssertFileStats(stats[1], "AB/CD/ef", FileType::File, time_, 6);
AssertFileStats(stats[2], "AB/cd", FileType::File, time_, 5);
selector.base_dir = "";
selector.recursive = false;
ASSERT_OK(subfs_->GetTargetStats(selector, &stats));
ASSERT_EQ(stats.size(), 2);
AssertFileStats(stats[0], "AB", FileType::Directory, time_);
AssertFileStats(stats[1], "ab", FileType::File, time_, 4);
selector.recursive = true;
ASSERT_OK(subfs_->GetTargetStats(selector, &stats));
ASSERT_EQ(stats.size(), 5);
AssertFileStats(stats[0], "AB", FileType::Directory, time_);
AssertFileStats(stats[1], "AB/CD", FileType::Directory, time_);
AssertFileStats(stats[2], "AB/CD/ef", FileType::File, time_, 6);
AssertFileStats(stats[3], "AB/cd", FileType::File, time_, 5);
AssertFileStats(stats[4], "ab", FileType::File, time_, 4);
selector.base_dir = "non-existent";
ASSERT_RAISES(IOError, subfs_->GetTargetStats(selector, &stats));
selector.allow_non_existent = true;
ASSERT_OK(subfs_->GetTargetStats(selector, &stats));
ASSERT_EQ(stats.size(), 0);
}
} // namespace internal
} // namespace fs
} // namespace arrow