LogManagerTest.h 21.3 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
42
43
44
45
46
  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; }
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

66
67
68
69
70
71
72
73
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 {
74
public:
75
  void testAddGetData() {
76
77
78
    LogManager runInfo;

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

81
    Property *pp = nullptr;
82
83
84
85
86
87
88
89
90
91
92
93
    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]));
94
95
  }

96
  void testRemoveLogData() {
97
    LogManager runInfo;
98

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

239
240
    TS_ASSERT_THROWS(runInfo.getPropertyValueAsType<int>("double_prop"),
                     std::invalid_argument);
241
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
  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);
  }

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

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

330
  void test_GetPropertyAsSingleValue_Throws_If_String_Is_Invalid() {
331
    LogManager runInfo;
332
    const std::string name = "string_prop";
333
334
335
336
337
338
339
340
341
342
343
344
    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
345

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

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

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

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

    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);
379
380
  }

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

387
388
389
390
    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);
391
392
  }

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

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

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

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

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

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

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

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

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

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

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

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

    LogManager run1;
    addTimeSeriesEntry(run1, "double_series", 45.0);
477
478
479
480
    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));
481
482
483
484
485
486
487
488
489
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);

    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");
495
496
497
498
    TS_ASSERT(run2.hasProperty("double_series"));
    TS_ASSERT(run2.hasProperty("int_val"));
    TS_ASSERT(run2.hasProperty("string_val"));
    TS_ASSERT(run2.hasProperty("double_val"));
499
500
    // This test both uses the goniometer axes AND looks up some values.

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

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

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

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

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

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

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

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

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