318TEST_CASE(
"Calculating subimage sizes",
"[ioutils::fits::fitsFile]" )
321#ifdef MXLIB_DOXYGEN_PROTECTED_REF
330 SECTION(
"a 1D image to read" )
332 SECTION(
"reading the whole image with default setup" )
334 fitsFile_test<float> fft;
336 fft.setnax( 1, 64, 0, 0 );
338 REQUIRE( fft.naxis() == 1 );
339 REQUIRE( fft.naxes( 0 ) == 64 );
340 REQUIRE( fft.naxes( 1 ) == -1 );
342 fitsFile_test<float>::pixarrTT pixarrs;
343 fft.calcPixarrs( pixarrs );
345 REQUIRE( pixarrs.fpix[0] == 1 );
346 REQUIRE( pixarrs.lpix[0] == 64 );
347 REQUIRE( pixarrs.inc[0] == 1 );
350 SECTION(
"reading a subimage" )
352 fitsFile_test<float> fft;
354 fft.setnax( 1, 64, 0, 0 );
356 REQUIRE( fft.naxis() == 1 );
357 REQUIRE( fft.naxes( 0 ) == 64 );
358 REQUIRE( fft.naxes( 1 ) == -1 );
360 fft.setReadSize( 10, 0, 7, 0 );
362 fitsFile_test<float>::pixarrTT pixarrs;
363 fft.calcPixarrs( pixarrs );
365 REQUIRE( pixarrs.fpix[0] == 11 );
366 REQUIRE( pixarrs.lpix[0] == 17 );
367 REQUIRE( pixarrs.inc[0] == 1 );
371 SECTION(
"a 2D image to read" )
373 SECTION(
"reading the whole image with default setup" )
375 fitsFile_test<float> fft;
377 fft.setnax( 2, 64, 64, 0 );
379 REQUIRE( fft.naxis() == 2 );
380 REQUIRE( fft.naxes( 0 ) == 64 );
381 REQUIRE( fft.naxes( 1 ) == 64 );
382 REQUIRE( fft.naxes( 2 ) == -1 );
384 fitsFile_test<float>::pixarrTT pixarrs;
385 fft.calcPixarrs( pixarrs );
387 REQUIRE( pixarrs.fpix[0] == 1 );
388 REQUIRE( pixarrs.fpix[1] == 1 );
389 REQUIRE( pixarrs.lpix[0] == 64 );
390 REQUIRE( pixarrs.lpix[1] == 64 );
391 REQUIRE( pixarrs.inc[0] == 1 );
392 REQUIRE( pixarrs.inc[1] == 1 );
395 SECTION(
"reading a subimage" )
397 fitsFile_test<float> fft;
399 fft.setnax( 2, 64, 64, 0 );
401 REQUIRE( fft.naxis() == 2 );
402 REQUIRE( fft.naxes( 0 ) == 64 );
403 REQUIRE( fft.naxes( 1 ) == 64 );
404 REQUIRE( fft.naxes( 2 ) == -1 );
406 fft.setReadSize( 10, 9, 7, 10 );
408 fitsFile_test<float>::pixarrTT pixarrs;
409 fft.calcPixarrs( pixarrs );
411 REQUIRE( pixarrs.fpix[0] == 11 );
412 REQUIRE( pixarrs.fpix[1] == 10 );
413 REQUIRE( pixarrs.lpix[0] == 17 );
414 REQUIRE( pixarrs.lpix[1] == 19 );
415 REQUIRE( pixarrs.inc[0] == 1 );
416 REQUIRE( pixarrs.inc[1] == 1 );
420 SECTION(
"a 3D image to read" )
422 WHEN(
"reading the whole image with default setup" )
424 fitsFile_test<float> fft;
426 fft.setnax( 3, 64, 64, 64 );
428 REQUIRE( fft.naxis() == 3 );
429 REQUIRE( fft.naxes( 0 ) == 64 );
430 REQUIRE( fft.naxes( 1 ) == 64 );
431 REQUIRE( fft.naxes( 2 ) == 64 );
432 REQUIRE( fft.naxes( 3 ) == -1 );
434 fitsFile_test<float>::pixarrTT pixarrs;
435 fft.calcPixarrs( pixarrs );
437 REQUIRE( pixarrs.fpix[0] == 1 );
438 REQUIRE( pixarrs.fpix[1] == 1 );
439 REQUIRE( pixarrs.fpix[2] == 1 );
441 REQUIRE( pixarrs.lpix[0] == 64 );
442 REQUIRE( pixarrs.lpix[1] == 64 );
443 REQUIRE( pixarrs.lpix[2] == 64 );
444 REQUIRE( pixarrs.inc[0] == 1 );
445 REQUIRE( pixarrs.inc[1] == 1 );
446 REQUIRE( pixarrs.inc[2] == 1 );
449 SECTION(
"reading a subimage" )
451 fitsFile_test<float> fft;
453 fft.setnax( 3, 64, 64, 64 );
455 REQUIRE( fft.naxis() == 3 );
456 REQUIRE( fft.naxes( 0 ) == 64 );
457 REQUIRE( fft.naxes( 1 ) == 64 );
458 REQUIRE( fft.naxes( 2 ) == 64 );
459 REQUIRE( fft.naxes( 3 ) == -1 );
461 fft.setReadSize( 10, 9, 7, 10 );
462 fft.setCubeReadSize( 5, 3 );
464 fitsFile_test<float>::pixarrTT pixarrs;
465 fft.calcPixarrs( pixarrs );
467 REQUIRE( pixarrs.fpix[0] == 11 );
468 REQUIRE( pixarrs.fpix[1] == 10 );
469 REQUIRE( pixarrs.fpix[2] == 6 );
470 REQUIRE( pixarrs.lpix[0] == 17 );
471 REQUIRE( pixarrs.lpix[1] == 19 );
472 REQUIRE( pixarrs.lpix[2] == 8 );
473 REQUIRE( pixarrs.inc[0] == 1 );
474 REQUIRE( pixarrs.inc[1] == 1 );
475 REQUIRE( pixarrs.inc[2] == 1 );
478 SECTION(
"reading complete images from selected cube planes" )
480 fitsFile_test<float> fft;
481 fft.setnax( 3, 64, 32, 8 );
482 fft.setCubeReadSize( 2, 3 );
484 fitsFile_test<float>::pixarrTT pixarrs;
485 fft.calcPixarrs( pixarrs );
486 REQUIRE( pixarrs.fpix[0] == 1 );
487 REQUIRE( pixarrs.lpix[0] == 64 );
488 REQUIRE( pixarrs.fpix[1] == 1 );
489 REQUIRE( pixarrs.lpix[1] == 32 );
490 REQUIRE( pixarrs.fpix[2] == 3 );
491 REQUIRE( pixarrs.lpix[2] == 5 );
494 SECTION(
"reading subimages from every cube plane" )
496 fitsFile_test<float> fft;
497 fft.setnax( 3, 64, 32, 8 );
498 fft.setReadSize( 10, 5, 7, 4 );
500 fitsFile_test<float>::pixarrTT pixarrs;
501 fft.calcPixarrs( pixarrs );
502 REQUIRE( pixarrs.fpix[0] == 11 );
503 REQUIRE( pixarrs.lpix[0] == 17 );
504 REQUIRE( pixarrs.fpix[1] == 6 );
505 REQUIRE( pixarrs.lpix[1] == 9 );
506 REQUIRE( pixarrs.fpix[2] == 1 );
507 REQUIRE( pixarrs.lpix[2] == 8 );
590TEST_CASE(
"Propagating FITS destination resize exceptions",
"[ioutils::fits::fitsFile]" )
592 throwingCube<0> badAllocationCube;
593 throwingCube<1> standardExceptionCube;
594 throwingCube<2> unknownExceptionCube;
595 eigenArrResize<throwingCube<0>,
verbose::vv,
true> badAllocationCubeResize;
596 eigenArrResize<throwingCube<1>,
verbose::vv,
true> standardExceptionCubeResize;
597 eigenArrResize<throwingCube<2>,
verbose::vv,
true> unknownExceptionCubeResize;
599 REQUIRE_THROWS_AS( badAllocationCubeResize.resize( badAllocationCube, 1, 1, 1 ), std::bad_alloc );
600 REQUIRE_THROWS_AS( standardExceptionCubeResize.resize( standardExceptionCube, 1, 1, 1 ), std::runtime_error );
601 REQUIRE_THROWS_AS( unknownExceptionCubeResize.resize( unknownExceptionCube, 1, 1, 1 ),
int );
603 throwingImage<0> badAllocationImage;
604 throwingImage<1> standardExceptionImage;
605 throwingImage<2> unknownExceptionImage;
606 eigenArrResize<throwingImage<0>,
verbose::vv> badAllocationImageResize;
607 eigenArrResize<throwingImage<1>,
verbose::vv> standardExceptionImageResize;
608 eigenArrResize<throwingImage<2>,
verbose::vv> unknownExceptionImageResize;
610 REQUIRE_THROWS_AS( badAllocationImageResize.resize( badAllocationImage, 1, 1, 1 ), std::bad_alloc );
611 REQUIRE_THROWS_AS( standardExceptionImageResize.resize( standardExceptionImage, 1, 1, 1 ), std::runtime_error );
612 REQUIRE_THROWS_AS( unknownExceptionImageResize.resize( unknownExceptionImage, 1, 1, 1 ),
int );
621TEST_CASE(
"Propagating FITS file lifecycle failures",
"[ioutils::fits::fitsFile]" )
623 temporaryDirectory testDirectory(
"mxlib-fitsFile-lifecycle-failure-test" );
624 const std::filesystem::path imagePath = testDirectory.path() /
"image.fits";
630 SECTION(
"open failure" )
632 fitsFileOpsGuard operationsGuard;
633 fitsFileDetail::fitsFileCfitsioOpsInstance().openFile = failOpen;
634 fitsFile_test<float> reader;
636 REQUIRE_FALSE( reader.isOpen() );
639 SECTION(
"image-axis count failure" )
641 fitsFileOpsGuard operationsGuard;
642 fitsFileDetail::fitsFileCfitsioOpsInstance().getImageDim = failGetImageDim;
643 fitsFile_test<float> reader;
645 REQUIRE_FALSE( reader.isOpen() );
648 SECTION(
"image-axis size failure" )
650 fitsFileOpsGuard operationsGuard;
651 fitsFileDetail::fitsFileCfitsioOpsInstance().getImageSize = failGetImageSize;
652 fitsFile_test<float> reader;
654 REQUIRE_FALSE( reader.isOpen() );
657 SECTION(
"close failure" )
659 fitsFile_test<float> reader;
662 fitsFileOpsGuard operationsGuard;
663 fitsFileDetail::fitsFileCfitsioOpsInstance().closeFile = failClose;
665 REQUIRE( reader.isOpen() );
668 REQUIRE_FALSE( reader.isOpen() );
671 fitsFileDetail::resetFitsFileCfitsioOps();
680TEST_CASE(
"Propagating FITS pixel-coordinate allocation failures",
"[ioutils::fits::fitsFile]" )
682 fitsFileOpsGuard operationsGuard;
683 fitsFileDetail::fitsFileCfitsioOpsInstance().allocateLongs = controlledAllocateLongs;
685 failAllocationOnCall = 1;
687 fitsFile_test<float> fixture;
688 fitsFile_test<float>::pixarrTT pixelArrays;
690 SECTION(
"bad allocation" )
692 allocationFailureKind = allocationFailure::badAllocation;
693 REQUIRE_THROWS_AS( fixture.allocatePixarrs( pixelArrays, 2 ), std::bad_alloc );
696 SECTION(
"standard exception" )
698 allocationFailureKind = allocationFailure::standardException;
699 REQUIRE_THROWS_AS( fixture.allocatePixarrs( pixelArrays, 2 ), std::runtime_error );
702 SECTION(
"unknown exception" )
704 allocationFailureKind = allocationFailure::unknownException;
705 REQUIRE_THROWS_AS( fixture.allocatePixarrs( pixelArrays, 2 ),
int );
708 SECTION(
"null first-pixel array" )
710 allocationFailureKind = allocationFailure::nullPointer;
711 failAllocationOnCall = 1;
715 SECTION(
"null last-pixel array" )
717 allocationFailureKind = allocationFailure::nullPointer;
718 failAllocationOnCall = 2;
722 SECTION(
"null increment array" )
724 allocationFailureKind = allocationFailure::nullPointer;
725 failAllocationOnCall = 3;
729 SECTION(
"allocation error propagates through read-coordinate calculation" )
731 allocationFailureKind = allocationFailure::nullPointer;
732 failAllocationOnCall = 1;
733 fixture.setnax( 2, 2, 2, 0 );
744TEST_CASE(
"Propagating FITS read failures",
"[ioutils::fits::fitsFile]" )
746 temporaryDirectory testDirectory(
"mxlib-fitsFile-read-failure-test" );
747 const std::filesystem::path imagePath = testDirectory.path() /
"image.fits";
749 image << 1.0F, 2.0F, 3.0F, 4.0F;
755 fitsFileOpsGuard operationsGuard;
757 SECTION(
"raw and Eigen subset failures" )
759 fitsFileDetail::fitsFileCfitsioOpsInstance().readSubset = controlledReadSubset;
761 failReadSubsetOnCall = 1;
762 readSubsetStatus = READ_ERROR;
764 std::vector<float> raw( 4 );
778 SECTION(
"end-of-file status is accepted" )
780 fitsFileDetail::fitsFileCfitsioOpsInstance().readSubset = controlledReadSubset;
782 failReadSubsetOnCall = 1;
783 readSubsetStatus = END_OF_FILE;
784 std::vector<float> raw( 4 );
789 SECTION(
"first and later cube plane failures" )
791 fitsFileDetail::fitsFileCfitsioOpsInstance().readSubset = controlledReadSubset;
792 const std::vector<std::string> files{ imagePath.string(), imagePath.string() };
797 failReadSubsetOnCall = 1;
798 readSubsetStatus = READ_ERROR;
802 failReadSubsetOnCall = 2;
806 SECTION(
"cube coordinate allocation failure" )
808 fitsFileDetail::fitsFileCfitsioOpsInstance().allocateLongs = controlledAllocateLongs;
810 failAllocationOnCall = 1;
811 allocationFailureKind = allocationFailure::nullPointer;
817 SECTION(
"first and later cube header failures" )
819 fitsFileDetail::fitsFileCfitsioOpsInstance().getHeaderSpace = controlledGetHeaderSpace;
820 const std::vector<std::string> files{ imagePath.string(), imagePath.string() };
821 std::vector<fitsHeader<>> headers( files.size() );
825 getHeaderSpaceCall = 0;
826 failGetHeaderSpaceOnCall = 1;
829 getHeaderSpaceCall = 0;
830 failGetHeaderSpaceOnCall = 2;
834 SECTION(
"header-space and header-card failures" )
838 fitsFileDetail::fitsFileCfitsioOpsInstance().getHeaderSpace = failGetHeaderSpace;
841 fitsFileDetail::fitsFileCfitsioOpsInstance().getHeaderSpace = ffghsp;
842 fitsFileDetail::fitsFileCfitsioOpsInstance().readKey = failReadKey;
846 SECTION(
"header failure after a successful Eigen read" )
848 fitsFileDetail::fitsFileCfitsioOpsInstance().getHeaderSpace = failGetHeaderSpace;
855 SECTION(
"header open failure" )
860 fitsFileDetail::fitsFileCfitsioOpsInstance().openFile = failOpen;
871TEST_CASE(
"Propagating FITS write failures",
"[ioutils::fits::fitsFile]" )
873 temporaryDirectory testDirectory(
"mxlib-fitsFile-write-failure-test" );
877 SECTION(
"file creation failure" )
879 fitsFileOpsGuard operationsGuard;
880 fitsFileDetail::fitsFileCfitsioOpsInstance().createFile = failCreateFile;
881 fitsFile_test<float> writer;
882 REQUIRE( writer.write( ( testDirectory.path() /
"create.fits" ).string(), image ) ==
884 REQUIRE_FALSE( writer.isOpen() );
887 SECTION(
"image creation failure" )
889 fitsFile_test<float> writer;
891 fitsFileOpsGuard operationsGuard;
892 fitsFileDetail::fitsFileCfitsioOpsInstance().createImage = failCreateImage;
893 REQUIRE( writer.write( ( testDirectory.path() /
"image.fits" ).string(), image ) ==
895 REQUIRE( writer.isOpen() );
900 SECTION(
"pixel write failure" )
902 fitsFile_test<float> writer;
904 fitsFileOpsGuard operationsGuard;
905 fitsFileDetail::fitsFileCfitsioOpsInstance().writePixels = failWritePixels;
906 REQUIRE( writer.write( ( testDirectory.path() /
"pixels.fits" ).string(), image ) ==
908 REQUIRE( writer.isOpen() );
913 SECTION(
"close failure" )
915 fitsFile_test<float> writer;
917 fitsFileOpsGuard operationsGuard;
918 fitsFileDetail::fitsFileCfitsioOpsInstance().closeFile = failClose;
919 REQUIRE( writer.write( ( testDirectory.path() /
"close.fits" ).string(), image ) ==
921 REQUIRE( writer.isOpen() );
926 SECTION(
"header-card failure" )
930 fitsFile_test<float> writer;
931 REQUIRE( writer.write( ( testDirectory.path() /
"header.fits" ).string(), image, header ) !=
933 if( writer.isOpen() )
980TEST_CASE(
"Cube writing and reading",
"[ioutils::fits::fitsFile]" )
984 #ifdef MXLIB_DOXYGEN_PROTECTED_REF
987 fh.
append(
"dummy", 2,
"dummy" );
989 ff.
write(
"dummy", im, fh );
993 SECTION(
"write 5 frames and read them back in as a cube" )
995 fitsFile_test<float> fft;
1003 std::vector<std::string> flist( {
"/tmp/fitsFile_test1.fits",
1004 "/tmp/fitsFile_test2.fits",
1005 "/tmp/fitsFile_test3.fits",
1006 "/tmp/fitsFile_test4.fits",
1007 "/tmp/fitsFile_test5.fits" } );
1012 std::vector<mx::fits::fitsHeader<mx::verbose::d>> headers( flist.size() );
1013 for(
size_t index = 0; index < headers.size(); ++index )
1015 headers[index].append( std::format(
"FLOAT{}", index + 1 ) );
1020 REQUIRE( imc.rows() == 1024 );
1021 REQUIRE( imc.cols() == 1024 );
1022 REQUIRE( imc.planes() == 5 );
1023 for(
size_t index = 0; index < headers.size(); ++index )
1025 REQUIRE_THAT( headers[index][std::format(
"FLOAT{}", index + 1 )].Float(), WithinAbs( 1.1, 1e-5 ) );
1028 std::vector<float> rawImages( 1024 * 1024 * flist.size() );
1029 std::vector<mx::fits::fitsHeader<mx::verbose::d>> rawHeaders( flist.size() );
1030 for(
size_t index = 0; index < rawHeaders.size(); ++index )
1032 rawHeaders[index].append( std::format(
"INT{}", index + 1 ) );
1036 REQUIRE( rawImages.front() == 0.0F );
1037 REQUIRE( rawImages[1024 * 1024 - 1] == 1048575.0F );
1038 REQUIRE( rawImages[1024 * 1024] == 0.0F );
1039 for(
size_t index = 0; index < rawHeaders.size(); ++index )
1041 REQUIRE( rawHeaders[index][std::format(
"INT{}", index + 1 )].Int() == 2 );
1094TEST_CASE(
"Reading raw FITS arrays and configured subsets",
"[ioutils::fits::fitsFile]" )
1096 temporaryDirectory testDirectory(
"mxlib-fitsFile-raw-read-test" );
1099 const std::filesystem::path vectorPath = testDirectory.path() /
"vector.fits";
1100 std::vector<float> vectorData{ 1.0F, 2.0F, 3.0F, 4.0F };
1115 REQUIRE( vectorImage.rows() == 4 );
1116 REQUIRE( vectorImage.cols() == 1 );
1117 REQUIRE( vectorImage( 3, 0 ) == 4.0F );
1120 image << 1.0F, 2.0F, 3.0F, 4.0F, 5.0F, 6.0F;
1124 const std::filesystem::path firstPath = testDirectory.path() /
"first.fits";
1125 const std::filesystem::path secondPath = testDirectory.path() /
"second.fits";
1126 REQUIRE(
fitsFile.
write( firstPath.string(), image.data(), image.rows(), image.cols(), 1, header ) ==
1128 REQUIRE(
fitsFile.
write( secondPath.string(), image.data(), image.rows(), image.cols(), 1 ) ==
1131 const std::filesystem::path directPath = testDirectory.path() /
"direct.fits";
1137 std::vector<float> raw( 6 );
1140 REQUIRE( std::equal( image.data(), image.data() + image.size(), raw.data() ) );
1143 readHeader.
append(
"TESTKEY" );
1145 REQUIRE( readHeader[
"TESTKEY"].value<int>() == 23 );
1148 REQUIRE( readHeader[
"TESTKEY"].value<int>() == 23 );
1153 REQUIRE( subset.rows() == 1 );
1154 REQUIRE( subset.cols() == 2 );
1155 REQUIRE( subset( 0, 0 ) == image( 1, 0 ) );
1156 REQUIRE( subset( 0, 1 ) == image( 1, 1 ) );
1159 std::vector<float> batch( 12 );
1160 const std::vector<std::string> files{ firstPath.string(), secondPath.string() };
1162 REQUIRE( std::equal( image.data(), image.data() + image.size(), batch.data() ) );
1163 REQUIRE( std::equal( image.data(), image.data() + image.size(), batch.data() + image.size() ) );
1165 const std::vector<std::string> noFiles;
1167 std::vector<fitsHeader<>> noHeaders;
1173 REQUIRE( lazyImage.isApprox( image ) );
1175 const std::string missingPath = ( testDirectory.path() /
"missing.fits" ).string();
1187 smallImage.setConstant( 9.0F );
1188 const std::filesystem::path smallPath = testDirectory.path() /
"small.fits";
1190 REQUIRE(
fitsFile.
read( imageCube, std::vector<std::string>{ firstPath.string(), smallPath.string() } ) !=
1193 fitsFile_test<float> noCommentReader;
1194 noCommentReader.noComment(
true );
1196 headerWithoutComments.
append(
"TESTKEY" );
1197 REQUIRE( noCommentReader.readHeader( headerWithoutComments, firstPath.string() ) ==
mx::error_t::noerror );
1198 REQUIRE( headerWithoutComments[
"TESTKEY"].value<int>() == 23 );
1199 REQUIRE( headerWithoutComments[
"TESTKEY"].comment().empty() );
1207TEST_CASE(
"Verbose float Eigen FITS writes round-trip",
"[ioutils::fits::fitsFile][hciReduce]" )
1209 temporaryDirectory testDirectory(
"mxlib-fitsFile-write-test" );
1213 image << 1.0F, 2.0F, 3.0F, 4.0F, 5.0F, 6.0F;
1215 const std::filesystem::path imagePath = testDirectory.path() /
"image.fits";
1220 REQUIRE( readImage.isApprox( image ) );
1226 header.
append(
"TESTKEY", 17,
"test header value" );
1227 header.
append(
"PREPROC MASK",
true,
"apply preprocessing mask" );
1228 header.
append(
"QTHRESH", 0.75F,
"quality threshold" );
1229 header.
append(
"", fitsCommentType(),
"HCI preprocessing parameters" );
1230 header.
append(
"HISTORY", fitsHistoryType(),
"coadded target000001.fits" );
1232 const std::filesystem::path headerImagePath = testDirectory.path() /
"image-with-header.fits";
1234 const std::filesystem::path unavailableImagePath = testDirectory.path() /
"missing" /
"image.fits";
1240 REQUIRE( readImage.isApprox( image ) );
1241 REQUIRE( readHeader[
"TESTKEY"].value<int>() == 17 );
1242 REQUIRE( readHeader[
"PREPROC MASK"].String() ==
"1" );
1243 REQUIRE_THAT( readHeader[
"QTHRESH"].value<float>(), WithinAbs( 0.75F, 1e-6F ) );
1245 bool foundHistory =
false;
1246 for(
auto &card : readHeader )
1250 foundHistory =
true;
1253 REQUIRE( foundHistory );
1255 const std::filesystem::path rawImagePath = testDirectory.path() /
"raw-image-with-header.fits";
1256 REQUIRE(
fitsFile.
write( rawImagePath.string(), image.data(), image.rows(), image.cols(), 1, header ) ==
1259 REQUIRE( readImage.isApprox( image ) );
1260 REQUIRE( readHeader[
"TESTKEY"].value<int>() == 17 );
1261 REQUIRE( readHeader[
"PREPROC MASK"].String() ==
"1" );
1264 cube.
image( 0 ) = image;
1265 cube.
image( 1 ) = image + 10.0F;
1267 const std::filesystem::path cubePath = testDirectory.path() /
"cube.fits";
1272 REQUIRE( readCube.rows() == cube.rows() );
1273 REQUIRE( readCube.cols() == cube.cols() );
1274 REQUIRE( readCube.planes() == cube.planes() );
1275 REQUIRE( readCube.
image( 0 ).isApprox( cube.
image( 0 ) ) );
1276 REQUIRE( readCube.
image( 1 ).isApprox( cube.
image( 1 ) ) );
1278 const std::filesystem::path headerCubePath = testDirectory.path() /
"cube-with-header.fits";
1280 const std::filesystem::path unavailableCubePath = testDirectory.path() /
"missing" /
"cube.fits";
1284 REQUIRE( readCube.
image( 0 ).isApprox( cube.
image( 0 ) ) );
1285 REQUIRE( readCube.
image( 1 ).isApprox( cube.
image( 1 ) ) );
1286 REQUIRE( readHeader[
"TESTKEY"].value<int>() == 17 );
1297#ifdef MXLIB_DOXYGEN_PROTECTED_REF
1300 fh.
append(
"dummy", 2,
"dummy" );
1302 ff.
write(
"dummy", im, fh );
1305 fitsFile_test<float> fft;
1307 std::vector<std::string> fnames(
1308 {
"/tmp/fitsFile_test1.fits",
"/tmp/fitsFile_test2.fits",
"/tmp/fitsFile_test3.fits" } );
1313 SECTION(
"write three frames and read their headers as a vector, not allocated" )
1317 std::vector<mx::fits::fitsHeader<mx::verbose::d>> fh;
1321 REQUIRE( fh.size() == fnames.size() );
1323 REQUIRE_THAT( fh[0][
"FLOAT1"].Float(), WithinAbs( 1.1, 1e-5 ) );
1324 REQUIRE( fh[0][
"INT1"].Int() == 2 );
1325 REQUIRE_THAT( fh[1][
"FLOAT2"].Float(), WithinAbs( 1.1, 1e-5 ) );
1326 REQUIRE( fh[1][
"INT2"].Int() == 2 );
1327 REQUIRE_THAT( fh[2][
"FLOAT3"].Float(), WithinAbs( 1.1, 1e-5 ) );
1328 REQUIRE( fh[2][
"INT3"].Int() == 2 );
1331 SECTION(
"write three frames and read their headers as a vector, allocated" )
1335 std::vector<mx::fits::fitsHeader<mx::verbose::d>> fh;
1338 fh.resize( fnames.size() );
1343 REQUIRE_THAT( fh[0][
"FLOAT1"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
1346 REQUIRE( fh[0][
"INT1"].Int( &errc ) == 2 );
1349 REQUIRE_THAT( fh[1][
"FLOAT2"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
1352 REQUIRE( fh[1][
"INT2"].Int( &errc ) == 2 );
1355 REQUIRE_THAT( fh[2][
"FLOAT3"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
1358 REQUIRE( fh[2][
"INT3"].Int( &errc ) == 2 );
1362 SECTION(
"write three frames and read their headers as a vector, allocated to wrong size" )
1365 std::vector<mx::fits::fitsHeader<mx::verbose::d>> fh;
1368 fh.resize( fnames.size() + 1 );
1372 SECTION(
"write three frames and read their headers as a vector, extracting only 1 keyword" )
1376 std::vector<mx::fits::fitsHeader<mx::verbose::d>> fh;
1379 fh.resize( fnames.size() );
1380 fh[0].append(
"FLOAT1" );
1381 fh[1].append(
"FLOAT2" );
1382 fh[2].append(
"FLOAT3" );
1388 REQUIRE_THAT( fh[0][
"FLOAT1"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
1390 REQUIRE( fh[0][
"FLOAT1"].valueGood() ==
true );
1392 REQUIRE( fh[0][
"INT1"].Int( &errc ) == 0 );
1394 REQUIRE( fh[0][
"INT1"].valueGood() ==
false );
1396 REQUIRE_THAT( fh[1][
"FLOAT2"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
1398 REQUIRE( fh[1][
"FLOAT2"].valueGood() ==
true );
1400 REQUIRE( fh[1][
"INT2"].Int( &errc ) == 0 );
1402 REQUIRE( fh[1][
"INT2"].valueGood() ==
false );
1404 REQUIRE_THAT( fh[2][
"FLOAT3"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
1406 REQUIRE( fh[2][
"FLOAT3"].valueGood() ==
true );
1408 REQUIRE( fh[2][
"INT3"].Int( &errc ) == 0 );
1410 REQUIRE( fh[2][
"INT3"].valueGood() ==
false );