/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others.
* All rights reserved. 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
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.phpsrc.eclipse.pti.tools.phpunit.core.model;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchManager;
import org.phpsrc.eclipse.pti.core.Messages;
import org.phpsrc.eclipse.pti.tools.phpunit.PHPUnitPlugin;
import org.phpsrc.eclipse.pti.tools.phpunit.core.model.TestElement.Status;
import org.phpsrc.eclipse.pti.ui.viewsupport.BasicElementLabels;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Central registry for JUnit test runs.
*/
public final class PHPUnitModel {
private static final class LegacyTestRunSessionListener implements ITestRunSessionListener {
private TestRunSession fActiveTestRunSession;
private ITestSessionListener fTestSessionListener;
public void sessionAdded(TestRunSession testRunSession) {
// Only serve one legacy ITestRunListener at a time, since they
// cannot distinguish between different concurrent test sessions:
if (fActiveTestRunSession != null)
return;
fActiveTestRunSession = testRunSession;
fTestSessionListener = new ITestSessionListener() {
public void testAdded(TestElement testElement) {
}
public void sessionStarted() {
ITestRunListener[] testRunListeners = PHPUnitPlugin.getDefault().getTestRunListeners();
for (int i = 0; i < testRunListeners.length; i++) {
testRunListeners[i].testRunStarted(fActiveTestRunSession.getTotalCount());
}
}
public void sessionTerminated() {
ITestRunListener[] testRunListeners = PHPUnitPlugin.getDefault().getTestRunListeners();
for (int i = 0; i < testRunListeners.length; i++) {
testRunListeners[i].testRunTerminated();
}
sessionRemoved(fActiveTestRunSession);
}
public void sessionStopped(long elapsedTime) {
ITestRunListener[] testRunListeners = PHPUnitPlugin.getDefault().getTestRunListeners();
for (int i = 0; i < testRunListeners.length; i++) {
testRunListeners[i].testRunStopped(elapsedTime);
}
sessionRemoved(fActiveTestRunSession);
}
public void sessionEnded(long elapsedTime) {
ITestRunListener[] testRunListeners = PHPUnitPlugin.getDefault().getTestRunListeners();
for (int i = 0; i < testRunListeners.length; i++) {
testRunListeners[i].testRunEnded(elapsedTime);
}
sessionRemoved(fActiveTestRunSession);
}
public void runningBegins() {
// ignore
}
public void testStarted(TestCaseElement testCaseElement) {
ITestRunListener[] testRunListeners = PHPUnitPlugin.getDefault().getTestRunListeners();
for (int i = 0; i < testRunListeners.length; i++) {
testRunListeners[i].testStarted(testCaseElement.getId(), testCaseElement.getTestName());
}
}
public void testFailed(TestElement testElement, Status status, String trace, String expected,
String actual) {
ITestRunListener[] testRunListeners = PHPUnitPlugin.getDefault().getTestRunListeners();
for (int i = 0; i < testRunListeners.length; i++) {
testRunListeners[i].testFailed(status.getOldCode(), testElement.getId(), testElement
.getTestName(), trace, expected, actual);
}
}
public void testEnded(TestCaseElement testCaseElement) {
ITestRunListener[] testRunListeners = PHPUnitPlugin.getDefault().getTestRunListeners();
for (int i = 0; i < testRunListeners.length; i++) {
testRunListeners[i].testEnded(testCaseElement.getId(), testCaseElement.getTestName());
}
}
public void testReran(TestCaseElement testCaseElement, Status status, String trace,
String expectedResult, String actualResult) {
ITestRunListener[] testRunListeners = PHPUnitPlugin.getDefault().getTestRunListeners();
for (int i = 0; i < testRunListeners.length; i++) {
testRunListeners[i].testReran(testCaseElement.getId(), testCaseElement.getClassName(),
testCaseElement.getTestMethodName(), status.getOldCode(), trace, expectedResult,
actualResult);
}
}
public boolean acceptsSwapToDisk() {
return true;
}
};
fActiveTestRunSession.addTestSessionListener(fTestSessionListener);
}
public void sessionRemoved(TestRunSession testRunSession) {
if (fActiveTestRunSession == testRunSession) {
fActiveTestRunSession.removeTestSessionListener(fTestSessionListener);
fTestSessionListener = null;
fActiveTestRunSession = null;
}
}
}
private final ListenerList fTestRunSessionListeners = new ListenerList();
/**
* Active test run sessions, youngest first.
*/
private final LinkedList/* <TestRunSession> */fTestRunSessions = new LinkedList();
/**
* Starts the model (called by the {@link JUnitPlugin} on startup).
*/
public void start() {
PHPUnitDebugEventHandler.getDefault().start();
/*
* TODO: restore on restart: - only import headers! - only import last n
* sessions; remove all other files in historyDirectory
*/
File historyDirectory = PHPUnitPlugin.getHistoryDirectory();
File[] swapFiles = historyDirectory.listFiles();
if (swapFiles != null) {
Arrays.sort(swapFiles, new Comparator() {
public int compare(Object o1, Object o2) {
String name1 = ((File) o1).getName();
String name2 = ((File) o2).getName();
return name1.compareTo(name2);
}
});
for (int i = 0; i < swapFiles.length; i++) {
final File file = swapFiles[i];
SafeRunner.run(new ISafeRunnable() {
public void run() throws Exception {
importTestRunSession(file);
}
public void handleException(Throwable exception) {
PHPUnitPlugin.log(exception);
}
});
}
}
addTestRunSessionListener(new LegacyTestRunSessionListener());
}
/**
* Stops the model (called by the {@link JUnitPlugin} on shutdown).
*/
public void stop() {
PHPUnitDebugEventHandler.getDefault().stop();
File historyDirectory = PHPUnitPlugin.getHistoryDirectory();
File[] swapFiles = historyDirectory.listFiles();
if (swapFiles != null) {
for (int i = 0; i < swapFiles.length; i++) {
swapFiles[i].delete();
}
}
// for (Iterator iter= fTestRunSessions.iterator(); iter.hasNext();) {
// final TestRunSession session= (TestRunSession) iter.next();
// SafeRunner.run(new ISafeRunnable() {
// public void run() throws Exception {
// session.swapOut();
// }
// public void handleException(Throwable exception) {
// JUnitPlugin.log(exception);
// }
// });
// }
}
public void addTestRunSessionListener(ITestRunSessionListener listener) {
fTestRunSessionListeners.add(listener);
}
public void removeTestRunSessionListener(ITestRunSessionListener listener) {
fTestRunSessionListeners.remove(listener);
}
/**
* @return a list of active {@link TestRunSession}s. The list is a copy of
* the internal data structure and modifications do not affect the
* global list of active sessions. The list is sorted by age,
* youngest first.
*/
public List getTestRunSessions() {
return new ArrayList(fTestRunSessions);
}
/**
* Adds the given {@link TestRunSession} and notifies all registered
* {@link ITestRunSessionListener}s.
* <p>
* <b>To be called in the UI thread only!</b>
* </p>
*
* @param testRunSession
* the session to add
*/
public void addTestRunSession(TestRunSession testRunSession) {
Assert.isNotNull(testRunSession);
Assert.isLegal(!fTestRunSessions.contains(testRunSession));
fTestRunSessions.addFirst(testRunSession);
notifyTestRunSessionAdded(testRunSession);
}
/**
* Imports a test run session from the given file.
*
* @param file
* a file containing a test run session transcript
* @return the imported test run session
* @throws CoreException
* if the import failed
*/
public static TestRunSession importTestRunSession(File file) throws CoreException {
try {
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
// parserFactory.setValidating(true); // TODO: add DTD and debug
// flag
SAXParser parser = parserFactory.newSAXParser();
TestRunHandler handler = new TestRunHandler();
parser.parse(file, handler);
TestRunSession session = handler.getTestRunSession();
PHPUnitPlugin.getModel().addTestRunSession(session);
return session;
} catch (ParserConfigurationException e) {
throwImportError(file, e);
} catch (SAXException e) {
throwImportError(file, e);
} catch (IOException e) {
throwImportError(file, e);
}
return null; // does not happen
}
public static void importIntoTestRunSession(File swapFile, TestRunSession testRunSession) throws CoreException {
try {
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
// parserFactory.setValidating(true); // TODO: add DTD and debug
// flag
SAXParser parser = parserFactory.newSAXParser();
TestRunHandler handler = new TestRunHandler(testRunSession);
parser.parse(swapFile, handler);
} catch (ParserConfigurationException e) {
throwImportError(swapFile, e);
} catch (SAXException e) {
throwImportError(swapFile, e);
} catch (IOException e) {
throwImportError(swapFile, e);
}
}
/**
* Exports the given test run session.
*
* @param testRunSession
* the test run session
* @param file
* the destination
* @throws CoreException
* if an error occurred
*/
public static void exportTestRunSession(TestRunSession testRunSession, File file) throws CoreException {
FileOutputStream out = null;
try {
out = new FileOutputStream(file);
exportTestRunSession(testRunSession, out);
} catch (IOException e) {
throwExportError(file, e);
} catch (TransformerConfigurationException e) {
throwExportError(file, e);
} catch (TransformerException e) {
throwExportError(file, e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e2) {
PHPUnitPlugin.log(e2);
}
}
}
}
public static void exportTestRunSession(TestRunSession testRunSession, OutputStream out)
throws TransformerFactoryConfigurationError, TransformerException {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
InputSource inputSource = new InputSource();
SAXSource source = new SAXSource(new TestRunSessionSerializer(testRunSession), inputSource);
StreamResult result = new StreamResult(out);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
/*
* Bug in Xalan: Only indents if proprietary property
* org.apache.xalan.templates.OutputProperties.S_KEY_INDENT_AMOUNT is
* set.
*
* Bug in Xalan as shipped with J2SE 5.0: Does not read the
* indent-amount property at all >:-(.
*/
try {
transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2"); //$NON-NLS-1$ //$NON-NLS-2$
} catch (IllegalArgumentException e) {
// no indentation today...
}
transformer.transform(source, result);
}
private static void throwExportError(File file, Exception e) throws CoreException {
throw new CoreException(new org.eclipse.core.runtime.Status(IStatus.ERROR, PHPUnitPlugin.PLUGIN_ID, Messages
.format(ModelMessages.JUnitModel_could_not_write, BasicElementLabels.getPathLabel(file)), e));
}
private static void throwImportError(File file, Exception e) throws CoreException {
throw new CoreException(new org.eclipse.core.runtime.Status(IStatus.ERROR, PHPUnitPlugin.PLUGIN_ID, Messages
.format(ModelMessages.JUnitModel_could_not_read, BasicElementLabels.getPathLabel(file)), e));
}
/**
* Removes the given {@link TestRunSession} and notifies all registered
* {@link ITestRunSessionListener}s.
* <p>
* <b>To be called in the UI thread only!</b>
* </p>
*
* @param testRunSession
* the session to remove
*/
public void removeTestRunSession(TestRunSession testRunSession) {
boolean existed = fTestRunSessions.remove(testRunSession);
if (existed) {
notifyTestRunSessionRemoved(testRunSession);
}
testRunSession.removeSwapFile();
}
private void notifyTestRunSessionRemoved(TestRunSession testRunSession) {
testRunSession.stopTestRun();
ILaunch launch = testRunSession.getLaunch();
if (launch != null) {
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
launchManager.removeLaunch(launch);
}
Object[] listeners = fTestRunSessionListeners.getListeners();
for (int i = 0; i < listeners.length; ++i) {
((ITestRunSessionListener) listeners[i]).sessionRemoved(testRunSession);
}
}
private void notifyTestRunSessionAdded(TestRunSession testRunSession) {
Object[] listeners = fTestRunSessionListeners.getListeners();
for (int i = 0; i < listeners.length; ++i) {
((ITestRunSessionListener) listeners[i]).sessionAdded(testRunSession);
}
}
}