package com.redhat.ceylon.test.eclipse.plugin.model;
import static com.redhat.ceylon.test.eclipse.plugin.CeylonTestPlugin.LAUNCH_CONFIG_TYPE;
import static com.redhat.ceylon.test.eclipse.plugin.CeylonTestPlugin.LAUNCH_CONFIG_TYPE_JS;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import com.redhat.ceylon.test.eclipse.plugin.CeylonTestPlugin;
import com.redhat.ceylon.test.eclipse.plugin.model.TestElement.State;
public class TestRun {
private static final Object NULL_LOCK = new Object();
public static Object acquireLock(TestRun testRun) {
return testRun != null ? testRun : NULL_LOCK;
}
private final Date startDate;
private final ILaunch launch;
private TestElement root;
private List<TestElement> atomicTests = new ArrayList<TestElement>();
private Map<String, List<TestElement>> testsByPackages = new LinkedHashMap<String, List<TestElement>>();
private boolean isRunning = true;
private boolean isFinished;
private boolean isInterrupted;
private boolean isPinned;
private int startedCount = 0;
private int successCount = 0;
private int failureCount = 0;
private int errorCount = 0;
private int skippedOrAbortedCount = 0;
public TestRun(ILaunch launch) {
this.launch = launch;
this.startDate = new Date();
}
public ILaunch getLaunch() {
return launch;
}
public TestElement getRoot() {
return root;
}
public List<TestElement> getAtomicTests() {
return atomicTests;
}
public Map<String, List<TestElement>> getTestsByPackages() {
return testsByPackages;
}
public boolean isJvm() {
try {
return LAUNCH_CONFIG_TYPE.equals(launch.getLaunchConfiguration().getType().getIdentifier());
} catch (CoreException e) {
return false;
}
}
public boolean isJs() {
try {
return LAUNCH_CONFIG_TYPE_JS.equals(launch.getLaunchConfiguration().getType().getIdentifier());
} catch (CoreException e) {
return false;
}
}
public boolean isRunning() {
return isRunning;
}
public boolean isFinished() {
return isFinished;
}
public boolean isInterrupted() {
return isInterrupted;
}
public boolean isPinned() {
return isPinned;
}
public void setPinned(boolean isPinned) {
this.isPinned = isPinned;
}
public boolean isSuccess() {
return failureCount == 0 && errorCount == 0;
}
public boolean isFailureOrError() {
return failureCount != 0 || errorCount != 0;
}
public int getTotalCount() {
return atomicTests.size();
}
public int getStartedCount() {
return startedCount;
}
public int getSuccessCount() {
return successCount;
}
public int getFailureCount() {
return failureCount;
}
public int getErrorCount() {
return errorCount;
}
public int getSkippedOrAbortedCount() {
return skippedOrAbortedCount;
}
public int getFinishedCount() {
return successCount + failureCount + errorCount;
}
public State getPackageState(String packageName) {
int undefined = 0;
int success = 0;
int failure = 0;
int error = 0;
int skippedOrAborted = 0;
int total = 0;
List<TestElement> testsInPackage = testsByPackages.get(packageName);
if (testsInPackage != null) {
total = testsInPackage.size();
for (TestElement testElement : testsInPackage) {
switch(testElement.getState()) {
case UNDEFINED : undefined++; break;
case SUCCESS: success++; break;
case FAILURE: failure++; break;
case ERROR: error++; break;
case SKIPPED_OR_ABORTED: skippedOrAborted++; break;
default: /* noop */ break;
}
}
}
if (error > 0) {
return State.ERROR;
} else if (failure > 0) {
return State.FAILURE;
} else if (skippedOrAborted == total) {
return State.SKIPPED_OR_ABORTED;
} else if (undefined == total) {
return State.UNDEFINED;
} else if (success + skippedOrAborted == total) {
return State.SUCCESS;
} else if (total > 0) {
return State.RUNNING;
}
return State.UNDEFINED;
}
public long getPackageElapsedTimeInMilis(String packageName) {
long elapsedTimeInMilis = 0;
List<TestElement> testsInPackage = testsByPackages.get(packageName);
if (testsInPackage != null) {
for (TestElement testElement : testsInPackage) {
if (testElement.getState().isFinished()) {
elapsedTimeInMilis += testElement.getElapsedTimeInMilis();
}
}
}
return elapsedTimeInMilis;
}
public String getRunName() {
String name = null;
ILaunchConfiguration launchConfig = launch.getLaunchConfiguration();
if (launchConfig != null) {
name = launchConfig.getName();
}
return name;
}
public long getRunElapsedTimeInMilis() {
long elapsedTimeInMilis = 0;
if( root != null && root.getChildren() != null ) {
for (TestElement testElement : root.getChildren()) {
if (testElement.getState().isFinished()) {
elapsedTimeInMilis += testElement.getElapsedTimeInMilis();
}
}
}
return elapsedTimeInMilis;
}
public Date getRunStartDate() {
return startDate;
}
public synchronized void processRemoteTestEvent(TestEventType eventType, TestElement element) {
switch (eventType) {
case TEST_RUN_STARTED:
updateRootElement(element);
isRunning = true;
isFinished = false;
isInterrupted = false;
fireTestRunStarted();
break;
case TEST_RUN_FINISHED:
isRunning = false;
isFinished = true;
isInterrupted = false;
fireTestRunFinished();
break;
case TEST_STARTED:
updateTestElement(eventType, element);
updateCounters(eventType, element);
fireTestStarted(element);
break;
case TEST_FINISHED:
updateTestElement(eventType, element);
updateCounters(eventType, element);
fireTestFinished(element);
break;
}
}
public synchronized void processLaunchTerminatedEvent() {
if( isRunning ) {
new TestVisitor() {
@Override
public void visitElement(TestElement e) {
if (e.getState() == State.RUNNING) {
e.setState(State.UNDEFINED);
}
}
}.visitElements(root);
isRunning = false;
isFinished = false;
isInterrupted = true;
fireTestRunInterrupted();
}
}
private void updateRootElement(final TestElement root) {
this.root = root;
new TestVisitor() {
@Override
public void visitElement(TestElement e) {
if (e != root && (e.getChildren() == null || e.getChildren().size() == 0)) {
atomicTests.add(e);
}
}
}.visitElements(root);
testsByPackages.clear();
if( root.getChildren() != null ) {
for(TestElement e : root.getChildren()) {
List<TestElement> testElementsInPackage = testsByPackages.get(e.getPackageName());
if (testElementsInPackage == null) {
testElementsInPackage = new ArrayList<TestElement>();
testsByPackages.put(e.getPackageName(), testElementsInPackage);
}
testElementsInPackage.add(e);
}
}
}
private void updateTestElement(final TestEventType eventType, final TestElement testElement) {
new TestVisitor() {
@Override
public void visitElement(TestElement e) {
if (e.equals(testElement)) {
e.setState(testElement.getState());
e.setException(testElement.getException());
e.setExpectedValue(testElement.getExpectedValue());
e.setActualValue(testElement.getActualValue());
e.setElapsedTimeInMilis(testElement.getElapsedTimeInMilis());
}
else if (eventType == TestEventType.TEST_STARTED
&& testElement.getVariant() != null
&& testElement.getVariantIndex() != null
&& Objects.equals(e.getQualifiedName(), testElement.getQualifiedName())
&& e.getVariant() == null
&& e.getVariantIndex() == null
&& !e.getChildren().contains(testElement)) {
e.addChild(testElement);
atomicTests.remove(e);
atomicTests.add(testElement);
}
}
}.visitElements(root);
}
private void updateCounters(TestEventType eventType, TestElement element) {
if (eventType == TestEventType.TEST_STARTED) {
startedCount++;
}
if (eventType == TestEventType.TEST_FINISHED) {
State state = element.getState();
switch (state) {
case SUCCESS:
if (atomicTests.contains(element)) {
successCount++;
}
break;
case FAILURE:
if (element.getException() != null) {
failureCount++;
}
break;
case ERROR:
if (element.getException() != null) {
errorCount++;
}
break;
case SKIPPED_OR_ABORTED:
skippedOrAbortedCount++;
break;
default:
throw new IllegalStateException(element.toString());
}
}
}
private List<TestRunListener> getTestRunListeners() {
return CeylonTestPlugin.getDefault().getModel().getTestRunListeners();
}
private void fireTestRunStarted() {
for (TestRunListener listener : getTestRunListeners()) {
listener.testRunStarted(this);
}
}
private void fireTestRunFinished() {
for (TestRunListener listener : getTestRunListeners()) {
listener.testRunFinished(this);
}
}
private void fireTestRunInterrupted() {
for (TestRunListener listener : getTestRunListeners()) {
listener.testRunInterrupted(this);
}
}
private void fireTestStarted(TestElement testElement) {
for (TestRunListener listener : getTestRunListeners()) {
listener.testStarted(this, testElement);
}
}
private void fireTestFinished(TestElement testElement) {
for (TestRunListener listener : getTestRunListeners()) {
listener.testFinished(this, testElement);
}
}
public static abstract class TestVisitor {
public final void visitElements(TestElement e) {
if (e != null) {
visitElement(e);
List<TestElement> children = e.getChildren();
if (children != null) {
for (TestElement child : children) {
visitElements(child);
}
}
}
}
public abstract void visitElement(TestElement e);
}
}