4#include "../../catch2/catch.hpp"
6#define MX_NO_ERROR_REPORTS
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
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
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
36enum class injectedException
45mx::ioutils::fileUtilsDetail::operation failingOperation = mx::ioutils::fileUtilsDetail::operation::stringToPath;
46injectedException injectedFailure = injectedException::none;
47std::error_code injectedErrorCode;
49void injectFileUtilsFailure( mx::ioutils::fileUtilsDetail::operation operation )
51 if( operation != failingOperation )
56 switch( injectedFailure )
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:
67 case injectedException::none:
72void injectFileUtilsErrorCode( mx::ioutils::fileUtilsDetail::operation operation, std::error_code &errorCode )
74 if( operation == mx::ioutils::fileUtilsDetail::operation::createDirectories && injectedErrorCode )
76 errorCode = injectedErrorCode;
80class fileUtilsHookGuard
84 : m_saved( mx::ioutils::fileUtilsDetail::operationHook() ),
85 m_savedErrorCode( mx::ioutils::fileUtilsDetail::errorCodeHook() )
87 injectedFailure = injectedException::none;
88 injectedErrorCode.clear();
89 mx::ioutils::fileUtilsDetail::operationHook() = injectFileUtilsFailure;
90 mx::ioutils::fileUtilsDetail::errorCodeHook() = injectFileUtilsErrorCode;
95 mx::ioutils::fileUtilsDetail::operationHook() = m_saved;
96 mx::ioutils::fileUtilsDetail::errorCodeHook() = m_savedErrorCode;
100 mx::ioutils::fileUtilsDetail::operationHookT m_saved;
101 mx::ioutils::fileUtilsDetail::errorCodeHookT m_savedErrorCode;
111namespace fileUtilsTest
118TEST_CASE(
"Checking existence and whether or not a path is a directory",
"[ioutils::fileUtils]" )
120 SECTION(
"directory exists and is a directory" )
122 std::filesystem::create_directories(
"/tmp/fileUtils_test/dir_is_exists/" );
128 REQUIRE( isdir ==
true );
131 SECTION(
"directory does not exist" )
137 REQUIRE( isdir ==
false );
140 SECTION(
"directory is a file does not exist" )
142 std::filesystem::create_directories(
"/tmp/fileUtils_test/dir_is_exists/" );
144 fout.open(
"/tmp/fileUtils_test/dir_is_exists/test.txt" );
152 REQUIRE( isdir ==
false );
155 SECTION(
"error from exists" )
159 mx::ioutils::MXLIBTEST_DIREXISTSIS_ISEXISTSERR_ns::dir_exists_is(
"/tmp/fileUtils_test/dir_is_exists/",
163 REQUIRE( isdir ==
false );
166 SECTION(
"error from is_directory" )
170 mx::ioutils::MXLIBTEST_DIREXISTSIS_ISDIRERR_ns::dir_exists_is(
"/tmp/fileUtils_test/dir_is_exists/", errc );
173 REQUIRE( isdir ==
false );
181TEST_CASE(
"creating sequential filenames",
"[ioutils::fileUtils]" )
183 SECTION(
"a varying numbers of digits desired" )
185 SECTION(
"default 4 digits, starting at 0" )
188 REQUIRE( fname ==
"base0000.test" );
191 SECTION(
"default 4 digits, starting at 1" )
194 REQUIRE( fname ==
"base0001.test" );
197 SECTION(
"default 7 digits, starting at 0" )
200 REQUIRE( fname ==
"base0000000.test" );
203 SECTION(
"default 7 digits, starting at 1" )
206 REQUIRE( fname ==
"base0000001.test" );
209 SECTION(
"default 12 digits, starting at 0" )
212 REQUIRE( fname ==
"base000000000000.test" );
215 SECTION(
"default 12 digits, starting at 1" )
218 REQUIRE( fname ==
"base000000000001.test" );
227TEST_CASE(
"decomposing and transforming file paths",
"[ioutils::fileUtils]" )
229 const std::string filename{
"/tmp/hciReduce/list/science.frame.fits" };
235 "/tmp/hciReduce/list/pre_science.frame_post.fits" );
247TEST_CASE(
"creating HCI output directories",
"[ioutils::fileUtils][hciReduce]" )
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";
253 std::filesystem::remove_all( root );
257 REQUIRE( std::filesystem::is_directory( directory ) );
260 injectedErrorCode = std::error_code( 9999, std::generic_category() );
263 std::filesystem::remove_all( root );
273TEST_CASE(
"selecting sequential HCI final-image filenames",
"[ioutils::fileUtils][hciReduce]" )
275 const std::filesystem::path root = std::filesystem::temp_directory_path() /
"mxlib-hciReduce-final-output";
276 const std::string basename = ( root /
"final" ).
string();
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();
285 std::filesystem::remove_all( root );
288void createfiles(
const std::string &basedir )
290 std::filesystem::create_directories( basedir );
294 fout.open( basedir +
"/file_txt_1.txt" );
296 fout.open( basedir +
"/file_xtx_2.txt" );
298 fout.open( basedir +
"/elif_txt_3.txt" );
300 fout.open( basedir +
"/file_xtx_4.txt" );
302 fout.open( basedir +
"/elif_txt_5.txt" );
304 fout.open( basedir +
"/elif_xtx_1.xxx" );
306 fout.open( basedir +
"/elif_txt_2.xxx" );
308 fout.open( basedir +
"/file_xtx_3.xxx" );
310 fout.open( basedir +
"/elif_txt_4.xxx" );
312 fout.open( basedir +
"/file_xtx_5.xxx" );
322TEST_CASE(
"Getting a list of files",
"[ioutils::fileUtils]" )
324 std::string basedir =
"/tmp/fileUtils_test/dir";
325 createfiles( basedir );
327 SECTION(
"a directory with files of various type and names" )
329 SECTION(
"directory only" )
331 std::vector<std::string> fnames;
335 REQUIRE( fnames.size() == 10 );
338 SECTION(
"single extension with ." )
340 std::vector<std::string> fnames;
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" );
352 SECTION(
"single extension without ." )
354 std::vector<std::string> fnames;
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" );
366 SECTION(
"different extension with ." )
368 std::vector<std::string> fnames;
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" );
380 SECTION(
"a prefix, no extension" )
382 std::vector<std::string> fnames;
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" );
394 SECTION(
"a prefix, with extension with ." )
396 std::vector<std::string> fnames;
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" );
406 SECTION(
"a substr alone" )
408 std::vector<std::string> fnames;
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" );
420 SECTION(
"a substr which is actually the prefix" )
422 std::vector<std::string> fnames;
426 REQUIRE( fnames.size() == 0 );
429 SECTION(
"a prefix and a substr which is actually the prefix" )
431 std::vector<std::string> fnames;
435 REQUIRE( fnames.size() == 0 );
438 SECTION(
"a prefix, and a substr, no extension" )
440 std::vector<std::string> fnames;
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" );
451 SECTION(
"a prefix, a substr, and an extension with ." )
453 std::vector<std::string> fnames;
457 REQUIRE( fnames.size() == 2 );
458 REQUIRE( fnames[0] == basedir +
"/file_xtx_2.txt" );
459 REQUIRE( fnames[1] == basedir +
"/file_xtx_4.txt" );
463 SECTION(
"a directory which does not exist" )
465 std::vector<std::string> fnames;
471 SECTION(
"a directory which is a file" )
473 std::vector<std::string> fnames;
479 SECTION(
"a directory which does not exist, verbose = vv" )
481 std::vector<std::string> fnames;
487 SECTION(
"a directory which is a file, verbose = vv" )
489 std::vector<std::string> fnames;
496 SECTION(
"a directory which does not exist, verbose = v" )
498 std::vector<std::string> fnames;
504 SECTION(
"a directory which is a file, verbose = v" )
506 std::vector<std::string> fnames;
513 SECTION(
"a directory which does not exist, verbose = o" )
515 std::vector<std::string> fnames;
521 SECTION(
"a directory which is a file, verbose = o" )
523 std::vector<std::string> fnames;
535TEST_CASE(
"Filtering short filenames",
"[ioutils::fileUtils]" )
537 const std::string directory{
"/tmp/fileUtils_test/short_names" };
538 std::filesystem::create_directories( directory );
539 std::ofstream{ directory +
"/a" }.close();
541 std::vector<std::string> filenames;
543 REQUIRE( filenames.empty() );
546 REQUIRE( filenames.empty() );
553TEST_CASE(
"Handling path conversion exceptions",
"[ioutils::fileUtils]" )
555 fileUtilsHookGuard guard;
556 failingOperation = mx::ioutils::fileUtilsDetail::operation::stringToPath;
557 std::filesystem::path path;
559 SECTION(
"allocation failure" )
561 injectedFailure = injectedException::badAlloc;
564 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::string2path<mx::verbose::vv>( path,
"unused" ) ==
568 SECTION(
"filesystem failure" )
570 injectedFailure = injectedException::filesystem;
573 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::string2path<mx::verbose::vv>( path,
"unused" ) ==
577 SECTION(
"standard failure" )
579 injectedFailure = injectedException::standard;
582 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::string2path<mx::verbose::vv>( path,
"unused" ) ==
586 SECTION(
"unknown failure" )
588 injectedFailure = injectedException::unknown;
591 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::string2path<mx::verbose::vv>( path,
"unused" ) ==
600TEST_CASE(
"Propagating path conversion failures through existence helpers",
"[ioutils::fileUtils]" )
602 fileUtilsHookGuard guard;
603 failingOperation = mx::ioutils::fileUtilsDetail::operation::stringToPath;
604 injectedFailure = injectedException::badAlloc;
608 REQUIRE_FALSE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::exists<mx::verbose::vv>(
"unused", errc ) );
612 REQUIRE_FALSE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::dir_exists_is<mx::verbose::vv>(
"unused", errc ) );
620TEST_CASE(
"Handling file discovery exceptions",
"[ioutils::fileUtils]" )
622 fileUtilsHookGuard guard;
623 failingOperation = mx::ioutils::fileUtilsDetail::operation::getFileNames;
624 std::vector<std::string> filenames;
626 SECTION(
"allocation failure" )
628 injectedFailure = injectedException::badAlloc;
631 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::getFileNames<mx::verbose::vv>( filenames,
639 SECTION(
"filesystem failure" )
641 injectedFailure = injectedException::filesystem;
644 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::getFileNames<mx::verbose::vv>( filenames,
652 SECTION(
"standard failure" )
654 injectedFailure = injectedException::standard;
657 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::getFileNames<mx::verbose::vv>( filenames,
665 SECTION(
"unknown failure" )
667 injectedFailure = injectedException::unknown;
670 REQUIRE( mx::ioutils::MXLIBTEST_CATCH_ALL_EXCEPTIONS_ns::getFileNames<mx::verbose::vv>( filenames,
Augments an exception with the source file and line.
Declarations of utilities for working with files.
error_t
The mxlib error codes.
@ noerror
No error has occurred.
@ std_exception
An exception was thrown.
@ eexist
File exists (EEXIST).
@ dirnotfound
The directory was not found.
@ exception
An exception was thrown.
@ std_bad_alloc
A bad allocation exception was thrown.
@ eacces
Permission denied (EACCES).
@ filesystem
A general filesystem error occurred.
@ invalidarg
An argument was invalid.
@ std_filesystem_error
A filesystem error exception was thrown.
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.
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.