@@ -202,10 +202,88 @@ TEST_CASE("VFS: Test long local paths", "[vfs][long-paths]") {
202202 }
203203}
204204
205+ TEST_CASE (" VFS: copy_file" , " [vfs][copy_file]" ) {
206+ LocalFsTest src_fs ({0 }), dst_fs ({0 });
207+ URI src_path = src_fs.temp_dir_ .add_trailing_slash ();
208+ URI dst_path = dst_fs.temp_dir_ .add_trailing_slash ();
209+
210+ ThreadPool compute_tp (4 );
211+ ThreadPool io_tp (4 );
212+ Config config = set_config_params ();
213+ VFS vfs{
214+ &g_helper_stats, g_helper_logger ().get (), &compute_tp, &io_tp, config};
215+
216+ size_t test_str_size = 0 ;
217+ SECTION (" Filesize = 0 MB" ) {
218+ test_str_size = 0 ;
219+ }
220+ SECTION (" Filesize = 1 MB" ) {
221+ test_str_size = 1048576 ;
222+ }
223+ SECTION (" Filesize = 10 MB" ) {
224+ test_str_size = 10 * 1048576 ;
225+ }
226+ SECTION (" Filesize = 100 MB" ) {
227+ test_str_size = 100 * 1048576 ;
228+ }
229+ SECTION (" Filesize = 150 MB" ) {
230+ test_str_size = 150 * 1048576 ;
231+ }
232+ const std::string test_chars = " abcdefghijklmnopqrstuvwxyz" ;
233+ std::string test_str;
234+ test_str.reserve (test_str_size);
235+ std::random_device rd;
236+ std::mt19937 gen (rd ());
237+ std::uniform_int_distribution<size_t > dist (0 , test_chars.length () - 1 );
238+ for (size_t i = 0 ; i < test_str_size; ++i) {
239+ test_str += test_chars[dist (gen)];
240+ }
241+ REQUIRE (test_str.size () == test_str_size);
242+
243+ // Create src_file and write data to it.
244+ auto src_file = URI (src_path.to_string () + " src_file" );
245+ REQUIRE_NOTHROW (vfs.touch (src_file));
246+ test_str_size = test_str.size ();
247+ REQUIRE_NOTHROW (vfs.write (src_file, test_str.data (), test_str_size));
248+ require_tiledb_ok (vfs.close_file (src_file));
249+
250+ // copy_file src -> dst using chunked-buffer I/O.
251+ // Note: it doesn't matter if the dst file exists; copy will create on write.
252+ auto dst_file = URI (dst_path.to_string () + " dst_file" );
253+ REQUIRE_NOTHROW (vfs.chunked_buffer_io (src_file, dst_file));
254+ CHECK (vfs.is_file (src_file));
255+
256+ // Validate the contents are the same.
257+ if (test_str_size > 0 ) {
258+ CHECK (vfs.is_file (dst_file));
259+ std::string dst_file_str;
260+ dst_file_str.resize (test_str_size);
261+ require_tiledb_ok (vfs.read_exactly (
262+ dst_file, 0 , (char *)dst_file_str.data (), test_str_size));
263+ CHECK (dst_file_str == test_str);
264+ }
265+
266+ // Clean up.
267+ if (src_path.is_gcs () || src_path.is_s3 () || src_path.is_azure ()) {
268+ REQUIRE_NOTHROW (vfs.remove_bucket (src_path));
269+ REQUIRE (!vfs.is_bucket (src_path));
270+ } else {
271+ REQUIRE_NOTHROW (vfs.remove_dir (src_path));
272+ REQUIRE (!vfs.is_dir (src_path));
273+ }
274+ if (dst_path.is_gcs () || dst_path.is_s3 () || dst_path.is_azure ()) {
275+ REQUIRE_NOTHROW (vfs.remove_bucket (dst_path));
276+ REQUIRE (!vfs.is_bucket (dst_path));
277+ } else {
278+ REQUIRE_NOTHROW (vfs.remove_dir (dst_path));
279+ REQUIRE (!vfs.is_dir (dst_path));
280+ }
281+ }
282+
205283using AllBackends = std::tuple<LocalFsTest, GCSTest, GSTest, S3Test, AzureTest>;
206284TEMPLATE_LIST_TEST_CASE (
207285 " VFS: URI semantics and file management" , " [vfs][uri]" , AllBackends) {
208- TestType fs ({0 });
286+ TestType fs ({});
209287 if (!fs.is_supported ()) {
210288 return ;
211289 }
@@ -218,19 +296,6 @@ TEMPLATE_LIST_TEST_CASE(
218296
219297 URI path = fs.temp_dir_ .add_trailing_slash ();
220298
221- // Set up
222- if (path.is_gcs () || path.is_s3 () || path.is_azure ()) {
223- if (vfs.is_bucket (path)) {
224- REQUIRE_NOTHROW (vfs.remove_bucket (path));
225- }
226- REQUIRE_NOTHROW (vfs.create_bucket (path));
227- } else {
228- if (vfs.is_dir (path)) {
229- REQUIRE_NOTHROW (vfs.remove_dir (path));
230- }
231- REQUIRE_NOTHROW (vfs.create_dir (path));
232- }
233-
234299 /* Create the following file hierarchy:
235300 *
236301 * path/dir1/subdir/file1
@@ -334,6 +399,7 @@ TEMPLATE_LIST_TEST_CASE(
334399 URI (children[1 ].path ().native ()) == ls_subdir.remove_trailing_slash ());
335400 CHECK (children[0 ].file_size () == s.size ());
336401 CHECK (children[1 ].file_size () == 0 ); // Directories don't get a size
402+ paths.clear ();
337403
338404 // Move file
339405 auto file6 = URI (path.to_string () + " file6" );
@@ -349,13 +415,11 @@ TEMPLATE_LIST_TEST_CASE(
349415 CHECK (vfs.is_dir (dir2));
350416 paths.clear ();
351417
352- // Remove files
418+ // Remove files & directories
353419 REQUIRE_NOTHROW (vfs.remove_file (file4));
354420 CHECK (!vfs.is_file (file4));
355421 REQUIRE_NOTHROW (vfs.remove_file (file6));
356422 CHECK (!vfs.is_file (file6));
357-
358- // Remove directories
359423 REQUIRE_NOTHROW (vfs.remove_dir (dir2));
360424 CHECK (!vfs.is_file (file1));
361425 CHECK (!vfs.is_file (file2));
@@ -480,21 +544,6 @@ TEMPLATE_LIST_TEST_CASE("VFS: File I/O", "[vfs][uri][file_io]", AllBackends) {
480544 CHECK_THROWS (vfs.file_size (non_existent));
481545 }
482546
483- // Set up
484- if (path.is_gcs () || path.is_s3 () || path.is_azure ()) {
485- if (vfs.is_bucket (path)) {
486- REQUIRE_NOTHROW (vfs.remove_bucket (path));
487- }
488- REQUIRE_NOTHROW (vfs.create_bucket (path));
489- } else {
490- if (vfs.is_dir (path)) {
491- REQUIRE_NOTHROW (vfs.remove_dir (path));
492- }
493- REQUIRE_NOTHROW (vfs.create_dir (path));
494- // Bucket-specific operations are only valid for object store filesystems.
495- CHECK_THROWS (vfs.create_bucket (path));
496- }
497-
498547 // Prepare buffers
499548 uint64_t buffer_size = multiplier * max_parallel_ops * chunk_size;
500549 auto write_buffer = new char [buffer_size];
@@ -801,6 +850,7 @@ TEST_CASE("VFS: Construct Azure Blob Storage endpoint URIs", "[azure][uri]") {
801850 config.set (" vfs.azure.storage_account_name" , " exampleaccount" ));
802851 require_tiledb_ok (config.set (" vfs.azure.blob_endpoint" , custom_endpoint));
803852 require_tiledb_ok (config.set (" vfs.azure.storage_sas_token" , sas_token));
853+ require_tiledb_ok (config.set (" vfs.azure.is_data_lake_endpoint" , " false" ));
804854 if (sas_token.empty ()) {
805855 // If the SAS token is empty, the VFS will try to connect to Microsoft Entra
806856 // ID to obtain credentials, which can take a long time because of retries.
0 commit comments