Skip to content
Snippets Groups Projects
BoxControllerSettingsAlgorithmTest.h 9.87 KiB
Newer Older
#ifndef MANTID_MDEVENTS_BOXCONTROLLERSETTINGSALGORITHMTEST_H_
#define MANTID_MDEVENTS_BOXCONTROLLERSETTINGSALGORITHMTEST_H_

#include "MantidAPI/FrameworkManager.h"
#include "MantidDataHandling/LoadParameterFile.h"
#include "MantidMDAlgorithms/BoxControllerSettingsAlgorithm.h"
#include "MantidTestHelpers/ComponentCreationHelper.h"
#include "MantidTestHelpers/ScopedFileHelper.h"
#include "MantidTestHelpers/WorkspaceCreationHelper.h"

#include <boost/format.hpp>
#include <cxxtest/TestSuite.h>

using namespace Mantid;
using namespace Mantid::API;
using namespace Mantid::MDAlgorithms;
using ScopedFileHelper::ScopedFile;

//------------------------------------------------------------------------------------------------
/** Concrete declaration of BoxControllerSettingsAlgorithm for testing */
class BoxControllerSettingsAlgorithmImpl
    : public BoxControllerSettingsAlgorithm {
  // Make all the members public so I can test them.
  friend class BoxControllerSettingsAlgorithmTest;
  const std::string name() const override {
    return "BoxControllerSettingsAlgorithmImpl";
  }
  int version() const override { return 1; }
  const std::string category() const override { return "Testing"; }
  const std::string summary() const override { return "Summary of this test."; }
  void init() override {}
  void exec() override {}
class BoxControllerSettingsAlgorithmTest : public CxxTest::TestSuite {
  Helper function. Runs LoadParameterAlg, to get an instrument parameter
  definition from a file onto a workspace.
  void apply_instrument_parameter_file_to_workspace(MatrixWorkspace_sptr ws,
                                                    const ScopedFile &file) {
    // Load the Instrument Parameter file over the existing test workspace +
    // instrument.
    using DataHandling::LoadParameterFile;
    LoadParameterFile loadParameterAlg;
    loadParameterAlg.setRethrows(true);
    loadParameterAlg.initialize();
    loadParameterAlg.setPropertyValue("Filename", file.getFileName());
    loadParameterAlg.setProperty("Workspace", ws);
    loadParameterAlg.execute();
  }

  MatrixWorkspace_sptr
  create_workspace_with_splitting_params(int splitThreshold, int splitInto,
                                         int maxRecursionDepth) {
    auto ws = boost::make_shared<Mantid::DataObjects::Workspace2D>();
    ws->initialize(1, 2, 1);
    ws->setInstrument(
        ComponentCreationHelper::createTestInstrumentRectangular(6, 1, 0.0));
    const std::string instrumentName = ws->getInstrument()->getName();

    // Create a parameter file, with a root equation that will apply to all
    // detectors.
    const std::string parameterFileContents = boost::str(
        boost::format("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\
       <parameter-file instrument = \"%1%\" date = \"2013-01-31T00:00:00\">\n\
          <component-link name=\"%1%\">\n\
           <parameter name=\"SplitThreshold\">\n\
               <value val=\"%2%\"/>\n\
           </parameter>\n\
           <parameter name=\"SplitInto\">\n\
               <value val=\"%3%\"/>\n\
           </parameter>\n\
           <parameter name=\"MaxRecursionDepth\">\n\
               <value val=\"%4%\"/>\n\
           </parameter>\n\
           </component-link>\n\
        </parameter-file>\n") %
        instrumentName % splitThreshold % splitInto % maxRecursionDepth);

    // Create a temporary Instrument Parameter file.
    ScopedFile file(parameterFileContents, instrumentName + "_Parameters.xml");

    // Apply parameter file to workspace.
    apply_instrument_parameter_file_to_workspace(ws, file);

    return ws;
  }

  void test_defaultProps() {
    BoxControllerSettingsAlgorithmImpl alg;
    alg.initBoxControllerProps();
    BoxController_sptr bc(new BoxController(3));
    alg.setBoxController(bc);
    TS_ASSERT_EQUALS(bc->getSplitInto(0), 5);
    TS_ASSERT_EQUALS(bc->getSplitThreshold(), 1000);
    TS_ASSERT_EQUALS(bc->getMaxDepth(), 5);
  }

  /** You can change the defaults given to the props */
  void test_initProps_otherDefaults() {
    BoxControllerSettingsAlgorithmImpl alg;
    alg.initBoxControllerProps("6", 1234, 34);
    BoxController_sptr bc(new BoxController(3));
    alg.setBoxController(bc);
    TS_ASSERT_EQUALS(bc->getSplitInto(0), 6);
    TS_ASSERT_EQUALS(bc->getSplitThreshold(), 1234);
    TS_ASSERT_EQUALS(bc->getMaxDepth(), 34);
  void doTest(BoxController_sptr bc, std::string SplitInto = "",
              std::string SplitThreshold = "",
              std::string MaxRecursionDepth = "") {
    if (!SplitInto.empty())
      alg.setPropertyValue("SplitInto", SplitInto);
    if (!SplitThreshold.empty())
      alg.setPropertyValue("SplitThreshold", SplitThreshold);
    if (!MaxRecursionDepth.empty())
      alg.setPropertyValue("MaxRecursionDepth", MaxRecursionDepth);
  void test_SplitInto() {
    BoxController_sptr bc(new BoxController(3));
    TSM_ASSERT_THROWS_ANYTHING("Too few parameters", doTest(bc, "5,5"));
    TSM_ASSERT_THROWS_ANYTHING("Too many parameters", doTest(bc, "1,2,3,4"));
    doTest(bc, "4");
    TS_ASSERT_EQUALS(bc->getSplitInto(2), 4);
    doTest(bc, "7,6,5");
    TS_ASSERT_EQUALS(bc->getSplitInto(0), 7);
    TS_ASSERT_EQUALS(bc->getSplitInto(1), 6);
    TS_ASSERT_EQUALS(bc->getSplitInto(2), 5);
  void test_SplitThreshold() {
    BoxController_sptr bc(new BoxController(3));
    TSM_ASSERT_THROWS_ANYTHING("Negative threshold", doTest(bc, "", "-3"));
    doTest(bc, "", "1234");
    TS_ASSERT_EQUALS(bc->getSplitThreshold(), 1234);
  void test_MaxRecursionDepth() {
    BoxController_sptr bc(new BoxController(3));
    TSM_ASSERT_THROWS_ANYTHING("Negative MaxRecursionDepth",
                               doTest(bc, "", "", "-1"));
    doTest(bc, "", "", "34");
    TS_ASSERT_EQUALS(bc->getMaxDepth(), 34);
  void test_take_instrument_parameters() {
    const int splitInto = 4;
    const int splitThreshold = 16;
    const int maxRecursionDepth = 5;

    // Workspace has instrument has parameters for all box splitting parameters.
    auto ws = create_workspace_with_splitting_params(splitThreshold, splitInto,
                                                     maxRecursionDepth);

    BoxController_sptr bc(new BoxController(1));

    BoxControllerSettingsAlgorithmImpl alg;
    alg.initBoxControllerProps();
    /* Note, not properties are set, so the algorithm will have defaults set.
    and should therefore look to
    pick-up any available in the instrument parameters.*/
    alg.setBoxController(bc, ws->getInstrument());

    int actualSplitThreshold = alg.getProperty("SplitThreshold");
    TS_ASSERT_EQUALS(splitThreshold, actualSplitThreshold);

    std::vector<int> actualSplitInto = alg.getProperty("SplitInto");
    TS_ASSERT_EQUALS(bc->getNDims(), actualSplitInto.size());
    std::vector<int> expectedSplitInto(bc->getNDims(), splitInto);
    TS_ASSERT_EQUALS(expectedSplitInto, actualSplitInto);

    int actualMaxRecursionDepth = alg.getProperty("MaxRecursionDepth");
    TS_ASSERT_EQUALS(maxRecursionDepth, actualMaxRecursionDepth);
  }

  // Test that the user providied values for spliting have precedence.
  void test_ignore_instrument_parameters() {
    const int splitInto = 8;
    const int splitThreshold = 16;
    const int maxRecursionDepth = 5;

    // Workspace has instrument has parameters for all box splitting parameters.
    auto ws = create_workspace_with_splitting_params(splitThreshold, splitInto,
                                                     maxRecursionDepth);

    BoxController_sptr bc(new BoxController(1));

    // Create splitting parameters that are not default and not the same as
    // those on the instrument parameters.
    const std::vector<int> nonDefaultSplitInto =
        std::vector<int>(bc->getNDims(), splitInto + 1);
    const int nonDefaultSplitThreshold = splitThreshold + 1;
    const int nonDefaultMaxRecursionDepth = maxRecursionDepth + 1;

    BoxControllerSettingsAlgorithmImpl alg;
    alg.initBoxControllerProps();
    alg.setProperty("SplitInto", nonDefaultSplitInto);
    alg.setProperty("SplitThreshold", nonDefaultSplitThreshold);
    alg.setProperty("MaxRecursionDepth", nonDefaultMaxRecursionDepth);
    alg.setBoxController(bc, ws->getInstrument());

    int actualSplitThreshold = alg.getProperty("SplitThreshold");
    TS_ASSERT_EQUALS(nonDefaultSplitThreshold, actualSplitThreshold);

    std::vector<int> actualSplitInto = alg.getProperty("SplitInto");
    TS_ASSERT_EQUALS(bc->getNDims(), actualSplitInto.size());
    TS_ASSERT_EQUALS(nonDefaultSplitInto, actualSplitInto);

    int actualMaxRecursionDepth = alg.getProperty("MaxRecursionDepth");
    TS_ASSERT_EQUALS(nonDefaultMaxRecursionDepth, actualMaxRecursionDepth);
  }

  void test_with_no_instrument_parameters() {
    // Create a workspace with an instrument, but no instrument parameters for
    // box splitting.
    auto ws = boost::make_shared<Mantid::DataObjects::Workspace2D>();
    ws->initialize(1, 2, 1);
    ws->setInstrument(
        ComponentCreationHelper::createTestInstrumentRectangular(6, 1, 0));

    BoxController_sptr bc(new BoxController(1));

    BoxControllerSettingsAlgorithmImpl alg;
    alg.initBoxControllerProps();
    // Note that no properties are actually set. All properties should fall-back
    // to their default values.
    alg.setRethrows(true);
    TSM_ASSERT_THROWS_NOTHING("Lack of specific instrument parameters should "
                              "not cause algorithm to fail.",
                              alg.setBoxController(bc, ws->getInstrument()));
    // Check that the properties are unaffected. Should just reflect the
    // defaults.
    Mantid::Kernel::Property *p = alg.getProperty("SplitThreshold");
    TS_ASSERT(p->isDefault());
    p = alg.getProperty("SplitInto");
    TS_ASSERT(p->isDefault());
    p = alg.getProperty("MaxRecursionDepth");
    TS_ASSERT(p->isDefault());
  }
};

#endif /* MANTID_MDEVENTS_BOXCONTROLLERSETTINGSALGORITHMTEST_H_ */