3939
4040namespace arrow {
4141namespace fs {
42+ // / Custom comparison for FileInfo, we need this to use complex googletest matchers.
43+ inline bool operator ==(const FileInfo& a, const FileInfo& b) {
44+ return a.path () == b.path () && a.type () == b.type ();
45+ }
46+
4247namespace {
4348
4449namespace bp = boost::process;
@@ -51,6 +56,7 @@ using ::testing::Not;
5156using ::testing::NotNull;
5257using ::testing::Pair;
5358using ::testing::UnorderedElementsAre;
59+ using ::testing::UnorderedElementsAreArray;
5460
5561auto const * kLoremIpsum = R"""(
5662Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
@@ -93,7 +99,7 @@ class GcsTestbench : public ::testing::Environment {
9399 error_ = std::move (error);
94100 }
95101
96- ~GcsTestbench () {
102+ ~GcsTestbench () override {
97103 // Brutal shutdown, kill the full process group because the GCS testbench may launch
98104 // additional children.
99105 group_.terminate ();
@@ -186,6 +192,29 @@ class GcsIntegrationTest : public ::testing::Test {
186192
187193 std::string RandomFolderName () { return RandomChars (32 ) + " /" ; }
188194
195+ struct Hierarchy {
196+ std::string base_dir;
197+ std::vector<FileInfo> contents;
198+ };
199+
200+ Result<Hierarchy> CreateHierarchy (std::shared_ptr<arrow::fs::FileSystem> fs) {
201+ const char * const kTestFolders [] = {
202+ " a/" , " a/0/" , " a/0/0/" , " a/1/" , " a/2/" ,
203+ };
204+ auto result = Hierarchy{PreexistingBucketPath () + " a/" , {}};
205+ for (auto const * f : kTestFolders ) {
206+ const auto folder = PreexistingBucketPath () + f;
207+ RETURN_NOT_OK (fs->CreateDir (folder, true ));
208+ result.contents .push_back (arrow::fs::Dir (folder));
209+ for (int i = 0 ; i != 64 ; ++i) {
210+ const auto filename = folder + " test-file-" + std::to_string (i);
211+ CreateFile (fs.get (), filename, filename);
212+ result.contents .push_back (arrow::fs::File (filename));
213+ }
214+ }
215+ return result;
216+ }
217+
189218 private:
190219 std::string RandomChars (std::size_t count) {
191220 auto const fillers = std::string (" abcdefghijlkmnopqrstuvwxyz0123456789" );
@@ -445,6 +474,63 @@ TEST_F(GcsIntegrationTest, GetFileInfoObject) {
445474 arrow::fs::AssertFileInfo (fs.get (), PreexistingObjectPath (), FileType::File);
446475}
447476
477+ TEST_F (GcsIntegrationTest, GetFileInfoSelectorRecursive) {
478+ auto fs = internal::MakeGcsFileSystemForTest (TestGcsOptions ());
479+ ASSERT_OK_AND_ASSIGN (auto hierarchy, CreateHierarchy (fs));
480+ std::vector<arrow::fs::FileInfo> expected;
481+ std::copy_if (
482+ hierarchy.contents .begin (), hierarchy.contents .end (), std::back_inserter (expected),
483+ [&](const arrow::fs::FileInfo& info) { return hierarchy.base_dir != info.path (); });
484+
485+ auto selector = FileSelector ();
486+ selector.base_dir = hierarchy.base_dir ;
487+ selector.allow_not_found = false ;
488+ selector.recursive = true ;
489+ ASSERT_OK_AND_ASSIGN (auto results, fs->GetFileInfo (selector));
490+ EXPECT_THAT (results, UnorderedElementsAreArray (expected.begin (), expected.end ()));
491+ }
492+
493+ TEST_F (GcsIntegrationTest, GetFileInfoSelectorNonRecursive) {
494+ auto fs = internal::MakeGcsFileSystemForTest (TestGcsOptions ());
495+ ASSERT_OK_AND_ASSIGN (auto hierarchy, CreateHierarchy (fs));
496+ std::vector<arrow::fs::FileInfo> expected;
497+ std::copy_if (hierarchy.contents .begin (), hierarchy.contents .end (),
498+ std::back_inserter (expected), [&](const arrow::fs::FileInfo& info) {
499+ if (info.path () == hierarchy.base_dir ) return false ;
500+ return internal::EnsureTrailingSlash (
501+ internal::GetAbstractPathParent (info.path ()).first ) ==
502+ hierarchy.base_dir ;
503+ });
504+
505+ auto selector = FileSelector ();
506+ selector.base_dir = hierarchy.base_dir ;
507+ selector.allow_not_found = false ;
508+ selector.recursive = false ;
509+ ASSERT_OK_AND_ASSIGN (auto results, fs->GetFileInfo (selector));
510+ EXPECT_THAT (results, UnorderedElementsAreArray (expected.begin (), expected.end ()));
511+ }
512+
513+ TEST_F (GcsIntegrationTest, GetFileInfoSelectorNotFoundTrue) {
514+ auto fs = internal::MakeGcsFileSystemForTest (TestGcsOptions ());
515+
516+ auto selector = FileSelector ();
517+ selector.base_dir = NotFoundObjectPath () + " /" ;
518+ selector.allow_not_found = true ;
519+ selector.recursive = true ;
520+ ASSERT_OK_AND_ASSIGN (auto results, fs->GetFileInfo (selector));
521+ EXPECT_THAT (results, IsEmpty ());
522+ }
523+
524+ TEST_F (GcsIntegrationTest, GetFileInfoSelectorNotFoundFalse) {
525+ auto fs = internal::MakeGcsFileSystemForTest (TestGcsOptions ());
526+
527+ auto selector = FileSelector ();
528+ selector.base_dir = NotFoundObjectPath () + " /" ;
529+ selector.allow_not_found = false ;
530+ selector.recursive = false ;
531+ ASSERT_RAISES (IOError, fs->GetFileInfo (selector));
532+ }
533+
448534TEST_F (GcsIntegrationTest, CreateDirSuccessBucketOnly) {
449535 auto fs = internal::MakeGcsFileSystemForTest (TestGcsOptions ());
450536 auto bucket_name = RandomBucketName ();
0 commit comments