Skip to content
Snippets Groups Projects
FunctionParameterDecoratorTest.h 14.6 KiB
Newer Older
#ifndef MANTID_API_WRAPPEDFUNCTIONTEST_H_
#define MANTID_API_WRAPPEDFUNCTIONTEST_H_

#include <cxxtest/TestSuite.h>

#include "MantidAPI/CompositeFunction.h"
#include "MantidAPI/FunctionFactory.h"
#include "MantidAPI/FunctionParameterDecorator.h"
#include "MantidAPI/ParamFunction.h"
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidKernel/Exception.h"
#include "MantidKernel/WarningSuppressions.h"
#include <boost/make_shared.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace Mantid::API;
using namespace Mantid::Kernel;

using ::testing::_;
using ::testing::Mock;

class FunctionParameterDecoratorTest;

class TestableFunctionParameterDecorator : public FunctionParameterDecorator {
  friend class FunctionParameterDecoratorTest;

public:
  TestableFunctionParameterDecorator() {}
  ~TestableFunctionParameterDecorator() override {}
  std::string name() const override {
    return "TestableFunctionParameterDecorator";
  }
  void function(const FunctionDomain &domain,
                FunctionValues &values) const override {
    throwIfNoFunctionSet();

    IFunction_sptr fn = getDecoratedFunction();
    fn->function(domain, values);
  }

  void functionDeriv(const FunctionDomain &domain,
                     Jacobian &jacobian) override {
    throwIfNoFunctionSet();

    IFunction_sptr fn = getDecoratedFunction();
    fn->functionDeriv(domain, jacobian);
  }
};

DECLARE_FUNCTION(TestableFunctionParameterDecorator)
class FunctionWithParameters : public ParamFunction {
public:
  FunctionWithParameters() : ParamFunction(), m_workspace() {}
  std::string name() const override { return "FunctionWithParameters"; }
  void init() override {
    declareParameter("Height");
    declareParameter("PeakCentre");
    declareParameter("Sigma");
  }

  void function(const FunctionDomain &domain,
                FunctionValues &values) const override {
    UNUSED_ARG(domain);
    UNUSED_ARG(values);
    // Does nothing, not required for this test.
  }
  void setWorkspace(boost::shared_ptr<const Workspace> ws) override {
    m_workspace = ws;
  }

  Workspace_const_sptr getWorkspace() const { return m_workspace; }

private:
  Workspace_const_sptr m_workspace;
DECLARE_FUNCTION(FunctionWithParameters)

class FunctionWithAttributes : public ParamFunction {
public:
  FunctionWithAttributes() : ParamFunction() {}

  std::string name() const override { return "FunctionWithAttributes"; }
  void init() override {
    declareParameter("PeakCentre");
    declareParameter("Sigma");
    declareParameter("Height");

    declareAttribute("Attribute1", IFunction::Attribute(1));
    declareAttribute("Attribute2", IFunction::Attribute("Test"));
  }

  void function(const FunctionDomain &domain,
                FunctionValues &values) const override {
    UNUSED_ARG(domain);
    UNUSED_ARG(values);
    // Does nothing, not required for this test.
  }
};

DECLARE_FUNCTION(FunctionWithAttributes)
class FunctionParameterDecoratorTest : public CxxTest::TestSuite {
public:
  // This pair of boilerplate methods prevent the suite being created statically
  // This means the constructor isn't called when running other tests
  static FunctionParameterDecoratorTest *createSuite() {
    return new FunctionParameterDecoratorTest();
  }
  static void destroySuite(FunctionParameterDecoratorTest *suite) {
    delete suite;
  }

  void testSetDecoratedFunction() {
    TestableFunctionParameterDecorator fn;

    TS_ASSERT_THROWS_NOTHING(fn.setDecoratedFunction("FunctionWithParameters"));

    IFunction_sptr decorated = fn.getDecoratedFunction();
    TS_ASSERT(decorated);
    TS_ASSERT_EQUALS(decorated->name(), "FunctionWithParameters");
  }

  void testSetDecoratedFunctionInvalidName() {
    TestableFunctionParameterDecorator fn;
    TS_ASSERT_THROWS(fn.setDecoratedFunction("INVALIDFUNCTION"),
                     Exception::NotFoundError);
    TS_ASSERT(!fn.getDecoratedFunction());
  }

  void testThrowIfNoFunctionSet() {
    TestableFunctionParameterDecorator fn;
    TS_ASSERT_THROWS(fn.throwIfNoFunctionSet(), std::runtime_error);
    fn.setDecoratedFunction("FunctionWithParameters");
    TS_ASSERT_THROWS_NOTHING(fn.throwIfNoFunctionSet());
  }

  void testNParams() {
    TestableFunctionParameterDecorator invalidFn;
    TS_ASSERT_EQUALS(invalidFn.nParams(), 0);

    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();
    IFunction_sptr decoratedFunction = fn->getDecoratedFunction();

    TS_ASSERT_EQUALS(fn->nParams(), decoratedFunction->nParams());
  }

  void testGetSetParameter() {
    TestableFunctionParameterDecorator invalidFn;
    TS_ASSERT_THROWS(invalidFn.setParameter(0, 2.0), std::runtime_error);
    TS_ASSERT_THROWS(invalidFn.getParameter(0), std::runtime_error);
    TS_ASSERT_THROWS(invalidFn.setParameter("Height", 2.0), std::runtime_error);
    TS_ASSERT_THROWS(invalidFn.getParameter("Height"), std::runtime_error);

    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();

    TS_ASSERT_THROWS_NOTHING(fn->setParameter(0, 2.0));

    IFunction_sptr decoratedFunction = fn->getDecoratedFunction();
    TS_ASSERT_EQUALS(fn->getParameter(0), decoratedFunction->getParameter(0));
    TS_ASSERT_EQUALS(fn->getParameter(0), 2.0);
    TS_ASSERT_THROWS(fn->getParameter(10), std::out_of_range);

    TS_ASSERT_THROWS_NOTHING(fn->setParameter("Height", 4.0));
    TS_ASSERT_EQUALS(fn->getParameter("Height"),
                     decoratedFunction->getParameter("Height"));
    TS_ASSERT_EQUALS(fn->getParameter("Height"), 4.0);
    TS_ASSERT_THROWS(fn->getParameter("DoesNotExist"), std::invalid_argument);
  }

  void testExplicitelySet() {
    TestableFunctionParameterDecorator invalidFn;
    TS_ASSERT_THROWS(invalidFn.isExplicitlySet(0), std::runtime_error);

    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();

    TS_ASSERT_THROWS_NOTHING(fn->setParameter(0, 2.0));

    IFunction_sptr decoratedFunction = fn->getDecoratedFunction();

    for (size_t i = 0; i < fn->nParams(); ++i) {
      TS_ASSERT_EQUALS(fn->isExplicitlySet(i),
                       decoratedFunction->isExplicitlySet(i));
    }
  }

  void testGetSetError() {
    TestableFunctionParameterDecorator invalidFn;
    TS_ASSERT_THROWS(invalidFn.getError(0), std::runtime_error);
    TS_ASSERT_THROWS(invalidFn.setError(0, 2.0), std::runtime_error);

    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();
    IFunction_sptr decoratedFunction = fn->getDecoratedFunction();

    TS_ASSERT_THROWS_NOTHING(fn->setError(0, 3.0));
    TS_ASSERT_EQUALS(fn->getError(0), 3.0);
    for (size_t i = 0; i < fn->nParams(); ++i) {
      TS_ASSERT_EQUALS(fn->getError(i), decoratedFunction->getError(i));
    }
  }

  void testFixUnfixIsFixed() {
    TestableFunctionParameterDecorator invalidFn;
    TS_ASSERT_THROWS(invalidFn.isFixed(0), std::runtime_error);
    TS_ASSERT_THROWS(invalidFn.fix(0), std::runtime_error);
    TS_ASSERT_THROWS(invalidFn.unfix(0), std::runtime_error);

    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();
    IFunction_sptr decoratedFunction = fn->getDecoratedFunction();

    for (size_t i = 0; i < fn->nParams(); ++i) {
      TS_ASSERT_THROWS_NOTHING(fn->fix(i));
      TS_ASSERT_EQUALS(fn->isFixed(i), decoratedFunction->isFixed(i));
      TS_ASSERT_EQUALS(fn->isFixed(i), true);
      TS_ASSERT_THROWS_NOTHING(fn->unfix(i));
      TS_ASSERT_EQUALS(fn->isFixed(i), decoratedFunction->isFixed(i));
      TS_ASSERT_EQUALS(fn->isFixed(i), false);
    }
  }

  void testAttributes() {
    TestableFunctionParameterDecorator invalidFn;
    TS_ASSERT_EQUALS(invalidFn.nAttributes(), 0);

    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();
    IFunction_sptr decoratedFunction = fn->getDecoratedFunction();

    TS_ASSERT_EQUALS(fn->nAttributes(), decoratedFunction->nAttributes());
    TS_ASSERT_EQUALS(fn->nAttributes(), 0);

    fn->setDecoratedFunction("FunctionWithAttributes");
    decoratedFunction = fn->getDecoratedFunction();
    TS_ASSERT_EQUALS(fn->nAttributes(), decoratedFunction->nAttributes());
    TS_ASSERT_DIFFERS(fn->nAttributes(), 0);

    std::vector<std::string> decoratorAttributes = fn->getAttributeNames();
    std::vector<std::string> wrappedAttributes =
        decoratedFunction->getAttributeNames();

    TS_ASSERT_EQUALS(decoratorAttributes.size(), wrappedAttributes.size());

    for (size_t i = 0; i < fn->nAttributes(); ++i) {
      TS_ASSERT_EQUALS(decoratorAttributes[i], wrappedAttributes[i]);
      std::string attribute = decoratorAttributes[i];

      TS_ASSERT_EQUALS(fn->hasAttribute(attribute),
                       decoratedFunction->hasAttribute(attribute));
      TS_ASSERT_EQUALS(fn->hasAttribute(attribute), true);
    }

    TS_ASSERT_THROWS_NOTHING(
        fn->setAttribute(decoratorAttributes[0], IFunction::Attribute(4.0)));
    TS_ASSERT_EQUALS(
        fn->getAttribute(decoratorAttributes[0]).value(),
        decoratedFunction->getAttribute(decoratorAttributes[0]).value());
  }

  void testTies() {
    TestableFunctionParameterDecorator invalidFn;
    TS_ASSERT_THROWS(invalidFn.tie("Name", "a=b"), std::runtime_error);
    TS_ASSERT_THROWS(invalidFn.applyTies(), std::runtime_error);
    TS_ASSERT_THROWS(invalidFn.clearTies(), std::runtime_error);
    TS_ASSERT_THROWS(invalidFn.removeTie(0), std::runtime_error);
    TS_ASSERT_THROWS(invalidFn.getTie(0), std::runtime_error);

    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();
    IFunction_sptr decoratedFunction = fn->getDecoratedFunction();

    ParameterTie *tie = fn->tie("Height", "Height=2.0*Sigma");
    TS_ASSERT(tie);
    TS_ASSERT_EQUALS(decoratedFunction->getTie(0), tie);

    TS_ASSERT_THROWS_NOTHING(fn->clearTies());
    TS_ASSERT_THROWS_NOTHING(fn->addTies("Height=4.0*Sigma"));
    TS_ASSERT_EQUALS(fn->getTie(0), decoratedFunction->getTie(0));
    TS_ASSERT(fn->getTie(0));
    TS_ASSERT_THROWS_NOTHING(fn->removeTie(0));

    tie = fn->getTie(0);
    TS_ASSERT(!tie);
  }

  void testTiesInComposite() {
    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();

    CompositeFunction_sptr composite = boost::make_shared<CompositeFunction>();
    composite->addFunction(fn);

    TS_ASSERT_THROWS_NOTHING(composite->addTies("f0.Height=2.0*f0.Sigma"));

    composite->setParameter("f0.Sigma", 3.0);
    composite->applyTies();
    TS_ASSERT_EQUALS(composite->getParameter("f0.Height"), 6.0);
  }

  void testTiesInWrappedComposite() {
    FunctionParameterDecorator_sptr outer =
        boost::make_shared<TestableFunctionParameterDecorator>();
    outer->setDecoratedFunction("CompositeFunction");

    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();

    CompositeFunction_sptr composite =
        boost::dynamic_pointer_cast<CompositeFunction>(
            outer->getDecoratedFunction());
    composite->addFunction(fn);

    TS_ASSERT_THROWS_NOTHING(outer->addTies("f0.Height=2.0*f0.Sigma"));

    outer->setParameter("f0.Sigma", 3.0);
    outer->applyTies();
    TS_ASSERT_EQUALS(outer->getParameter("f0.Height"), 6.0);
  }

  void testParameterNames() {
    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();

    IFunction_sptr decoratedFunction = fn->getDecoratedFunction();

    std::vector<std::string> decoratorNames = fn->getParameterNames();
    std::vector<std::string> wrappedNames =
        decoratedFunction->getParameterNames();

    TS_ASSERT_EQUALS(decoratorNames.size(), wrappedNames.size());
    TS_ASSERT_EQUALS(wrappedNames.size(), 3);

    for (size_t i = 0; i < decoratorNames.size(); ++i) {
      TS_ASSERT_EQUALS(decoratorNames[i], wrappedNames[i]);
    }
  }

  void testSetParameterDescription() {
    TestableFunctionParameterDecorator invalidFn;
    TS_ASSERT_THROWS(invalidFn.setParameterDescription(0, "None"),
                     std::runtime_error);
    TS_ASSERT_THROWS(invalidFn.parameterDescription(0), std::runtime_error);

    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();

    TS_ASSERT_THROWS_NOTHING(fn->setParameterDescription(0, "None"));

    IFunction_sptr decoratedFunction = fn->getDecoratedFunction();
    TS_ASSERT_EQUALS(fn->parameterDescription(0),
                     decoratedFunction->parameterDescription(0));
    TS_ASSERT_EQUALS(fn->parameterDescription(0), "None");
    TS_ASSERT_THROWS(fn->parameterDescription(10), std::out_of_range);

    TS_ASSERT_THROWS_NOTHING(
        fn->setParameterDescription("Height", "Something"));
    TS_ASSERT_THROWS(fn->setParameterDescription("DoesNotExist", "Something"),
                     std::invalid_argument);
  }

  void testBeforeDecoratedFunctionSetIsCalled() {
    MockTestableFunctionParameterDecorator fn;
    EXPECT_CALL(fn, beforeDecoratedFunctionSet(_)).Times(1);

    fn.setDecoratedFunction("FunctionWithParameters");

    TS_ASSERT(Mock::VerifyAndClearExpectations(&fn));
  }

    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();
    fn->setParameter("Height", 3.0);
    fn->setParameter("PeakCentre", 0.5);
    fn->setParameter("Sigma", 0.3);
    IFunction_sptr cloned = fn->clone();
    FunctionParameterDecorator_sptr castedClone =
        boost::dynamic_pointer_cast<FunctionParameterDecorator>(cloned);
    TS_ASSERT(castedClone);
    TS_ASSERT_EQUALS(cloned->name(), fn->name());
    TS_ASSERT_EQUALS(cloned->getParameter("Height"), 3.0);
    TS_ASSERT_EQUALS(cloned->getParameter("PeakCentre"), 0.5);
    TS_ASSERT_EQUALS(cloned->getParameter("Sigma"), 0.3);
  }
  void testSetWorkspace() {
    // using WorkspaceGroup because it is in API
    Workspace_const_sptr ws = boost::make_shared<const WorkspaceGroup>();

    TestableFunctionParameterDecorator invalidFn;
    TS_ASSERT_THROWS(invalidFn.setWorkspace(ws), std::runtime_error);

    FunctionParameterDecorator_sptr fn =
        getFunctionParameterDecoratorGaussian();

    TS_ASSERT_THROWS_NOTHING(fn->setWorkspace(ws));

    boost::shared_ptr<FunctionWithParameters> decorated =
        boost::dynamic_pointer_cast<FunctionWithParameters>(
            fn->getDecoratedFunction());

    TS_ASSERT_EQUALS(decorated->getWorkspace(), ws);
  }

private:
  FunctionParameterDecorator_sptr getFunctionParameterDecoratorGaussian() {
    FunctionParameterDecorator_sptr fn =
        boost::make_shared<TestableFunctionParameterDecorator>();
    fn->setDecoratedFunction("FunctionWithParameters");

  class MockTestableFunctionParameterDecorator
      : public TestableFunctionParameterDecorator {
  public:
    GCC_DIAG_OFF_SUGGEST_OVERRIDE
    MOCK_METHOD1(beforeDecoratedFunctionSet, void(const IFunction_sptr &));
    GCC_DIAG_ON_SUGGEST_OVERRIDE
};

#endif /* MANTID_API_WRAPPEDFUNCTIONTEST_H_ */