Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
LogParserTest.h 32.80 KiB
#ifndef LOGPARSERTEST_H_
#define LOGPARSERTEST_H_

#include <cxxtest/TestSuite.h>

#include <fstream>

#include "MantidKernel/LogParser.h"
#include "MantidKernel/make_unique.h"
#include "MantidKernel/PropertyWithValue.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include <boost/scoped_ptr.hpp>

#include <Poco/File.h>

using namespace Mantid::Kernel;

class LogParserTest : public CxxTest::TestSuite {
public:
  static LogParserTest *createSuite() { return new LogParserTest(); }
  static void destroySuite(LogParserTest *suite) { delete suite; }

  class TmpFile {
    Poco::File m_file;

  public:
    TmpFile(const std::string &fname) : m_file(fname) {}
    ~TmpFile() { remove(); }
    const std::string &path() const { return m_file.path(); }
    bool exists() const { return m_file.exists(); }
    void remove() {
      if (m_file.exists())
        m_file.remove();
    }
  };

  LogParserTest()
      : log_num_good("TST000000_good.txt"), log_num_late("TST000000_late.txt"),
        log_num_early("TST000000_early.txt"),
        log_num_single("TST000000_single.txt"), log_str("TST000000_str.txt"),
        icp_file("TST000000_icpevent.txt"),
        log_str_repeat("TST000000_repeat.txt"),
        log_num_repeat("TST000000_num_repeat.txt"),
        log_str_continuations("TST000000_str_continue.txt") {}

  void testGood() {
    mkICP();
    mkGood();
    Property *icp_log =
        LogParser::createLogProperty(icp_file.path(), "icpevent");
    LogParser lp(icp_log);
    Property *p1 = lp.createLogProperty(log_num_good.path(), "good");
    TS_ASSERT(p1);
    TimeSeriesProperty<double> *tp1 =
        dynamic_cast<TimeSeriesProperty<double> *>(p1);
    std::map<DateAndTime, double> vmap = tp1->valueAsMap();
    std::map<DateAndTime, double>::iterator v = vmap.begin();
    // time 1
    TS_ASSERT_EQUALS(vmap.size(), 9);
    TS_ASSERT_EQUALS(v->second, 1);
    ti_data = v->first.to_tm();
    ti = &ti_data;

    TS_ASSERT_EQUALS(ti->tm_hour, 12);
    TS_ASSERT_EQUALS(ti->tm_min, 22);
    v++;
    v++;
    v++;
    v++;
    // time 5
    // TS_ASSERT(isNaN(v->second));
    ti_data = v->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 12);
    TS_ASSERT_EQUALS(ti->tm_min, 22);
    // last time
    std::map<DateAndTime, double>::reverse_iterator rv = vmap.rbegin();
    TS_ASSERT_EQUALS(rv->second, 9);
    ti_data = rv->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 14);
    TS_ASSERT_EQUALS(ti->tm_min, 3);
    TS_ASSERT_DELTA(timeMean(p1), 8.4904, 0.001);

    TS_ASSERT_EQUALS(tp1->nthValue(0), 1);
    TS_ASSERT_EQUALS(tp1->nthValue(1), 2);
    TS_ASSERT_EQUALS(tp1->nthValue(2), 3);
    TS_ASSERT_EQUALS(tp1->nthValue(3), 4);
    TS_ASSERT_EQUALS(tp1->nthValue(4), 5);
    TS_ASSERT_EQUALS(tp1->nthValue(5), 6);
    TS_ASSERT_EQUALS(tp1->nthValue(6), 7);
    TS_ASSERT_EQUALS(tp1->nthValue(7), 8);

    TS_ASSERT_EQUALS(tp1->firstValue(), 1);
    // TS_ASSERT_EQUALS(secondValue(p1),2);
    TS_ASSERT_EQUALS(tp1->lastValue(), 9);

    delete p1;
    delete icp_log;
  }

  void testLate() {
    mkICP();
    mkLate();
    Property *icp_log =
        LogParser::createLogProperty(icp_file.path(), "icpevent");
    LogParser lp(icp_log);
    Property *p1 = lp.createLogProperty(log_num_late.path(), "late");
    TS_ASSERT(p1);
    TimeSeriesProperty<double> *tp1 =
        dynamic_cast<TimeSeriesProperty<double> *>(p1);
    std::map<DateAndTime, double> vmap = tp1->valueAsMap();
    std::map<DateAndTime, double>::iterator v = vmap.begin();

    // time 1
    TS_ASSERT_EQUALS(vmap.size(), 8);
    TS_ASSERT_EQUALS(v->second, 2);

    ti_data = v->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 12);
    TS_ASSERT_EQUALS(ti->tm_min, 22);
    v++;
    v++;
    v++;
    v++;
    // time 5
    // TS_ASSERT(isNaN(v->second));
    ti_data = v->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 12);
    TS_ASSERT_EQUALS(ti->tm_min, 22);
    // last time
    std::map<DateAndTime, double>::reverse_iterator rv = vmap.rbegin();
    TS_ASSERT_EQUALS(rv->second, 9);
    ti_data = rv->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 14);
    TS_ASSERT_EQUALS(ti->tm_min, 3);
    TS_ASSERT_DELTA(timeMean(p1), 8.4941, 0.001);

    delete p1;
    delete icp_log;
  }

  void testEarly() {
    mkICP();
    mkEarly();
    Property *icp_log =
        LogParser::createLogProperty(icp_file.path(), "icpevent");
    LogParser lp(icp_log);
    Property *p1 = lp.createLogProperty(log_num_early.path(), "early");
    TS_ASSERT(p1);
    TimeSeriesProperty<double> *tp1 =
        dynamic_cast<TimeSeriesProperty<double> *>(p1);
    std::map<DateAndTime, double> vmap = tp1->valueAsMap();
    std::map<DateAndTime, double>::iterator v = vmap.begin();

    // time 1
    TS_ASSERT_EQUALS(vmap.size(), 8);
    TS_ASSERT_EQUALS(v->second, 1);
    ti_data = v->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 12);
    TS_ASSERT_EQUALS(ti->tm_min, 22);
    v++;
    v++;
    v++;
    v++;
    // time 5
    // TS_ASSERT(isNaN(v->second));
    ti_data = v->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 12);
    TS_ASSERT_EQUALS(ti->tm_min, 22);
    // last time
    std::map<DateAndTime, double>::reverse_iterator rv = vmap.rbegin();
    TS_ASSERT_EQUALS(rv->second, 8);
    ti_data = rv->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 12);
    TS_ASSERT_EQUALS(ti->tm_min, 23);
    TS_ASSERT_DELTA(timeMean(p1), 4.9090, 0.001);

    delete p1;
    delete icp_log;
  }

  void testSingle() {
    mkICP();
    mkSingle();
    Property *icp_log =
        LogParser::createLogProperty(icp_file.path(), "icpevent");
    LogParser lp(icp_log);
    Property *p1 = lp.createLogProperty(log_num_single.path(), "single");
    TS_ASSERT(p1);
    TimeSeriesProperty<double> *tp1 =
        dynamic_cast<TimeSeriesProperty<double> *>(p1);
    std::map<DateAndTime, double> vmap = tp1->valueAsMap();
    std::map<DateAndTime, double>::iterator v = vmap.begin();

    // time 1
    TS_ASSERT_EQUALS(vmap.size(), 1);
    TS_ASSERT_EQUALS(v->second, 4);
    ti_data = v->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 12);
    TS_ASSERT_EQUALS(ti->tm_min, 22);
    // Can't get a valid mean with a single time and no intervals in it.
    // TS_ASSERT_DELTA(timeMean(p1),4., 0.001);

    delete p1;
    delete icp_log;
  }

  void testStr() {
    mkICP();
    mkStr();
    Property *icp_log =
        LogParser::createLogProperty(icp_file.path(), "icpevent");
    LogParser lp(icp_log);
    Property *p1 = lp.createLogProperty(log_str.path(), "str");
    TS_ASSERT(p1);
    TimeSeriesProperty<std::string> *tp1 =
        dynamic_cast<TimeSeriesProperty<std::string> *>(p1);
    std::map<DateAndTime, std::string> vmap = tp1->valueAsMap();
    std::map<DateAndTime, std::string>::iterator v = vmap.begin();
    // time 1
    TS_ASSERT_EQUALS(vmap.size(), 9);
    TS_ASSERT_EQUALS(v->second, "   line 1");
    ti_data = v->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 12);
    TS_ASSERT_EQUALS(ti->tm_min, 22);
    v++;
    v++;
    v++;
    // time 4
    TS_ASSERT_EQUALS(v->second, "   line 4");
    ti_data = v->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 12);
    TS_ASSERT_EQUALS(ti->tm_min, 22);
    // last time
    std::map<DateAndTime, std::string>::reverse_iterator rv = vmap.rbegin();
    TS_ASSERT_EQUALS(rv->second, "   line 9");
    ti_data = rv->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 14);
    TS_ASSERT_EQUALS(ti->tm_min, 3);
    // assert_throws(timeMean(p1));
    delete p1;
    delete icp_log;
  }

  // Test a variant of the log file containing CHANGE_PERIOD flags
  void testConstructionFromFileUsingICPVariant_CHANGE_PERIOD() {
    mkICPVariant();
    Property *icp_log =
        LogParser::createLogProperty(icp_file.path(), "icpevent");
    LogParser lp(icp_log);
    const Property *prop = lp.createAllPeriodsLog();
    const auto *timeseriesprop =
        dynamic_cast<const TimeSeriesProperty<int> *>(prop);
    TS_ASSERT(timeseriesprop);
    // Check the size
    TS_ASSERT_EQUALS(4, timeseriesprop->size());
    // Check the exact time stamps
    TS_ASSERT_EQUALS(DateAndTime("2000-09-05T12:22:55").toSimpleString(),
                     timeseriesprop->nthTime(0).toSimpleString());
    TS_ASSERT_EQUALS(DateAndTime("2000-09-05T12:23:08").toSimpleString(),
                     timeseriesprop->nthTime(1).toSimpleString());
    TS_ASSERT_EQUALS(DateAndTime("2000-09-05T12:23:22").toSimpleString(),
                     timeseriesprop->nthTime(2).toSimpleString());
    TS_ASSERT_EQUALS(DateAndTime("2000-09-05T12:23:37").toSimpleString(),
                     timeseriesprop->nthTime(3).toSimpleString());

    delete prop;
    delete icp_log;
  }

  void testConstructionFromPropertyUsingICPVariant_CHANGE_PERIOD() {
    auto *log = new TimeSeriesProperty<std::string>("ICPLog");
    // Notice we are using "CHANGE_PERIOD"
    TS_ASSERT_THROWS_NOTHING(
        log->addValue("2007-11-30T16:15:00", "CHANGE_PERIOD 1"));
    TS_ASSERT_THROWS_NOTHING(
        log->addValue("2007-11-30T16:16:00", "CHANGE_PERIOD 2"));
    TS_ASSERT_THROWS_NOTHING(
        log->addValue("2007-11-30T16:17:00", "CHANGE_PERIOD 3"));
    TS_ASSERT_THROWS_NOTHING(
        log->addValue("2007-11-30T16:18:00", "CHANGE_PERIOD 2"));

    LogParser logparser(log);

    const Property *prop = logparser.createAllPeriodsLog();
    const auto *timeseriesprop =
        dynamic_cast<const TimeSeriesProperty<int> *>(prop);
    TS_ASSERT(timeseriesprop);
    // Check the size
    TS_ASSERT_EQUALS(4, timeseriesprop->size());
    // Check the exact time stamps
    TS_ASSERT_EQUALS(DateAndTime("2007-11-30T16:15:00").toSimpleString(),
                     timeseriesprop->nthTime(0).toSimpleString());
    TS_ASSERT_EQUALS(DateAndTime("2007-11-30T16:16:00").toSimpleString(),
                     timeseriesprop->nthTime(1).toSimpleString());
    TS_ASSERT_EQUALS(DateAndTime("2007-11-30T16:17:00").toSimpleString(),
                     timeseriesprop->nthTime(2).toSimpleString());
    TS_ASSERT_EQUALS(DateAndTime("2007-11-30T16:18:00").toSimpleString(),
                     timeseriesprop->nthTime(3).toSimpleString());

    delete log;
    delete prop;
  }

  void testConstructionFromPropertyUsingICPVariant_CHANGE_SPACE_PERIOD() {
    auto *log = new TimeSeriesProperty<std::string>("ICPLog");
    // Notice we are using "CHANGE PERIOD"
    TS_ASSERT_THROWS_NOTHING(
        log->addValue("2007-11-30T16:15:00", "CHANGE PERIOD 1"));
    TS_ASSERT_THROWS_NOTHING(
        log->addValue("2007-11-30T16:16:00", "CHANGE PERIOD 2"));
    TS_ASSERT_THROWS_NOTHING(
        log->addValue("2007-11-30T16:17:00", "CHANGE PERIOD 3"));
    TS_ASSERT_THROWS_NOTHING(
        log->addValue("2007-11-30T16:18:00", "CHANGE PERIOD 2"));

    LogParser logparser(log);

    const Property *prop = logparser.createAllPeriodsLog();
    const auto *timeseriesprop =
        dynamic_cast<const TimeSeriesProperty<int> *>(prop);
    TS_ASSERT(timeseriesprop);
    // Check the size
    TS_ASSERT_EQUALS(4, timeseriesprop->size());
    // Check the exact time stamps
    TS_ASSERT_EQUALS(DateAndTime("2007-11-30T16:15:00").toSimpleString(),
                     timeseriesprop->nthTime(0).toSimpleString());
    TS_ASSERT_EQUALS(DateAndTime("2007-11-30T16:16:00").toSimpleString(),
                     timeseriesprop->nthTime(1).toSimpleString());
    TS_ASSERT_EQUALS(DateAndTime("2007-11-30T16:17:00").toSimpleString(),
                     timeseriesprop->nthTime(2).toSimpleString());
    TS_ASSERT_EQUALS(DateAndTime("2007-11-30T16:18:00").toSimpleString(),
                     timeseriesprop->nthTime(3).toSimpleString());

    delete log;
    delete prop;
  }

  // Check that periods that don't have a full "CHANGE PERIOD" flag are not
  // added.
  void testWontAddPeriodWithoutPERIODpartOfCHANGE_SPACE_PERIOD() {
    auto *log = new TimeSeriesProperty<std::string>("ICPLog");
    // Notice we are using "CHANGE PERIOD"
    TS_ASSERT_THROWS_NOTHING(
        log->addValue("2007-11-30T16:15:00", "CHANGE PERIOD 1"));
    TS_ASSERT_THROWS_NOTHING(
        log->addValue("2007-11-30T16:16:00", "CHANGE PERIOD 2"));
    TS_ASSERT_THROWS_NOTHING(
        log->addValue("2007-11-30T16:17:00", "CHANGE PERIOD 3"));
    TS_ASSERT_THROWS_NOTHING(log->addValue(
        "2007-11-30T16:18:00",
        "CHANGE 2")); // This is a duff entry. Shouldn't get added.

    LogParser logparser(log);

    const Property *prop = logparser.createAllPeriodsLog();
    const auto *timeseriesprop =
        dynamic_cast<const TimeSeriesProperty<int> *>(prop);
    TS_ASSERT(timeseriesprop);
    // Check the size
    TS_ASSERT_EQUALS(3, timeseriesprop->size());
    // Check the exact time stamps
    TS_ASSERT_EQUALS(DateAndTime("2007-11-30T16:15:00").toSimpleString(),
                     timeseriesprop->nthTime(0).toSimpleString());
    TS_ASSERT_EQUALS(DateAndTime("2007-11-30T16:16:00").toSimpleString(),
                     timeseriesprop->nthTime(1).toSimpleString());
    TS_ASSERT_EQUALS(DateAndTime("2007-11-30T16:17:00").toSimpleString(),
                     timeseriesprop->nthTime(2).toSimpleString());

    delete log;
    delete prop;
  }

  void testCreatesCurrentPeriodLog() {
    // Check it with a few expected period numbers.
    doTestCurrentPeriodLog(1);
    doTestCurrentPeriodLog(2);
    doTestCurrentPeriodLog(3);
  }

  void testNoICPevent() {
    if (icp_file.exists())
      icp_file.remove();
    mkGood();
    Property *icp_log =
        LogParser::createLogProperty(icp_file.path(), "icpevent");
    LogParser lp(icp_log);
    Property *p1 = lp.createLogProperty(log_num_good.path(), "good");
    TS_ASSERT(p1);
    TimeSeriesProperty<double> *tp1 =
        dynamic_cast<TimeSeriesProperty<double> *>(p1);
    std::map<DateAndTime, double> vmap = tp1->valueAsMap();
    std::map<DateAndTime, double>::iterator v = vmap.begin();

    // time 1
    TS_ASSERT_EQUALS(vmap.size(), 9);
    TS_ASSERT_EQUALS(v->second, 1);
    ti_data = v->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 12);
    TS_ASSERT_EQUALS(ti->tm_min, 22);
    v++;
    v++;
    v++;
    v++;
    // time 5
    // TS_ASSERT(!isNaN(v->second));
    // last time
    std::map<DateAndTime, double>::reverse_iterator rv = vmap.rbegin();
    TS_ASSERT_EQUALS(rv->second, 9);
    ti_data = rv->first.to_tm();
    ti = &ti_data;
    TS_ASSERT_EQUALS(ti->tm_hour, 14);
    TS_ASSERT_EQUALS(ti->tm_min, 3);
    TS_ASSERT_DELTA(timeMean(p1), 8.4904, 0.001);

    delete p1;
  }

  //----------------------------------------------------------------------------
  void test_timeMean() {
    TimeSeriesProperty<double> *log =
        new TimeSeriesProperty<double>("MydoubleLog");
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:00", 1));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:10", 2));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:20", 3));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:30", 4));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:40", 5));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:50", 6));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:18:00", 7));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:18:10", 8));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:18:20", 9));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:18:30", 10));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:18:40", 11));
    TS_ASSERT_EQUALS(log->realSize(), 11);

    TS_ASSERT_DELTA(timeMean(log), 6.0, 1e-3);
    delete log;
  }

  void test_timeMean_one_Value() {
    TimeSeriesProperty<double> *log =
        new TimeSeriesProperty<double>("MydoubleLog");
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:00", 56));
    TS_ASSERT_EQUALS(log->realSize(), 1);

    TS_ASSERT_DELTA(timeMean(log), 56.0, 1e-3);
    delete log;
  }

  /// Tests to see if we can cope with duplicate log values that have the same
  /// time.
  void test_timeMean_duplicate_values_with_same_timestamp() {
    TimeSeriesProperty<double> *log =
        new TimeSeriesProperty<double>("MydoubleLog");
    // Add the same value twice
    TS_ASSERT_THROWS_NOTHING(log->addValue("2012-07-19T20:00:00", 666));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2012-07-19T20:00:00", 666));
    TS_ASSERT_EQUALS(log->realSize(), 2);
    TS_ASSERT_DELTA(timeMean(log), 666, 1e-3);
    delete log;
  }

  void test_isICPEventLogNewStyle_works() {
    TimeSeriesProperty<std::string> *oldlog =
        new TimeSeriesProperty<std::string>("MyOldICPevent");
    TS_ASSERT_THROWS_NOTHING(oldlog->addValue("2012-07-19T20:00:00", "START"));
    TS_ASSERT_THROWS_NOTHING(oldlog->addValue("2012-07-19T20:00:01", "BEGIN"));
    TS_ASSERT_THROWS_NOTHING(oldlog->addValue("2012-07-19T20:00:02", "PAUSE"));

    auto logm = oldlog->valueAsMultiMap();
    TS_ASSERT(!LogParser::isICPEventLogNewStyle(logm));
    delete oldlog;

    TimeSeriesProperty<std::string> *newlog =
        new TimeSeriesProperty<std::string>("MyNewICPevent");
    TS_ASSERT_THROWS_NOTHING(newlog->addValue("2012-07-19T20:00:00", "START"));
    TS_ASSERT_THROWS_NOTHING(
        newlog->addValue("2012-07-19T20:00:01", "START_COLLECTION PERIOD 1"));
    TS_ASSERT_THROWS_NOTHING(newlog->addValue("2012-07-19T20:00:02", "PAUSE"));

    logm = newlog->valueAsMultiMap();
    TS_ASSERT(LogParser::isICPEventLogNewStyle(logm));
    delete newlog;

    newlog = new TimeSeriesProperty<std::string>("MyNewICPevent1");
    TS_ASSERT_THROWS_NOTHING(newlog->addValue("2012-07-19T20:00:00", "START"));
    TS_ASSERT_THROWS_NOTHING(
        newlog->addValue("2012-07-19T20:00:01", "STOP_COLLECTION PERIOD 1"));
    TS_ASSERT_THROWS_NOTHING(newlog->addValue("2012-07-19T20:00:02", "PAUSE"));

    logm = newlog->valueAsMultiMap();
    TS_ASSERT(LogParser::isICPEventLogNewStyle(logm));
    delete newlog;
  }

  void test_new_style_command_parsing() {
    TimeSeriesProperty<std::string> *log =
        new TimeSeriesProperty<std::string>("MyICPevent");
    log->addValue("2013-10-16T19:04:47", "CHANGE_PERIOD 1");
    log->addValue("2013-10-16T19:04:48", "RESUME");
    log->addValue("2013-10-16T19:04:48",
                  "START_COLLECTION PERIOD 1 GF 60015 RF 75039 GUAH 69.875610");
    log->addValue(
        "2013-10-16T19:06:53",
        "STOP_COLLECTION PERIOD 1 GF 65024 RF 81303 GUAH 75.712013 DUR 125");
    log->addValue("2013-10-16T19:06:53", "PAUSE");
    log->addValue("2013-10-16T19:06:53", "CHANGE_PERIOD 2");
    log->addValue("2013-10-16T19:06:53", "RESUME");
    log->addValue("2013-10-16T19:06:53",
                  "START_COLLECTION PERIOD 2 GF 65024 RF 81303 GUAH 75.712013");
    log->addValue(
        "2013-10-16T19:08:58",
        "STOP_COLLECTION PERIOD 2 GF 70033 RF 87567 GUAH 81.547050 DUR 125");
    log->addValue("2013-10-16T19:08:58", "PAUSE");
    log->addValue("2013-10-16T19:08:58", "CHANGE_PERIOD 1");
    log->addValue("2013-10-16T19:08:59", "RESUME");
    log->addValue("2013-10-16T19:08:59",
                  "START_COLLECTION PERIOD 1 GF 70033 RF 87567 GUAH 81.547050");
    log->addValue(
        "2013-10-16T19:11:03",
        "STOP_COLLECTION PERIOD 1 GF 75005 RF 93784 GUAH 87.339035 DUR 124");
    log->addValue("2013-10-16T19:11:03", "PAUSE");
    log->addValue("2013-10-16T19:11:03", "CHANGE_PERIOD 2");
    log->addValue("2013-10-16T19:11:04", "RESUME");
    log->addValue("2013-10-16T19:11:04",
                  "START_COLLECTION PERIOD 2 GF 75005 RF 93784 GUAH 87.339035");
    log->addValue(
        "2013-10-16T19:13:09",
        "STOP_COLLECTION PERIOD 2 GF 80016 RF 100049 GUAH 93.174751 DUR 125");
    log->addValue("2013-10-16T19:13:09", "PAUSE");
    log->addValue("2013-10-16T19:13:09", "CHANGE_PERIOD 1");
    log->addValue("2013-10-16T19:13:09", "RESUME");

    std::vector<std::pair<std::string, int>> checkPeriod(5);
    checkPeriod[0] = std::make_pair("2013-10-16T19:04:47", 1);
    checkPeriod[1] = std::make_pair("2013-10-16T19:06:53", 2);
    checkPeriod[2] = std::make_pair("2013-10-16T19:08:58", 1);
    checkPeriod[3] = std::make_pair("2013-10-16T19:11:03", 2);
    checkPeriod[4] = std::make_pair("2013-10-16T19:13:09", 1);

    std::vector<std::pair<std::string, bool>> checkRunning(8);
    checkRunning[0] = std::make_pair("2013-10-16T19:04:48", true);
    checkRunning[1] = std::make_pair("2013-10-16T19:06:53", false);
    checkRunning[2] = std::make_pair("2013-10-16T19:06:53", true);
    checkRunning[3] = std::make_pair("2013-10-16T19:08:58", false);
    checkRunning[4] = std::make_pair("2013-10-16T19:08:59", true);
    checkRunning[5] = std::make_pair("2013-10-16T19:11:03", false);
    checkRunning[6] = std::make_pair("2013-10-16T19:11:04", true);
    checkRunning[7] = std::make_pair("2013-10-16T19:13:09", false);

    LogParser logparser(log);

    const Property *prop = logparser.createAllPeriodsLog();
    const auto *allPeriodsProp =
        dynamic_cast<const TimeSeriesProperty<int> *>(prop);
    TS_ASSERT(allPeriodsProp);

    TS_ASSERT_EQUALS(5, allPeriodsProp->size());
    auto logm = allPeriodsProp->valueAsMultiMap();
    size_t i = 0;
    for (auto it = logm.begin(); it != logm.end(); ++it) {
      TS_ASSERT_EQUALS(it->first.toISO8601String(), checkPeriod[i].first);
      TS_ASSERT_EQUALS(it->second, checkPeriod[i].second);
      ++i;
    }

    delete prop;
    prop = logparser.createRunningLog();
    const auto *runningProp =
        dynamic_cast<const TimeSeriesProperty<bool> *>(prop);
    TS_ASSERT(runningProp);

    TS_ASSERT_EQUALS(8, runningProp->size());
    auto logm1 = runningProp->valueAsMultiMap();
    i = 0;
    for (auto it = logm1.begin(); it != logm1.end(); ++it) {
      TS_ASSERT_EQUALS(it->first.toISO8601String(), checkRunning[i].first);
      TS_ASSERT_EQUALS(it->second, checkRunning[i].second);
      ++i;
    }

    delete prop;
    delete log;
  }

  void test_str_repeat() {
    mkStrRepeat();
    Property *prop = LogParser::createLogProperty(log_str_repeat.path(), "log");
    const auto *log =
        dynamic_cast<const TimeSeriesProperty<std::string> *>(prop);
    TS_ASSERT(log);
    auto logm = log->valueAsMultiMap();
    auto it = logm.begin();
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:22:34");
    TS_ASSERT_EQUALS(it->second, "   First line");
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:22:34");
    TS_ASSERT_EQUALS(it->second, "   Second line");
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:23:33");
    TS_ASSERT_EQUALS(it->second, "   First line");
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:23:33");
    TS_ASSERT_EQUALS(it->second, "   Second line");
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:23:33");
    TS_ASSERT_EQUALS(it->second, "   Third line");
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:23:33");
    TS_ASSERT_EQUALS(it->second, "   Fourth line");
    ++it;
    delete prop;
  }

  void test_num_repeat() {
    mkNumRepeat();
    Property *prop = LogParser::createLogProperty(log_str_repeat.path(), "log");
    const auto *log = dynamic_cast<const TimeSeriesProperty<double> *>(prop);
    TS_ASSERT(log);
    auto logm = log->valueAsMultiMap();
    auto it = logm.begin();
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:22:34");
    TS_ASSERT_EQUALS(it->second, 1);
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:22:34");
    TS_ASSERT_EQUALS(it->second, 2);
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:23:33");
    TS_ASSERT_EQUALS(it->second, 3);
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:23:33");
    TS_ASSERT_EQUALS(it->second, 4);
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:23:33");
    TS_ASSERT_EQUALS(it->second, 5);
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:23:33");
    TS_ASSERT_EQUALS(it->second, 6);
    ++it;
    delete prop;
  }

  void test_str_continuation() {
    mkStrContinuations();
    Property *prop =
        LogParser::createLogProperty(log_str_continuations.path(), "log");
    const auto *log =
        dynamic_cast<const TimeSeriesProperty<std::string> *>(prop);
    TS_ASSERT(log);
    auto logm = log->valueAsMultiMap();
    auto it = logm.begin();
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:22:31");
    TS_ASSERT_EQUALS(it->second, "   First line Second line");
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:22:34");
    TS_ASSERT_EQUALS(it->second, "   First line");
    ++it;
    TS_ASSERT_EQUALS(it->first.toISO8601String(), "2000-09-05T12:22:34");
    TS_ASSERT_EQUALS(it->second, "   Second line Third line");
    ++it;
    delete prop;
  }

  /// If a run is aborted and then restarted, the "running" log should be set to
  /// false at all times during the aborted run.
  void test_abort_runningLogAlwaysFalseBeforeRestart() {
    auto log = Mantid::Kernel::make_unique<TimeSeriesProperty<std::string>>(
        "MyICPevent");

    // (This is a cut-down version of EMU66122)
    const DateAndTime timeZero{"2016-10-01T10:01:44"};
    const std::vector<DateAndTime> times{
        timeZero,        timeZero + 3.0,  timeZero + 3.0,   timeZero + 3.0,
        timeZero + 51.0, timeZero + 51.0, timeZero + 57.0,  timeZero + 60.0,
        timeZero + 60.0, timeZero + 60.0, timeZero + 111.0, timeZero + 111.0};
    const std::vector<std::string> values{
        "CHANGE_PERIOD 1",
        "CHANGE_PERIOD 1",
        "START_COLLECTION PERIOD 1 GF 0 RF 0 GUAH 0.000000",
        "BEGIN",
        "STOP_COLLECTION PERIOD 1 GF 1931 RF 1933 GUAH 0.000000 DUR 48",
        "ABORT",
        "CHANGE_PERIOD 1",
        "CHANGE_PERIOD 1",
        "START_COLLECTION PERIOD 1 GF 0 RF 0 GUAH 0.000000",
        "BEGIN",
        "STOP_COLLECTION PERIOD 1 GF 2062 RF 2064 GUAH 0.000000 DUR 51",
        "END"};
    log->addValues(times, values);

    const std::multimap<DateAndTime, bool> expectedRunning{
        {timeZero + 3.0, false},    // start - run later aborted
        {timeZero + 51.0, false},   // stop
        {timeZero + 51.0, false},   // abort
        {timeZero + 60.0, true},    // start
        {timeZero + 111.0, false}}; // stop

    const LogParser logparser(log.get());

    const auto prop = std::unique_ptr<Property>(logparser.createRunningLog());
    const auto *runningProp =
        dynamic_cast<const TimeSeriesProperty<bool> *>(prop.get());
    TS_ASSERT(runningProp);
    TS_ASSERT_EQUALS(expectedRunning.size(), runningProp->size());
    const auto &runningMap = runningProp->valueAsMultiMap();
    TS_ASSERT_EQUALS(expectedRunning, runningMap);
  }

private:
  /// Helper method to run common test code for checking period logs.
  void doTestCurrentPeriodLog(const int &expected_period) {
    auto *log = new TimeSeriesProperty<std::string>("ICPLog");
    LogParser logparser(log);
    Property *prop = logparser.createCurrentPeriodLog(expected_period);
    PropertyWithValue<int> *prop_with_value =
        dynamic_cast<PropertyWithValue<int> *>(prop);

    int value;
    TS_ASSERT(prop_with_value != NULL);
    Mantid::Kernel::toValue<int>(prop_with_value->value(), value);
    TS_ASSERT_EQUALS(expected_period, value);
    delete prop;
    delete log;
  }

  void mkICP() {
    std::ofstream f(icp_file.path().c_str());
    int dt = 0;
    f << "2000-09-05T12:22:28   START_SE_WAIT\n";
    f << "2000-09-05T12:22:33   BEGIN\n";
    dt += 8;
    f << "2000-09-05T12:22:41   PAUSE\n";
    dt += 4;
    f << "2000-09-05T12:22:55   CHANGE PERIOD 2\n";
    dt += 3;
    f << "2000-09-05T12:22:58   RESUME\n";
    dt += 6;
    f << "2000-09-05T12:23:04   PAUSE\n";
    dt += 4;
    f << "2000-09-05T12:23:08   CHANGE PERIOD 1\n";
    dt += 2;
    f << "2000-09-05T12:23:10   RESUME\n";
    dt += 8;
    f << "2000-09-05T12:23:18   START_SE_WAIT\n";
    dt += 4;
    f << "2000-09-05T12:23:22   CHANGE PERIOD 2\n";
    dt += 5;
    f << "2000-09-05T12:23:27   RESUME\n";
    dt += 7;
    f << "2000-09-05T12:23:34   ABORT\n";
    dt += 3;
    f << "2000-09-05T12:23:37   CHANGE PERIOD 1\n";
    dt += 5;
    f << "2000-09-05T12:23:42   END_SE_WAIT\n";
    f << "2000-09-05T14:03:54   END\n";
    f.close();
  }
  void mkICPVariant() {
    std::ofstream f(icp_file.path().c_str());
    int dt = 0;
    f << "2000-09-05T12:22:28   START_SE_WAIT\n";
    f << "2000-09-05T12:22:33   BEGIN\n";
    dt += 8;
    f << "2000-09-05T12:22:41   PAUSE\n";
    dt += 4;
    f << "2000-09-05T12:22:55   CHANGE_PERIOD 2\n";
    dt += 3;
    f << "2000-09-05T12:22:58   RESUME\n";
    dt += 6;
    f << "2000-09-05T12:23:04   PAUSE\n";
    dt += 4;
    f << "2000-09-05T12:23:08   CHANGE_PERIOD 1\n";
    dt += 2;
    f << "2000-09-05T12:23:10   RESUME\n";
    dt += 8;
    f << "2000-09-05T12:23:18   START_SE_WAIT\n";
    dt += 4;
    f << "2000-09-05T12:23:22   CHANGE_PERIOD 2\n";
    dt += 5;
    f << "2000-09-05T12:23:27   RESUME\n";
    dt += 7;
    f << "2000-09-05T12:23:34   ABORT\n";
    dt += 3;
    f << "2000-09-05T12:23:37   CHANGE_PERIOD 1\n";
    dt += 5;
    f << "2000-09-05T12:23:42   END_SE_WAIT\n";
    f << "2000-09-05T14:03:54   END\n";
    f.close();
  }

  void mkGood() {
    std::ofstream f(log_num_good.path().c_str());
    int dt = 4;
    f << "2000-09-05T12:22:31   " << 1 << '\n';
    f << "2000-09-05T12:22:37   " << 2 << '\n';
    dt += 1;
    f << "2000-09-05T12:22:38   " << 3 << '\n';
    dt += 1;
    f << "2000-09-05T12:22:39   " << 4 << '\n';
    dt += 3;
    f << "2000-09-05T12:22:42   " << 5 << '\n';
    dt += 5;
    f << "2000-09-05T12:22:47   " << 6 << '\n';
    dt += 9;
    f << "2000-09-05T12:22:56   " << 7 << '\n';
    dt += 4;
    f << "2000-09-05T12:23:00   " << 8 << '\n';
    f << "2000-09-05T14:03:56   " << 9 << '\n';
    f.close();
  }

  void mkLate() {
    std::ofstream f(log_num_late.path().c_str());
    int dt = 4;
    f << "2000-09-05T12:22:37   " << 2 << '\n';
    dt += 1;
    f << "2000-09-05T12:22:38   " << 3 << '\n';
    dt += 1;
    f << "2000-09-05T12:22:39   " << 4 << '\n';
    dt += 3;
    f << "2000-09-05T12:22:42   " << 5 << '\n';
    dt += 5;
    f << "2000-09-05T12:22:47   " << 6 << '\n';
    dt += 9;
    f << "2000-09-05T12:22:56   " << 7 << '\n';
    dt += 4;
    f << "2000-09-05T12:23:00   " << 8 << '\n';
    f << "2000-09-05T14:03:56   " << 9 << '\n';
    f.close();
  }

  void mkEarly() {
    std::ofstream f(log_num_early.path().c_str());
    int dt = 4;
    f << "2000-09-05T12:22:31   " << 1 << '\n';
    f << "2000-09-05T12:22:37   " << 2 << '\n';
    dt += 1;
    f << "2000-09-05T12:22:38   " << 3 << '\n';
    dt += 1;
    f << "2000-09-05T12:22:39   " << 4 << '\n';
    dt += 3;
    f << "2000-09-05T12:22:42   " << 5 << '\n';
    dt += 5;
    f << "2000-09-05T12:22:47   " << 6 << '\n';
    dt += 9;
    f << "2000-09-05T12:22:56   " << 7 << '\n';
    dt += 4;
    f << "2000-09-05T12:23:00   " << 8 << '\n';
    f.close();
  }

  void mkSingle() {
    std::ofstream f(log_num_single.path().c_str());
    f << "2000-09-05T12:22:51   " << 4 << '\n';
    f.close();
  }

  void mkStr() {
    std::ofstream f(log_str.path().c_str());
    int dt = 4;
    f << "2000-09-05T12:22:31   line " << 1 << '\n';
    f << "2000-09-05T12:22:37   line " << 2 << '\n';
    dt += 1;
    f << "2000-09-05T12:22:38   line " << 3 << '\n';
    dt += 1;
    f << "2000-09-05T12:22:39   line " << 4 << '\n';
    dt += 3;
    f << "2000-09-05T12:22:42   line " << 5 << '\n';
    dt += 5;
    f << "2000-09-05T12:22:47   line " << 6 << '\n';
    dt += 9;
    f << "2000-09-05T12:22:56   line " << 7 << '\n';
    dt += 4;
    f << "2000-09-05T12:23:00   line " << 8 << '\n';
    f << "2000-09-05T14:03:56   line " << 9 << '\n';
    f.close();
  }

  void mkStrContinuations() {
    std::ofstream f(log_str_continuations.path().c_str());
    f << "2000-09-05T12:22:31   First line\n";
    f << "Second line\n";
    f << "2000-09-05T12:22:34   First line\n";
    f << "2000-09-05T12:22:34   Second line\n";
    f << "Third line\n";
    f.close();
  }

  void mkStrRepeat() {
    std::ofstream f(log_str_repeat.path().c_str());
    f << "2000-09-05T12:22:34   First line\n";
    f << "2000-09-05T12:22:34   Second line\n";
    f << "2000-09-05T12:23:33   First line\n";
    f << "2000-09-05T12:23:33   Second line\n";
    f << "2000-09-05T12:23:33   Third line\n";
    f << "2000-09-05T12:23:33   Fourth line\n";
    f.close();
  }

  void mkNumRepeat() {
    std::ofstream f(log_str_repeat.path().c_str());
    f << "2000-09-05T12:22:34   1\n";
    f << "2000-09-05T12:22:34   2\n";
    f << "2000-09-05T12:23:33   3\n";
    f << "2000-09-05T12:23:33   4\n";
    f << "2000-09-05T12:23:33   5\n";
    f << "2000-09-05T12:23:33   6\n";
    f.close();
  }

  TmpFile
      log_num_good; // run time interval is within first - last times of the log
  TmpFile log_num_late;          // first time is later than run start
  TmpFile log_num_early;         // last time is earlier than run ends
  TmpFile log_num_single;        // single value
  TmpFile log_str;               // file of strings
  TmpFile icp_file;              // icpevent file
  TmpFile log_str_repeat;        // string log with repeating lines
  TmpFile log_num_repeat;        // num log with repeating lines
  TmpFile log_str_continuations; // string log with continuation lines

  tm ti_data;
  tm *ti;
};

#endif /*LOGPARSERTEST_H_*/