1 /**
2  * @file   unit-cppapi-vfs.cc
3  *
4  * @section LICENSE
5  *
6  * The MIT License
7  *
8  * @copyright Copyright (c) 2017-2021 TileDB Inc.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  * @section DESCRIPTION
29  *
30  * Tests the C++ API for the VFS functionality.
31  */
32 
33 #include "catch.hpp"
34 #include "tiledb/sm/cpp_api/tiledb"
35 
36 #ifdef _WIN32
37 #include "tiledb/sm/filesystem/win.h"
38 #else
39 #include "tiledb/sm/filesystem/posix.h"
40 #endif
41 
42 TEST_CASE("C++ API: Test VFS ls", "[cppapi][cppapi-vfs][cppapi-vfs-ls]") {
43   using namespace tiledb;
44   Context ctx;
45   VFS vfs(ctx);
46 #ifdef _WIN32
47   std::string path = sm::Win::current_dir() + "\\vfs_test\\";
48 #else
49   std::string path =
50       std::string("file://") + sm::Posix::current_dir() + "/vfs_test/";
51 #endif
52 
53   // Clean up
54   if (vfs.is_dir(path))
55     vfs.remove_dir(path);
56 
57   std::string dir = path + "ls_dir";
58   std::string file = dir + "/file";
59   std::string file2 = dir + "/file2";
60   std::string subdir = dir + "/subdir";
61   std::string subdir2 = dir + "/subdir2";
62   std::string subdir_file = subdir + "/file";
63   std::string subdir_file2 = subdir2 + "/file2";
64 
65   // Create directories and files
66   vfs.create_dir(path);
67   vfs.create_dir(dir);
68   vfs.create_dir(subdir);
69   vfs.create_dir(subdir2);
70   vfs.touch(file);
71   vfs.touch(file2);
72   vfs.touch(subdir_file);
73   vfs.touch(subdir_file2);
74 
75   // List
76   std::vector<std::string> children = vfs.ls(dir);
77 
78 #ifdef _WIN32
79   // Normalization only for Windows
80   file = tiledb::sm::Win::uri_from_path(file);
81   file2 = tiledb::sm::Win::uri_from_path(file2);
82   subdir = tiledb::sm::Win::uri_from_path(subdir);
83   subdir2 = tiledb::sm::Win::uri_from_path(subdir2);
84 #endif
85 
86   // Check results
87   std::sort(children.begin(), children.end());
88   REQUIRE(children.size() == 4);
89   CHECK(children[0] == file);
90   CHECK(children[1] == file2);
91   CHECK(children[2] == subdir);
92   CHECK(children[3] == subdir2);
93 
94   // Clean up
95   vfs.remove_dir(path);
96 }
97 
98 TEST_CASE(
99     "C++ API: Test VFS dir size", "[cppapi][cppapi-vfs][cppapi-vfs-dir-size]") {
100   using namespace tiledb;
101   Context ctx;
102   VFS vfs(ctx);
103 
104 #ifdef _WIN32
105   std::string path = sm::Win::current_dir() + "\\vfs_test\\";
106 #else
107   std::string path =
108       std::string("file://") + sm::Posix::current_dir() + "/vfs_test/";
109 #endif
110 
111   // Clean up
112   if (vfs.is_dir(path))
113     vfs.remove_dir(path);
114 
115   std::string dir = path + "ls_dir";
116   std::string file = dir + "/file";
117   std::string file2 = dir + "/file2";
118   std::string subdir = dir + "/subdir";
119   std::string subdir2 = dir + "/subdir2";
120   std::string subdir_file = subdir + "/file";
121   std::string subdir_file2 = subdir + "/file2";
122 
123   // Create directories and files
124   vfs.create_dir(path);
125   vfs.create_dir(dir);
126   vfs.create_dir(subdir);
127   vfs.create_dir(subdir2);
128   vfs.touch(file);
129   vfs.touch(file2);
130   vfs.touch(subdir_file);
131   vfs.touch(subdir_file2);
132 
133   // Write to file
134   tiledb::VFS::filebuf fbuf(vfs);
135   fbuf.open(file, std::ios::out);
136   std::ostream os(&fbuf);
137   std::string s1 = "abcd";
138   os.write(s1.data(), s1.size());
139   fbuf.close();
140 
141   // Write to file2
142   fbuf.open(file2, std::ios::out);
143   std::ostream os2(&fbuf);
144   std::string s2 = "abcdefgh";
145   os2.write(s2.data(), s2.size());
146   fbuf.close();
147 
148   // Write to subdir_file
149   fbuf.open(subdir_file, std::ios::out);
150   std::ostream os3(&fbuf);
151   std::string s3 = "a";
152   os3.write(s3.data(), s3.size());
153   fbuf.close();
154 
155   // Write to subdir_file2
156   fbuf.open(subdir_file2, std::ios::out);
157   std::ostream os4(&fbuf);
158   std::string s4 = "ab";
159   os4.write(s4.data(), s4.size());
160   fbuf.close();
161 
162   // Get directory size
163   auto dir_size = vfs.dir_size(path);
164   CHECK(dir_size == 15);
165 
166   // Clean up
167   vfs.remove_dir(path);
168 }
169 
170 TEST_CASE(
171     "C++ API: Test VFS copy file",
172     "[cppapi][cppapi-vfs][cppapi-vfs-copy-file]") {
173   using namespace tiledb;
174   Context ctx;
175   VFS vfs(ctx);
176 
177 #ifndef _WIN32
178   std::string path =
179       std::string("file://") + sm::Posix::current_dir() + "/vfs_test/";
180 
181   // Clean up
182   if (vfs.is_dir(path))
183     vfs.remove_dir(path);
184 
185   std::string dir = path + "ls_dir";
186   std::string file = dir + "/file";
187   std::string file2 = dir + "/file2";
188 
189   // Create directories and files
190   vfs.create_dir(path);
191   vfs.create_dir(dir);
192   vfs.touch(file);
193 
194   // Write to file
195   tiledb::VFS::filebuf fbuf(vfs);
196   fbuf.open(file, std::ios::out);
197   std::ostream os(&fbuf);
198   std::string s1 = "abcd";
199   os.write(s1.data(), s1.size());
200 
201   // Copy file when running on POSIX
202   vfs.copy_file(file, file2);
203   REQUIRE(vfs.is_file(file2));
204 
205   fbuf.open(file, std::ios::in);
206   std::istream is1(&fbuf);
207   if (!is1.good()) {
208     std::cerr << "Error opening file.\n";
209     return;
210   }
211   std::string s2;
212   s2.resize(vfs.file_size(file));
213   is1.read((char*)s2.data(), vfs.file_size(file));
214   fbuf.close();
215 
216   fbuf.open(file2, std::ios::in);
217   std::istream is2(&fbuf);
218   if (!is2.good()) {
219     std::cerr << "Error opening file.\n";
220     return;
221   }
222   std::string s3;
223   s3.resize(vfs.file_size(file2));
224   is2.read((char*)s3.data(), vfs.file_size(file2));
225   fbuf.close();
226 
227   REQUIRE(s2 == s3);
228 
229   // Clean up
230   vfs.remove_dir(path);
231 
232 #endif
233 }
234 
235 TEST_CASE(
236     "C++ API: Test VFS copy directory",
237     "[cppapi][cppapi-vfs][cppapi-vfs-copy-dir]") {
238   using namespace tiledb;
239   Context ctx;
240   VFS vfs(ctx);
241 
242 #ifndef _WIN32
243   std::string path =
244       std::string("file://") + sm::Posix::current_dir() + "/vfs_test/";
245 
246   // Clean up
247   if (vfs.is_dir(path))
248     vfs.remove_dir(path);
249 
250   std::string dir = path + "ls_dir";
251   std::string dir2 = path + "ls_dir2";
252   std::string file = dir + "/file";
253   std::string file2 = dir + "/file2";
254   std::string subdir = dir + "/subdir";
255   std::string subdir2 = dir + "/subdir2";
256   std::string sub_subdir = subdir + "/subdir";
257   std::string subdir_file = subdir + "/file";
258   std::string subdir_file2 = subdir + "/file2";
259   std::string sub_subdir_file = sub_subdir + "/file";
260 
261   std::string file3 = dir2 + "/file";
262   std::string file4 = dir2 + "/file2";
263   std::string subdir3 = dir2 + "/subdir";
264   std::string subdir4 = dir2 + "/subdir2";
265   std::string sub_subdir2 = subdir3 + "/subdir";
266   std::string subdir_file3 = subdir3 + "/file";
267   std::string subdir_file4 = subdir3 + "/file2";
268   std::string sub_subdir_file2 = sub_subdir2 + "/file";
269 
270   // Create directories and files
271   vfs.create_dir(path);
272   vfs.create_dir(dir);
273   vfs.create_dir(subdir);
274   vfs.create_dir(sub_subdir);
275   vfs.create_dir(subdir2);
276   vfs.touch(file);
277   vfs.touch(file2);
278   vfs.touch(subdir_file);
279   vfs.touch(subdir_file2);
280   vfs.touch(sub_subdir_file);
281 
282   // Write to files
283   tiledb::VFS::filebuf fbuf(vfs);
284   fbuf.open(file, std::ios::out);
285   std::ostream os(&fbuf);
286   std::string s1 = "abcd";
287   os.write(s1.data(), s1.size());
288 
289   fbuf.open(file2, std::ios::out);
290   std::ostream os1(&fbuf);
291   std::string s2 = "efgh";
292   os1.write(s2.data(), s2.size());
293 
294   fbuf.open(subdir_file, std::ios::out);
295   std::ostream os2(&fbuf);
296   std::string s3 = "ijkl";
297   os2.write(s3.data(), s3.size());
298 
299   fbuf.open(subdir_file2, std::ios::out);
300   std::ostream os3(&fbuf);
301   std::string s4 = "mnop";
302   os3.write(s4.data(), s4.size());
303   fbuf.close();
304 
305   fbuf.open(sub_subdir_file, std::ios::out);
306   std::ostream os4(&fbuf);
307   std::string s5 = "qrst";
308   os3.write(s5.data(), s5.size());
309   fbuf.close();
310 
311   // Copy directory when running on POSIX
312   vfs.copy_dir(dir, dir2);
313   REQUIRE(vfs.is_dir(dir2));
314 
315   // Check that ls trees are correct
316   // and that all files / dirs exist as expected
317   std::vector<std::string> dir_vector = vfs.ls(dir);
318   std::vector<std::string> dir2_vector = vfs.ls(dir2);
319   while (!dir_vector.empty() || !dir2_vector.empty()) {
320     std::string dir_file_name_abs = dir_vector.front();
321     std::string dir_file_name = dir_file_name_abs.substr(dir.length() + 1);
322     std::string dir2_file_name_abs = dir2_vector.front();
323     std::string dir2_file_name = dir2_file_name_abs.substr(dir2.length() + 1);
324 
325     if (vfs.is_dir(dir_file_name_abs)) {
326       REQUIRE(vfs.is_dir(dir_file_name_abs));
327       std::vector<std::string> dir_vector2 = vfs.ls(dir_file_name_abs);
328       dir_vector.insert(
329           dir_vector.end(), dir_vector2.begin(), dir_vector2.end());
330     } else {
331       REQUIRE(vfs.is_file(dir_file_name_abs));
332     }
333     if (vfs.is_dir(dir2_file_name_abs)) {
334       REQUIRE(vfs.is_dir(dir2_file_name_abs));
335       std::vector<std::string> dir2_vector2 = vfs.ls(dir2_file_name_abs);
336       dir2_vector.insert(
337           dir2_vector.end(), dir2_vector2.begin(), dir2_vector2.end());
338     } else {
339       REQUIRE(vfs.is_file(dir2_file_name_abs));
340     }
341     REQUIRE(dir_file_name == dir2_file_name);
342     dir_vector.erase(dir_vector.begin());
343     dir2_vector.erase(dir2_vector.begin());
344   }
345 
346   // Check that files in dir2 are equal to their
347   // corresponding file in dir
348   fbuf.open(file, std::ios::in);
349   std::istream is1(&fbuf);
350   if (!is1.good()) {
351     std::cerr << "Error opening file.\n";
352     return;
353   }
354   std::string s6;
355   s6.resize(vfs.file_size(file));
356   is1.read((char*)s6.data(), vfs.file_size(file));
357   fbuf.close();
358   fbuf.open(file3, std::ios::in);
359   std::istream is2(&fbuf);
360   if (!is2.good()) {
361     std::cerr << "Error opening file.\n";
362     return;
363   }
364   std::string s7;
365   s7.resize(vfs.file_size(file3));
366   is2.read((char*)s7.data(), vfs.file_size(file3));
367   fbuf.close();
368   REQUIRE(s6 == s7);
369 
370   fbuf.open(file2, std::ios::in);
371   std::istream is3(&fbuf);
372   if (!is3.good()) {
373     std::cerr << "Error opening file.\n";
374     return;
375   }
376   std::string s8;
377   s8.resize(vfs.file_size(file2));
378   is3.read((char*)s8.data(), vfs.file_size(file2));
379   fbuf.close();
380   fbuf.open(file4, std::ios::in);
381   std::istream is4(&fbuf);
382   if (!is4.good()) {
383     std::cerr << "Error opening file.\n";
384     return;
385   }
386   std::string s9;
387   s9.resize(vfs.file_size(file4));
388   is4.read((char*)s9.data(), vfs.file_size(file4));
389   fbuf.close();
390   REQUIRE(s8 == s9);
391 
392   fbuf.open(subdir_file, std::ios::in);
393   std::istream is5(&fbuf);
394   if (!is5.good()) {
395     std::cerr << "Error opening file.\n";
396     return;
397   }
398   std::string s10;
399   s10.resize(vfs.file_size(subdir_file));
400   is5.read((char*)s10.data(), vfs.file_size(subdir_file));
401   fbuf.close();
402   fbuf.open(subdir_file3, std::ios::in);
403   std::istream is6(&fbuf);
404   if (!is6.good()) {
405     std::cerr << "Error opening file.\n";
406     return;
407   }
408   std::string s11;
409   s11.resize(vfs.file_size(subdir_file3));
410   is6.read((char*)s11.data(), vfs.file_size(subdir_file3));
411   fbuf.close();
412   REQUIRE(s10 == s11);
413 
414   fbuf.open(subdir_file2, std::ios::in);
415   std::istream is7(&fbuf);
416   if (!is7.good()) {
417     std::cerr << "Error opening file.\n";
418     return;
419   }
420   std::string s12;
421   s12.resize(vfs.file_size(subdir_file2));
422   is7.read((char*)s12.data(), vfs.file_size(subdir_file2));
423   fbuf.close();
424   fbuf.open(subdir_file4, std::ios::in);
425   std::istream is8(&fbuf);
426   if (!is8.good()) {
427     std::cerr << "Error opening file.\n";
428     return;
429   }
430   std::string s13;
431   s13.resize(vfs.file_size(subdir_file4));
432   is8.read((char*)s13.data(), vfs.file_size(subdir_file4));
433   fbuf.close();
434   REQUIRE(s12 == s13);
435 
436   fbuf.open(sub_subdir_file, std::ios::in);
437   std::istream is9(&fbuf);
438   if (!is9.good()) {
439     std::cerr << "Error opening file.\n";
440     return;
441   }
442   std::string s14;
443   s14.resize(vfs.file_size(sub_subdir_file));
444   is9.read((char*)s14.data(), vfs.file_size(sub_subdir_file));
445   fbuf.close();
446   fbuf.open(sub_subdir_file2, std::ios::in);
447   std::istream is10(&fbuf);
448   if (!is10.good()) {
449     std::cerr << "Error opening file.\n";
450     return;
451   }
452   std::string s15;
453   s15.resize(vfs.file_size(sub_subdir_file2));
454   is10.read((char*)s15.data(), vfs.file_size(sub_subdir_file2));
455   fbuf.close();
456   REQUIRE(s14 == s15);
457 
458   // Clean up
459   vfs.remove_dir(path);
460 
461 #endif
462 }