LogManagerTest.h 16.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
#ifndef LOG_MANAGER_TEST_H_
#define LOG_MANAGER_TEST_H_

#include "MantidAPI/LogManager.h"
#include "MantidKernel/Exception.h"
#include "MantidKernel/Matrix.h"
#include "MantidKernel/Property.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/V3D.h"
#include <cxxtest/TestSuite.h>
11
#include "MantidTestHelpers/NexusTestHelper.h"
12
13
14
15
16
17
18
#include "MantidGeometry/Instrument/Goniometer.h"

using namespace Mantid::Kernel;
using namespace Mantid::API;
using namespace Mantid::Geometry;

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

void addTestTimeSeries(LogManager &run, const std::string &name) {
  auto timeSeries = new TimeSeriesProperty<double>(name);
  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);
}
49
50
}

51
52
53
54
55
56
57
58
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 {
59
public:
60
  void testAddGetData() {
61
62
63
    LogManager runInfo;

    Property *p = new ConcreteProperty();
64
    TS_ASSERT_THROWS_NOTHING(runInfo.addProperty(p));
65
66

    Property *pp = NULL;
67
68
69
70
71
72
73
74
75
76
77
78
    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]));
79
80
  }

81
  void testRemoveLogData() {
82
    LogManager runInfo;
83

84
    Property *p = new ConcreteProperty();
85
86
87
    TS_ASSERT_THROWS_NOTHING(runInfo.addProperty(p));
    TS_ASSERT_THROWS_NOTHING(runInfo.removeProperty("Test"));
    TS_ASSERT_EQUALS(runInfo.getProperties().size(), 0);
88
89
  }

90
  void testStartTime() {
91
92
    LogManager runInfo;
    // Nothing there yet
93
    TS_ASSERT_THROWS(runInfo.startTime(), std::runtime_error);
94
95
    // Add run_start and see that get picked up
    const std::string run_start("2013-12-19T13:38:00");
96
97
    auto run_start_prop =
        new PropertyWithValue<std::string>("run_start", run_start);
98
    runInfo.addProperty(run_start_prop);
99
    TS_ASSERT_EQUALS(runInfo.startTime(), DateAndTime(run_start));
100
101
    // Add start_time and see that get picked up in preference
    const std::string start_time("2013-12-19T13:40:00");
102
103
    auto start_time_prop =
        new PropertyWithValue<std::string>("start_time", start_time);
104
    runInfo.addProperty(start_time_prop);
105
    TS_ASSERT_EQUALS(runInfo.startTime(), DateAndTime(start_time));
106
107
108
    // 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);
109
    TS_ASSERT_EQUALS(runInfo.startTime(), DateAndTime(run_start));
110
111
    // And back to failure if they're both that
    run_start_prop->setValue(epoch);
112
    TS_ASSERT_THROWS(runInfo.startTime(), std::runtime_error);
113
114
115
116

    // Set run_start back to valid value and make start_time contain nonsense
    run_start_prop->setValue(run_start);
    start_time_prop->setValue("__");
117
    TS_ASSERT_EQUALS(runInfo.startTime(), DateAndTime(run_start));
118
119
    // Now make start_time a completely different property type
    runInfo.removeProperty("start_time");
120
121
    runInfo.addProperty(new PropertyWithValue<double>("start_time", 3.33));
    TS_ASSERT_EQUALS(runInfo.startTime(), DateAndTime(run_start));
122
123
    // Now make run_start something invalid
    run_start_prop->setValue("notADate");
124
    TS_ASSERT_THROWS(runInfo.startTime(), std::runtime_error);
125
126
    // And check things if it's the wrong property type
    runInfo.removeProperty("run_start");
127
128
    addTimeSeriesEntry(runInfo, "run_start", 4.44);
    TS_ASSERT_THROWS(runInfo.startTime(), std::runtime_error);
129
  }
130

131
  void testEndTime() {
132
133
    LogManager runInfo;
    // Nothing there yet
134
    TS_ASSERT_THROWS(runInfo.endTime(), std::runtime_error);
135
136
    // Add run_end and see that get picked up
    const std::string run_end("2013-12-19T13:38:00");
137
    auto run_end_prop = new PropertyWithValue<std::string>("run_end", run_end);
138
    runInfo.addProperty(run_end_prop);
139
    TS_ASSERT_EQUALS(runInfo.endTime(), DateAndTime(run_end));
140
141
    // Add end_time and see that get picked up in preference
    const std::string end_time("2013-12-19T13:40:00");
142
143
    auto end_time_prop =
        new PropertyWithValue<std::string>("end_time", end_time);
144
    runInfo.addProperty(end_time_prop);
145
    TS_ASSERT_EQUALS(runInfo.endTime(), DateAndTime(end_time));
146
147
148
149

    // Set run_end back to valid value and make end_time contain nonsense
    run_end_prop->setValue(run_end);
    end_time_prop->setValue("__");
150
    TS_ASSERT_EQUALS(runInfo.endTime(), DateAndTime(run_end));
151
152
    // Now make end_time a completely different property type
    runInfo.removeProperty("end_time");
153
154
    runInfo.addProperty(new PropertyWithValue<double>("end_time", 3.33));
    TS_ASSERT_EQUALS(runInfo.endTime(), DateAndTime(run_end));
155
156
    // Now make run_end something invalid
    run_end_prop->setValue("notADate");
157
    TS_ASSERT_THROWS(runInfo.endTime(), std::runtime_error);
158
159
    // And check things if it's the wrong property type
    runInfo.removeProperty("run_end");
160
161
    addTimeSeriesEntry(runInfo, "run_end", 4.44);
    TS_ASSERT_THROWS(runInfo.endTime(), std::runtime_error);
162
163
  }

164
  void testMemory() {
165
    LogManager runInfo;
166
167
    TS_ASSERT_EQUALS(runInfo.getMemorySize(), 0);

168
169
170
    Property *p = new ConcreteProperty();
    runInfo.addProperty(p);

171
172
    TS_ASSERT_EQUALS(runInfo.getMemorySize(),
                     sizeof(ConcreteProperty) + sizeof(void *));
173
174
  }

175
  void test_GetTimeSeriesProperty_Returns_TSP_When_Log_Exists() {
176
    LogManager runInfo;
177
    const std::string &name = "double_time_series";
178
179
180
    const double value = 10.9;
    addTimeSeriesEntry(runInfo, name, value);

181
    TimeSeriesProperty<double> *tsp(NULL);
182
183
184
185
    TS_ASSERT_THROWS_NOTHING(tsp = runInfo.getTimeSeriesProperty<double>(name));
    TS_ASSERT_DELTA(tsp->firstValue(), value, 1e-12);
  }

186
  void test_GetTimeSeriesProperty_Throws_When_Log_Does_Not_Exist() {
187
    LogManager runInfo;
188
189
    TS_ASSERT_THROWS(runInfo.getTimeSeriesProperty<double>("not_a_log"),
                     Exception::NotFoundError);
190
191
  }

192
193
  void
  test_GetTimeSeriesProperty_Throws_When_Log_Exists_But_Is_Not_Correct_Type() {
194
    LogManager runInfo;
195
    const std::string &name = "double_prop";
196
197
    runInfo.addProperty(name, 5.6); // Standard double property

198
199
    TS_ASSERT_THROWS(runInfo.getTimeSeriesProperty<double>(name),
                     std::invalid_argument);
200
201
  }

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

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

    double retrieved(0.0);
215
216
    TS_ASSERT_THROWS_NOTHING(retrieved =
                                 runInfo.getPropertyValueAsType<double>(name));
217
218
219
    TS_ASSERT_DELTA(retrieved, value, 1e-12);
  }

220
  void test_GetPropertyAsType_Throws_When_Requested_Type_Does_Not_Match() {
221
222
223
    LogManager runInfo;
    runInfo.addProperty("double_prop", 6.7); // Standard double property

224
225
    TS_ASSERT_THROWS(runInfo.getPropertyValueAsType<int>("double_prop"),
                     std::invalid_argument);
226
227
  }

228
229
  void
  test_GetPropertyAsSingleValue_Throws_If_Type_Is_Not_Double_Or_TimeSeries_Double() {
230
231
232
233
    LogManager runInfo;
    const std::string name = "int_prop";
    runInfo.addProperty(name, 1); // Adds an int property

234
235
    TS_ASSERT_THROWS(runInfo.getPropertyAsSingleValue(name),
                     std::invalid_argument);
236
237
  }

238
239
  void
  test_GetPropertyAsSingleValue_Returns_Simple_Mean_By_Default_For_Time_Series() {
240
241
242
243
244
    LogManager runInfo;
    const std::string name = "series";
    addTestTimeSeries(runInfo, name);

    const double expectedValue(13.0);
245
246
    TS_ASSERT_DELTA(runInfo.getPropertyAsSingleValue(name), expectedValue,
                    1e-12);
247
248
  }

249
250
  void
  test_GetPropertyAsSingleValue_Returns_Correct_SingleValue_For_Each_StatisticType() {
251
252
253
    LogManager runInfo;
    const std::string name = "series";
    addTestTimeSeries(runInfo, name);
254
255
256
257
258
259
260
261
262
263
264
265
266

    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);
267
268
  }

269
270
  void
  test_GetPropertyAsSingleValue_Returns_Expected_Single_Value_On_Successive_Calls_With_Different_Stat_Types() {
271
272
273
274
    LogManager run;
    const std::string name = "series";
    addTestTimeSeries(run, name);

275
276
277
278
    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);
279
280
  }

281
282
  void
  test_GetPropertyAsSingleValue_Returns_Correct_Value_On_Second_Call_When_Log_Has_Been_Replaced() {
283
284
285
286
287
288
289
290
291
    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;
292
    runInfo.addProperty(name, value, /*overwrite*/ true);
293
294
295
296

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

297
298
299
  void test_clear() {
    // Set up a Run object with 3 properties in it (1 time series, 2 single
    // value)
300
301
302
    LogManager runInfo;
    const std::string stringProp("aStringProp");
    const std::string stringVal("testing");
303
    runInfo.addProperty(stringProp, stringVal);
304
    const std::string intProp("anIntProp");
305
    runInfo.addProperty(intProp, 99);
306
    const std::string tspProp("tsp");
307
    addTestTimeSeries(runInfo, "tsp");
308
309

    // Check it's set up right
310
    TS_ASSERT_EQUALS(runInfo.getProperties().size(), 3);
311
    auto tsp = runInfo.getTimeSeriesProperty<double>(tspProp);
312
    TS_ASSERT_EQUALS(tsp->realSize(), 10)
313
314

    // Do the clearing work
315
    TS_ASSERT_THROWS_NOTHING(runInfo.clearTimeSeriesLogs());
316
317

    // Check the time-series property is empty, but not the others
318
319
320
321
322
    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);
323
324
  }

325
326
327
  void clearOutdatedTimeSeriesLogValues() {
    // Set up a Run object with 3 properties in it (1 time series, 2 single
    // value)
328
329
330
    LogManager runInfo;
    const std::string stringProp("aStringProp");
    const std::string stringVal("testing");
331
    runInfo.addProperty(stringProp, stringVal);
332
    const std::string intProp("anIntProp");
333
    runInfo.addProperty(intProp, 99);
334
    const std::string tspProp("tsp");
335
    addTestTimeSeries(runInfo, "tsp");
336

337
    // Check it's set up right
338
    TS_ASSERT_EQUALS(runInfo.getProperties().size(), 3);
339
    auto tsp = runInfo.getTimeSeriesProperty<double>(tspProp);
340
    TS_ASSERT_EQUALS(tsp->realSize(), 10);
341
342
343
344
345

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

    // Do the clearing work
346
    TS_ASSERT_THROWS_NOTHING(runInfo.clearOutdatedTimeSeriesLogValues());
347
348

    // Check the time-series property has 1 entry, & the others are unchanged
349
350
351
352
353
354
355
    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);
356
  }
357
358

  /** Save and load to NXS file */
359
  void test_nexus() {
360
361
362
363
364
    NexusTestHelper th(true);
    th.createFile("LogManagerTest.nxs");

    LogManager run1;
    addTimeSeriesEntry(run1, "double_series", 45.0);
365
366
367
368
    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));
369
370
371
372
373
374
375
376
377
378
379
380
381
382
    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");
383
384
385
386
    TS_ASSERT(run2.hasProperty("double_series"));
    TS_ASSERT(run2.hasProperty("int_val"));
    TS_ASSERT(run2.hasProperty("string_val"));
    TS_ASSERT(run2.hasProperty("double_val"));
387
388
    // This test both uses the goniometer axes AND looks up some values.

389
390
    // Reload without opening the group (for backwards-compatible reading of old
    // files)
391
392
393
    LogManager run3;
    th.file->openGroup("logs", "NXgroup");
    run3.loadNexus(th.file, "");
394
395
396
397
    TS_ASSERT(run3.hasProperty("double_series"));
    TS_ASSERT(run3.hasProperty("int_val"));
    TS_ASSERT(run3.hasProperty("string_val"));
    TS_ASSERT(run3.hasProperty("double_val"));
398
399
400
  }

  /** Check for loading the old way of saving proton_charge */
401
  void test_legacy_nexus() {
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
    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, "");
  }
};

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

417
class LogManagerTestPerformance : public CxxTest::TestSuite {
418
419
420
public:
  // This pair of boilerplate methods prevent the suite being created statically
  // This means the constructor isn't called when running other tests
421
422
423
424
  static LogManagerTestPerformance *createSuite() {
    return new LogManagerTestPerformance();
  }
  static void destroySuite(LogManagerTestPerformance *suite) { delete suite; }
425

426
  LogManagerTestPerformance() : m_testRun(), m_propName("test") {
427
428
429
    addTestTimeSeries(m_testRun, m_propName);
  }

430
  void test_Accessing_Single_Value_From_Times_Series_A_Large_Number_Of_Times() {
431
    double value(0.0);
432
    for (size_t i = 0; i < 20000; ++i) {
433
434
435
436
437
438
439
440
441
442
443
      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