/**
* Copyright (c) 2009-2010 MATSUFUJI Hideharu <matsufuji2008@gmail.com>,
* 2010-2014 KUBO Atsuhiro <kubo@iteman.jp>,
* All rights reserved.
*
* This file is part of MakeGood.
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License v1.0 which accompanies this
* distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package com.piece_framework.makegood.ui.views;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.php.internal.debug.core.model.IPHPDebugTarget;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.progress.UIJob;
import com.piece_framework.makegood.core.result.TestCaseResult;
import com.piece_framework.makegood.core.result.TestSuiteResult;
import com.piece_framework.makegood.core.run.ResultReaderListener;
import com.piece_framework.makegood.launch.MakeGoodLaunch;
import com.piece_framework.makegood.launch.TestLifecycle;
import com.piece_framework.makegood.ui.Activator;
import com.piece_framework.makegood.ui.MakeGoodContext;
import com.piece_framework.makegood.ui.MakeGoodStatus;
import com.piece_framework.makegood.ui.actions.StopTestRunAction;
import com.piece_framework.makegood.ui.markers.FatalErrorMarkerFactory;
import com.piece_framework.makegood.ui.markers.TestMarkerFactory;
import com.piece_framework.makegood.ui.markers.UnknownFatalErrorMessageException;
import com.piece_framework.makegood.ui.widgets.ResultSquare;
@SuppressWarnings("restriction")
public class ResultViewController implements IDebugEventSetListener {
private static final String MAKEGOOD_RESULTVIEWCONTROLLER_MARKER_CREATE = "MAKEGOOD_RESULTVIEWCONTROLLER_MARKER_CREATE"; //$NON-NLS-1$
private static final String MAKEGOOD_RESULTVIEWCONTROLLER_MARKER_TERMINATE = "MAKEGOOD_RESULTVIEWCONTROLLER_MARKER_TERMINATE"; //$NON-NLS-1$
private TestLifecycle testLifecycle;
@Override
public void handleDebugEvents(DebugEvent[] events) {
if (events == null) return;
int size = events.length;
for (int i = 0; i < size; ++i) {
final Object source = events[i].getSource();
ILaunch launch = extractLaunchFromDebugTarget(source);
if (launch == null && source instanceof IProcess) {
launch = ((IProcess) source).getLaunch();
}
if (launch == null) continue;
if (!(launch instanceof MakeGoodLaunch)) continue;
if (events[i].getKind() == DebugEvent.CREATE) {
handleCreateEvent((MakeGoodLaunch) launch);
} else if (events[i].getKind() == DebugEvent.TERMINATE) {
handleTerminateEvent((MakeGoodLaunch) launch);
}
}
}
/**
* Starts a test lifecycle for the given launch.
*
* The TestLifecycle.start() method should be called in the UI thread
* since sometimes the ResultProjector.startTestCase() method is called
* earlier than the UIJob object that is created in this method.
*
* @param launch
*/
private void handleCreateEvent(final MakeGoodLaunch launch) {
// TODO This marker is to avoid calling create() twice by PDT.
if (createEventFired(launch)) {
return;
}
if (!TestLifecycle.getInstance().validateLaunchIdentity(launch)) {
return;
}
markAsCreateEventFired(launch);
testLifecycle = TestLifecycle.getInstance();
try {
testLifecycle.initialize(new ResultProjector());
} catch (CoreException e) {
Activator.getDefault().getLog().log(new Status(Status.WARNING, Activator.PLUGIN_ID, e.getMessage(), e));
return;
}
Job job = new UIJob("MakeGood Test Start") { //$NON-NLS-1$
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
testLifecycle.start();
MakeGoodContext.getInstance().updateStatus(MakeGoodStatus.RunningTest);
ResultView resultView = (ResultView) ViewOpener.find(ResultView.VIEW_ID);
if (resultView == null) {
resultView = (ResultView) ViewOpener.open(ResultView.VIEW_ID);
}
MakeGoodContext.getInstance().getTestRunner().restoreFocusToLastActivePart();
if (resultView != null) {
resultView.startTest(testLifecycle);
}
ResultSquare.getInstance().startTest();
try {
new TestMarkerFactory().clear(TestLifecycle.getInstance().getTestTargets().getProject());
new FatalErrorMarkerFactory().clear(TestLifecycle.getInstance().getTestTargets().getProject());
} catch (CoreException e) {
Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
}
return Status.OK_STATUS;
}
};
job.schedule();
}
private void handleTerminateEvent(final MakeGoodLaunch launch) {
// TODO This code is to avoid calling terminate() twice by PDT.
if (terminateEventFired(launch)) {
return;
}
if (!createEventFired(launch)) {
handleCreateEvent(launch);
}
if (!createEventFired(launch)) {
return;
}
markAsTerminateEventFired(launch);
Job job = new UIJob("MakeGood Test End") { //$NON-NLS-1$
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
testLifecycle.end();
if (testLifecycle.getProgress().noTestsFound()) {
MakeGoodContext.getInstance().updateStatus(MakeGoodStatus.TestsNotFound);
} else {
MakeGoodContext.getInstance().updateStatus(
MakeGoodStatus.WaitingForTestRun,
MakeGoodContext.getInstance().getActivePart().getProject()
);
}
ResultSquare.getInstance().endTest();
if (testLifecycle.getProgress().hasFailures()) {
ResultSquare.getInstance().markAsFailed();
} else {
if (testLifecycle.getProgress().noTestsFound()) {
ResultSquare.getInstance().markAsNoTests();
} else {
ResultSquare.getInstance().markAsPassed();
}
}
ResultView resultView = (ResultView) ViewOpener.find(ResultView.VIEW_ID);
if (resultView != null) {
resultView.endTest();
}
try {
if (testLifecycle.hasErrors()) {
ResultSquare.getInstance().markAsStopped();
if (resultView != null) {
testLifecycle.getProgress().markAsStopped();
resultView.markAsStopped();
resultView.expandResultTreeToResult(testLifecycle.getProgress().getResult().getLast());
}
if (!StopTestRunAction.isStoppedByAction(launch)) {
FatalErrorMarkerFactory markerFactory = new FatalErrorMarkerFactory();
try {
IMarker marker = markerFactory.create(launch.getStreamOutput());
if (marker != null) {
EditorOpener.open(marker);
} else {
ViewOpener.open(IConsoleConstants.ID_CONSOLE_VIEW);
EditorOpener.open(markerFactory.getFile(), markerFactory.getLine());
}
} catch (CoreException e) {
Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
} catch (UnknownFatalErrorMessageException e) {
Activator.getDefault().getLog().log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, e.getMessage(), e));
ViewOpener.open(IConsoleConstants.ID_CONSOLE_VIEW);
}
}
}
} catch (DebugException e) {
Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
}
TestLifecycle.destroy();
return Status.OK_STATUS;
}
};
job.schedule();
}
/**
* @since 2.2.0
*/
private ILaunch extractLaunchFromDebugTarget(Object eventSource) {
if (eventSource instanceof IPHPDebugTarget) {
return ((IPHPDebugTarget) eventSource).getLaunch();
} else {
return null;
}
}
private void markAsCreateEventFired(ILaunch launch) {
launch.setAttribute(MAKEGOOD_RESULTVIEWCONTROLLER_MARKER_CREATE, Boolean.TRUE.toString());
}
private boolean createEventFired(ILaunch launch) {
String isCreated = launch.getAttribute(MAKEGOOD_RESULTVIEWCONTROLLER_MARKER_CREATE);
if (isCreated == null) return false;
return Boolean.TRUE.toString().equals(isCreated);
}
private void markAsTerminateEventFired(ILaunch launch) {
launch.setAttribute(MAKEGOOD_RESULTVIEWCONTROLLER_MARKER_TERMINATE, Boolean.TRUE.toString());
}
private boolean terminateEventFired(ILaunch launch) {
String isTerminated = launch.getAttribute(MAKEGOOD_RESULTVIEWCONTROLLER_MARKER_TERMINATE);
if (isTerminated == null) return false;
return Boolean.TRUE.toString().equals(isTerminated);
}
public class ResultProjector implements ResultReaderListener {
@Override
public void startTestSuite(TestSuiteResult testSuite) {
}
@Override
public void endTestSuite(TestSuiteResult testSuite) {
}
@Override
public void startTestCase(final TestCaseResult testCase) {
Job job = new UIJob("MakeGood Test Case Start") { //$NON-NLS-1$
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
ResultView resultView = (ResultView) ViewOpener.find(ResultView.VIEW_ID);
if (resultView != null) {
resultView.updateOnStartTestCase(testCase);
}
return Status.OK_STATUS;
}
};
job.schedule();
}
@Override
public void endTestCase(final TestCaseResult testCase) {
if (Job.getJobManager().find(testLifecycle).length == 0) {
EndTestCaseUIJob job = new EndTestCaseUIJob("MakeGood Test Case End", testLifecycle); //$NON-NLS-1$
job.schedule();
}
}
@Override
public void startFailure(final TestCaseResult failure) {
Job job = new UIJob("MakeGood Marker Create") { //$NON-NLS-1$
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
try {
new TestMarkerFactory().create(failure);
} catch (CoreException e) {
Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
}
return Status.OK_STATUS;
}
};
job.schedule();
}
@Override
public void endFailure(TestCaseResult failure) {
}
/**
* @since 1.7.0
*/
@Override
public void startError(final TestCaseResult error) {
startFailure(error);
}
/**
* @since 1.7.0
*/
@Override
public void endError(TestCaseResult error) {
}
/**
* @since 1.7.0
*/
@Override
public void startTest() {
}
@Override
public void endTest() {
}
/**
* @since 1.7.0
*/
@Override
public void onFirstTestSuite(final TestSuiteResult testSuite) {
Job job = new UIJob("MakeGood Result Tree Set") { //$NON-NLS-1$
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
ResultView resultView = (ResultView) ViewOpener.find(ResultView.VIEW_ID);
if (resultView != null) {
resultView.setTreeInput(testSuite);
}
return Status.OK_STATUS;
}
};
job.schedule();
}
}
/**
* @since 1.9.0
*/
private class EndTestCaseUIJob extends UIJob {
private TestLifecycle testLifecycle;
public EndTestCaseUIJob(String name, TestLifecycle testLifecycle) {
super(name);
this.testLifecycle = testLifecycle;
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
ResultView resultView = (ResultView) ViewOpener.find(ResultView.VIEW_ID);
if (resultView != null) {
resultView.updateOnEndTestCase();
}
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(Object family) {
return testLifecycle.equals(family);
}
}
}