All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fi.jumi.core.runs.Run Maven / Gradle / Ivy

There is a newer version: 0.5.437
Show newest version
// Copyright © 2011-2013, Esko Luontola 
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

package fi.jumi.core.runs;

import fi.jumi.actors.ActorRef;
import fi.jumi.api.drivers.*;
import fi.jumi.core.api.RunId;
import fi.jumi.core.output.*;

import javax.annotation.concurrent.ThreadSafe;
import java.util.*;

/**
 * Checks that the notifier API is used correctly. Those checks that are not convenient to do here, are done in {@link
 * RunEventNormalizer}.
 */
@ThreadSafe
class Run {

    private final ActorRef listener;
    private final OutputCapturer outputCapturer;

    private final RunId runId;
    /**
     * There is a race condition if {@link #fireTestStarted} or {@link Test#fireTestFinished()} is called concurrently
     * within a single {@code Run}, but it shouldn't be dangerous. A testing framework must not call those methods
     * concurrently, but if somebody anyways calls them, our error checking should notice it over 90% of the time, which
     * should be adequate for the testing framework developer to notice his mistake and fix it.
     */
    private volatile Test currentTest = null;

    public Run(ActorRef listener, OutputCapturer outputCapturer, RunId runId) {
        this.listener = listener;
        this.outputCapturer = outputCapturer;
        this.runId = runId;
    }

    public void fireRunStarted() {
        listener.tell().onRunStarted(runId);
        outputCapturer.captureTo(new OutputListenerAdapter(listener, runId));
    }

    private void fireRunFinished() {
        outputCapturer.captureTo(new NullOutputListener());
        listener.tell().onRunFinished(runId);
    }

    public boolean isRunFinished() {
        return currentTest == null;
    }

    public TestNotifier fireTestStarted(TestId testId) {
        listener.tell().onTestStarted(runId, testId);

        Test test = new Test(testId, currentTest);
        currentTest = test;
        return test;
    }


    @ThreadSafe
    private class Test implements TestNotifier {

        private final TestId testId;
        private final Test parent;

        public Test(TestId testId, Test parent) {
            this.testId = testId;
            this.parent = parent;
        }

        @Override
        public void fireFailure(Throwable cause) {
            checkInnermostNonFinishedTest(cause);
            listener.tell().onFailure(runId, testId, cause);
        }

        @Override
        public void fireTestFinished() {
            checkInnermostNonFinishedTest(null);
            listener.tell().onTestFinished(runId, testId);
            currentTest = parent;

            if (isRunFinished()) {
                fireRunFinished();
            }
        }

        private void checkInnermostNonFinishedTest(Throwable testFailureBeingReported) {
            if (currentTest != this) {
                String message = "must be called on the innermost non-finished TestNotifier; " +
                        "expected " + currentTest + " but was " + this + " " +
                        "which " + (isTestFinished() ? "is finished" : "is not innermost");
                IllegalStateException e = new IllegalStateException(message, testFailureBeingReported);
                listener.tell().onInternalError("Incorrect notifier API usage", e);
                throw e;
            }
        }

        private boolean isTestFinished() {
            for (Test activeTest = currentTest; activeTest != null; activeTest = activeTest.parent) {
                if (this == activeTest) {
                    return false;
                }
            }
            return true;
        }

        @Override
        public String toString() {
            Deque activeTestsStack = new ArrayDeque<>();
            for (Test t = this; t != null; t = t.parent) {
                activeTestsStack.push(t.testId);
            }
            return "TestNotifier(" + runId + ", " + activeTestsStack + ")";
        }
    }

    @ThreadSafe
    private static class OutputListenerAdapter implements OutputListener {
        private final ActorRef listener;
        private final RunId runId;

        public OutputListenerAdapter(ActorRef listener, RunId runId) {
            this.listener = listener;
            this.runId = runId;
        }

        @Override
        public void out(String text) {
            listener.tell().onPrintedOut(runId, text);
        }

        @Override
        public void err(String text) {
            listener.tell().onPrintedErr(runId, text);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy