LogManagerTest.h 21.8 KB
Newer Older
1
2
3
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4
5
//   NScD Oak Ridge National Laboratory, European Spallation Source,
//   Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6
// SPDX - License - Identifier: GPL - 3.0 +
7
#pragma once
8
9

#include "MantidAPI/LogManager.h"
LamarMoore's avatar
LamarMoore committed
10
#include "MantidGeometry/Instrument/Goniometer.h"
11
12
13
14
15
#include "MantidKernel/Exception.h"
#include "MantidKernel/Matrix.h"
#include "MantidKernel/Property.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/V3D.h"
16
#include "MantidTestHelpers/NexusTestHelper.h"
17
#include <cmath>
LamarMoore's avatar
LamarMoore committed
18
#include <cxxtest/TestSuite.h>
19
#include <json/value.h>
20
21
22
23

using namespace Mantid::Kernel;
using namespace Mantid::API;
using namespace Mantid::Geometry;
24
using Mantid::Types::Core::DateAndTime;
25
26

// Helper class
27
28
29
30
namespace {
class ConcreteProperty : public Property {
public:
  ConcreteProperty() : Property("Test", typeid(int)) {}
31
32
33
34
35
  ConcreteProperty *clone() const override {
    return new ConcreteProperty(*this);
  }
  bool isDefault() const override { return true; }
  std::string getDefault() const override {
36
    return "getDefault() is not implemented in this class";
37
  }
38
  std::string value() const override { return "Nothing"; }
39
  Json::Value valueAsJson() const override { return Json::Value(); }
40
  std::string setValue(const std::string &) override { return ""; }
41
  std::string setValueFromJson(const Json::Value &) override { return ""; }
42
43
44
45
46
  std::string setValueFromProperty(const Property &) override { return ""; }
  std::string setDataItem(const boost::shared_ptr<DataItem>) override {
    return "";
  }
  Property &operator+=(Property const *) override { return *this; }
47
48
};

49
template <typename T>
50
void addTestTimeSeries(LogManager &run, const std::string &name) {
51
  auto timeSeries = new TimeSeriesProperty<T>(name);
52
53
54
55
56
57
58
59
60
61
62
63
  timeSeries->addValue("2012-07-19T16:17:00", 2);
  timeSeries->addValue("2012-07-19T16:17:10", 3);
  timeSeries->addValue("2012-07-19T16:17:20", 4);
  timeSeries->addValue("2012-07-19T16:17:30", 5);
  timeSeries->addValue("2012-07-19T16:17:40", 6);
  timeSeries->addValue("2012-07-19T16:17:50", 20);
  timeSeries->addValue("2012-07-19T16:18:00", 21);
  timeSeries->addValue("2012-07-19T16:18:10", 22);
  timeSeries->addValue("2012-07-19T16:19:20", 23);
  timeSeries->addValue("2012-07-19T16:19:20", 24);
  run.addProperty(timeSeries);
}
LamarMoore's avatar
LamarMoore committed
64
} // namespace
65

David Fairbrother's avatar
David Fairbrother committed
66
67
void addTimeSeriesEntry(LogManager &runInfo, const std::string &name,
                        double val) {
68
69
70
71
72
73
74
  TimeSeriesProperty<double> *tsp;
  tsp = new TimeSeriesProperty<double>(name);
  tsp->addValue("2011-05-24T00:00:00", val);
  runInfo.addProperty(tsp);
}

class LogManagerTest : public CxxTest::TestSuite {
75
public:
76
  void testAddGetData() {
77
78
79
    LogManager runInfo;

    Property *p = new ConcreteProperty();
80
    TS_ASSERT_THROWS_NOTHING(runInfo.addProperty(p));
81

82
    Property *pp = nullptr;
83
84
85
86
87
    TS_ASSERT_THROWS_NOTHING(pp = runInfo.getProperty("Test"));
    TS_ASSERT_EQUALS(p, pp);
    TS_ASSERT(!pp->name().compare("Test"));
    TS_ASSERT(dynamic_cast<ConcreteProperty *>(pp));
    TS_ASSERT_THROWS(pp = runInfo.getProperty("NotThere"),
88
                     const Exception::NotFoundError &);
89
90
91
92
93
94

    std::vector<Property *> props = runInfo.getProperties();
    TS_ASSERT(!props.empty());
    TS_ASSERT_EQUALS(props.size(), 1);
    TS_ASSERT(!props[0]->name().compare("Test"));
    TS_ASSERT(dynamic_cast<ConcreteProperty *>(props[0]));
95
96
  }

97
  void testRemoveLogData() {
98
    LogManager runInfo;
99

100
    Property *p = new ConcreteProperty();
101
102
103
    TS_ASSERT_THROWS_NOTHING(runInfo.addProperty(p));
    TS_ASSERT_THROWS_NOTHING(runInfo.removeProperty("Test"));
    TS_ASSERT_EQUALS(runInfo.getProperties().size(), 0);
104
105
  }

106
  void testStartTime() {
107
108
    LogManager runInfo;
    // Nothing there yet
109
    TS_ASSERT_THROWS(runInfo.startTime(), const std::runtime_error &);
110
111
    // Add run_start and see that get picked up
    const std::string run_start("2013-12-19T13:38:00");
112
113
    auto run_start_prop =
        new PropertyWithValue<std::string>("run_start", run_start);
114
    runInfo.addProperty(run_start_prop);
115
    TS_ASSERT_EQUALS(runInfo.startTime(), DateAndTime(run_start));
116
117
    // Add start_time and see that get picked up in preference
    const std::string start_time("2013-12-19T13:40:00");
118
119
    auto start_time_prop =
        new PropertyWithValue<std::string>("start_time", start_time);
120
    runInfo.addProperty(start_time_prop);
121
    TS_ASSERT_EQUALS(runInfo.startTime(), DateAndTime(start_time));
122
123
124
    // But get back run_start again if start_time is equal to the epoch
    const std::string epoch("1990-01-01T00:00:00");
    start_time_prop->setValue(epoch);
125
    TS_ASSERT_EQUALS(runInfo.startTime(), DateAndTime(run_start));
126
127
    // And back to failure if they're both that
    run_start_prop->setValue(epoch);
128
    TS_ASSERT_THROWS(runInfo.startTime(), const std::runtime_error &);
129
130
131
132

    // Set run_start back to valid value and make start_time contain nonsense
    run_start_prop->setValue(run_start);
    start_time_prop->setValue("__");
133
    TS_ASSERT_EQUALS(runInfo.startTime(), DateAndTime(run_start));
134
135
    // Now make start_time a completely different property type
    runInfo.removeProperty("start_time");
136
    runInfo.addProperty(new PropertyWithValue<double>("start_time", 3.33));
137
    TS_ASSERT_EQUALS(runInfo.startTime(), DateAndTime(run_start));
138
139
    // Now make run_start something invalid
    run_start_prop->setValue("notADate");
140
    TS_ASSERT_THROWS(runInfo.startTime(), const std::runtime_error &);
141
142
    // And check things if it's the wrong property type
    runInfo.removeProperty("run_start");
143
    addTimeSeriesEntry(runInfo, "run_start", 4.44);
144
    TS_ASSERT_THROWS(runInfo.startTime(), const std::runtime_error &);
145
  }
146

147
  void testEndTime() {
148
149
    LogManager runInfo;
    // Nothing there yet
150
    TS_ASSERT_THROWS(runInfo.endTime(), const std::runtime_error &);
151
152
    // Add run_end and see that get picked up
    const std::string run_end("2013-12-19T13:38:00");
153
    auto run_end_prop = new PropertyWithValue<std::string>("run_end", run_end);
154
    runInfo.addProperty(run_end_prop);
155
    TS_ASSERT_EQUALS(runInfo.endTime(), DateAndTime(run_end));
156
157
    // Add end_time and see that get picked up in preference
    const std::string end_time("2013-12-19T13:40:00");
158
159
    auto end_time_prop =
        new PropertyWithValue<std::string>("end_time", end_time);
160
    runInfo.addProperty(end_time_prop);
161
    TS_ASSERT_EQUALS(runInfo.endTime(), DateAndTime(end_time));
162
163
164
165

    // Set run_end back to valid value and make end_time contain nonsense
    run_end_prop->setValue(run_end);
    end_time_prop->setValue("__");
166
    TS_ASSERT_EQUALS(runInfo.endTime(), DateAndTime(run_end));
167
168
    // Now make end_time a completely different property type
    runInfo.removeProperty("end_time");
169
    runInfo.addProperty(new PropertyWithValue<double>("end_time", 3.33));
170
    TS_ASSERT_EQUALS(runInfo.endTime(), DateAndTime(run_end));
171
172
    // Now make run_end something invalid
    run_end_prop->setValue("notADate");
173
    TS_ASSERT_THROWS(runInfo.endTime(), const std::runtime_error &);
174
175
    // And check things if it's the wrong property type
    runInfo.removeProperty("run_end");
176
    addTimeSeriesEntry(runInfo, "run_end", 4.44);
177
    TS_ASSERT_THROWS(runInfo.endTime(), const std::runtime_error &);
178
179
  }

180
  void testMemory() {
181
    LogManager runInfo;
182
183
    TS_ASSERT_EQUALS(runInfo.getMemorySize(), 0);

184
185
186
    Property *p = new ConcreteProperty();
    runInfo.addProperty(p);

187
188
    TS_ASSERT_EQUALS(runInfo.getMemorySize(),
                     sizeof(ConcreteProperty) + sizeof(void *));
189
190
  }

191
  void test_GetTimeSeriesProperty_Returns_TSP_When_Log_Exists() {
192
    LogManager runInfo;
193
    const std::string &name = "double_time_series";
194
195
196
    const double value = 10.9;
    addTimeSeriesEntry(runInfo, name, value);

197
    TimeSeriesProperty<double> *tsp(nullptr);
198
199
200
201
    TS_ASSERT_THROWS_NOTHING(tsp = runInfo.getTimeSeriesProperty<double>(name));
    TS_ASSERT_DELTA(tsp->firstValue(), value, 1e-12);
  }

202
  void test_GetTimeSeriesProperty_Throws_When_Log_Does_Not_Exist() {
203
    LogManager runInfo;
204
    TS_ASSERT_THROWS(runInfo.getTimeSeriesProperty<double>("not_a_log"),
205
                     const Exception::NotFoundError &);
206
207
  }

208
209
  void
  test_GetTimeSeriesProperty_Throws_When_Log_Exists_But_Is_Not_Correct_Type() {
210
    LogManager runInfo;
211
    const std::string &name = "double_prop";
212
213
    runInfo.addProperty(name, 5.6); // Standard double property

214
    TS_ASSERT_THROWS(runInfo.getTimeSeriesProperty<double>(name),
215
                     const std::invalid_argument &);
216
217
  }

218
  void test_GetPropertyAsType_Throws_When_Property_Does_Not_Exist() {
219
    LogManager runInfo;
220
    TS_ASSERT_THROWS(runInfo.getPropertyValueAsType<double>("not_a_log"),
221
                     const Exception::NotFoundError &);
222
223
  }

224
  void test_GetPropertyAsType_Returns_Expected_Value_When_Type_Is_Correct() {
225
    LogManager runInfo;
226
    const std::string &name = "double_prop";
227
228
229
230
    const double value = 5.6;
    runInfo.addProperty(name, value); // Standard double property

    double retrieved(0.0);
231
232
    TS_ASSERT_THROWS_NOTHING(retrieved =
                                 runInfo.getPropertyValueAsType<double>(name));
233
234
235
    TS_ASSERT_DELTA(retrieved, value, 1e-12);
  }

236
  void test_GetPropertyAsType_Throws_When_Requested_Type_Does_Not_Match() {
237
238
239
    LogManager runInfo;
    runInfo.addProperty("double_prop", 6.7); // Standard double property

240
    TS_ASSERT_THROWS(runInfo.getPropertyValueAsType<int>("double_prop"),
241
                     const std::invalid_argument &);
242
243
  }

244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  void test_GetPropertyAsSingleValue_SingleValue_DoubleType() {
    doTest_GetPropertyAsSingleValue_SingleType<double>(1.0);
  }

  void test_GetPropertyAsSingleValue_SingleValue_FloatType() {
    doTest_GetPropertyAsSingleValue_SingleType<float>(1.0F);
  }

  void test_GetPropertyAsSingleValue_SingleValue_Int32Type() {
    doTest_GetPropertyAsSingleValue_SingleType<int32_t>(1);
  }

  void test_GetPropertyAsSingleValue_SingleValue_Int64Type() {
    doTest_GetPropertyAsSingleValue_SingleType<int64_t>(1L);
  }

  void test_GetPropertyAsSingleValue_SingleValue_Uint32Type() {
    doTest_GetPropertyAsSingleValue_SingleType<uint32_t>(1U);
  }

  void test_GetPropertyAsSingleValue_SingleValue_Uint64Type() {
    doTest_GetPropertyAsSingleValue_SingleType<uint64_t>(1UL);
  }

268
  void test_GetPropertyAsSingleValue_SingleValue_StringType() {
269
270
271
272
273
    LogManager runInfo;
    const std::string name = "string_prop", value = "1";
    runInfo.addProperty<std::string>(name, value);
    double result = std::nan("1");
    TS_ASSERT_THROWS_NOTHING(result = runInfo.getPropertyAsSingleValue(name));
274
    TS_ASSERT_DELTA(1.0, result, 1e-12);
275
276
  }

277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  void test_GetPropertyAsIntegerValue_SingleValue_Int32Type() {
    doTest_GetPropertyAsIntegerValue<int32_t>(1);
  }

  void test_GetPropertyAsIntegerValue_SingleValue_Int64Type() {
    doTest_GetPropertyAsIntegerValue<int64_t>(1L);
  }

  void test_GetPropertyAsIntegerValue_SingleValue_Uint32Type() {
    doTest_GetPropertyAsIntegerValue<uint32_t>(1U);
  }

  void test_GetPropertyAsIntegerValue_SingleValue_Uint64Type() {
    doTest_GetPropertyAsIntegerValue<uint64_t>(1UL);
  }

  void test_GetPropertyAsSingleInteger_DoubleType_Throws() {
    LogManager runInfo;
    const std::string name = "T_prop";
    runInfo.addProperty<double>(name, 1.0);
    TS_ASSERT_THROWS(runInfo.getPropertyAsIntegerValue(name),
298
                     const std::invalid_argument &);
299
300
301
302
303
  }

  void test_GetPropertyAsSingleInteger_Throws_for_nonexistant_property() {
    LogManager runInfo;
    TS_ASSERT_THROWS(runInfo.getPropertyAsIntegerValue("T_prop"),
304
                     const Exception::NotFoundError &);
305
306
  }

307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
  void test_GetPropertyAsSingleValue_TimeSeries_DoubleType() {
    doTest_GetPropertyAsSingleValue_TimeSeriesType<double>();
  }

  void test_GetPropertyAsSingleValue_TimeSeries_FloatType() {
    doTest_GetPropertyAsSingleValue_TimeSeriesType<float>();
  }

  void test_GetPropertyAsSingleValue_TimeSeries_Int32Type() {
    doTest_GetPropertyAsSingleValue_TimeSeriesType<int32_t>();
  }

  void test_GetPropertyAsSingleValue_TimeSeries_Int64Type() {
    doTest_GetPropertyAsSingleValue_TimeSeriesType<int64_t>();
  }

  void test_GetPropertyAsSingleValue_TimeSeries_Uint32Type() {
    doTest_GetPropertyAsSingleValue_TimeSeriesType<uint32_t>();
  }

  void test_GetPropertyAsSingleValue_TimeSeries_Uint64Type() {
    doTest_GetPropertyAsSingleValue_TimeSeriesType<uint64_t>();
  }

331
  void test_GetPropertyAsSingleValue_Throws_If_String_Is_Invalid() {
332
    LogManager runInfo;
333
    const std::string name = "string_prop";
334
335
336
    runInfo.addProperty<std::string>(name, "hello"); // not a number

    TS_ASSERT_THROWS(runInfo.getPropertyAsSingleValue(name),
337
                     const std::invalid_argument &);
338
339
340
341
342
343
344
345
  }

  void
  test_GetPropertyAsSingleValue_Throws_If_Type_Is_Not_Numeric_Or_TimeSeries_Numeric_Or_Valid_String() {
    LogManager runInfo;
    const std::string name = "bool_prop";
    const bool value(false);
    runInfo.addProperty<bool>(name, value); // Adds a bool property
346

347
    TS_ASSERT_THROWS(runInfo.getPropertyAsSingleValue(name),
348
                     const std::invalid_argument &);
349
350
  }

351
352
  void
  test_GetPropertyAsSingleValue_Returns_Simple_Mean_By_Default_For_Time_Series() {
353
354
    LogManager runInfo;
    const std::string name = "series";
355
    addTestTimeSeries<double>(runInfo, name);
356
357

    const double expectedValue(13.0);
358
359
    TS_ASSERT_DELTA(runInfo.getPropertyAsSingleValue(name), expectedValue,
                    1e-12);
360
361
  }

362
363
  void
  test_GetPropertyAsSingleValue_Returns_Correct_SingleValue_For_Each_StatisticType() {
364
365
    LogManager runInfo;
    const std::string name = "series";
366
    addTestTimeSeries<double>(runInfo, name);
367
368
369
370
371
372
373
374
375
376
377
378
379

    TS_ASSERT_DELTA(runInfo.getPropertyAsSingleValue(name, Math::Mean), 13.0,
                    1e-12);
    TS_ASSERT_DELTA(runInfo.getPropertyAsSingleValue(name, Math::Minimum), 2.0,
                    1e-12);
    TS_ASSERT_DELTA(runInfo.getPropertyAsSingleValue(name, Math::Maximum), 24.0,
                    1e-12);
    TS_ASSERT_DELTA(runInfo.getPropertyAsSingleValue(name, Math::FirstValue),
                    2.0, 1e-12);
    TS_ASSERT_DELTA(runInfo.getPropertyAsSingleValue(name, Math::LastValue),
                    24.0, 1e-12);
    TS_ASSERT_DELTA(runInfo.getPropertyAsSingleValue(name, Math::Median), 13.0,
                    1e-12);
380
381
  }

382
383
  void
  test_GetPropertyAsSingleValue_Returns_Expected_Single_Value_On_Successive_Calls_With_Different_Stat_Types() {
384
385
    LogManager run;
    const std::string name = "series";
386
    addTestTimeSeries<double>(run, name);
387

388
389
390
391
    TS_ASSERT_EQUALS(run.getPropertyAsSingleValue(name, Math::Mean), 13.0);
    TS_ASSERT_EQUALS(run.getPropertyAsSingleValue(name, Math::Mean), 13.0);
    TS_ASSERT_EQUALS(run.getPropertyAsSingleValue(name, Math::Minimum), 2.0);
    TS_ASSERT_EQUALS(run.getPropertyAsSingleValue(name, Math::Minimum), 2.0);
392
393
  }

394
395
  void
  test_GetPropertyAsSingleValue_Returns_Correct_Value_On_Second_Call_When_Log_Has_Been_Replaced() {
396
397
398
399
400
401
402
403
404
    LogManager runInfo;
    const std::string name = "double";
    double value(5.1);
    runInfo.addProperty(name, value);

    TS_ASSERT_EQUALS(runInfo.getPropertyAsSingleValue(name), value);

    // Replace the log with a different value
    value = 10.3;
405
    runInfo.addProperty(name, value, /*overwrite*/ true);
406
407
408
409

    TS_ASSERT_EQUALS(runInfo.getPropertyAsSingleValue(name), value);
  }

410
411
412
413
414
  void test_getTimeAveragedStd() {
    LogManager run;
    const std::string name = "series";
    addTestTimeSeries<double>(run, name);

Savici, Andrei T's avatar
Savici, Andrei T committed
415
    TS_ASSERT_DELTA(run.getTimeAveragedStd(name), 8.5239, 0.001);
416
417
  }

418
419
420
  void test_clear() {
    // Set up a Run object with 3 properties in it (1 time series, 2 single
    // value)
421
422
423
    LogManager runInfo;
    const std::string stringProp("aStringProp");
    const std::string stringVal("testing");
424
    runInfo.addProperty(stringProp, stringVal);
425
    const std::string intProp("anIntProp");
426
    runInfo.addProperty(intProp, 99);
427
    const std::string tspProp("tsp");
428
    addTestTimeSeries<double>(runInfo, "tsp");
429
430

    // Check it's set up right
431
    TS_ASSERT_EQUALS(runInfo.getProperties().size(), 3);
432
    auto tsp = runInfo.getTimeSeriesProperty<double>(tspProp);
433
    TS_ASSERT_EQUALS(tsp->realSize(), 10)
434
435

    // Do the clearing work
436
    TS_ASSERT_THROWS_NOTHING(runInfo.clearTimeSeriesLogs());
437
438

    // Check the time-series property is empty, but not the others
439
440
441
442
443
    TS_ASSERT_EQUALS(runInfo.getProperties().size(), 3);
    TS_ASSERT_EQUALS(tsp->realSize(), 0)
    TS_ASSERT_EQUALS(runInfo.getPropertyValueAsType<std::string>(stringProp),
                     stringVal);
    TS_ASSERT_EQUALS(runInfo.getPropertyValueAsType<int>(intProp), 99);
444
445
  }

446
447
448
  void clearOutdatedTimeSeriesLogValues() {
    // Set up a Run object with 3 properties in it (1 time series, 2 single
    // value)
449
450
451
    LogManager runInfo;
    const std::string stringProp("aStringProp");
    const std::string stringVal("testing");
452
    runInfo.addProperty(stringProp, stringVal);
453
    const std::string intProp("anIntProp");
454
    runInfo.addProperty(intProp, 99);
455
    const std::string tspProp("tsp");
456
    addTestTimeSeries<double>(runInfo, "tsp");
457

458
    // Check it's set up right
459
    TS_ASSERT_EQUALS(runInfo.getProperties().size(), 3);
460
    auto tsp = runInfo.getTimeSeriesProperty<double>(tspProp);
461
    TS_ASSERT_EQUALS(tsp->realSize(), 10);
462
463
464
465
466

    auto lastTime = tsp->lastTime();
    auto lastValue = tsp->lastValue();

    // Do the clearing work
467
    TS_ASSERT_THROWS_NOTHING(runInfo.clearOutdatedTimeSeriesLogValues());
468
469

    // Check the time-series property has 1 entry, & the others are unchanged
470
471
472
473
474
475
476
    TS_ASSERT_EQUALS(runInfo.getProperties().size(), 3);
    TS_ASSERT_EQUALS(tsp->realSize(), 1);
    TS_ASSERT_EQUALS(tsp->firstTime(), lastTime);
    TS_ASSERT_EQUALS(tsp->firstValue(), lastValue);
    TS_ASSERT_EQUALS(runInfo.getPropertyValueAsType<std::string>(stringProp),
                     stringVal);
    TS_ASSERT_EQUALS(runInfo.getPropertyValueAsType<int>(intProp), 99);
477
  }
478
479

  /** Save and load to NXS file */
480
  void test_nexus() {
481
482
483
484
485
    NexusTestHelper th(true);
    th.createFile("LogManagerTest.nxs");

    LogManager run1;
    addTimeSeriesEntry(run1, "double_series", 45.0);
486
487
488
489
    run1.addProperty(new PropertyWithValue<int>("int_val", 1234));
    run1.addProperty(new PropertyWithValue<std::string>(
        "string_val", "help_im_stuck_in_a_log_file"));
    run1.addProperty(new PropertyWithValue<double>("double_val", 5678.9));
490
491
492
493
494
    addTimeSeriesEntry(run1, "phi", 12.3);
    addTimeSeriesEntry(run1, "chi", 45.6);
    addTimeSeriesEntry(run1, "omega", 78.9);
    addTimeSeriesEntry(run1, "proton_charge", 78.9);

495
    run1.saveNexus(th.file.get(), "logs");
496
497
498
499
500
501
502
    th.file->openGroup("logs", "NXgroup");
    th.file->makeGroup("junk_to_ignore", "NXmaterial");
    th.file->makeGroup("more_junk_to_ignore", "NXsample");

    // ---- Now re-load the same and compare ------
    th.reopenFile();
    LogManager run2;
503
    run2.loadNexus(th.file.get(), "logs");
504
505
506
507
    TS_ASSERT(run2.hasProperty("double_series"));
    TS_ASSERT(run2.hasProperty("int_val"));
    TS_ASSERT(run2.hasProperty("string_val"));
    TS_ASSERT(run2.hasProperty("double_val"));
508
509
    // This test both uses the goniometer axes AND looks up some values.

510
511
    // Reload without opening the group (for backwards-compatible reading of old
    // files)
512
    LogManager run3;
513
514
    th.file.get()->openGroup("logs", "NXgroup");
    run3.loadNexus(th.file.get(), "");
515
516
517
518
    TS_ASSERT(run3.hasProperty("double_series"));
    TS_ASSERT(run3.hasProperty("int_val"));
    TS_ASSERT(run3.hasProperty("string_val"));
    TS_ASSERT(run3.hasProperty("double_val"));
519
520
521
  }

  /** Check for loading the old way of saving proton_charge */
522
  void test_legacy_nexus() {
523
524
525
526
527
528
529
    NexusTestHelper th(true);
    th.createFile("LogManagerTest.nxs");
    th.file->makeGroup("sample", "NXsample", 1);
    th.file->writeData("proton_charge", 1.234);
    th.reopenFile();
    th.file->openGroup("sample", "NXsample");
    LogManager run3;
530
    run3.loadNexus(th.file.get(), "");
531
  }
532
533
534
535
536
537
538
539
540
541
542

private:
  template <typename T>
  void doTest_GetPropertyAsSingleValue_SingleType(const T value) {
    LogManager runInfo;
    const std::string name = "T_prop";
    runInfo.addProperty<T>(name, value);
    double result = std::nan("1");
    TS_ASSERT_THROWS_NOTHING(result = runInfo.getPropertyAsSingleValue(name));
    TS_ASSERT_EQUALS(value, static_cast<T>(result));
  }
543
544
545
546
547

  template <typename T> void doTest_GetPropertyAsSingleValue_TimeSeriesType() {
    LogManager runInfo;
    const std::string name = "T_series";
    addTestTimeSeries<T>(runInfo, name);
548
    const double expectedValue(13.0);
549
    TS_ASSERT_DELTA(
550
        runInfo.getPropertyAsSingleValue(name, Mantid::Kernel::Math::Mean),
551
        expectedValue, 1e-12);
552
  }
553
554
555
556
557
558
559
560
561
562

  template <typename T> void doTest_GetPropertyAsIntegerValue(const T value) {
    LogManager runInfo;
    const std::string name = "T_prop";
    runInfo.addProperty<T>(name, value);
    int result(-1);
    result = runInfo.getPropertyAsIntegerValue(name);
    TS_ASSERT_THROWS_NOTHING(result = runInfo.getPropertyAsIntegerValue(name));
    TS_ASSERT_EQUALS(value, static_cast<T>(result));
  }
563
564
565
566
567
568
};

//---------------------------------------------------------------------------------------
// Performance test
//---------------------------------------------------------------------------------------

569
class LogManagerTestPerformance : public CxxTest::TestSuite {
570
571
572
public:
  // This pair of boilerplate methods prevent the suite being created statically
  // This means the constructor isn't called when running other tests
573
574
575
576
  static LogManagerTestPerformance *createSuite() {
    return new LogManagerTestPerformance();
  }
  static void destroySuite(LogManagerTestPerformance *suite) { delete suite; }
577

578
  LogManagerTestPerformance() : m_testRun(), m_propName("test") {
579
    addTestTimeSeries<double>(m_testRun, m_propName);
580
581
  }

582
  void test_Accessing_Single_Value_From_Times_Series_A_Large_Number_Of_Times() {
583
    double value(0.0);
584
    for (size_t i = 0; i < 20000; ++i) {
585
586
587
588
589
590
591
592
593
      value = m_testRun.getPropertyAsSingleValue(m_propName);
    }
    // Enure variable is used so that it is not optimised away by the compiler
    value += 1.0;
  }

  LogManager m_testRun;
  std::string m_propName;
};