ifw-daq  2.1.0-pre1
IFW Data Acquisition modules
testKeyword.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  * @ingroup daq_ocm_fits_test
4  * @copyright 2022 ESO - European Southern Observatory
5  *
6  * @brief Unit tests for json handling.
7  */
8 #include <algorithm>
9 #include <ostream>
10 #include <random>
11 
12 #include <gmock/gmock.h>
13 #include <gtest/gtest.h>
14 #include <nlohmann/json.hpp>
15 
16 #include <daq/error/report.hpp>
17 #include <daq/fits/json.hpp>
18 
19 #include "matchers.hpp"
20 
21 using namespace daq::fits;
22 using namespace std::string_view_literals;
23 
24 class TestLiteralKeyword : public ::testing::Test {
25 public:
26  void ExpectKeyword(std::string_view record,
27  KeywordType type,
28  std::string_view name,
29  std::string_view logical_name) {
30  using namespace ::testing;
31  try {
32  LiteralKeyword kw(record);
33 
34  EXPECT_THAT(record, StartsWith(kw.GetRecord()));
35  EXPECT_EQ(type, kw.GetType());
36  auto [kw_name, kw_type] = kw.GetName();
37  EXPECT_EQ(kw_name, logical_name);
38  EXPECT_EQ(type, kw_type);
39  } catch (...) {
41  throw;
42  }
43  }
44 };
45 
46 TEST_F(TestLiteralKeyword, ConstructStrangeKeywords1) {
47  std::string too_long;
48  std::fill_n(std::back_inserter(too_long), constants::RECORD_LENGTH + 1, 'N');
49  ASSERT_EQ(too_long.size(), constants::RECORD_LENGTH + 1);
50  EXPECT_THROW(LiteralKeyword{too_long}, std::invalid_argument);
51 }
52 
53 TEST_F(TestLiteralKeyword, ConstructStrangeKeywords4) {
54  LiteralKeyword kw{" "};
55  auto [name, type] = kw.GetName();
56  EXPECT_EQ(name, " ");
57  EXPECT_EQ(type, KeywordType::Commentary);
58  EXPECT_EQ(kw.GetRecord(), " ");
59 }
60 
61 TEST_F(TestLiteralKeyword, ConstructStrangeKeywords3) {
62  LiteralKeyword kw{"A"};
63  auto [name, type] = kw.GetName();
64  EXPECT_EQ(name, "A");
65  EXPECT_EQ(type, KeywordType::Commentary);
66  EXPECT_EQ(kw.GetRecord(), "A");
67 }
68 
69 TEST_F(TestLiteralKeyword, ConstructStrangeKeywords5) {
70  LiteralKeyword kw{"A = "};
71  auto [name, type] = kw.GetName();
72  EXPECT_EQ(name, "A");
73  EXPECT_EQ(type, KeywordType::Value);
74  EXPECT_EQ(kw.GetRecord(), "A =");
75 }
76 
77 TEST_F(TestLiteralKeyword, ConstructStrangeKeywords6) {
78  LiteralKeyword kw{"HIERARCH ESO A=T/"};
79  auto [name, type] = kw.GetName();
80  EXPECT_EQ(name, "A");
81  EXPECT_EQ(type, KeywordType::Eso);
82  EXPECT_EQ(kw.GetRecord(), "HIERARCH ESO A=T/");
83 }
84 
85 TEST_F(TestLiteralKeyword, ConstructStrangeKeywords7) {
86  LiteralKeyword kw{"HIERARCH ESO A=''/"};
87  auto [name, type] = kw.GetName();
88  EXPECT_EQ(name, "A");
89  EXPECT_EQ(type, KeywordType::Eso);
90  EXPECT_EQ(kw.GetRecord(), "HIERARCH ESO A=''/");
91 }
92 
93 TEST_F(TestLiteralKeyword, ConstructRegularKeywords) {
94  using namespace ::testing;
95  std::string_view record = "ORIGIN = 'ESO-PARANAL' / European Southern Observatory";
96 
97  std::array<char, constants::RECORD_LENGTH> array_record = {' '};
98  std::copy(std::begin(record), std::end(record), std::begin(array_record));
99  LiteralKeyword kw(array_record);
100  EXPECT_THAT(kw.GetRecord(), StartsWith(record));
101 }
102 
103 TEST_F(TestLiteralKeyword, ValueKeywordTypes) {
104  try {
105  {
106  SCOPED_TRACE("");
107  ExpectKeyword("SIMPLE = T / Standard FITS ",
109  "SIMPLE ",
110  "SIMPLE");
111  }
112  {
113  SCOPED_TRACE("");
114  ExpectKeyword("DATE = '2019-12-12T04:30:53.8930' / Date the file was written",
116  "DATE ",
117  "DATE");
118  }
119  } catch (...) {
121  }
122 }
123 
124 TEST_F(TestLiteralKeyword, InvalidEsoKeywordTypes) {
125  EXPECT_THROW(LiteralKeyword("HIERARCH ESO HAS NO VALUE AFTER INDICATOR="),
126  std::invalid_argument);
127 }
128 
129 TEST_F(TestLiteralKeyword, EsoKeywordTypes) {
130  {
131  SCOPED_TRACE("");
132  ExpectKeyword("HIERARCH ESO ADA ABSROT END = 36.49838 / [deg] Abs rot angle at exp end",
134  "HIERARCH",
135  "ADA ABSROT END");
136  }
137  {
138  SCOPED_TRACE("");
139  ExpectKeyword(
140  "HIERARCH ESO DEL DLT2 VCM CURV = 0.001512604 / mm-1 actual curv. of VCM beam i",
142  "HIERARCH",
143  "DEL DLT2 VCM CURV");
144  }
145  {
146  SCOPED_TRACE("");
147  ExpectKeyword(
148  "HIERARCH ESO DEL DLT2 VCM CURV=0.001512604 / mm-1 actual curv. of VCM beam i",
150  "HIERARCH",
151  "DEL DLT2 VCM CURV");
152  }
153  {
154  SCOPED_TRACE("");
155  ExpectKeyword("HIERARCH ESO N = 0.001512604 / mm-1 actual curv. of VCM beam i",
157  "HIERARCH",
158  "N");
159  }
160  {
161  SCOPED_TRACE("");
162  ExpectKeyword("HIERARCH ESO N =0.001512604 / mm-1 actual curv. of VCM beam i",
164  "HIERARCH",
165  "N");
166  }
167  {
168  SCOPED_TRACE("");
169  ExpectKeyword("HIERARCH ESO N= 0.001512604 / mm-1 actual curv. of VCM beam i",
171  "HIERARCH",
172  "N");
173  }
174  {
175  SCOPED_TRACE("");
176  ExpectKeyword("HIERARCH ESO N=0.001512604 / mm-1 actual curv. of VCM beam i",
178  "HIERARCH",
179  "N");
180  }
181 }
182 
183 TEST_F(TestLiteralKeyword, CommentaryKeywordTypes) {
184  {
185  SCOPED_TRACE("");
186  ExpectKeyword("COMMENT Some comment here", KeywordType::Commentary, "COMMENT ", "COMMENT");
187  }
188  {
189  SCOPED_TRACE("");
190  ExpectKeyword("HISTORY Some comment here", KeywordType::Commentary, "HISTORY ", "HISTORY");
191  }
192  {
193  SCOPED_TRACE("");
194  ExpectKeyword(
195  "HIERARCH DET.CAM.ID = 'TestCamera1Id' / System wide unique ID allocated to camer",
197  "HIERARCH ",
198  "HIERARCH");
199  }
200  {
201  SCOPED_TRACE("");
202  // Invalid ESO HIERARCH keyword
203  ExpectKeyword("HIERARCH ESOADA ABSROT END = 36.49838 / [deg] Abs rot angle at exp end",
205  "HIERARCH",
206  "HIERARCH");
207  }
208  {
209  SCOPED_TRACE("");
210  // Missing = disqualifies keyword to be an HIERARCH ESO keyword.
211  ExpectKeyword("HIERARCH ESO ADA ABSROT END 36.49838 / [deg] Abs rot angle at exp end",
213  "HIERARCH",
214  "HIERARCH");
215  }
216 }
217 
218 TEST_F(TestLiteralKeyword, EqualityOperator) {
219  auto kw1 = LiteralKeyword("HIERARCH ESO UNKN A = T / Comment");
220  auto kw2 = LiteralKeyword("HIERARCH ESO UNKN B = T / Comment");
221  auto kw2_copy = kw2;
222  EXPECT_EQ(kw2, kw2_copy);
223  EXPECT_NE(kw1, kw2);
224  EXPECT_NE(KeywordVariant(kw1), KeywordVariant(kw2));
225  EXPECT_EQ(KeywordVariant(kw2_copy), KeywordVariant(kw2));
226 }
227 
228 TEST_F(TestLiteralKeyword, OstreamOperator) {
229  // Setup
230  using namespace ::testing;
231  using namespace std::literals::string_view_literals;
232 
233  // Test
234  {
235  auto view = "HIERARCH ESO TEL MOON DEC = 22.00047 / [deg] 22:00:01.7 DEC (J2000)"sv;
236  LiteralKeyword kw(view);
237  std::stringstream ss;
238  ss << kw;
239  EXPECT_EQ(ss.str(), view);
240  }
241  {
242  auto view = "COMMENT "sv;
243  LiteralKeyword kw(view);
244  std::stringstream ss;
245  ss << kw;
246  EXPECT_EQ(ss.str(), "COMMENT"sv);
247  }
248 }
249 /**
250  * Note that test cases must use `TypeParam` and cannot declare templated aliases.
251  */
252 template <typename Type>
253 class TestKeyword : public ::testing::Test {};
254 
255 using Types = ::testing::Types<ValueKeyword, EsoKeyword>;
257 
258 TYPED_TEST(TestKeyword, Construction) {
259  using Keyword = TypeParam;
260 
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");
265 
266  Keyword copy_assign;
267  copy_assign = orig;
268  EXPECT_EQ(orig, copy_assign);
269 
270  Keyword move_assign("TOBE", "replaced");
271  move_assign = std::move(copy_assign);
272  EXPECT_EQ(orig, move_assign);
273 
274  Keyword copy_construct(orig);
275  EXPECT_EQ(orig, copy_construct);
276 
277  Keyword move_construct(std::move(copy_construct));
278  EXPECT_EQ(orig, move_construct);
279 }
280 
281 TYPED_TEST(TestKeyword, OptionalComment) {
282  using Keyword = TypeParam;
283 
284  Keyword orig("NAME", 1.0, std::nullopt);
285  EXPECT_EQ(orig.comment, std::nullopt);
286 }
287 
288 /**
289  * Test that construction of keyword from a C string is stored as std::string and not a bool,
290  * which is the implicitly converted type for the variant construction otherwise.
291  */
292 TYPED_TEST(TestKeyword, ConstructFromString) {
293  using Keyword = TypeParam;
294 
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));
298 }
299 
301  using Keyword = TypeParam;
302 
303  Keyword lhs("NAME", 1.0, "comment");
304  Keyword rhs("NAME", 1.0, "comment");
305 
306  EXPECT_TRUE(lhs == rhs);
307  EXPECT_EQ(KeywordVariant(lhs), KeywordVariant(rhs));
308  EXPECT_FALSE(lhs != rhs);
309 
310  rhs.comment = std::nullopt;
311  EXPECT_NE(lhs, rhs);
312 
313  rhs.comment = "comment ";
314  EXPECT_NE(lhs, rhs);
315 }
316 
318  using Keyword = TypeParam;
319 
320  Keyword lhs("A", 1.0, "comment");
321  Keyword rhs("B", 1.0, "comment");
322 
323  EXPECT_LT(lhs, rhs);
324  EXPECT_FALSE(rhs < lhs);
325  EXPECT_FALSE(lhs < lhs);
326 }
327 
328 TYPED_TEST(TestKeyword, OstreamOperator) {
329  using Keyword = TypeParam;
330 
331  // Setup
332  using namespace ::testing;
333 
334  // Test
335  {
336  Keyword kw("NAME", std::string("value"), "comment");
337  std::stringstream ss;
338  ss << kw;
339  EXPECT_EQ(ss.str(), "name='NAME', value=(str)'value', comment='comment'");
340  }
341  {
342  Keyword kw("NAME", true, std::nullopt);
343  std::stringstream ss;
344  ss << kw;
345  EXPECT_EQ(ss.str(), "name='NAME', value=(bool)true, comment=n/a");
346  }
347  {
348  Keyword kw("NAME", 1234ul, std::nullopt);
349  std::stringstream ss;
350  ss << kw;
351  EXPECT_THAT(ss.str(), "name='NAME', value=(uint64_t)1234, comment=n/a");
352  }
353  {
354  Keyword kw("NAME", 1234l, std::nullopt);
355  std::stringstream ss;
356  ss << kw;
357  EXPECT_THAT(ss.str(), "name='NAME', value=(int64_t)1234, comment=n/a");
358  }
359  {
360  Keyword kw("NAME", 1.234, std::nullopt);
361  std::stringstream ss;
362  ss << kw;
363  EXPECT_THAT(ss.str(), "name='NAME', value=(double)1.234, comment=n/a");
364  }
365 }
366 
367 TEST(TestKeywordCombinations, OperatorLessComparesKeywordName) {
368  EsoKeyword eso_kw("A", "B");
369  ValueKeyword value_kw("A", "B");
370 
371  LiteralKeyword literal_value_kw1("B1 = B");
372  LiteralKeyword literal_value_kw2("B2 = B");
373  ASSERT_EQ(KeywordType::Value, literal_value_kw1.GetType());
374  ASSERT_EQ(KeywordType::Value, literal_value_kw2.GetType());
375 
376  LiteralKeyword literal_eso_kw1("HIERARCH ESO A1 = B");
377  LiteralKeyword literal_eso_kw2("HIERARCH ESO A2 = B");
378  ASSERT_EQ(KeywordType::Eso, literal_eso_kw1.GetType());
379  ASSERT_EQ(KeywordType::Eso, literal_eso_kw2.GetType());
380 
381  LiteralKeyword literal_commentary_kw1("COMMENT A");
382  LiteralKeyword literal_commentary_kw2("HIERARCH B");
383  ASSERT_EQ(KeywordType::Commentary, literal_commentary_kw1.GetType());
384  ASSERT_EQ(KeywordType::Commentary, literal_commentary_kw2.GetType());
385 
386  EXPECT_FALSE(value_kw < eso_kw);
387  EXPECT_LT(KeywordVariant(value_kw), KeywordVariant(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);
392 
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);
398 
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);
406  EXPECT_FALSE(KeywordVariant(eso_kw) < KeywordVariant(value_kw));
407 }
408 
409 TEST(TestKeywordCombinations, NameEquals) {
410  EsoKeyword eso_kw("A", "B");
411  ValueKeyword value_kw("A", "B");
412  LiteralKeyword literal_value_kw1("A = B");
413  LiteralKeyword literal_eso_kw1("HIERARCH ESO A = B");
414  LiteralKeyword literal_commentary_kw1("COMMENT A");
415  LiteralKeyword literal_commentary_kw2("COMMENT B");
416 
417  auto view = GetKeywordName(eso_kw);
418  EXPECT_EQ(view, eso_kw.GetName());
419 
420  EXPECT_FALSE(NameEquals(value_kw, eso_kw));
421  EXPECT_FALSE(NameEquals(eso_kw, value_kw));
422 
423  EXPECT_NE(literal_value_kw1.GetName(), literal_eso_kw1.GetName());
424  EXPECT_NE(literal_value_kw1.GetName(), literal_commentary_kw1.GetName());
425  EXPECT_EQ(eso_kw.GetName(), literal_eso_kw1.GetName());
426  EXPECT_EQ(literal_value_kw1.GetName(), value_kw.GetName());
427  EXPECT_EQ(literal_commentary_kw1.GetName(), literal_commentary_kw2.GetName());
428 
429  EXPECT_TRUE(NameEquals(value_kw, ValueKeyword("A", "NOT B")));
430  EXPECT_TRUE(NameEquals(eso_kw, EsoKeyword("A", "NOT B")));
431 }
432 
434  using namespace daq::fits;
435  using namespace ::testing;
436  // Setup
437 
438  // Initial values to be updated
439  KeywordVector to{EsoKeyword("A", "A1-orig"),
440  ValueKeyword("A", "A2-orig"),
441  ValueKeyword("B", static_cast<uint64_t>(1u)),
442  EsoKeyword("B", "B")};
443 
444  // Overwrite the A keywords and insert the C keywords
445  KeywordVector from{EsoKeyword("A", "A1-updated"),
446  ValueKeyword("A", "A2-updated"),
447  ValueKeyword("C", "C"),
448  EsoKeyword("C", true)};
449 
450  KeywordVector result{EsoKeyword("A", "A1-updated"),
451  ValueKeyword("A", "A2-updated"),
452  ValueKeyword("B", static_cast<uint64_t>(1u)),
453  EsoKeyword("B", "B"),
454  ValueKeyword("C", "C"),
455  EsoKeyword("C", true)};
456  // Run
457  UpdateKeywords(to, from);
458  EXPECT_THAT(to, ContainerEq(result));
459 }
460 
461 TEST(KeywordVector, InsertKeywordsEnd) {
462  using namespace daq::fits;
463  using namespace ::testing;
464  // Setup
465 
466  // Initial values to be updated
467  KeywordVector to{
468  EsoKeyword("A", "A1-orig"),
469  ValueKeyword("A", "A2-orig"),
470  ValueKeyword("B", static_cast<uint64_t>(1u)),
471  EsoKeyword("B", "B")
472  // << INSERT POSITION
473  };
474 
475  // Overwrite the A keywords and insert the C keywords
476  KeywordVector from{EsoKeyword("A", "A1-updated"),
477  ValueKeyword("A", "A2-updated"),
478  ValueKeyword("C", "C"),
479  EsoKeyword("C", true)};
480 
481  KeywordVector result{ValueKeyword("B", static_cast<uint64_t>(1u)),
482  EsoKeyword("B", "B"),
483  EsoKeyword("A", "A1-updated"),
484  ValueKeyword("A", "A2-updated"),
485  ValueKeyword("C", "C"),
486  EsoKeyword("C", true)
487 
488  };
489  // Run
490  InsertKeywords(to, to.end(), from.begin(), from.end());
491  EXPECT_THAT(to, ContainerEq(result));
492 }
493 
494 TEST(KeywordVector, InsertKeywordsBegin) {
495  using namespace daq::fits;
496  using namespace ::testing;
497  // Setup
498 
499  // Initial values to be updated
500  KeywordVector to{// << INSERT POSITION
501  EsoKeyword("A", "A1-orig"),
502  ValueKeyword("A", "A2-orig"),
503  ValueKeyword("B", static_cast<uint64_t>(1u)),
504  EsoKeyword("B", "B")};
505 
506  // Overwrite the A keywords and insert the C keywords
507  KeywordVector from{EsoKeyword("A", "A1-updated"),
508  ValueKeyword("A", "A2-updated"),
509  ValueKeyword("C", "C"),
510  EsoKeyword("C", true)};
511 
512  KeywordVector result{EsoKeyword("A", "A1-updated"),
513  ValueKeyword("A", "A2-updated"),
514  ValueKeyword("C", "C"),
515  EsoKeyword("C", true),
516  ValueKeyword("B", static_cast<uint64_t>(1u)),
517  EsoKeyword("B", "B")
518 
519  };
520  // Run
521  InsertKeywords(to, to.begin(), from.begin(), from.end());
522  EXPECT_THAT(to, ContainerEq(result));
523 }
524 
525 TEST(KeywordVector, InsertKeywordsMiddle) {
526  using namespace daq::fits;
527  using namespace ::testing;
528  // Setup
529 
530  // Initial values to be updated
531  KeywordVector to{EsoKeyword("A", "A1-orig"),
532  ValueKeyword("A", "A2-orig"),
533  ValueKeyword("B", static_cast<uint64_t>(1u)),
534  // << INSERT POSITION
535  EsoKeyword("B", "B")};
536 
537  // Overwrite the A keywords and insert the C keywords
538  KeywordVector from{EsoKeyword("A", "A1-updated"),
539  ValueKeyword("A", "A2-updated"),
540  ValueKeyword("C", "C"),
541  EsoKeyword("C", true)};
542 
543  KeywordVector result{ValueKeyword("B", static_cast<uint64_t>(1u)),
544  EsoKeyword("A", "A1-updated"),
545  ValueKeyword("A", "A2-updated"),
546  ValueKeyword("C", "C"),
547  EsoKeyword("C", true),
548  EsoKeyword("B", "B")
549 
550  };
551 
552  // Run
553  auto pos = to.begin();
554  std::advance(pos, 3);
555  InsertKeywords(to, pos, from.begin(), from.end());
556  EXPECT_THAT(to, ContainerEq(result));
557 }
558 
559 TEST(TestFormatting, ValueKeyword) {
560  EXPECT_EQ(LiteralKeyword("DATE-OBS= '2019-12-12T04:25:48.0068' / Observing date"),
561  Format(ValueKeyword("DATE-OBS", "2019-12-12T04:25:48.0068", "Observing date")));
562  EXPECT_EQ(
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")));
565  EXPECT_EQ(LiteralKeyword("EQUINOX = 2000.0 / Standard FK5"),
566  Format(ValueKeyword("EQUINOX ", 2000.0, "Standard FK5")));
567  EXPECT_EQ(LiteralKeyword("EXTEND = T / FITS Extension may be present"),
568  Format(ValueKeyword("EXTEND", true, "FITS Extension may be present")));
569  EXPECT_EQ(LiteralKeyword("ORIGIN = 'ESO-PARANAL' / European Southern Observatory"),
570  Format(ValueKeyword("ORIGIN", "ESO-PARANAL", "European Southern Observatory")));
571  EXPECT_EQ(
572  LiteralKeyword("INHERIT = F / denotes the INHERIT keyword convention"),
573  Format(ValueKeyword("INHERIT", false, "denotes the INHERIT keyword convention")));
574 
575  EXPECT_EQ(LiteralKeyword("XTENSION= 'IMAGE ' / Image extension"),
576  Format(ValueKeyword("XTENSION", "IMAGE", "Image extension")))
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"),
579  Format(ValueKeyword("XTENSION", "CUSTOM-EXTENSION-TYPE", "Image extension")))
580  << "XTENSION has an exception and must be padded to be minimum 8 chars for legacy reasons";
581 }
582 
583 TEST(TestFormatting, EsoKeyword) {
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"),
587  Format(EsoKeyword("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"),
591  Format(EsoKeyword("DET ADC1 CLAMP", false, "Clamp-and-Sample")));
592  EXPECT_EQ(
594  "HIERARCH ESO DET CLDC1 DCNM16 = 'DC16_RelaySwitchLowActive' / Name of bias volta"),
595  Format(
596  EsoKeyword("DET CLDC1 DCNM16", "DC16_RelaySwitchLowActive", "Name of bias voltage")));
597  EXPECT_EQ(
599  "HIERARCH ESO PCR SUMMARY NSCANNED = 1273000 / Total Number of scanned records."),
600  Format(EsoKeyword("PCR SUMMARY NSCANNED", 1273000ul, "Total Number of scanned records.")));
601  EXPECT_EQ(
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")));
605 }
606 
607 TEST(TestFormatting, LiteralKeyword) {
608  EXPECT_EQ(
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")));
613 }
614 
615 TEST(TestFormatting, TooLongKeywords) {
616  // First test the longest possible ESO keyword while still formatting with padded value
617  // indicator ' = '.
618  EXPECT_EQ(LiteralKeyword("HIERARCH ESO LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG "
619  "LONG LON = T"),
620  Format(EsoKeyword("LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LON",
621  true)));
622  EXPECT_THROW(
623  LiteralKeyword("HIERARCH ESO LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG "
624  "LONG LONX = T"),
625  std::invalid_argument);
626  EXPECT_THROW(Format(EsoKeyword(
627  "LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONX", true)),
628  std::invalid_argument);
629  EXPECT_THROW(
630  Format(EsoKeyword("LONG LONG LONG LONG LONG LONG LONG", "LONG LONG LONG LONG LONG LONX")),
631  std::invalid_argument);
632 }
633 
634 TEST(TestFormatting, KeywordVariant) {
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"))));
641 
642  auto kw = LiteralKeyword("HIERARCH ESO ADA ABSROT END = 36.5 / [deg] Abs rot angle at exp end");
643  EXPECT_EQ(kw, Format(KeywordVariant(kw)));
644 }
645 
646 TEST(TestStandardSortV1, StableSortsValueKeywords) {
647  using namespace testing;
648  // Setup
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");
653 
654  // Test
655  v1::StandardSort(kws);
656  EXPECT_THAT(kws,
657  ElementsAre(IsKeyword("SIMPLE", KeywordType::Value),
658  IsKeyword("BITPIX", KeywordType::Value),
659  IsKeyword("NAXIS", KeywordType::Value)));
660 }
661 
662 TEST(TestStandardSortV1, StableSortsByKeywordType) {
663  using namespace testing;
664  // Setup
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");
672 
673  // Test
674  auto sorted = kws;
675  v1::StandardSort(sorted);
676  EXPECT_THAT(sorted,
677  ElementsAre(IsKeyword("SIMPLE", KeywordType::Value),
678  IsKeyword("BITPIX", KeywordType::Value),
679  IsKeyword("DET A", KeywordType::Eso),
680  IsKeyword("KW", KeywordType::Eso),
681  kws[0],
682  kws[4]));
683 }
684 
685 TEST(TestStandardSortV1, EsoKeywordsAreSortedByCategory) {
686  using namespace testing;
687  // Setup
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");
714 
715  auto [name, type] = kws[0].GetName();
716  ASSERT_EQ(name, "DPR A"sv);
717  ASSERT_EQ(type, KeywordType::Eso);
718 
719  std::random_device rd;
720  std::mt19937 g(rd());
721  // Test
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);
725  v1::StandardSort(to_sort_kws);
726  ASSERT_THAT(to_sort_kws, ContainerEq(kws));
727  // Sorting again shouldn't do anything
728  v1::StandardSort(to_sort_kws);
729  EXPECT_THAT(to_sort_kws, ContainerEq(kws));
730  }
731 }
732 
733 TEST(TestStandardSortV1, EsoKeywordsAreSortedByNumberedCategory) {
734  using namespace testing;
735  // Setup
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");
745 
746  std::random_device rd;
747  std::mt19937 g(rd());
748  // Test
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);
752  v1::StandardSort(to_sort_kws);
753  ASSERT_THAT(to_sort_kws, ContainerEq(kws));
754  // Sorting again shouldn't do anything
755  v1::StandardSort(to_sort_kws);
756  EXPECT_THAT(to_sort_kws, ContainerEq(kws));
757  }
758 }
759 
760 TEST(TestStandardSortV1, EsoKeywordAreSortedEvenIfCloslyMatched) {
761  using namespace testing;
762  // Setup
763  auto dpr = LiteralKeyword("HIERARCH ESO DPR=T");
764  auto det1 = LiteralKeyword("HIERARCH ESO DET1 A=T");
765  auto deta = LiteralKeyword("HIERARCH ESO DETA=Z/D");
766  EXPECT_TRUE(v1::StandardLess()(dpr, det1));
767  EXPECT_FALSE(v1::StandardLess()(det1, dpr));
768 
769  EXPECT_TRUE(v1::StandardLess()(dpr, deta));
770  EXPECT_FALSE(v1::StandardLess()(deta, dpr));
771 
772  EXPECT_TRUE(v1::StandardLess()(det1, deta));
773  EXPECT_FALSE(v1::StandardLess()(deta, det1));
774 }
775 
776 TEST(TestStandardSortV1, EsoKeywordAreSortedEvenIfStrange) {
777  using namespace testing;
778  // Setup
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");
787 
788  std::random_device rd;
789  std::mt19937 g(rd());
790  // Test
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);
794  v1::StandardSort(to_sort_kws);
795  EXPECT_THAT(to_sort_kws, ContainerEq(kws));
796  }
797 }
798 
799 TEST(TestStandardSortV1, CommentsSortedByName) {
800  using namespace testing;
801  // Setup
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");
807 
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");
813 
814  // Test
815  v1::StandardSort(unordered);
816  EXPECT_THAT(unordered, ContainerEq(kws));
817 }
818 
819 TEST(TestStandardSortV1, EsoKeywordsAreSortedAlphabetically) {
820  using namespace testing;
821  // Setup
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");
827 
828  // Test
829  v1::StandardSort(kws);
830  EXPECT_THAT(kws,
831  ElementsAre(IsKeyword("KWA", KeywordType::Eso),
832  IsKeyword("KWA A", KeywordType::Eso),
833  IsKeyword("KWA1 A", KeywordType::Eso),
834  IsKeyword("KWB", KeywordType::Eso)));
835 }
daq::fits::LiteralKeyword::GetRecord
std::string_view GetRecord() const &noexcept
Definition: keyword.cpp:414
daq::error::ReportNestedExceptions
void ReportNestedExceptions(std::ostream &os) noexcept
Definition: report.cpp:50
TYPED_TEST
TYPED_TEST(TestKeyword, Construction)
Definition: testKeyword.cpp:258
daq::TEST
TEST(TestDaqContext, Files)
Definition: testDaqContext.cpp:16
daq::fits::KeywordVariant
std::variant< ValueKeyword, EsoKeyword, LiteralKeyword > KeywordVariant
The different variants of keywords that are supported.
Definition: keyword.hpp:400
TestLiteralKeyword::ExpectKeyword
void ExpectKeyword(std::string_view record, KeywordType type, std::string_view name, std::string_view logical_name)
Definition: testKeyword.cpp:26
json.hpp
Contains data structure for FITS keywords.
daq::fits::v1::StandardSort
void StandardSort(std::vector< LiteralKeyword > &keywords)
Sorts keywords according to ESO DICD standards.
Definition: keyword.cpp:716
Types
::testing::Types< ValueKeyword, EsoKeyword > Types
Definition: testKeyword.cpp:255
daq::fits::KeywordType
KeywordType
Type of FITS keyword.
Definition: keyword.hpp:64
daq::fits::NameEquals
bool NameEquals(KeywordVariant const &lhs, KeywordVariant const &rhs) noexcept
Compare logical keyword names of keyword of the same type.
Definition: keyword.cpp:477
TYPED_TEST_CASE
TYPED_TEST_CASE(TestKeyword, Types)
daq::fits::Eso
@ Eso
An ESO hiearchical keyword.
Definition: keyword.hpp:72
daq::fits::BasicKeyword::GetName
constexpr KeywordNameView GetName() const &noexcept
Query logical keyword name.
Definition: keyword.hpp:285
report.hpp
daq::fits::InsertKeywords
void InsertKeywords(KeywordVector &keywords, KeywordVector::iterator position, KeywordVector::const_iterator from_first, KeywordVector::const_iterator from_last)
Insert keywords.
Definition: keyword.cpp:569
daq::fits
Definition: cfitsio.cpp:14
daq::fits::EsoKeyword
BasicKeyword< EsoKeywordTraits > EsoKeyword
ESO hiearchical keyword.
Definition: keyword.hpp:337
daq::fits::Format
LiteralKeyword Format(KeywordVariant const &keyword)
Definition: keyword.cpp:622
daq::fits::BasicKeyword
A type safe version of LiteralKeyword that consist of the three basic components of a FITS keyword ke...
Definition: keyword.hpp:266
daq::fits::LiteralKeyword::GetName
constexpr KeywordNameView GetName() const &noexcept
Query logical keyword name.
Definition: keyword.hpp:186
daq::TEST_F
TEST_F(TestDpmDaqController, StatusUpdateInNotScheduledSucceeds)
Definition: testDpmDaqController.cpp:60
TestKeyword
Note that test cases must use TypeParam and cannot declare templated aliases.
Definition: testKeyword.cpp:253
daq::fits::Commentary
@ Commentary
A commentary keyword, which are keywords that do not fall into the previous categories.
Definition: keyword.hpp:76
matchers.hpp
Contains common matchers.
daq::fits::LiteralKeyword::GetType
constexpr KeywordType GetType() const noexcept
Definition: keyword.hpp:170
daq::fits::v1::StandardLess
Sorting function object.
Definition: keyword.hpp:533
daq::fits::UpdateKeywords
void UpdateKeywords(KeywordVector &to, KeywordVector const &from, ConflictPolicy policy=ConflictPolicy::Replace)
Updates to with keywords from from.
Definition: keyword.cpp:553
TestLiteralKeyword
Definition: testKeyword.cpp:24
daq::fits::ValueKeyword
BasicKeyword< ValueKeywordTraits > ValueKeyword
Standard FITS value keyword.
Definition: keyword.hpp:330
daq::fits::KeywordVector
std::vector< KeywordVariant > KeywordVector
Vector of keywords.
Definition: keyword.hpp:414
daq::fits::Value
@ Value
A value keyword.
Definition: keyword.hpp:68
daq::fits::GetKeywordName
constexpr KeywordNameView GetKeywordName(EsoKeyword const &keyword) noexcept
Get keyword name from keyword.
Definition: keyword.hpp:427
segfault.i
i
Definition: segfault.py:30
daq::fits::LiteralKeyword
Represents the literal 80-character FITS keyword record.
Definition: keyword.hpp:125