LogManagerTest.h 21.2 KB
Newer Older
1
2
3
4
5
6
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
//     NScD Oak Ridge National Laboratory, European Spallation Source
//     & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
7
8
9
10
#ifndef LOG_MANAGER_TEST_H_
#define LOG_MANAGER_TEST_H_

#include "MantidAPI/LogManager.h"
LamarMoore's avatar
LamarMoore committed
11
#include "MantidGeometry/Instrument/Goniometer.h"
12
13
14
15
16
#include "MantidKernel/Exception.h"
#include "MantidKernel/Matrix.h"
#include "MantidKernel/Property.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/V3D.h"
17
#include "MantidTestHelpers/NexusTestHelper.h"
18
#include <cmath>
LamarMoore's avatar
LamarMoore committed
19
#include <cxxtest/TestSuite.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
39
40
41
42
43
44
  std::string value() const override { return "Nothing"; }
  std::string setValue(const std::string &) override { return ""; }
  std::string setValueFromProperty(const Property &) override { return ""; }
  std::string setDataItem(const boost::shared_ptr<DataItem>) override {
    return "";
  }
  Property &operator+=(Property const *) override { return *this; }
45
46
};

47
template <typename T>
48
void addTestTimeSeries(LogManager &run, const std::string &name) {
49
  auto timeSeries = new TimeSeriesProperty<T>(name);
50
51
52
53
54
55
56
57
58
59
60
61
  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
62
} // namespace
63

64
65
66
67
68
69
70
71
void addTimeSeriesEntry(LogManager &runInfo, std::string name, double val) {
  TimeSeriesProperty<double> *tsp;
  tsp = new TimeSeriesProperty<double>(name);
  tsp->addValue("2011-05-24T00:00:00", val);
  runInfo.addProperty(tsp);
}

class LogManagerTest : public CxxTest::TestSuite {
72
public:
73
  void testAddGetData() {
74
75
76
    LogManager runInfo;

    Property *p = new ConcreteProperty();
77
    TS_ASSERT_THROWS_NOTHING(runInfo.addProperty(p));
78

79
    Property *pp = nullptr;
80
81
82
83
84
85
86
87
88
89
90
91
    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"),
                     Exception::NotFoundError);

    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]));
92
93
  }

94
  void testRemoveLogData() {
95
    LogManager runInfo;
96

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

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

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

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

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

177
  void testMemory() {
178
    LogManager runInfo;
179
180
    TS_ASSERT_EQUALS(runInfo.getMemorySize(), 0);

181
182
183
    Property *p = new ConcreteProperty();
    runInfo.addProperty(p);

184
185
    TS_ASSERT_EQUALS(runInfo.getMemorySize(),
                     sizeof(ConcreteProperty) + sizeof(void *));
186
187
  }

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

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

199
  void test_GetTimeSeriesProperty_Throws_When_Log_Does_Not_Exist() {
200
    LogManager runInfo;
201
202
    TS_ASSERT_THROWS(runInfo.getTimeSeriesProperty<double>("not_a_log"),
                     Exception::NotFoundError);
203
204
  }

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

211
212
    TS_ASSERT_THROWS(runInfo.getTimeSeriesProperty<double>(name),
                     std::invalid_argument);
213
214
  }

215
  void test_GetPropertyAsType_Throws_When_Property_Does_Not_Exist() {
216
    LogManager runInfo;
217
218
    TS_ASSERT_THROWS(runInfo.getPropertyValueAsType<double>("not_a_log"),
                     Exception::NotFoundError);
219
220
  }

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

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

233
  void test_GetPropertyAsType_Throws_When_Requested_Type_Does_Not_Match() {
234
235
236
    LogManager runInfo;
    runInfo.addProperty("double_prop", 6.7); // Standard double property

237
238
    TS_ASSERT_THROWS(runInfo.getPropertyValueAsType<int>("double_prop"),
                     std::invalid_argument);
239
240
  }

241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
  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);
  }

265
  void test_GetPropertyAsSingleValue_SingleValue_StringType() {
266
267
268
269
270
    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));
271
    TS_ASSERT_DELTA(1.0, result, 1e-12);
272
273
  }

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
  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),
                     std::invalid_argument);
  }

  void test_GetPropertyAsSingleInteger_Throws_for_nonexistant_property() {
    LogManager runInfo;
    TS_ASSERT_THROWS(runInfo.getPropertyAsIntegerValue("T_prop"),
                     Exception::NotFoundError);
  }

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
  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>();
  }

328
  void test_GetPropertyAsSingleValue_Throws_If_String_Is_Invalid() {
329
    LogManager runInfo;
330
    const std::string name = "string_prop";
331
332
333
334
335
336
337
338
339
340
341
342
    runInfo.addProperty<std::string>(name, "hello"); // not a number

    TS_ASSERT_THROWS(runInfo.getPropertyAsSingleValue(name),
                     std::invalid_argument);
  }

  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
343

344
345
    TS_ASSERT_THROWS(runInfo.getPropertyAsSingleValue(name),
                     std::invalid_argument);
346
347
  }

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

    const double expectedValue(13.0);
355
356
    TS_ASSERT_DELTA(runInfo.getPropertyAsSingleValue(name), expectedValue,
                    1e-12);
357
358
  }

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

    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);
377
378
  }

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

385
386
387
388
    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);
389
390
  }

391
392
  void
  test_GetPropertyAsSingleValue_Returns_Correct_Value_On_Second_Call_When_Log_Has_Been_Replaced() {
393
394
395
396
397
398
399
400
401
    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;
402
    runInfo.addProperty(name, value, /*overwrite*/ true);
403
404
405
406

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

407
408
409
  void test_clear() {
    // Set up a Run object with 3 properties in it (1 time series, 2 single
    // value)
410
411
412
    LogManager runInfo;
    const std::string stringProp("aStringProp");
    const std::string stringVal("testing");
413
    runInfo.addProperty(stringProp, stringVal);
414
    const std::string intProp("anIntProp");
415
    runInfo.addProperty(intProp, 99);
416
    const std::string tspProp("tsp");
417
    addTestTimeSeries<double>(runInfo, "tsp");
418
419

    // Check it's set up right
420
    TS_ASSERT_EQUALS(runInfo.getProperties().size(), 3);
421
    auto tsp = runInfo.getTimeSeriesProperty<double>(tspProp);
422
    TS_ASSERT_EQUALS(tsp->realSize(), 10)
423
424

    // Do the clearing work
425
    TS_ASSERT_THROWS_NOTHING(runInfo.clearTimeSeriesLogs());
426
427

    // Check the time-series property is empty, but not the others
428
429
430
431
432
    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);
433
434
  }

435
436
437
  void clearOutdatedTimeSeriesLogValues() {
    // Set up a Run object with 3 properties in it (1 time series, 2 single
    // value)
438
439
440
    LogManager runInfo;
    const std::string stringProp("aStringProp");
    const std::string stringVal("testing");
441
    runInfo.addProperty(stringProp, stringVal);
442
    const std::string intProp("anIntProp");
443
    runInfo.addProperty(intProp, 99);
444
    const std::string tspProp("tsp");
445
    addTestTimeSeries<double>(runInfo, "tsp");
446

447
    // Check it's set up right
448
    TS_ASSERT_EQUALS(runInfo.getProperties().size(), 3);
449
    auto tsp = runInfo.getTimeSeriesProperty<double>(tspProp);
450
    TS_ASSERT_EQUALS(tsp->realSize(), 10);
451
452
453
454
455

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

    // Do the clearing work
456
    TS_ASSERT_THROWS_NOTHING(runInfo.clearOutdatedTimeSeriesLogValues());
457
458

    // Check the time-series property has 1 entry, & the others are unchanged
459
460
461
462
463
464
465
    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);
466
  }
467
468

  /** Save and load to NXS file */
469
  void test_nexus() {
470
471
472
473
474
    NexusTestHelper th(true);
    th.createFile("LogManagerTest.nxs");

    LogManager run1;
    addTimeSeriesEntry(run1, "double_series", 45.0);
475
476
477
478
    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));
479
480
481
482
483
484
485
486
487
488
489
490
491
492
    addTimeSeriesEntry(run1, "phi", 12.3);
    addTimeSeriesEntry(run1, "chi", 45.6);
    addTimeSeriesEntry(run1, "omega", 78.9);
    addTimeSeriesEntry(run1, "proton_charge", 78.9);

    run1.saveNexus(th.file, "logs");
    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;
    run2.loadNexus(th.file, "logs");
493
494
495
496
    TS_ASSERT(run2.hasProperty("double_series"));
    TS_ASSERT(run2.hasProperty("int_val"));
    TS_ASSERT(run2.hasProperty("string_val"));
    TS_ASSERT(run2.hasProperty("double_val"));
497
498
    // This test both uses the goniometer axes AND looks up some values.

499
500
    // Reload without opening the group (for backwards-compatible reading of old
    // files)
501
502
503
    LogManager run3;
    th.file->openGroup("logs", "NXgroup");
    run3.loadNexus(th.file, "");
504
505
506
507
    TS_ASSERT(run3.hasProperty("double_series"));
    TS_ASSERT(run3.hasProperty("int_val"));
    TS_ASSERT(run3.hasProperty("string_val"));
    TS_ASSERT(run3.hasProperty("double_val"));
508
509
510
  }

  /** Check for loading the old way of saving proton_charge */
511
  void test_legacy_nexus() {
512
513
514
515
516
517
518
519
520
    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;
    run3.loadNexus(th.file, "");
  }
521
522
523
524
525
526
527
528
529
530
531

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));
  }
532
533
534
535
536

  template <typename T> void doTest_GetPropertyAsSingleValue_TimeSeriesType() {
    LogManager runInfo;
    const std::string name = "T_series";
    addTestTimeSeries<T>(runInfo, name);
537
    const double expectedValue(13.0);
538
    TS_ASSERT_DELTA(
539
        runInfo.getPropertyAsSingleValue(name, Mantid::Kernel::Math::Mean),
540
        expectedValue, 1e-12);
541
  }
542
543
544
545
546
547
548
549
550
551

  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));
  }
552
553
554
555
556
557
};

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

558
class LogManagerTestPerformance : public CxxTest::TestSuite {
559
560
561
public:
  // This pair of boilerplate methods prevent the suite being created statically
  // This means the constructor isn't called when running other tests
562
563
564
565
  static LogManagerTestPerformance *createSuite() {
    return new LogManagerTestPerformance();
  }
  static void destroySuite(LogManagerTestPerformance *suite) { delete suite; }
566

567
  LogManagerTestPerformance() : m_testRun(), m_propName("test") {
568
    addTestTimeSeries<double>(m_testRun, m_propName);
569
570
  }

571
  void test_Accessing_Single_Value_From_Times_Series_A_Large_Number_Of_Times() {
572
    double value(0.0);
573
    for (size_t i = 0; i < 20000; ++i) {
574
575
576
577
578
579
580
581
582
583
584
      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;
};

#endif