//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Tests/Suite/Common/TestSuite.h
//! @brief     Provides TEST_F stances that define the BornAgain standard tests.
//!
//!            Each of these stances runs one standard test.
//!            The function run, defined in file RunTest.cpp, is the same for Core/Py/GUI std tests.
//!            It calls a function checkSimulation that is different for those three test suites.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#ifndef BORNAGAIN_TESTS_SUITE_COMMON_TESTSUITE_H
#define BORNAGAIN_TESTS_SUITE_COMMON_TESTSUITE_H

#include "Base/Axis/Scale.h"
#include "Resample/Option/SimulationOptions.h"
#include "Sample/ComponentBuilder/FormfactorComponent.h"
#include "Sample/ComponentBuilder/Profile2DComponents.h"
#include "Sample/Multilayer/Sample.h"
#include "Sample/StandardSample/ExemplarySamples.h"
#include "Sim/Background/ConstantBackground.h"
#include "Sim/Simulation/includeSimulations.h"
#include "Tests/GTestWrapper/google_test.h"
#include "Tests/SimFactory/MakeSimulations.h"

// The macro eps_direct_vs_python allows us to set different tolerances for
// - direct simulations (fully in C++),
// - simulations that are exported from C++ to Python, then executed as Python scripts.
// Python tests require larger tolerances because parameters are not written with
// full double precision to exported scripts.
#ifndef PYTHON_STD_TEST
#define eps_direct_vs_python(eps_direct, eps_python) eps_direct
#else
#define eps_direct_vs_python(eps_direct, eps_python) eps_python
#endif

#ifdef BA_OTHER_ARCH
#define ARCH3 true
#else
#define ARCH3 false
#endif

#define eps_dir 5e-13
#define eps_py 8e-9
#define eps_ARCH3 3e-10

TEST(TESTNAME, Formfactors)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py); // 2e-13 except on i386
    for (const std::string& ffname : FormfactorComponent().keys()) {
        const IFormfactor* ff = FormfactorComponent().getItem(ffname)->clone();
        ASSERT(ff);
        std::unique_ptr<const Sample> sample(ExemplarySamples::createParticleInVacuumWithFF(ff));
        ASSERT(sample);
        auto sim = test::makeSimulation::MiniGISAS(*sample);
        EXPECT_TRUE(runTest("Formfactors_" + ff->className(), *sim, eps));
    }
}

TEST(TESTNAME, FormfactorsWithAbsorption)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    for (const std::string& ffname : FormfactorComponent().keys()) {
        const IFormfactor* ff = FormfactorComponent().getItem(ffname)->clone();
        ASSERT(ff);
        std::unique_ptr<const Sample> sample(
            ExemplarySamples::createLayersWithAbsorptionWithFF(ff));
        ASSERT(sample);
        auto sim = test::makeSimulation::MiniGISAS_v2(*sample);
        EXPECT_TRUE(runTest("FormfactorsWithAbsorption_" + ff->className(), *sim, eps));
    }
}

TEST(TESTNAME, GISASAbsorptiveSLDLayers)
{
    const double eps =
        eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py); // ARCH3 only for i386
    std::unique_ptr<const Sample> sample(ExemplarySamples::createLayersWithAbsorptionBySLD());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("GISASAbsorptiveSLDLayers", *sim, eps));
}

TEST(TESTNAME, CylindersAndPrisms)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCylindersAndPrisms());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("CylindersAndPrisms", *sim, eps));
}

TEST(TESTNAME, RadialParacrystal)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createRadialParacrystal());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("RadialParacrystal", *sim, eps));
}

TEST(TESTNAME, HardDisk)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createHardDisk());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("HardDisk", *sim, eps));
}

TEST(TESTNAME, Basic2DParacrystal)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    for (const std::string& pdfname : Profile2DComponents().keys()) {
        const IProfile2D* pdf2 = Profile2DComponents().getItem(pdfname)->clone();
        ASSERT(pdf2);
        std::cout << pdf2->pythonConstructor() << std::endl;
        std::unique_ptr<const Sample> sample(
            ExemplarySamples::createBasic2DParacrystalWithFTDis(pdf2));
        ASSERT(sample);
        auto sim = test::makeSimulation::MiniGISAS(*sample);
        EXPECT_TRUE(runTest("Basic2DParacrystal_" + pdf2->className(), *sim, eps));
    }
}

TEST(TESTNAME, HexParacrystal)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createHexParacrystal());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("HexParacrystal", *sim, eps));
}

TEST(TESTNAME, Lattice1D)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createLattice1D());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("Lattice1D", *sim, eps));
}

TEST(TESTNAME, RectParacrystal)
{
    // TODO: investigate numeric integration, which has become problematic under Windows
    //       after moving code from Core/Tools to Base/Utils
    std::unique_ptr<const Sample> sample(ExemplarySamples::createRectParacrystal());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("RectParacrystal", *sim, eps_dir));
}

TEST(TESTNAME, CoreShellParticle)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCoreShellParticle());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("CoreShellParticle", *sim, eps));
}

TEST(TESTNAME, CoreShellBoxRotateZandY)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCoreShellBoxRotateZandY());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("CoreShellBoxRotateZandY", *sim, eps));
}

TEST(TESTNAME, MultiLayerWithRoughness)
{
    const double eps = eps_direct_vs_python(ARCH3 ? 2e-11 : eps_dir, eps_py); // ARCH3 only for i386
    std::unique_ptr<const Sample> sample(ExemplarySamples::createMultiLayerWithRoughness());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("MultiLayerWithRoughness", *sim, eps));
}

TEST(TESTNAME, SquareLattice2D)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createSquareLattice2D());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("SquareLattice2D", *sim, eps));
}

TEST(TESTNAME, CenteredSquareLattice2D)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCenteredSquareLattice2D());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("CenteredSquareLattice2D", *sim, eps));
}

TEST(TESTNAME, RotatedSquareLattice2D)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createRotatedSquareLattice2D());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("RotatedSquareLattice2D", *sim, eps));
}

TEST(TESTNAME, FiniteSquareLattice2D)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createFiniteSquareLattice2D());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("FiniteSquareLattice2D", *sim, eps));
}

TEST(TESTNAME, RotatedPyramids)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createRotatedPyramids());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("RotatedPyramids", *sim, eps));
}

TEST(TESTNAME, ThickAbsorptiveSampleWithRoughness)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createThickAbsorptiveSample());
    auto sim = test::makeSimulation::ExtraLongWavelengthGISAS(*sample);
    EXPECT_TRUE(runTest("ThickAbsorptiveSampleWithRoughness", *sim, eps));
}

TEST(TESTNAME, Compound)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCompound());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("Compound", *sim, eps));
}

TEST(TESTNAME, CompoundPlus)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCompoundPlus());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("CompoundPlus", *sim, eps));
}

TEST(TESTNAME, BoxCompositionRotateX)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createBoxCompositionRotateX());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("BoxCompositionRotateX", *sim, eps));
}

TEST(TESTNAME, BoxCompositionRotateY)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createBoxCompositionRotateY());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("BoxCompositionRotateY", *sim, eps));
}

TEST(TESTNAME, BoxCompositionRotateZ)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : 6e-13, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createBoxCompositionRotateZ());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("BoxCompositionRotateZ", *sim, eps));
}

TEST(TESTNAME, BoxCompositionRotateZandY)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createBoxCompositionRotateZandY());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("BoxCompositionRotateZandY", *sim, eps));
}

TEST(TESTNAME, BoxStackComposition)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createBoxStackComposition());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("BoxStackComposition", *sim, eps));
}

TEST(TESTNAME, MultipleLayout)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createMultipleLayout());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("MultipleLayout", *sim, eps));
}

TEST(TESTNAME, ApproximationDA)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createSizeDistributionDAModel());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("ApproximationDA", *sim, eps));
}

TEST(TESTNAME, ApproximationLMA)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createSizeDistributionLMAModel());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("ApproximationLMA", *sim, eps));
}

TEST(TESTNAME, ApproximationSSCA)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createSizeDistributionSSCAModel());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("ApproximationSSCA", *sim, eps));
}

TEST(TESTNAME, CosineRipple)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCosineRipple());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("CosineRipple", *sim, eps));
}

TEST(TESTNAME, TriangularRipple)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createTriangularRipple());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("TriangularRipple", *sim, eps));
}

TEST(TESTNAME, AsymRipple)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createAsymRipple());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("AsymRipple", *sim, eps));
}

TEST(TESTNAME, Mesocrystal)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createMesocrystal());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("Mesocrystal", *sim, eps));
}

TEST(TESTNAME, MesocrystalPlus)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createMesocrystalPlus());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("MesocrystalPlus", *sim, eps));
}

TEST(TESTNAME, CustomMorphology)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCustomMorphology());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("CustomMorphology", *sim, eps));
}

TEST(TESTNAME, TransformBox)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createTransformBox());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("TransformBox", *sim, eps));
}

TEST(TESTNAME, MagneticParticleZeroField)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createMagneticParticleZeroField());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("MagneticParticleZeroField", *sim, eps));
}

TEST(TESTNAME, SlicedComposition)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createSlicedComposition());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("SlicedComposition", *sim, eps));
}

TEST(TESTNAME, MagneticSubstrateZeroField)
{
    const double eps = eps_direct_vs_python(ARCH3 ? 2e-11 : eps_dir, eps_py); // ARCH3 only for i386
    std::unique_ptr<const Sample> sample(ExemplarySamples::createMagneticSubstrateZeroField());
    auto sim = test::makeSimulation::MiniZPolarizedGISAS(*sample, "PP");
    EXPECT_TRUE(runTest("MagneticSubstrateZeroField", *sim, eps));
}

TEST(TESTNAME, MagneticRotation)
{
    const double eps = eps_direct_vs_python(ARCH3 ? 6e-11 : eps_dir, eps_py); // ARCH3 only for i386
    std::unique_ptr<const Sample> sample(ExemplarySamples::createMagneticRotation());
    auto sim = test::makeSimulation::MiniZPolarizedGISAS(*sample, "PM");
    EXPECT_TRUE(runTest("MagneticRotationZPM", *sim, eps));
}

TEST(TESTNAME, MagneticRotationUnpol)
{
    const double eps = eps_direct_vs_python(ARCH3 ? 2e-11 : eps_dir, eps_py); // ARCH3 only for i386
    std::unique_ptr<const Sample> sample(ExemplarySamples::createMagneticRotation());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("MagneticRotationUnpol", *sim, eps));
}

TEST(TESTNAME, MagneticSpheres)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createMagneticSpheres());
    auto sim = test::makeSimulation::MiniZPolarizedGISAS(*sample, "PM");
    EXPECT_TRUE(runTest("MagneticSpheres", *sim, eps));
}

TEST(TESTNAME, MagneticCylinders)
{
    const double eps = eps_direct_vs_python(ARCH3 ? 5e-12 : eps_dir, eps_py); // ARCH3 only for i386
    for (const std::string polCase : {"PP", "MP", "PM", "MM"}) {
        std::unique_ptr<const Sample> sample(ExemplarySamples::createMagneticCylinders());
        auto sim = test::makeSimulation::MiniZPolarizedGISAS(*sample, polCase);
        EXPECT_TRUE(runTest("MagneticCylinders" + polCase, *sim, eps));
    }
}

TEST(TESTNAME, MagneticSpheresInMagLayer)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py);
    for (const std::string polCase : {"PP", "MP"}) {
        std::unique_ptr<const Sample> sample(ExemplarySamples::createMagneticLayer());
        auto sim = test::makeSimulation::MiniZPolarizedGISAS(*sample, polCase);
        EXPECT_TRUE(runTest("MagneticSpheresInMagLayer" + polCase, *sim, eps));
    }
}

TEST(TESTNAME, BeamDivergence)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCylindersInBA());
    auto sim = test::makeSimulation::MiniGISASBeamDivergence(*sample);
    EXPECT_TRUE(runTest("BeamDivergence", *sim, eps));
}

TEST(TESTNAME, DetectorResolution)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCylindersInBA());
    auto sim = test::makeSimulation::MiniGISASDetectorResolution(*sample);
    EXPECT_TRUE(runTest("DetectorResolution", *sim, eps));
}

TEST(TESTNAME, BoxesWithSpecular)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createBoxesSquareLattice2D());
    auto sim = test::makeSimulation::MiniGISASSpecularPeak(*sample);
    EXPECT_TRUE(runTest("BoxesWithSpecular", *sim, eps));
}

TEST(TESTNAME, ConstantBackground)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py);
    auto* sample = ExemplarySamples::createCylindersInBA();
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    sim->setBackground(ConstantBackground(1e3));
    EXPECT_TRUE(runTest("ConstantBackground", *sim, eps));
}

TEST(TESTNAME, HomogeneousTiNiSample)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py);
    auto* sample = ExemplarySamples::createHomogeneousMultilayer();
    auto sim = test::makeSimulation::BasicSpecular(*sample, false);
    EXPECT_TRUE(runTest("HomogeneousTiNiSample", *sim, eps));
}

TEST(TESTNAME, HomogeneousTiNiSampleWithAbsorption)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py);
    auto* sample = ExemplarySamples::createPlainMultiLayerBySLD();
    auto sim = test::makeSimulation::BasicSpecular(*sample, false);
    EXPECT_TRUE(runTest("HomogeneousTiNiSampleWithAbsorption", *sim, eps));
}

TEST(TESTNAME, RoughnessInSpecular)
{
    auto* sample = ExemplarySamples::createMultiLayerWithRoughness();
    auto sim = test::makeSimulation::BasicSpecular(*sample, false);
    EXPECT_TRUE(runTest("RoughnessInSpecular", *sim, eps_dir));
}

TEST(TESTNAME, GaussianBeamFootprint)
{
    const double eps = eps_direct_vs_python(ARCH3 ? 7e-11 : eps_dir, eps_py); // ARCH3 only for i386
    auto* sample = ExemplarySamples::createHomogeneousMultilayer();
    auto sim = test::makeSimulation::SpecularWithGaussianBeam(*sample);
    EXPECT_TRUE(runTest("GaussianBeamFootprint", *sim, eps));
}

TEST(TESTNAME, SquareBeamFootprint)
{
    const double eps = eps_direct_vs_python(ARCH3 ? 7e-11 : eps_dir, eps_py); // ARCH3 only for i386
    auto* sample = ExemplarySamples::createHomogeneousMultilayer();
    auto sim = test::makeSimulation::SpecularWithSquareBeam(*sample);
    EXPECT_TRUE(runTest("SquareBeamFootprint", *sim, eps));
}

TEST(TESTNAME, SpecularDivergentBeam)
{
    const double eps = eps_direct_vs_python(ARCH3 ? 5e-11 : eps_dir, eps_py); // ARCH3 only for i386
    auto* sample = ExemplarySamples::createHomogeneousMultilayer();
    auto sim = test::makeSimulation::SpecularDivergentBeam(*sample);
    EXPECT_TRUE(runTest("SpecularDivergentBeam", *sim, eps));
}

TEST(TESTNAME, SimulationWithMasks)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCylindersAndPrisms());
    auto sim = test::makeSimulation::GISASWithMasks(*sample);
    EXPECT_TRUE(runTest("SimulationWithMasks", *sim, eps));
}

TEST(TESTNAME, RectDetWithRoi)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCylindersAndPrisms());
    auto sim = test::makeSimulation::RectDetWithRoi(*sample);
    EXPECT_TRUE(runTest("RectDetWithRoi", *sim, eps));
}

//  ************************************************************************************************
//  TODO: broken under GUI
//  ************************************************************************************************

#ifndef GUI_STD_TEST

TEST(TESTNAME, RelativeResolutionTOF)
{
    const double eps = eps_direct_vs_python(eps_dir, eps_py);
    auto* sample = ExemplarySamples::createPlainMultiLayerBySLD();
    auto sim = test::makeSimulation::TOFRWithRelativeResolution(*sample);
    EXPECT_TRUE(runTest("RelativeResolutionTOF", *sim, eps));
}

TEST(TESTNAME, SphericalDetWithRoi)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    std::unique_ptr<const Sample> sample(ExemplarySamples::createCylindersAndPrisms());
    auto sim = test::makeSimulation::SphericalDetWithRoi(*sample);
    EXPECT_TRUE(runTest("SphericalDetWithRoi", *sim, eps));
}

TEST(TESTNAME, DepthprobeAveragedLayer)
{
    auto* sample = ExemplarySamples::createBoxesLayer();
    auto sim = test::makeSimulation::BasicDepthprobeShiftedZ(*sample, 10);
    sim->options().setUseAvgMaterials(true);
    EXPECT_TRUE(runTest("DepthprobeAveragedLayer", *sim, eps_dir));
}

TEST(TESTNAME, DepthprobeSimpleLayer)
{
    auto* sample = ExemplarySamples::createSimpleLayer();
    auto sim = test::makeSimulation::BasicDepthprobe(*sample);
    EXPECT_TRUE(runTest("DepthprobeSimpleLayer", *sim, eps_dir));
}

#endif // GUI_STD_TEST

//  ************************************************************************************************
//  TODO: broken under Python
//  ************************************************************************************************

#ifndef PYTHON_STD_TEST

TEST(TESTNAME, OffspecResonator)
{
    const double eps = eps_direct_vs_python(ARCH3 ? eps_ARCH3 : eps_dir, eps_py);
    auto* sample = ExemplarySamples::createResonator();
    auto sim = test::makeSimulation::MiniOffspec(*sample);
    EXPECT_TRUE(runTest("OffspecResonator", *sim, eps));
}

#endif // PYTHON_STD_TEST

//  ************************************************************************************************
//  TODO: broken under GUI and Python
//  ************************************************************************************************

#ifndef PYTHON_STD_TEST
#ifndef GUI_STD_TEST

TEST(TESTNAME, NCRoughnessInSpecular)
{
    auto* sample = ExemplarySamples::createMultiLayerWithNCRoughness();
    auto sim = test::makeSimulation::BasicSpecular(*sample, false);
    EXPECT_TRUE(runTest("NCRoughnessInSpecular", *sim, 2e-9));
}

TEST(TESTNAME, SuperLattice)
{
    std::unique_ptr<const Sample> sample(ExemplarySamples::createSuperLattice());
    auto sim = test::makeSimulation::MiniGISAS(*sample);
    EXPECT_TRUE(runTest("SuperLattice", *sim, eps_py));
}

TEST(TESTNAME, SpecularWithSlicing1)
{
    // data almost fully agree with SpecularWithSlicing2, as verified by ConsistenceTest
    auto* sample = ExemplarySamples::createSlicedCylinders();
    auto sim = test::makeSimulation::BasicSpecular(*sample, false);
    EXPECT_TRUE(runTest("SpecularWithSlicing1", *sim, eps_py));
}

TEST(TESTNAME, SpecularWithSlicing2)
{
    auto* sample = ExemplarySamples::createSLDSlicedCylinders();
    auto sim = test::makeSimulation::BasicSpecular(*sample, false);
    EXPECT_TRUE(runTest("SpecularWithSlicing2", *sim, eps_py));
    auto simQ = test::makeSimulation::BasicSpecular(*sample, true);
    EXPECT_TRUE(runTest("SpecularWithSlicing2Q", *simQ, eps_py));
}

TEST(TESTNAME, SpecularWithSlicing3)
{
    // data fully agree with SpecularWithSlicing2, as verified by ConsistenceTest
    auto* sample = ExemplarySamples::createAveragedSlicedCylinders();
    auto sim = test::makeSimulation::BasicSpecular(*sample, false);
    EXPECT_TRUE(runTest("SpecularWithSlicing3", *sim, eps_py));
}

TEST(TESTNAME, InstrumentDefinitionComparison)
// also tests persistent result for the intensity fudge parameter
{
    auto* sample = ExemplarySamples::createPlainMultiLayerBySLD();
    auto sim = test::makeSimulation::BasicSpecular(*sample, false, 1.05);
    EXPECT_TRUE(runTest("InstrumentDefinitionComparison_0", *sim, eps_py));
    auto simQ = test::makeSimulation::BasicSpecular(*sample, true, 1.05);
    EXPECT_TRUE(runTest("InstrumentDefinitionComparison_Q", *simQ, eps_py));
}

TEST(TESTNAME, TOFResolutionComparison)
{
    auto* sample = ExemplarySamples::createPlainMultiLayerBySLD();
    auto sim1 = test::makeSimulation::TOFRWithRelativeResolution(*sample);
    EXPECT_TRUE(runTest("TOFResolutionComparison_TR", *sim1, eps_py));
    auto sim2 = test::makeSimulation::TOFRWithPointwiseResolution(*sample);
    EXPECT_TRUE(runTest("TOFResolutionComparison_TP", *sim2, eps_py));
}

TEST(TESTNAME, BasicSpecular)
{
    std::unique_ptr<const Sample> sample(ExemplarySamples::createSimpleMagneticLayer());
    for (const std::string polCase : {"PP", "MM"}) {
        auto sim = test::makeSimulation::BasicYPolarizedSpecular(*sample, polCase, false);
        EXPECT_TRUE(runTest("BasicSpecular" + polCase, *sim, eps_py));
    }
}

TEST(TESTNAME, PolarizedQAngleReflectivity)
{
    std::unique_ptr<const Sample> sample(ExemplarySamples::createSimpleMagneticLayer());
    for (const std::string polKey : {"PP", "MM"}) {
        auto sim = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, true);
        EXPECT_TRUE(runTest("PolarizedQAngleReflectivity" + polKey + "_Q", *sim, eps_py));
    }
}

TEST(TESTNAME, MagneticRotationReflectivity)
{
    for (const auto roughnessKey : {"Flat", "Tanh", "Erf"}) {
        std::unique_ptr<const Sample> sample(
            ExemplarySamples::createSimpleMagneticRotationWithRoughness(roughnessKey));
        for (const std::string polKey : {"PP", "PM", "MP", "MM"}) {
            auto sim = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, false);
            EXPECT_TRUE(runTest("MagneticRotationReflectivity" + polKey + "_" + roughnessKey, *sim,
                                eps_py));
        }
    }
}

TEST(TESTNAME, PolarizedFeNiBilayer)
{
    std::unique_ptr<const Sample> sample(ExemplarySamples::createFeNiBilayer());
    for (const std::string polKey : {"PP", "MM"}) {
        auto sim1 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, false);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayer" + polKey, *sim1, 1e-7));
        auto sim2 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, true);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayer" + polKey + "_Q", *sim2, 1e-7));
    }
}

TEST(TESTNAME, PolarizedFeNiBilayerTanh)
{
    std::unique_ptr<const Sample> sample(ExemplarySamples::createFeNiBilayerTanh());
    for (const std::string polKey : {"PP", "MM"}) {
        auto sim1 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, false);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayerTanh" + polKey, *sim1, 1e-7));
        auto sim2 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, true);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayerTanh" + polKey + "_Q", *sim2, 1e-7));
    }
}

TEST(TESTNAME, PolarizedFeNiBilayerNC)
{
    std::unique_ptr<const Sample> sample(ExemplarySamples::createFeNiBilayerNC());
    for (const std::string polKey : {"PP", "MM"}) {
        auto sim1 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, false);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayerNC" + polKey, *sim1, 1e-7));
        auto sim2 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, true);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayerNC" + polKey + "_Q", *sim2, 1e-7));
    }
}

TEST(TESTNAME, PolarizedFeNiBilayerSpinFlip)
{
    std::unique_ptr<const Sample> sample(ExemplarySamples::createFeNiBilayerSpinFlip());
    for (const std::string polKey : {"PP", "PM", "MP", "MM"}) {
        auto sim1 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, false);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayerSpinFlip" + polKey, *sim1, 1e-7));
        auto sim2 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, true);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayerSpinFlip" + polKey + "_Q", *sim2, 1e-7));
    }
}

TEST(TESTNAME, PolarizedFeNiBilayerSpinFlipTanh)
{
    std::unique_ptr<const Sample> sample(ExemplarySamples::createFeNiBilayerSpinFlipTanh());
    for (const std::string polKey : {"PP", "PM", "MP", "MM"}) {
        auto sim1 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, false);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayerSpinFlipTanh" + polKey, *sim1, 1e-7));
        auto sim2 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, true);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayerSpinFlipTanh" + polKey + "_Q", *sim2, 1e-7));
    }
}

TEST(TESTNAME, PolarizedFeNiBilayerSpinFlipNC)
{
    std::unique_ptr<const Sample> sample(ExemplarySamples::createFeNiBilayerSpinFlipNC());
    for (const std::string polKey : {"PP", "PM", "MP", "MM"}) {
        auto sim1 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, false);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayerSpinFlipNC" + polKey, *sim1, 1e-7));
        auto sim2 = test::makeSimulation::BasicYPolarizedSpecular(*sample, polKey, true);
        EXPECT_TRUE(runTest("PolarizedFeNiBilayerSpinFlipNC" + polKey + "_Q", *sim2, 1e-7));
    }
}

TEST(TESTNAME, Depthprobe)
{
    auto* sample = ExemplarySamples::createHomogeneousMultilayer();
    auto sim = test::makeSimulation::BasicDepthprobe(*sample);
    EXPECT_TRUE(runTest("DepthprobeTest", *sim, eps_py));
}

#endif // GUI_STD_TEST
#endif // PYTHON_STD_TEST

//  ************************************************************************************************
//  TODO: broken altogether
//  ************************************************************************************************

// TEST(TESTNAME, LargeCylindersMonteCarlo)
// {
//     std::unique_ptr<const MultiLayer> sample(ExemplarySamples::createLargeCylindersInDWBA());
//     auto sim = test::makeSimulation::MiniGISASMonteCarlo(*sample);
//     EXPECT_TRUE(runTest("LargeCylindersMonteCarlo", *sim, 5e-1));
// }

#endif // BORNAGAIN_TESTS_SUITE_COMMON_TESTSUITE_H
