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