12 #include <gmock/gmock.h> 
   13 #include <gtest/gtest.h> 
   14 #include <nlohmann/json.hpp> 
   22 using namespace std::string_view_literals;
 
   28                        std::string_view name,
 
   29                        std::string_view logical_name) {
 
   30         using namespace ::testing;
 
   34             EXPECT_THAT(record, StartsWith(kw.
GetRecord()));
 
   36             auto [kw_name, kw_type] = kw.
GetName();
 
   37             EXPECT_EQ(kw_name, logical_name);
 
   38             EXPECT_EQ(type, kw_type);
 
   48     std::fill_n(std::back_inserter(too_long), constants::RECORD_LENGTH + 1, 
'N');
 
   49     ASSERT_EQ(too_long.size(), constants::RECORD_LENGTH + 1);
 
   55     auto [name, type] = kw.
GetName();
 
   58     EXPECT_EQ(kw.GetRecord(), 
" ");
 
   63     auto [name, type] = kw.
GetName();
 
   66     EXPECT_EQ(kw.GetRecord(), 
"A");
 
   71     auto [name, type] = kw.
GetName();
 
   74     EXPECT_EQ(kw.GetRecord(), 
"A       =");
 
   79     auto [name, type] = kw.
GetName();
 
   82     EXPECT_EQ(kw.GetRecord(), 
"HIERARCH ESO A=T/");
 
   87     auto [name, type] = kw.
GetName();
 
   90     EXPECT_EQ(kw.GetRecord(), 
"HIERARCH ESO A=''/");
 
   94     using namespace ::testing;
 
   95     std::string_view record = 
"ORIGIN  = 'ESO-PARANAL'    / European Southern Observatory";
 
   97     std::array<char, constants::RECORD_LENGTH> array_record = {
' '};
 
   98     std::copy(std::begin(record), std::end(record), std::begin(array_record));
 
  100     EXPECT_THAT(kw.
GetRecord(), StartsWith(record));
 
  107             ExpectKeyword(
"SIMPLE  =                T / Standard FITS     ",
 
  114             ExpectKeyword(
"DATE    = '2019-12-12T04:30:53.8930' / Date the file was written",
 
  125     EXPECT_THROW(
LiteralKeyword(
"HIERARCH ESO HAS NO VALUE AFTER INDICATOR="),
 
  126                  std::invalid_argument);
 
  132         ExpectKeyword(
"HIERARCH ESO ADA ABSROT END = 36.49838 / [deg] Abs rot angle at exp end",
 
  140             "HIERARCH ESO DEL DLT2 VCM CURV = 0.001512604 / mm-1 actual curv. of VCM beam i",
 
  143             "DEL DLT2 VCM CURV");
 
  148             "HIERARCH ESO DEL DLT2 VCM CURV=0.001512604 / mm-1 actual curv. of VCM beam i",
 
  151             "DEL DLT2 VCM CURV");
 
  155         ExpectKeyword(
"HIERARCH ESO N = 0.001512604 / mm-1 actual curv. of VCM beam i",
 
  162         ExpectKeyword(
"HIERARCH ESO N =0.001512604 / mm-1 actual curv. of VCM beam i",
 
  169         ExpectKeyword(
"HIERARCH ESO N= 0.001512604 / mm-1 actual curv. of VCM beam i",
 
  176         ExpectKeyword(
"HIERARCH ESO N=0.001512604 / mm-1 actual curv. of VCM beam i",
 
  195             "HIERARCH DET.CAM.ID = 'TestCamera1Id' / System wide unique ID allocated to camer",
 
  203         ExpectKeyword(
"HIERARCH ESOADA ABSROT END = 36.49838 / [deg] Abs rot angle at exp end",
 
  211         ExpectKeyword(
"HIERARCH ESO ADA ABSROT END  36.49838 / [deg] Abs rot angle at exp end",
 
  222     EXPECT_EQ(kw2, kw2_copy);
 
  230     using namespace ::testing;
 
  231     using namespace std::literals::string_view_literals;
 
  235         auto view = 
"HIERARCH ESO TEL MOON DEC = 22.00047 / [deg] 22:00:01.7 DEC (J2000)"sv;
 
  237         std::stringstream ss;
 
  239         EXPECT_EQ(ss.str(), view);
 
  242         auto view = 
"COMMENT    "sv;
 
  244         std::stringstream ss;
 
  246         EXPECT_EQ(ss.str(), 
"COMMENT"sv);
 
  252 template <
typename Type>
 
  255 using Types = ::testing::Types<ValueKeyword, EsoKeyword>;
 
  259     using Keyword = TypeParam;
 
  261     Keyword orig(
"NAME", 1.0, 
"comment");
 
  262     EXPECT_EQ(orig.name, 
"NAME");
 
  263     EXPECT_EQ(orig.value, 
typename Keyword::ValueType(1.0));
 
  264     EXPECT_EQ(orig.comment, 
"comment");
 
  268     EXPECT_EQ(orig, copy_assign);
 
  270     Keyword move_assign(
"TOBE", 
"replaced");
 
  271     move_assign = std::move(copy_assign);
 
  272     EXPECT_EQ(orig, move_assign);
 
  274     Keyword copy_construct(orig);
 
  275     EXPECT_EQ(orig, copy_construct);
 
  277     Keyword move_construct(std::move(copy_construct));
 
  278     EXPECT_EQ(orig, move_construct);
 
  282     using Keyword = TypeParam;
 
  284     Keyword orig(
"NAME", 1.0, std::nullopt);
 
  285     EXPECT_EQ(orig.comment, std::nullopt);
 
  293     using Keyword = TypeParam;
 
  295     Keyword kw(
"NAME", 
"str", 
"comment");
 
  296     ASSERT_TRUE(std::holds_alternative<std::string>(kw.value));
 
  297     EXPECT_EQ(
"str", std::get<std::string>(kw.value));
 
  301     using Keyword = TypeParam;
 
  303     Keyword lhs(
"NAME", 1.0, 
"comment");
 
  304     Keyword rhs(
"NAME", 1.0, 
"comment");
 
  306     EXPECT_TRUE(lhs == rhs);
 
  308     EXPECT_FALSE(lhs != rhs);
 
  310     rhs.comment = std::nullopt;
 
  313     rhs.comment = 
"comment ";
 
  318     using Keyword = TypeParam;
 
  320     Keyword lhs(
"A", 1.0, 
"comment");
 
  321     Keyword rhs(
"B", 1.0, 
"comment");
 
  324     EXPECT_FALSE(rhs < lhs);
 
  325     EXPECT_FALSE(lhs < lhs);
 
  329     using Keyword = TypeParam;
 
  332     using namespace ::testing;
 
  336         Keyword kw(
"NAME", std::string(
"value"), 
"comment");
 
  337         std::stringstream ss;
 
  339         EXPECT_EQ(ss.str(), 
"name='NAME', value=(str)'value', comment='comment'");
 
  342         Keyword kw(
"NAME", 
true, std::nullopt);
 
  343         std::stringstream ss;
 
  345         EXPECT_EQ(ss.str(), 
"name='NAME', value=(bool)true, comment=n/a");
 
  348         Keyword kw(
"NAME", 1234ul, std::nullopt);
 
  349         std::stringstream ss;
 
  351         EXPECT_THAT(ss.str(), 
"name='NAME', value=(uint64_t)1234, comment=n/a");
 
  354         Keyword kw(
"NAME", 1234l, std::nullopt);
 
  355         std::stringstream ss;
 
  357         EXPECT_THAT(ss.str(), 
"name='NAME', value=(int64_t)1234, comment=n/a");
 
  360         Keyword kw(
"NAME", 1.234, std::nullopt);
 
  361         std::stringstream ss;
 
  363         EXPECT_THAT(ss.str(), 
"name='NAME', value=(double)1.234, comment=n/a");
 
  367 TEST(TestKeywordCombinations, OperatorLessComparesKeywordName) {
 
  386     EXPECT_FALSE(value_kw < eso_kw);
 
  388     EXPECT_LT(literal_value_kw1, literal_value_kw2);
 
  389     EXPECT_FALSE(literal_value_kw1 < literal_eso_kw1);
 
  390     EXPECT_FALSE(literal_value_kw1 < literal_eso_kw2);
 
  391     EXPECT_LT(literal_eso_kw1, literal_eso_kw2);
 
  393     EXPECT_LT(value_kw, literal_commentary_kw1);
 
  394     EXPECT_LT(eso_kw, literal_commentary_kw1);
 
  395     EXPECT_LT(literal_value_kw1, literal_commentary_kw1);
 
  396     EXPECT_LT(literal_eso_kw1, literal_commentary_kw1);
 
  397     EXPECT_LT(literal_commentary_kw1, literal_commentary_kw2);
 
  399     EXPECT_FALSE(literal_eso_kw1 < eso_kw);
 
  400     EXPECT_FALSE(value_kw < value_kw);
 
  401     EXPECT_FALSE(eso_kw < eso_kw);
 
  402     EXPECT_FALSE(eso_kw < value_kw);
 
  403     EXPECT_FALSE(literal_eso_kw1 < value_kw);
 
  404     EXPECT_FALSE(literal_commentary_kw1 < literal_commentary_kw1);
 
  405     EXPECT_FALSE(literal_commentary_kw2 < literal_commentary_kw1);
 
  418     EXPECT_EQ(view, eso_kw.
GetName());
 
  423     EXPECT_NE(literal_value_kw1.
GetName(), literal_eso_kw1.
GetName());
 
  424     EXPECT_NE(literal_value_kw1.
GetName(), literal_commentary_kw1.
GetName());
 
  427     EXPECT_EQ(literal_commentary_kw1.
GetName(), literal_commentary_kw2.
GetName());
 
  435     using namespace ::testing;
 
  458     EXPECT_THAT(to, ContainerEq(result));
 
  463     using namespace ::testing;
 
  491     EXPECT_THAT(to, ContainerEq(result));
 
  496     using namespace ::testing;
 
  522     EXPECT_THAT(to, ContainerEq(result));
 
  527     using namespace ::testing;
 
  553     auto pos = to.begin();
 
  554     std::advance(pos, 3);
 
  556     EXPECT_THAT(to, ContainerEq(result));
 
  560     EXPECT_EQ(
LiteralKeyword(
"DATE-OBS= '2019-12-12T04:25:48.0068' / Observing date"),
 
  563         LiteralKeyword(
"DATE    = '2019-12-12T04:30:53.8930' / Date the file was written"),
 
  564         Format(
ValueKeyword(
"DATE", 
"2019-12-12T04:30:53.8930", 
"Date the file was written")));
 
  567     EXPECT_EQ(
LiteralKeyword(
"EXTEND  =                    T / FITS Extension may be present"),
 
  569     EXPECT_EQ(
LiteralKeyword(
"ORIGIN  = 'ESO-PARANAL'        / European Southern Observatory"),
 
  572         LiteralKeyword(
"INHERIT =                    F / denotes the INHERIT keyword convention"),
 
  577         << 
"XTENSION has an exception and must be padded to be minimum 8 chars for legacy reasons";
 
  578     EXPECT_EQ(
LiteralKeyword(
"XTENSION= 'CUSTOM-EXTENSION-TYPE' / Image extension"),
 
  580         << 
"XTENSION has an exception and must be padded to be minimum 8 chars for legacy reasons";
 
  584     EXPECT_EQ(
LiteralKeyword(
"HIERARCH ESO ADA ABSROT END = 36.5 / [deg] Abs rot angle at exp end"),
 
  585               Format(
EsoKeyword(
"ADA ABSROT END", 36.5, 
"[deg] Abs rot angle at exp end")));
 
  586     EXPECT_EQ(
LiteralKeyword(
"HIERARCH ESO ADA ABSROT PPOS = 'POS' / sign of probe position"),
 
  588     EXPECT_EQ(
LiteralKeyword(
"HIERARCH ESO DET ACQ1 REV = 1 / Pre-processor framework revision"),
 
  589               Format(
EsoKeyword(
"DET ACQ1 REV", 1lu, 
"Pre-processor framework revision")));
 
  590     EXPECT_EQ(
LiteralKeyword(
"HIERARCH ESO DET ADC1 CLAMP = F / Clamp-and-Sample"),
 
  594             "HIERARCH ESO DET CLDC1 DCNM16 = 'DC16_RelaySwitchLowActive' / Name of bias volta"),
 
  596             EsoKeyword(
"DET CLDC1 DCNM16", 
"DC16_RelaySwitchLowActive", 
"Name of bias voltage")));
 
  599             "HIERARCH ESO PCR SUMMARY NSCANNED = 1273000 / Total Number of scanned records."),
 
  600         Format(
EsoKeyword(
"PCR SUMMARY NSCANNED", 1273000ul, 
"Total Number of scanned records.")));
 
  603             "HIERARCH ESO INS TIM2 START = '2019-07-27T02:46:45.00' / Start UTC time in ISO f"),
 
  604         Format(
EsoKeyword(
"INS TIM2 START", 
"2019-07-27T02:46:45.00", 
"Start UTC time in ISO f")));
 
  610             "HIERARCH DET.CAM.ID = 'TestCamera1Id' / System wide unique ID allocated to camer"),
 
  612             "HIERARCH DET.CAM.ID = 'TestCamera1Id' / System wide unique ID allocated to camer")));
 
  615 TEST(TestFormatting, TooLongKeywords) {
 
  618     EXPECT_EQ(
LiteralKeyword(
"HIERARCH ESO LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG " 
  620               Format(
EsoKeyword(
"LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LON",
 
  623         LiteralKeyword(
"HIERARCH ESO LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG " 
  625         std::invalid_argument);
 
  627                      "LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONX", 
true)),
 
  628                  std::invalid_argument);
 
  630         Format(
EsoKeyword(
"LONG LONG LONG LONG LONG LONG LONG", 
"LONG LONG LONG LONG LONG LONX")),
 
  631         std::invalid_argument);
 
  635     EXPECT_EQ(
LiteralKeyword(
"ORIGIN  = 'ESO-PARANAL'        / European Southern Observatory"),
 
  637                   ValueKeyword(
"ORIGIN", 
"ESO-PARANAL", 
"European Southern Observatory"))));
 
  638     EXPECT_EQ(
LiteralKeyword(
"HIERARCH ESO ADA ABSROT END = 36.5 / [deg] Abs rot angle at exp end"),
 
  640                   EsoKeyword(
"ADA ABSROT END", 36.5, 
"[deg] Abs rot angle at exp end"))));
 
  642     auto kw = 
LiteralKeyword(
"HIERARCH ESO ADA ABSROT END = 36.5 / [deg] Abs rot angle at exp end");
 
  646 TEST(TestStandardSortV1, StableSortsValueKeywords) {
 
  647     using namespace testing;
 
  649     std::vector<LiteralKeyword> kws;
 
  650     kws.emplace_back(
"SIMPLE  =                T / Standard FITS");
 
  651     kws.emplace_back(
"BITPIX  =                8 / # of bits per pix value");
 
  652     kws.emplace_back(
"NAXIS   =                0 / # of axes in data array");
 
  662 TEST(TestStandardSortV1, StableSortsByKeywordType) {
 
  663     using namespace testing;
 
  665     std::vector<LiteralKeyword> kws;
 
  666     kws.emplace_back(
"COMMENT First");
 
  667     kws.emplace_back(
"SIMPLE  =                T / Standard FITS");
 
  668     kws.emplace_back(
"HIERARCH ESO KW = T / Comment");
 
  669     kws.emplace_back(
"HIERARCH ESO DET A = T / Comment");
 
  670     kws.emplace_back(
"COMMENT Second");
 
  671     kws.emplace_back(
"BITPIX  =                8 / # of bits per pix value");
 
  685 TEST(TestStandardSortV1, EsoKeywordsAreSortedByCategory) {
 
  686     using namespace testing;
 
  688     std::vector<LiteralKeyword> kws;
 
  689     kws.emplace_back(
"HIERARCH ESO DPR A = T / Comment");
 
  690     kws.emplace_back(
"HIERARCH ESO DPR B = T / Comment");
 
  691     kws.emplace_back(
"HIERARCH ESO DPR1 A = T / Comment");
 
  692     kws.emplace_back(
"HIERARCH ESO DPR2 A = T / Comment");
 
  693     kws.emplace_back(
"HIERARCH ESO OBS A = T / Comment");
 
  694     kws.emplace_back(
"HIERARCH ESO OBS B = T / Comment");
 
  695     kws.emplace_back(
"HIERARCH ESO TPL A = T / Comment");
 
  696     kws.emplace_back(
"HIERARCH ESO TPL B = T / Comment");
 
  697     kws.emplace_back(
"HIERARCH ESO GEN A = T / Comment");
 
  698     kws.emplace_back(
"HIERARCH ESO GEN B = T / Comment");
 
  699     kws.emplace_back(
"HIERARCH ESO TEL A = T / Comment");
 
  700     kws.emplace_back(
"HIERARCH ESO TEL B = T / Comment");
 
  701     kws.emplace_back(
"HIERARCH ESO ADA A = T / Comment");
 
  702     kws.emplace_back(
"HIERARCH ESO ADA B = T / Comment");
 
  703     kws.emplace_back(
"HIERARCH ESO INS A = T / Comment");
 
  704     kws.emplace_back(
"HIERARCH ESO INS B = T / Comment");
 
  705     kws.emplace_back(
"HIERARCH ESO DET A = T / Comment");
 
  706     kws.emplace_back(
"HIERARCH ESO DET B = T / Comment");
 
  707     kws.emplace_back(
"HIERARCH ESO DET1 A = T / Comment");
 
  708     kws.emplace_back(
"HIERARCH ESO DET2 B = T / Comment");
 
  709     kws.emplace_back(
"HIERARCH ESO DET3 A = T / Comment");
 
  710     kws.emplace_back(
"HIERARCH ESO DET3 B = T / Comment");
 
  711     kws.emplace_back(
"HIERARCH ESO DPRUN A = T / Comment");
 
  712     kws.emplace_back(
"HIERARCH ESO UNK A = T / Comment");
 
  713     kws.emplace_back(
"HIERARCH ESO UNK B = T / Comment");
 
  715     auto [name, type] = kws[0].GetName();
 
  716     ASSERT_EQ(name, 
"DPR A"sv);
 
  719     std::random_device rd;
 
  720     std::mt19937 g(rd());
 
  722     for (
auto i = 0u; 
i < 20u; ++
i) {
 
  723         auto to_sort_kws = kws;
 
  724         std::shuffle(std::begin(to_sort_kws), std::end(to_sort_kws), g);
 
  726         ASSERT_THAT(to_sort_kws, ContainerEq(kws));
 
  729         EXPECT_THAT(to_sort_kws, ContainerEq(kws));
 
  733 TEST(TestStandardSortV1, EsoKeywordsAreSortedByNumberedCategory) {
 
  734     using namespace testing;
 
  736     std::vector<LiteralKeyword> kws;
 
  737     kws.emplace_back(
"HIERARCH ESO DPR Z = T / Comment");
 
  738     kws.emplace_back(
"HIERARCH ESO DPR1 A = T / Comment");
 
  739     kws.emplace_back(
"HIERARCH ESO DPR2 A = T / Comment");
 
  740     kws.emplace_back(
"HIERARCH ESO DET ZZ ZZ = T / Comment");
 
  741     kws.emplace_back(
"HIERARCH ESO DET1 A = T / Comment");
 
  742     kws.emplace_back(
"HIERARCH ESO DET2 B = T / Comment");
 
  743     kws.emplace_back(
"HIERARCH ESO DET3 A = T / Comment");
 
  744     kws.emplace_back(
"HIERARCH ESO DET3 B = T / Comment");
 
  746     std::random_device rd;
 
  747     std::mt19937 g(rd());
 
  749     for (
auto i = 0u; 
i < 20u; ++
i) {
 
  750         auto to_sort_kws = kws;
 
  751         std::shuffle(std::begin(to_sort_kws), std::end(to_sort_kws), g);
 
  753         ASSERT_THAT(to_sort_kws, ContainerEq(kws));
 
  756         EXPECT_THAT(to_sort_kws, ContainerEq(kws));
 
  760 TEST(TestStandardSortV1, EsoKeywordAreSortedEvenIfCloslyMatched) {
 
  761     using namespace testing;
 
  776 TEST(TestStandardSortV1, EsoKeywordAreSortedEvenIfStrange) {
 
  777     using namespace testing;
 
  779     std::vector<LiteralKeyword> kws;
 
  780     kws.emplace_back(
"HIERARCH ESO A=T");
 
  781     kws.emplace_back(
"HIERARCH ESO D=T");
 
  782     kws.emplace_back(
"HIERARCH ESO D C=T/D");
 
  783     kws.emplace_back(
"HIERARCH ESO E1=T");
 
  784     kws.emplace_back(
"HIERARCH ESO E2 C=T/D");
 
  785     kws.emplace_back(
"HIERARCH ESO F1A1=T");
 
  786     kws.emplace_back(
"HIERARCH ESO F1A2 C=T/D");
 
  788     std::random_device rd;
 
  789     std::mt19937 g(rd());
 
  791     for (
auto i = 0u; 
i < 20u; ++
i) {
 
  792         auto to_sort_kws = kws;
 
  793         std::shuffle(std::begin(to_sort_kws), std::end(to_sort_kws), g);
 
  795         EXPECT_THAT(to_sort_kws, ContainerEq(kws));
 
  799 TEST(TestStandardSortV1, CommentsSortedByName) {
 
  800     using namespace testing;
 
  802     std::vector<LiteralKeyword> kws;
 
  803     kws.emplace_back(
"COMMENT XXX");
 
  804     kws.emplace_back(
"COMMENT AAA");
 
  805     kws.emplace_back(
"HISTORY XXX");
 
  806     kws.emplace_back(
"HISTORY AAA");
 
  808     std::vector<LiteralKeyword> unordered;
 
  809     unordered.emplace_back(
"HISTORY XXX");
 
  810     unordered.emplace_back(
"COMMENT XXX");
 
  811     unordered.emplace_back(
"HISTORY AAA");
 
  812     unordered.emplace_back(
"COMMENT AAA");
 
  816     EXPECT_THAT(unordered, ContainerEq(kws));
 
  819 TEST(TestStandardSortV1, EsoKeywordsAreSortedAlphabetically) {
 
  820     using namespace testing;
 
  822     std::vector<LiteralKeyword> kws;
 
  823     kws.emplace_back(
"HIERARCH ESO KWB = T / Comment");
 
  824     kws.emplace_back(
"HIERARCH ESO KWA = T / Comment");
 
  825     kws.emplace_back(
"HIERARCH ESO KWA A = T / Comment");
 
  826     kws.emplace_back(
"HIERARCH ESO KWA1 A = T / Comment");