|
32 | 32 |
|
33 | 33 | #include <test/support/src/helpers.h> |
34 | 34 | #include <test/support/tdb_catch.h> |
| 35 | +#include "test/support/src/vfs_helpers.h" |
35 | 36 | #include "tiledb/sm/cpp_api/tiledb" |
| 37 | +#include "tiledb/sm/cpp_api/vfs_experimental.h" |
36 | 38 |
|
37 | 39 | #ifdef _WIN32 |
38 | 40 | #include "tiledb/sm/filesystem/path_win.h" |
@@ -500,3 +502,187 @@ TEST_CASE( |
500 | 502 | } |
501 | 503 | } |
502 | 504 | } |
| 505 | + |
| 506 | +TEST_CASE("CPP API: VFS ls_recursive filter", "[cppapi][vfs][ls-recursive]") { |
| 507 | + using namespace tiledb::test; |
| 508 | + S3Test s3_test({10, 100, 0}); |
| 509 | + if (!s3_test.is_supported()) { |
| 510 | + return; |
| 511 | + } |
| 512 | + auto expected_results = s3_test.expected_results(); |
| 513 | + |
| 514 | + vfs_config cfg; |
| 515 | + tiledb::Context ctx(tiledb::Config(&cfg.config)); |
| 516 | + tiledb::VFS vfs(ctx); |
| 517 | + |
| 518 | + tiledb::VFSExperimental::LsObjects ls_objects; |
| 519 | + // Predicate filter to apply to ls_recursive. |
| 520 | + tiledb::VFSExperimental::LsInclude include; |
| 521 | + // Callback to populate ls_objects vector using a filter. |
| 522 | + tiledb::VFSExperimental::LsCallback cb = [&](std::string_view path, |
| 523 | + uint64_t size) { |
| 524 | + if (include(path, size)) { |
| 525 | + ls_objects.emplace_back(path, size); |
| 526 | + } |
| 527 | + return true; |
| 528 | + }; |
| 529 | + |
| 530 | + SECTION("Default filter (include all)") { |
| 531 | + include = [](std::string_view, uint64_t) { return true; }; |
| 532 | + } |
| 533 | + SECTION("Custom filter (include none)") { |
| 534 | + include = [](std::string_view, uint64_t) { return false; }; |
| 535 | + } |
| 536 | + |
| 537 | + bool include_result = true; |
| 538 | + SECTION("Custom filter (include half)") { |
| 539 | + include = [&include_result](std::string_view, uint64_t) { |
| 540 | + include_result = !include_result; |
| 541 | + return include_result; |
| 542 | + }; |
| 543 | + } |
| 544 | + |
| 545 | + SECTION("Custom filter (search for test_file_50)") { |
| 546 | + include = [](std::string_view object_name, uint64_t) { |
| 547 | + return object_name.find("test_file_50") != std::string::npos; |
| 548 | + }; |
| 549 | + } |
| 550 | + SECTION("Custom filter (search for test_file_1*)") { |
| 551 | + include = [](std::string_view object_name, uint64_t) { |
| 552 | + return object_name.find("test_file_1") != std::string::npos; |
| 553 | + }; |
| 554 | + } |
| 555 | + SECTION("Custom filter (reject files over 50 bytes)") { |
| 556 | + include = [](std::string_view, uint64_t size) { return size <= 50; }; |
| 557 | + } |
| 558 | + |
| 559 | + // Test collecting results with LsInclude predicate. |
| 560 | + auto results = tiledb::VFSExperimental::ls_recursive_filter( |
| 561 | + ctx, vfs, s3_test.temp_dir_.to_string(), include); |
| 562 | + std::erase_if(expected_results, [&include](const auto& object) { |
| 563 | + return !include(object.first, object.second); |
| 564 | + }); |
| 565 | + CHECK(results.size() == expected_results.size()); |
| 566 | + CHECK(expected_results == results); |
| 567 | + |
| 568 | + // Test collecting results with LsCallback, writing data into ls_objects. |
| 569 | + tiledb::VFSExperimental::ls_recursive( |
| 570 | + ctx, vfs, s3_test.temp_dir_.to_string(), cb); |
| 571 | + CHECK(ls_objects.size() == expected_results.size()); |
| 572 | + CHECK(expected_results == ls_objects); |
| 573 | +} |
| 574 | + |
| 575 | +TEST_CASE("CPP API: Callback stops traversal", "[cppapi][vfs][ls-recursive]") { |
| 576 | + using namespace tiledb::test; |
| 577 | + S3Test s3_test({10, 50, 15}); |
| 578 | + if (!s3_test.is_supported()) { |
| 579 | + return; |
| 580 | + } |
| 581 | + auto expected_results = s3_test.expected_results(); |
| 582 | + |
| 583 | + vfs_config cfg; |
| 584 | + tiledb::Context ctx(tiledb::Config(&cfg.config)); |
| 585 | + tiledb::VFS vfs(ctx); |
| 586 | + |
| 587 | + tiledb::VFSExperimental::LsObjects ls_objects; |
| 588 | + size_t cb_count = GENERATE(1, 10, 11, 50); |
| 589 | + auto cb = [&](std::string_view path, uint64_t size) { |
| 590 | + // Always emplace to check the callback is not invoked more than `cb_count`. |
| 591 | + ls_objects.emplace_back(path, size); |
| 592 | + // Signal to stop traversal when we have seen `cb_count` objects. |
| 593 | + if (ls_objects.size() == cb_count) { |
| 594 | + return false; |
| 595 | + } |
| 596 | + return true; |
| 597 | + }; |
| 598 | + tiledb::VFSExperimental::ls_recursive( |
| 599 | + ctx, vfs, s3_test.temp_dir_.to_string(), cb); |
| 600 | + expected_results.resize(cb_count); |
| 601 | + CHECK(ls_objects.size() == cb_count); |
| 602 | + CHECK(ls_objects == expected_results); |
| 603 | +} |
| 604 | + |
| 605 | +TEST_CASE("CPP API: Throwing filter", "[cppapi][vfs][ls-recursive]") { |
| 606 | + using namespace tiledb::test; |
| 607 | + S3Test s3_test({0}); |
| 608 | + if (!s3_test.is_supported()) { |
| 609 | + return; |
| 610 | + } |
| 611 | + |
| 612 | + vfs_config cfg; |
| 613 | + tiledb::Context ctx(tiledb::Config(&cfg.config)); |
| 614 | + tiledb::VFS vfs(ctx); |
| 615 | + |
| 616 | + tiledb::VFSExperimental::LsInclude filter = [](std::string_view, |
| 617 | + uint64_t) -> bool { |
| 618 | + throw std::runtime_error("Throwing filter"); |
| 619 | + }; |
| 620 | + auto path = s3_test.temp_dir_.to_string(); |
| 621 | + |
| 622 | + // If the test directory is empty the filter should not throw. |
| 623 | + SECTION("Throwing filter with 0 objects should not throw") { |
| 624 | + CHECK_NOTHROW( |
| 625 | + tiledb::VFSExperimental::ls_recursive_filter(ctx, vfs, path, filter)); |
| 626 | + CHECK_NOTHROW( |
| 627 | + tiledb::VFSExperimental::ls_recursive(ctx, vfs, path, filter)); |
| 628 | + } |
| 629 | + SECTION("Throwing filter with N objects should throw") { |
| 630 | + vfs.touch(s3_test.temp_dir_.join_path("test_file").to_string()); |
| 631 | + CHECK_THROWS_AS( |
| 632 | + tiledb::VFSExperimental::ls_recursive_filter(ctx, vfs, path, filter), |
| 633 | + std::runtime_error); |
| 634 | + CHECK_THROWS_WITH( |
| 635 | + tiledb::VFSExperimental::ls_recursive_filter(ctx, vfs, path, filter), |
| 636 | + Catch::Matchers::ContainsSubstring("Throwing filter")); |
| 637 | + CHECK_THROWS_AS( |
| 638 | + tiledb::VFSExperimental::ls_recursive(ctx, vfs, path, filter), |
| 639 | + std::runtime_error); |
| 640 | + CHECK_THROWS_WITH( |
| 641 | + tiledb::VFSExperimental::ls_recursive(ctx, vfs, path, filter), |
| 642 | + Catch::Matchers::ContainsSubstring("Throwing filter")); |
| 643 | + } |
| 644 | +} |
| 645 | + |
| 646 | +TEST_CASE( |
| 647 | + "CPP API: CallbackWrapperCPP construction validation", |
| 648 | + "[ls-recursive][callback][wrapper]") { |
| 649 | + using tiledb::sm::CallbackWrapperCPP; |
| 650 | + tiledb::VFSExperimental::LsObjects data; |
| 651 | + auto cb = [&](std::string_view, uint64_t) -> bool { return true; }; |
| 652 | + SECTION("Null callback") { |
| 653 | + CHECK_THROWS(CallbackWrapperCPP(nullptr)); |
| 654 | + } |
| 655 | + SECTION("Valid callback") { |
| 656 | + CHECK_NOTHROW(CallbackWrapperCPP(cb)); |
| 657 | + } |
| 658 | +} |
| 659 | + |
| 660 | +TEST_CASE( |
| 661 | + "CPP API: CallbackWrapperCPP operator() validation", |
| 662 | + "[ls-recursive][callback][wrapper]") { |
| 663 | + tiledb::VFSExperimental::LsObjects data; |
| 664 | + auto cb = [&](std::string_view path, uint64_t object_size) -> bool { |
| 665 | + if (object_size > 100) { |
| 666 | + // Throw if object size is greater than 100 bytes. |
| 667 | + throw std::runtime_error("Throwing callback"); |
| 668 | + } else if (!path.ends_with(".txt")) { |
| 669 | + // Reject non-txt files. |
| 670 | + return false; |
| 671 | + } |
| 672 | + data.emplace_back(path, object_size); |
| 673 | + return true; |
| 674 | + }; |
| 675 | + tiledb::sm::CallbackWrapperCPP wrapper(cb); |
| 676 | + |
| 677 | + SECTION("Callback return true accepts object") { |
| 678 | + CHECK(wrapper("file.txt", 10) == true); |
| 679 | + CHECK(data.size() == 1); |
| 680 | + } |
| 681 | + SECTION("Callback return false rejects object") { |
| 682 | + CHECK(wrapper("some/dir/", 0) == false); |
| 683 | + CHECK(data.empty()); |
| 684 | + } |
| 685 | + SECTION("Callback exception is propagated") { |
| 686 | + CHECK_THROWS_WITH(wrapper("path", 101) == 0, "Throwing callback"); |
| 687 | + } |
| 688 | +} |
0 commit comments