LogManagerTest.h 21.4 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
#include <json/value.h>
21
22
23
24

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

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

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

67
68
69
70
71
72
73
74
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 {
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
88
89
90
91
92
93
94
    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]));
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(), 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(), 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(), std::runtime_error);
141
142
    // And check things if it's the wrong property type
    runInfo.removeProperty("run_start");
143
144
    addTimeSeriesEntry(runInfo, "run_start", 4.44);
    TS_ASSERT_THROWS(runInfo.startTime(), std::runtime_error);
145
  }
146

147
  void testEndTime() {
148
149
    LogManager runInfo;
    // Nothing there yet
150
    TS_ASSERT_THROWS(runInfo.endTime(), 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(), std::runtime_error);
174
175
    // And check things if it's the wrong property type
    runInfo.removeProperty("run_end");
176
177
    addTimeSeriesEntry(runInfo, "run_end", 4.44);
    TS_ASSERT_THROWS(runInfo.endTime(), 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
205
    TS_ASSERT_THROWS(runInfo.getTimeSeriesProperty<double>("not_a_log"),
                     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
215
    TS_ASSERT_THROWS(runInfo.getTimeSeriesProperty<double>(name),
                     std::invalid_argument);
216
217
  }

218
  void test_GetPropertyAsType_Throws_When_Property_Does_Not_Exist() {
219
    LogManager runInfo;
220
221
    TS_ASSERT_THROWS(runInfo.getPropertyValueAsType<double>("not_a_log"),
                     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
241
    TS_ASSERT_THROWS(runInfo.getPropertyValueAsType<int>("double_prop"),
                     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
298
299
300
301
302
303
304
305
306
  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);
  }

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
337
338
339
340
341
342
343
344
345
    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
346

347
348
    TS_ASSERT_THROWS(runInfo.getPropertyAsSingleValue(name),
                     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
  void test_clear() {
    // Set up a Run object with 3 properties in it (1 time series, 2 single
    // value)
413
414
415
    LogManager runInfo;
    const std::string stringProp("aStringProp");
    const std::string stringVal("testing");
416
    runInfo.addProperty(stringProp, stringVal);
417
    const std::string intProp("anIntProp");
418
    runInfo.addProperty(intProp, 99);
419
    const std::string tspProp("tsp");
420
    addTestTimeSeries<double>(runInfo, "tsp");
421
422

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

    // Do the clearing work
428
    TS_ASSERT_THROWS_NOTHING(runInfo.clearTimeSeriesLogs());
429
430

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

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

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

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

    // Do the clearing work
459
    TS_ASSERT_THROWS_NOTHING(runInfo.clearOutdatedTimeSeriesLogValues());
460
461

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

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

    LogManager run1;
    addTimeSeriesEntry(run1, "double_series", 45.0);
478
479
480
481
    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));
482
483
484
485
486
487
488
489
490
491
492
493
494
495
    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");
496
497
498
499
    TS_ASSERT(run2.hasProperty("double_series"));
    TS_ASSERT(run2.hasProperty("int_val"));
    TS_ASSERT(run2.hasProperty("string_val"));
    TS_ASSERT(run2.hasProperty("double_val"));
500
501
    // This test both uses the goniometer axes AND looks up some values.

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

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

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));
  }
535
536
537
538
539

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

  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));
  }
555
556
557
558
559
560
};

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

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

570
  LogManagerTestPerformance() : m_testRun(), m_propName("test") {
571
    addTestTimeSeries<double>(m_testRun, m_propName);
572
573
  }

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