mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
fileUtils_test.cpp
Go to the documentation of this file.
1/** \file fileUtils_test.cpp
2 * \brief Tests filesystem utilities.
3 */
4#include "../../catch2/catch.hpp"
5
6#define MX_NO_ERROR_REPORTS
7
8#include <fstream>
10
11#undef ioutils_fileUtils_hpp
12#define MXLIBTEST_NAMESPACE MXLIBTEST_DIREXISTSIS_ISEXISTSERR_ns
13#define MXLIBTEST_DIREXISTSIS_ISEXISTSERR
15#undef MXLIBTEST_NAMESPACE
16#undef MXLIBTEST_DIREXISTSIS_ISEXISTSERR
17
18#undef ioutils_fileUtils_hpp
19#define MXLIBTEST_NAMESPACE MXLIBTEST_DIREXISTSIS_ISDIRERR_ns
20#define MXLIBTEST_DIREXISTSIS_ISDIRERR
22#undef MXLIBTEST_NAMESPACE
23#undef MXLIBTEST_DIREXISTSIS_ISDIRERR
24
25#undef ioutils_fileUtils_hpp
26#define MXLIBTEST_NAMESPACE MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns
27#define MXLIB_CATCH_ALL_EXCEPTIONS
29#undef MXLIBTEST_NAMESPACE
30#undef MXLIB_CATCH_ALL_EXCEPTIONS
31
32/** \cond */
33namespace
34{
35
36enum class injectedException
37{
38 none,
39 badAlloc,
41 standard,
42 unknown
43};
44
45mx::ioutils::fileUtilsDetail::operation failingOperation = mx::ioutils::fileUtilsDetail::operation::stringToPath;
46injectedException injectedFailure = injectedException::none;
47std::error_code injectedErrorCode;
48
49void injectFileUtilsFailure( mx::ioutils::fileUtilsDetail::operation operation )
50{
51 if( operation != failingOperation )
52 {
53 return;
54 }
55
56 switch( injectedFailure )
57 {
58 case injectedException::badAlloc:
59 throw std::bad_alloc();
60 case injectedException::filesystem:
61 throw std::filesystem::filesystem_error( "injected fileUtils failure",
62 std::make_error_code( std::errc::io_error ) );
63 case injectedException::standard:
64 throw std::runtime_error( "injected fileUtils failure" );
65 case injectedException::unknown:
66 throw 1;
67 case injectedException::none:
68 return;
69 }
70}
71
72void injectFileUtilsErrorCode( mx::ioutils::fileUtilsDetail::operation operation, std::error_code &errorCode )
73{
74 if( operation == mx::ioutils::fileUtilsDetail::operation::createDirectories && injectedErrorCode )
75 {
76 errorCode = injectedErrorCode;
77 }
78}
79
80class fileUtilsHookGuard
81{
82 public:
83 fileUtilsHookGuard()
84 : m_saved( mx::ioutils::fileUtilsDetail::operationHook() ),
85 m_savedErrorCode( mx::ioutils::fileUtilsDetail::errorCodeHook() )
86 {
87 injectedFailure = injectedException::none;
88 injectedErrorCode.clear();
89 mx::ioutils::fileUtilsDetail::operationHook() = injectFileUtilsFailure;
90 mx::ioutils::fileUtilsDetail::errorCodeHook() = injectFileUtilsErrorCode;
91 }
92
93 ~fileUtilsHookGuard()
94 {
95 mx::ioutils::fileUtilsDetail::operationHook() = m_saved;
96 mx::ioutils::fileUtilsDetail::errorCodeHook() = m_savedErrorCode;
97 }
98
99 private:
100 mx::ioutils::fileUtilsDetail::operationHookT m_saved;
101 mx::ioutils::fileUtilsDetail::errorCodeHookT m_savedErrorCode;
102};
103
104} // namespace
105/** \endcond */
106
107namespace unitTest
108{
109namespace ioutilsTest
110{
111namespace fileUtilsTest
112{
113
114/// Checking existence and whether or not a path is a directory
115/**
116 * \ingroup fileUtils_unit_tests
117 */
118TEST_CASE( "Checking existence and whether or not a path is a directory", "[ioutils::fileUtils]" )
119{
120 SECTION( "directory exists and is a directory" )
121 {
122 std::filesystem::create_directories( "/tmp/fileUtils_test/dir_is_exists/" );
123
124 mx::error_t errc;
125 bool isdir = mx::ioutils::dir_exists_is( "/tmp/fileUtils_test/dir_is_exists/", errc );
126
127 REQUIRE( errc == mx::error_t::noerror );
128 REQUIRE( isdir == true );
129 }
130
131 SECTION( "directory does not exist" )
132 {
133 mx::error_t errc;
134 bool isdir = mx::ioutils::dir_exists_is( "/tmp/fileUtils_test/dir_is_exists0/", errc );
135
136 REQUIRE( errc == mx::error_t::noerror );
137 REQUIRE( isdir == false );
138 }
139
140 SECTION( "directory is a file does not exist" )
141 {
142 std::filesystem::create_directories( "/tmp/fileUtils_test/dir_is_exists/" );
143 std::ofstream fout;
144 fout.open( "/tmp/fileUtils_test/dir_is_exists/test.txt" );
145 fout << "test";
146 fout.close();
147
148 mx::error_t errc;
149 bool isdir = mx::ioutils::dir_exists_is( "/tmp/fileUtils_test/dir_is_exists/test.txt", errc );
150
151 REQUIRE( errc == mx::error_t::noerror );
152 REQUIRE( isdir == false );
153 }
154
155 SECTION( "error from exists" )
156 {
157 mx::error_t errc;
158 bool isdir =
159 mx::ioutils::MXLIBTEST_DIREXISTSIS_ISEXISTSERR_ns::dir_exists_is( "/tmp/fileUtils_test/dir_is_exists/",
160 errc );
161
162 REQUIRE( errc == mx::error_t::eexist );
163 REQUIRE( isdir == false );
164 }
165
166 SECTION( "error from is_directory" )
167 {
168 mx::error_t errc;
169 bool isdir =
170 mx::ioutils::MXLIBTEST_DIREXISTSIS_ISDIRERR_ns::dir_exists_is( "/tmp/fileUtils_test/dir_is_exists/", errc );
171
172 REQUIRE( errc == mx::error_t::eacces );
173 REQUIRE( isdir == false );
174 }
175}
176
177/// Verify creation of sequential file names
178/**
179 * \ingroup fileUtils_unit_tests
180 */
181TEST_CASE( "creating sequential filenames", "[ioutils::fileUtils]" )
182{
183 SECTION( "a varying numbers of digits desired" )
184 {
185 SECTION( "default 4 digits, starting at 0" )
186 {
187 std::string fname = mx::ioutils::getSequentialFilename( "base", ".test" );
188 REQUIRE( fname == "base0000.test" );
189 }
190
191 SECTION( "default 4 digits, starting at 1" )
192 {
193 std::string fname = mx::ioutils::getSequentialFilename( "base", ".test", 1 );
194 REQUIRE( fname == "base0001.test" );
195 }
196
197 SECTION( "default 7 digits, starting at 0" )
198 {
199 std::string fname = mx::ioutils::getSequentialFilename( "base", ".test", 0, 7 );
200 REQUIRE( fname == "base0000000.test" );
201 }
202
203 SECTION( "default 7 digits, starting at 1" )
204 {
205 std::string fname = mx::ioutils::getSequentialFilename( "base", ".test", 1, 7 );
206 REQUIRE( fname == "base0000001.test" );
207 }
208
209 SECTION( "default 12 digits, starting at 0" )
210 {
211 std::string fname = mx::ioutils::getSequentialFilename( "base", ".test", 0, 12 );
212 REQUIRE( fname == "base000000000000.test" );
213 }
214
215 SECTION( "default 12 digits, starting at 1" )
216 {
217 std::string fname = mx::ioutils::getSequentialFilename( "base", ".test", 1, 12 );
218 REQUIRE( fname == "base000000000001.test" );
219 }
220 }
221}
222
223/** \brief Verifies path decomposition and basename transformations used by file-list consumers.
224 *
225 * \ingroup fileUtils_unit_tests
226 */
227TEST_CASE( "decomposing and transforming file paths", "[ioutils::fileUtils]" )
228{
229 const std::string filename{ "/tmp/hciReduce/list/science.frame.fits" };
230
231 REQUIRE( mx::ioutils::pathStem( filename ) == "science.frame" );
232 REQUIRE( mx::ioutils::pathFilename( filename ) == "science.frame.fits" );
233 REQUIRE( mx::ioutils::parentPath( filename ) == "/tmp/hciReduce/list" );
234 REQUIRE( mx::ioutils::fileNamePrependAppend( filename, "pre_", "_post" ) ==
235 "/tmp/hciReduce/list/pre_science.frame_post.fits" );
236 REQUIRE( mx::ioutils::fileNameAppend( filename, "_coadd" ) == "/tmp/hciReduce/list/science.frame_coadd.fits" );
237 REQUIRE( mx::ioutils::fileNamePrepend( filename, "rdi_" ) == "/tmp/hciReduce/list/rdi_science.frame.fits" );
238}
239
240/** \brief Verifies nested output-directory creation used before HCI preprocessed-image writes.
241 *
242 * Exercises mx::ioutils::parentPath and mx::ioutils::createDirectories with a new nested directory and an
243 * idempotent repeat call.
244 *
245 * \ingroup fileUtils_unit_tests
246 */
247TEST_CASE( "creating HCI output directories", "[ioutils::fileUtils][hciReduce]" )
248{
249 fileUtilsHookGuard hookGuard;
250 const std::filesystem::path root = std::filesystem::temp_directory_path() / "mxlib-hciReduce-output-directory";
251 const std::filesystem::path prefix = root / "preprocessed" / "target";
252
253 std::filesystem::remove_all( root );
254
255 const std::string directory = mx::ioutils::parentPath( prefix.string() );
256 REQUIRE( mx::ioutils::createDirectories( directory ) == mx::error_t::noerror );
257 REQUIRE( std::filesystem::is_directory( directory ) );
258 REQUIRE( mx::ioutils::createDirectories( directory ) == mx::error_t::noerror );
259
260 injectedErrorCode = std::error_code( 9999, std::generic_category() );
262
263 std::filesystem::remove_all( root );
264}
265
266/** \brief Verifies HCI final-image filename selection skips existing numbered outputs.
267 *
268 * Exercises mx::ioutils::getSequentialFilename over existing FITS outputs as used when HCI does not request an
269 * exact final-image name.
270 *
271 * \ingroup fileUtils_unit_tests
272 */
273TEST_CASE( "selecting sequential HCI final-image filenames", "[ioutils::fileUtils][hciReduce]" )
274{
275 const std::filesystem::path root = std::filesystem::temp_directory_path() / "mxlib-hciReduce-final-output";
276 const std::string basename = ( root / "final" ).string();
277
278 std::filesystem::remove_all( root );
279 std::filesystem::create_directories( root );
280 std::ofstream( basename + "0000.fits" ).close();
281 std::ofstream( basename + "0001.fits" ).close();
282
283 REQUIRE( mx::ioutils::getSequentialFilename( basename, ".fits" ) == basename + "0002.fits" );
284
285 std::filesystem::remove_all( root );
286}
287
288void createfiles( const std::string &basedir )
289{
290 std::filesystem::create_directories( basedir );
291
292 std::ofstream fout;
293
294 fout.open( basedir + "/file_txt_1.txt" );
295 fout.close();
296 fout.open( basedir + "/file_xtx_2.txt" );
297 fout.close();
298 fout.open( basedir + "/elif_txt_3.txt" );
299 fout.close();
300 fout.open( basedir + "/file_xtx_4.txt" );
301 fout.close();
302 fout.open( basedir + "/elif_txt_5.txt" );
303 fout.close();
304 fout.open( basedir + "/elif_xtx_1.xxx" );
305 fout.close();
306 fout.open( basedir + "/elif_txt_2.xxx" );
307 fout.close();
308 fout.open( basedir + "/file_xtx_3.xxx" );
309 fout.close();
310 fout.open( basedir + "/elif_txt_4.xxx" );
311 fout.close();
312 fout.open( basedir + "/file_xtx_5.xxx" );
313 fout.close();
314}
315
316/// Getting a list of files
317/**
318 * Tests that files are read according to the specification and sorted. Also tests basic errors.
319 *
320 * \ingroup fileUtils_unit_tests
321 */
322TEST_CASE( "Getting a list of files", "[ioutils::fileUtils]" )
323{
324 std::string basedir = "/tmp/fileUtils_test/dir";
325 createfiles( basedir );
326
327 SECTION( "a directory with files of various type and names" )
328 {
329 SECTION( "directory only" )
330 {
331 std::vector<std::string> fnames;
332 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir, "", "", "" );
333
334 REQUIRE( errc == mx::error_t::noerror );
335 REQUIRE( fnames.size() == 10 );
336 }
337
338 SECTION( "single extension with ." )
339 {
340 std::vector<std::string> fnames;
341 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir, "", "", ".txt" );
342
343 REQUIRE( errc == mx::error_t::noerror );
344 REQUIRE( fnames.size() == 5 );
345 REQUIRE( fnames[0] == basedir + "/elif_txt_3.txt" );
346 REQUIRE( fnames[1] == basedir + "/elif_txt_5.txt" );
347 REQUIRE( fnames[2] == basedir + "/file_txt_1.txt" );
348 REQUIRE( fnames[3] == basedir + "/file_xtx_2.txt" );
349 REQUIRE( fnames[4] == basedir + "/file_xtx_4.txt" );
350 }
351
352 SECTION( "single extension without ." )
353 {
354 std::vector<std::string> fnames;
355 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir, "", "", "txt" );
356
357 REQUIRE( errc == mx::error_t::noerror );
358 REQUIRE( fnames.size() == 5 );
359 REQUIRE( fnames[0] == basedir + "/elif_txt_3.txt" );
360 REQUIRE( fnames[1] == basedir + "/elif_txt_5.txt" );
361 REQUIRE( fnames[2] == basedir + "/file_txt_1.txt" );
362 REQUIRE( fnames[3] == basedir + "/file_xtx_2.txt" );
363 REQUIRE( fnames[4] == basedir + "/file_xtx_4.txt" );
364 }
365
366 SECTION( "different extension with ." )
367 {
368 std::vector<std::string> fnames;
369 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir, "", "", ".xxx" );
370
371 REQUIRE( errc == mx::error_t::noerror );
372 REQUIRE( fnames.size() == 5 );
373 REQUIRE( fnames[0] == basedir + "/elif_txt_2.xxx" );
374 REQUIRE( fnames[1] == basedir + "/elif_txt_4.xxx" );
375 REQUIRE( fnames[2] == basedir + "/elif_xtx_1.xxx" );
376 REQUIRE( fnames[3] == basedir + "/file_xtx_3.xxx" );
377 REQUIRE( fnames[4] == basedir + "/file_xtx_5.xxx" );
378 }
379
380 SECTION( "a prefix, no extension" )
381 {
382 std::vector<std::string> fnames;
383 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir, "file", "", "" );
384
385 REQUIRE( errc == mx::error_t::noerror );
386 REQUIRE( fnames.size() == 5 );
387 REQUIRE( fnames[0] == basedir + "/file_txt_1.txt" );
388 REQUIRE( fnames[1] == basedir + "/file_xtx_2.txt" );
389 REQUIRE( fnames[2] == basedir + "/file_xtx_3.xxx" );
390 REQUIRE( fnames[3] == basedir + "/file_xtx_4.txt" );
391 REQUIRE( fnames[4] == basedir + "/file_xtx_5.xxx" );
392 }
393
394 SECTION( "a prefix, with extension with ." )
395 {
396 std::vector<std::string> fnames;
397 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir, "file", "", ".txt" );
398
399 REQUIRE( errc == mx::error_t::noerror );
400 REQUIRE( fnames.size() == 3 );
401 REQUIRE( fnames[0] == basedir + "/file_txt_1.txt" );
402 REQUIRE( fnames[1] == basedir + "/file_xtx_2.txt" );
403 REQUIRE( fnames[2] == basedir + "/file_xtx_4.txt" );
404 }
405
406 SECTION( "a substr alone" )
407 {
408 std::vector<std::string> fnames;
409 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir, "", "xtx", "" );
410
411 REQUIRE( errc == mx::error_t::noerror );
412 REQUIRE( fnames.size() == 5 );
413 REQUIRE( fnames[0] == basedir + "/elif_xtx_1.xxx" );
414 REQUIRE( fnames[1] == basedir + "/file_xtx_2.txt" );
415 REQUIRE( fnames[2] == basedir + "/file_xtx_3.xxx" );
416 REQUIRE( fnames[3] == basedir + "/file_xtx_4.txt" );
417 REQUIRE( fnames[4] == basedir + "/file_xtx_5.xxx" );
418 }
419
420 SECTION( "a substr which is actually the prefix" )
421 {
422 std::vector<std::string> fnames;
423 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir, "", "file", "" );
424
425 REQUIRE( errc == mx::error_t::noerror );
426 REQUIRE( fnames.size() == 0 );
427 }
428
429 SECTION( "a prefix and a substr which is actually the prefix" )
430 {
431 std::vector<std::string> fnames;
432 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir, "file", "file", "" );
433
434 REQUIRE( errc == mx::error_t::noerror );
435 REQUIRE( fnames.size() == 0 );
436 }
437
438 SECTION( "a prefix, and a substr, no extension" )
439 {
440 std::vector<std::string> fnames;
441 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir, "file", "xtx", "" );
442
443 REQUIRE( errc == mx::error_t::noerror );
444 REQUIRE( fnames.size() == 4 );
445 REQUIRE( fnames[0] == basedir + "/file_xtx_2.txt" );
446 REQUIRE( fnames[1] == basedir + "/file_xtx_3.xxx" );
447 REQUIRE( fnames[2] == basedir + "/file_xtx_4.txt" );
448 REQUIRE( fnames[3] == basedir + "/file_xtx_5.xxx" );
449 }
450
451 SECTION( "a prefix, a substr, and an extension with ." )
452 {
453 std::vector<std::string> fnames;
454 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir, "file", "xtx", ".txt" );
455
456 REQUIRE( errc == mx::error_t::noerror );
457 REQUIRE( fnames.size() == 2 );
458 REQUIRE( fnames[0] == basedir + "/file_xtx_2.txt" );
459 REQUIRE( fnames[1] == basedir + "/file_xtx_4.txt" );
460 }
461 }
462
463 SECTION( "a directory which does not exist" )
464 {
465 std::vector<std::string> fnames;
466 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir + "nf", "file", "xtx", ".txt" );
467
468 REQUIRE( errc == mx::error_t::dirnotfound );
469 }
470
471 SECTION( "a directory which is a file" )
472 {
473 std::vector<std::string> fnames;
474 mx::error_t errc = mx::ioutils::getFileNames( fnames, basedir + "/file_xtx_2.txt", "file", "xtx", ".txt" );
475
476 REQUIRE( errc == mx::error_t::invalidarg );
477 }
478
479 SECTION( "a directory which does not exist, verbose = vv" )
480 {
481 std::vector<std::string> fnames;
482 mx::error_t errc = mx::ioutils::getFileNames<mx::verbose::vv>( fnames, basedir + "nf", "file", "xtx", ".txt" );
483
484 REQUIRE( errc == mx::error_t::dirnotfound );
485 }
486
487 SECTION( "a directory which is a file, verbose = vv" )
488 {
489 std::vector<std::string> fnames;
490 mx::error_t errc =
491 mx::ioutils::getFileNames<mx::verbose::vv>( fnames, basedir + "/file_xtx_2.txt", "file", "xtx", ".txt" );
492
493 REQUIRE( errc == mx::error_t::invalidarg );
494 }
495
496 SECTION( "a directory which does not exist, verbose = v" )
497 {
498 std::vector<std::string> fnames;
499 mx::error_t errc = mx::ioutils::getFileNames<mx::verbose::v>( fnames, basedir + "nf", "file", "xtx", ".txt" );
500
501 REQUIRE( errc == mx::error_t::dirnotfound );
502 }
503
504 SECTION( "a directory which is a file, verbose = v" )
505 {
506 std::vector<std::string> fnames;
507 mx::error_t errc =
508 mx::ioutils::getFileNames<mx::verbose::v>( fnames, basedir + "/file_xtx_2.txt", "file", "xtx", ".txt" );
509
510 REQUIRE( errc == mx::error_t::invalidarg );
511 }
512
513 SECTION( "a directory which does not exist, verbose = o" )
514 {
515 std::vector<std::string> fnames;
516 mx::error_t errc = mx::ioutils::getFileNames<mx::verbose::o>( fnames, basedir + "nf", "file", "xtx", ".txt" );
517
518 REQUIRE( errc == mx::error_t::dirnotfound );
519 }
520
521 SECTION( "a directory which is a file, verbose = o" )
522 {
523 std::vector<std::string> fnames;
524 mx::error_t errc =
525 mx::ioutils::getFileNames<mx::verbose::o>( fnames, basedir + "/file_xtx_2.txt", "file", "xtx", ".txt" );
526
527 REQUIRE( errc == mx::error_t::invalidarg );
528 }
529}
530
531/** \brief Verifies that filename filters safely reject names shorter than their search term.
532 *
533 * \ingroup fileUtils_unit_tests
534 */
535TEST_CASE( "Filtering short filenames", "[ioutils::fileUtils]" )
536{
537 const std::string directory{ "/tmp/fileUtils_test/short_names" };
538 std::filesystem::create_directories( directory );
539 std::ofstream{ directory + "/a" }.close();
540
541 std::vector<std::string> filenames;
542 REQUIRE( mx::ioutils::getFileNames( filenames, directory, "prefix", "", "" ) == mx::error_t::noerror );
543 REQUIRE( filenames.empty() );
544
545 REQUIRE( mx::ioutils::getFileNames( filenames, directory, "", "sub", "" ) == mx::error_t::noerror );
546 REQUIRE( filenames.empty() );
547}
548
549/** \brief Verifies path conversion translates or propagates each documented exception category.
550 *
551 * \ingroup fileUtils_unit_tests
552 */
553TEST_CASE( "Handling path conversion exceptions", "[ioutils::fileUtils]" )
554{
555 fileUtilsHookGuard guard;
556 failingOperation = mx::ioutils::fileUtilsDetail::operation::stringToPath;
557 std::filesystem::path path;
558
559 SECTION( "allocation failure" )
560 {
561 injectedFailure = injectedException::badAlloc;
562 REQUIRE_THROWS_AS( mx::ioutils::string2path<mx::verbose::vv>( path, "unused" ),
564 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::string2path<mx::verbose::vv>( path, "unused" ) ==
566 }
567
568 SECTION( "filesystem failure" )
569 {
570 injectedFailure = injectedException::filesystem;
571 REQUIRE_THROWS_AS( mx::ioutils::string2path<mx::verbose::vv>( path, "unused" ),
573 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::string2path<mx::verbose::vv>( path, "unused" ) ==
575 }
576
577 SECTION( "standard failure" )
578 {
579 injectedFailure = injectedException::standard;
580 REQUIRE_THROWS_AS( mx::ioutils::string2path<mx::verbose::vv>( path, "unused" ),
582 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::string2path<mx::verbose::vv>( path, "unused" ) ==
584 }
585
586 SECTION( "unknown failure" )
587 {
588 injectedFailure = injectedException::unknown;
589 REQUIRE_THROWS_AS( mx::ioutils::string2path<mx::verbose::vv>( path, "unused" ),
591 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::string2path<mx::verbose::vv>( path, "unused" ) ==
593 }
594}
595
596/** \brief Verifies existence helpers preserve path-conversion failures.
597 *
598 * \ingroup fileUtils_unit_tests
599 */
600TEST_CASE( "Propagating path conversion failures through existence helpers", "[ioutils::fileUtils]" )
601{
602 fileUtilsHookGuard guard;
603 failingOperation = mx::ioutils::fileUtilsDetail::operation::stringToPath;
604 injectedFailure = injectedException::badAlloc;
606
607 REQUIRE_THROWS_AS( mx::ioutils::exists<mx::verbose::vv>( "unused", errc ), mx::exception<mx::verbose::vv> );
608 REQUIRE_FALSE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::exists<mx::verbose::vv>( "unused", errc ) );
609 REQUIRE( errc == mx::error_t::std_bad_alloc );
610
612 REQUIRE_FALSE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::dir_exists_is<mx::verbose::vv>( "unused", errc ) );
613 REQUIRE( errc == mx::error_t::std_bad_alloc );
614}
615
616/** \brief Verifies file discovery translates or propagates each documented exception category.
617 *
618 * \ingroup fileUtils_unit_tests
619 */
620TEST_CASE( "Handling file discovery exceptions", "[ioutils::fileUtils]" )
621{
622 fileUtilsHookGuard guard;
623 failingOperation = mx::ioutils::fileUtilsDetail::operation::getFileNames;
624 std::vector<std::string> filenames;
625
626 SECTION( "allocation failure" )
627 {
628 injectedFailure = injectedException::badAlloc;
629 REQUIRE_THROWS_AS( mx::ioutils::getFileNames<mx::verbose::vv>( filenames, "unused", "", "", "" ),
631 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::getFileNames<mx::verbose::vv>( filenames,
632 "unused",
633 "",
634 "",
635 "" ) ==
637 }
638
639 SECTION( "filesystem failure" )
640 {
641 injectedFailure = injectedException::filesystem;
642 REQUIRE_THROWS_AS( mx::ioutils::getFileNames<mx::verbose::vv>( filenames, "unused", "", "", "" ),
644 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::getFileNames<mx::verbose::vv>( filenames,
645 "unused",
646 "",
647 "",
648 "" ) ==
650 }
651
652 SECTION( "standard failure" )
653 {
654 injectedFailure = injectedException::standard;
655 REQUIRE_THROWS_AS( mx::ioutils::getFileNames<mx::verbose::vv>( filenames, "unused", "", "", "" ),
657 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::getFileNames<mx::verbose::vv>( filenames,
658 "unused",
659 "",
660 "",
661 "" ) ==
663 }
664
665 SECTION( "unknown failure" )
666 {
667 injectedFailure = injectedException::unknown;
668 REQUIRE_THROWS_AS( mx::ioutils::getFileNames<mx::verbose::vv>( filenames, "unused", "", "", "" ),
670 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::getFileNames<mx::verbose::vv>( filenames,
671 "unused",
672 "",
673 "",
674 "" ) ==
676 }
677}
678
679} // namespace fileUtilsTest
680} // namespace ioutilsTest
681} // namespace unitTest
Augments an exception with the source file and line.
Definition exception.hpp:42
Declarations of utilities for working with files.
error_t
The mxlib error codes.
Definition error_t.hpp:26
@ noerror
No error has occurred.
Definition error_t.hpp:27
@ std_exception
An exception was thrown.
Definition error_t.hpp:52
@ eexist
File exists (EEXIST).
Definition error_t.hpp:87
@ dirnotfound
The directory was not found.
Definition error_t.hpp:46
@ exception
An exception was thrown.
Definition error_t.hpp:51
@ std_bad_alloc
A bad allocation exception was thrown.
Definition error_t.hpp:53
@ eacces
Permission denied (EACCES).
Definition error_t.hpp:71
@ filesystem
A general filesystem error occurred.
Definition error_t.hpp:39
@ invalidarg
An argument was invalid.
Definition error_t.hpp:29
@ std_filesystem_error
A filesystem error exception was thrown.
Definition error_t.hpp:61
TEST_CASE("Checking existence and whether or not a path is a directory", "[ioutils::fileUtils]")
Checking existence and whether or not a path is a directory.
error_t getFileNames(std::vector< std::string > &fileNames, const std::string &directory, const std::string &prefix, const std::string &substr, const std::string &extension)
Get a list of file names from the specified directory, specifying a prefix, a substring to match,...
std::string fileNamePrepend(const std::string &fname, const std::string &prepend)
Prepend strings to a file name, leaving the directory and extension unaltered.
std::string getSequentialFilename(const std::string &basename, const std::string &extension="", const int startat=0, int ndigit=4)
Get the next file in a numbered sequence.
error_t string2path(std::filesystem::path &path, const std::string &str)
Convert a string to a path, handling exceptions.
std::string fileNamePrependAppend(const std::string &fname, const std::string &prepend, const std::string &append)
Prepend and/or append strings to a file name, leaving the directory and extension unaltered.
std::string fileNameAppend(const std::string &fname, const std::string &append)
Append a string to a file name, leaving the directory and extension unaltered.
bool exists(const std::string &path, mx::error_t &errc)
Check if a path exists.
error_t createDirectories(const std::string &path)
Create a directory or directories.
Definition fileUtils.cpp:83
bool dir_exists_is(const std::string &dir, mx::error_t &errc)
Check if a path exists and is a directory.
std::string parentPath(const std::string &fname)
Get the parent path from a filename.
std::string pathStem(const std::string &fname)
Get the stem of the filename.
std::string pathFilename(const std::string &fname)
Get the base filename.