package de.renew.engine.thread;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * Test class for the {@link InheritableSimulationThreadLock}.
 *
 * @author JackFrost
 */
public class InheritableSimulationThreadLockTest {
    private final static String TEST_THREAD_NAME = "test";

    private InheritableSimulationThreadLock _underTest;
    private SimulationThread _simThread;
    private Thread _normalThread;
    private boolean _currThreadIsSim;
    private boolean _testOutcome;
    private SimulationThreadPool _pool;
    private Thread _externalThread;
    private boolean _testOutcomeExt;

    /**
     * Setup method to initialize this test environment.
     * Sets the test booleans to false and initializes the fields.
     */
    @BeforeEach
    public void setUp() {
        Thread current = Thread.currentThread();

        _externalThread = new Thread();

        _pool = SimulationThreadPool.getNew();

        _testOutcome = false;
        _currThreadIsSim = false;

        Runnable dummyTask = () -> {
            // here we invoke the method mayLock which we are testing.
            // By running this runnable from out a simulation thread, we cause
            // a different behavior in the test method.

            // if this is true, the mayLock-method enters the elseif block
            _currThreadIsSim = Thread.currentThread() instanceof SimulationThread;

            // if this is true, the ancestor of the currentThread is the locking one
            // note that _normalThread is NOT the currentThread anymore, since this
            // runnable is executed from out a simulation thread
            _testOutcome = _underTest.mayLock(_normalThread);

            // if this is false, some other thread than the ancestor of the currentThread
            // is holding the lock.
            _testOutcomeExt = _underTest.mayLock(_externalThread);

            // tip: to verify / understand this behavior, switch on the sysout statements
            // in the InheritableSimulationLock#mayLock method.
        };

        _simThread = new SimulationThread(current.getThreadGroup(), dummyTask, TEST_THREAD_NAME, 5);
        _normalThread = current;

        _underTest = new InheritableSimulationThreadLock();
    }

    /**
     * Test method for {@link InheritableSimulationThreadLock#mayLock}.
     */
    @Test
    public final void testMayLock() {
        //when/then
        //current Thread may always lock (first if case)
        assertTrue(_underTest.mayLock(_normalThread));

        //though it is a simulation thread, it is not the current 
        assertFalse(_underTest.mayLock(_simThread));

        //make a simulationThread invoke the mayLock-method
        _pool.executeAndWait(_simThread);

        //then
        // if this is true, the mayLock-method has entered the elseif block
        assertTrue(_currThreadIsSim);

        //if this is true, we were able to successfully test all if-cases of mayLock.
        assertTrue(_testOutcome);

        // this tests the else-case in mayLock: although the executing thread
        // is a simulation thread, its ancestor is not the owner of the lock,
        // thus we expect false (denoting disallowance to acquire the lock).
        assertFalse(_testOutcomeExt);
    }
}