package etomica;

import etomica.integrator.Integrator;
import etomica.normalmode.NormalModeAnalysisDisplay2D;
import etomica.normalmode.NormalModeAnalysisDisplay3D;
import etomica.simulation.Simulation;
import etomica.space.Space;
import etomica.space3d.Space3D;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static etomica.TestSimConstructors.SCAN;

public class TestSimShortRuns {
    private static final ExecutorService exec = Executors.newSingleThreadExecutor();

    static List<Arguments> getIntegrators() throws IllegalAccessException, InstantiationException, InvocationTargetException {
        // This beautiful method will search through constructible simulations looking for ones
        // with one and only one field of type Integrator (getIntegrator isn't reliable, and with
        // more than one we don't know which one to use), instantiate the simulations, and return the integrators.
        List<Class<?>> classes =  SCAN.classNamesToClassRefs(SCAN.getNamesOfSubclassesOf(Simulation.class));
        List<Arguments> args = new ArrayList<>();

        for (Class<?> cl : classes) {
            if (cl == NormalModeAnalysisDisplay3D.class) continue;
            Field[] fields = cl.getDeclaredFields();
            List<Field> integratorFields = Arrays.stream(fields)
                    .filter(f -> Integrator.class.isAssignableFrom(f.getType()))
                    .collect(Collectors.toList());

            if (integratorFields.size() == 1) {
                Simulation sim = null;
                try {
                    sim = (Simulation) cl.getConstructor().newInstance();
                } catch (NoSuchMethodException e) {
                    try {
                        sim = (Simulation) cl.getConstructor(Space.class).newInstance(Space3D.getInstance());
                    } catch (NoSuchMethodException e1) {
                        continue;
                    }
                }

                integratorFields.get(0).setAccessible(true);
                args.add(Arguments.of(cl, (Integrator)integratorFields.get(0).get(sim)));
            }
        }

        System.out.println(args);
        return args;
    }

    @ParameterizedTest
    @MethodSource("getIntegrators")
    void testShortRuns(Class<?> simClass, Integrator simIntegrator) {
        Assertions.assertDoesNotThrow(() -> {
            simIntegrator.reset();
            Future future = exec.submit(() -> {
                while (true) {
                    simIntegrator.doStep();
                }
            });
            Thread.sleep(1000);
            future.cancel(true);
        });
    }
}

Add test which runs all sims to check they don't crash
Add test which runs all sims to check they don't crash

Gathers and constructs simulation classes on the classpath, finds
the field of type Integrator, and runs the integrator for 1 second.
Currently a few like OsmosisSim crash with overlap exception because
they don't actually initialize the coordinates in the constructor.
