blob: 1d6b50df15c1cfa5196dc22bc5eb79e50f6d5c4f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Christian Pontesegger 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:
* Christian Pontesegger - initial API and implementation
*******************************************************************************/
package org.eclipse.ease.modules.unittest.ui.views;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.ease.modules.unittest.ITestListener;
import org.eclipse.ease.modules.unittest.components.TestFile;
import org.eclipse.ease.modules.unittest.components.TestStatus;
import org.eclipse.ease.modules.unittest.components.TestSuite;
import org.eclipse.ease.modules.unittest.ui.Activator;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.XMLMemento;
public class SuiteRuntimeInformation implements ITestListener {
private static final String XML_PARAMETER_TIMING = "timing";
private static final String XML_PARAMETER_INDEX = "index";
private static final String XML_NODE_RUN = "run";
private static final String XML_NODE_FILE = "file";
/** Typical script runtime. In case we have no other information. */
private static final long DEFAULT_RUNTIME = 10000;
private static final int RUNS_TO_SAVE = 10;
private class RuntimeInformation {
private final ArrayList<Long> fTimings = new ArrayList<Long>();
public synchronized long getEstimatedRuntime() {
if (fTimings.isEmpty())
return DEFAULT_RUNTIME;
// use a linear weighted average
int weight = 0;
long timing = 0;
for (final Long time : fTimings) {
weight++;
timing += time * weight;
}
timing /= (weight + 1) * (weight / 2.0);
return timing;
}
public synchronized void addRuntime(final long time) {
fTimings.add(time);
}
public ArrayList<Long> getTimings() {
return fTimings;
}
}
private final HashMap<String, RuntimeInformation> fRuntimes = new HashMap<String, RuntimeInformation>();
private List<TestFile> fTestFiles;
private long fEstimatedEndOfTests;
private final TestSuite fTestSuite;
public SuiteRuntimeInformation(final TestSuite suite) {
fTestSuite = suite;
load();
// suite might already have started
fTestFiles = fTestSuite.getActiveTestFiles();
suite.addTestListener(this);
}
private void load() {
try {
final XMLMemento root = XMLMemento.createReadRoot(new FileReader(getSettingsFile()));
final IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot();
for (final IMemento fileNode : root.getChildren(XML_NODE_FILE)) {
for (final IMemento runNode : fileNode.getChildren(XML_NODE_RUN))
// TODO currently ignore index, expect we read sequentially
addTiming(fileNode.getTextData(), Long.parseLong(runNode.getString(XML_PARAMETER_TIMING)));
}
} catch (final Exception e) {
// cannot load stats, ignore
}
}
public synchronized void save() {
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(getSettingsFile());
// create xml output
final XMLMemento rootNode = XMLMemento.createWriteRoot("root");
for (final Entry<String, RuntimeInformation> entry : fRuntimes.entrySet()) {
final IMemento fileNode = rootNode.createChild(XML_NODE_FILE);
fileNode.putTextData(entry.getKey());
final List<Long> timings = entry.getValue().getTimings();
int nodeIndex = 0;
for (int index = Math.max(0, timings.size() - RUNS_TO_SAVE); index < timings.size(); index++) {
final IMemento runNode = fileNode.createChild(XML_NODE_RUN);
runNode.putInteger(XML_PARAMETER_INDEX, nodeIndex++);
runNode.putString(XML_PARAMETER_TIMING, Long.toString(timings.get(index)));
}
}
rootNode.save(new OutputStreamWriter(outputStream));
} catch (final Exception e) {
} finally {
try {
outputStream.close();
} catch (final IOException e) {
}
}
}
@Override
public synchronized void notify(final Object testObject, final TestStatus status) {
if ((testObject instanceof TestSuite) && (status == TestStatus.RUNNING)) {
// testsuite started
fTestFiles = fTestSuite.getActiveTestFiles();
estimateEndOfTests();
} else if ((testObject instanceof TestFile) && (status != TestStatus.RUNNING) && (status != TestStatus.NOT_RUN)) {
addTiming(createTestToken((TestFile) testObject), ((TestFile) testObject).getExecutionTime());
fTestFiles.remove(testObject);
estimateEndOfTests();
}
}
private static String createTestToken(final TestFile testFile) {
final Object file = testFile.getFile();
if (file instanceof IFile)
return ((IFile) file).getFullPath().toPortableString();
if (file instanceof File)
return ((File) file).getAbsolutePath();
if (file != null)
return file.toString();
return "<undefind>";
}
private synchronized void addTiming(final String fileIdentifier, final long timing) {
if (!fRuntimes.containsKey(fileIdentifier))
fRuntimes.put(fileIdentifier, new RuntimeInformation());
fRuntimes.get(fileIdentifier).addRuntime(timing);
}
private File getSettingsFile() {
final IPath path = Activator.getDefault().getStateLocation().append("timing_" + fTestSuite.getModel().getFile().getProject().hashCode() + ".xml");
return path.toFile();
}
private synchronized void estimateEndOfTests() {
long time = 0;
for (final TestFile file : fTestFiles) {
final RuntimeInformation info = fRuntimes.get(createTestToken(file));
time += (info != null) ? info.getEstimatedRuntime() : getAverageTestTime();
}
fEstimatedEndOfTests = System.currentTimeMillis() + time;
}
private synchronized long getAverageTestTime() {
long time = 0;
int items = 0;
for (final RuntimeInformation info : fRuntimes.values()) {
if (info.getEstimatedRuntime() > 0) {
time += info.getEstimatedRuntime();
items++;
}
}
if (items > 0)
return time / items;
// we do not have any information at all, assume a typical test time of 10 seconds
return DEFAULT_RUNTIME;
}
public long getEstimatedTestTime() {
return fEstimatedEndOfTests - System.currentTimeMillis();
}
}