/*
* Copyright (C) 2014-2015 Daniel Dietsch (dietsch@informatik.uni-freiburg.de)
* Copyright (C) 2014-2015 Matthias Heizmann (heizmann@informatik.uni-freiburg.de)
* Copyright (C) 2015 University of Freiburg
*
* This file is part of the ULTIMATE UnitTest Library.
*
* The ULTIMATE UnitTest Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ULTIMATE UnitTest Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ULTIMATE UnitTest Library. If not, see .
*
* Additional permission under GNU GPL version 3 section 7:
* If you modify the ULTIMATE UnitTest Library, or any covered work, by linking
* or combining it with Eclipse RCP (or a modified version of Eclipse RCP),
* containing parts covered by the terms of the Eclipse Public License, the
* licensors of the ULTIMATE UnitTest Library grant you additional permission
* to convey the resulting work.
*/
package de.uni_freiburg.informatik.ultimate.test;
import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.UnaryOperator;
import de.uni_freiburg.informatik.ultimate.core.model.services.IUltimateServiceProvider;
import de.uni_freiburg.informatik.ultimate.test.UltimateTestCase.AfterTest;
import de.uni_freiburg.informatik.ultimate.test.util.TestUtil;
import de.uni_freiburg.informatik.ultimate.util.CoreUtil;
/**
* A run of ultimate is defined by four things:
*
* - a list of input files (e.g. an ANSI C file that is verified),
*
- a settings file,
*
- a toolchain file, and
*
- a timeout in milliseconds.
*
*
* @author heizmann@informatik.uni-freiburg.de
* @author Daniel Dietsch (dietsch@informatik.uni-freiburg.de)
*
*/
public final class UltimateRunDefinition implements Comparable {
private static final String[] PRIMARY_ENDINGS = new String[] { ".i", ".c", ".bpl", ".ats" };
private static final String PATH_SEPARATOR = ";";
private static final String NO_SETTINGS_NAME = "Default Settings";
private final File[] mInput;
private final File mSettings;
private final File mToolchain;
private final long mTimeout;
private final NamedServiceCallback mServiceCallback;
private final AfterTest mFunAfterTest;
private String mInputFilenames;
private final String mStringRepr;
public UltimateRunDefinition(final File input, final File settings, final File toolchain, final long timeout) {
this(new File[] { input }, settings, toolchain, timeout);
}
public UltimateRunDefinition(final File input, final File settings, final File toolchain, final long timeout,
final NamedServiceCallback serviceCallback) {
this(new File[] { input }, settings, toolchain, timeout, serviceCallback, null);
}
public UltimateRunDefinition(final File input, final File settings, final File toolchain, final long timeout,
final AfterTest funAfterTest) {
this(new File[] { input }, settings, toolchain, timeout, funAfterTest);
}
public UltimateRunDefinition(final File[] input, final File settings, final File toolchain, final long timeout) {
this(input, settings, toolchain, timeout, null);
}
public UltimateRunDefinition(final File[] input, final File settings, final File toolchain, final long timeout,
final AfterTest funAfterTest) {
this(input, settings, toolchain, timeout, null, funAfterTest);
}
public UltimateRunDefinition(final File[] input, final File settings, final File toolchain, final long timeout,
final NamedServiceCallback serviceCaĺlback, final AfterTest funAfterTest) {
if (input == null || toolchain == null) {
throw new IllegalArgumentException("Toolchain and Input may not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("Timeout must be larger or equal to 0");
}
mInput = input;
mSettings = settings;
mToolchain = toolchain;
mTimeout = timeout;
mServiceCallback = serviceCaĺlback;
mFunAfterTest = funAfterTest;
mStringRepr = generateShortStringRepresentation();
}
public File[] getInput() {
return mInput;
}
public File getSettings() {
return mSettings;
}
public File getToolchain() {
return mToolchain;
}
public long getTimeout() {
return mTimeout;
}
public NamedServiceCallback getServiceCallback() {
return mServiceCallback;
}
public AfterTest getAfterTestMethod() {
return mFunAfterTest;
}
public String getSettingsName() {
return getSettings() == null ? NO_SETTINGS_NAME : getSettings().getName();
}
public String getSettingsAbsolutePath() {
return getSettings() == null ? NO_SETTINGS_NAME : getSettings().getAbsolutePath();
}
/**
* This method tries to find the "primary" input file. This method is a hack to retain compatibility with the times
* were we only accepted one input file.
*/
public File selectPrimaryInputFile() {
if (mInput.length == 1) {
return mInput[0];
}
// DD: If we see multiple files here, we just select the first that ends in .i or .c. This is quite hacky,
// but I do not see another option.
final Optional first = Arrays.stream(mInput)
.filter(a -> Arrays.stream(PRIMARY_ENDINGS).anyMatch(ending -> a.getName().endsWith(ending)))
.findFirst();
if (first.isPresent()) {
return first.get();
}
return null;
}
public String getInputFileNames() {
if (mInputFilenames == null) {
final StringBuilder sb = new StringBuilder();
final File[] input = getInput();
if (input.length == 1) {
sb.append(removeTrunkExamplesPrefix(input[0].getAbsolutePath()));
} else if (input.length > 1) {
sb.append("[");
sb.append(removeTrunkExamplesPrefix(input[0].getAbsolutePath()));
for (int i = 1; i < input.length; ++i) {
sb.append(PATH_SEPARATOR);
sb.append(removeTrunkExamplesPrefix(input[i].getAbsolutePath()));
}
sb.append("]");
}
mInputFilenames = sb.toString();
}
return mInputFilenames;
}
/**
* Returns a string describing the folders from which the input files come.
*
* @return
*/
public String getInputFileFolders() {
final StringBuilder sb = new StringBuilder();
final File[] input = getInput();
final Set set = new HashSet<>();
if (input.length == 1) {
sb.append(removeTrunkExamplesPrefix(input[0].getParent()));
} else if (input.length > 1) {
sb.append("[");
String path = removeTrunkExamplesPrefix(input[0].getParent());
set.add(path);
sb.append(path);
for (int i = 1; i < input.length; ++i) {
path = removeTrunkExamplesPrefix(input[i].getParent());
if (set.add(path)) {
sb.append(PATH_SEPARATOR);
sb.append(path);
}
}
sb.append("]");
}
return sb.toString();
}
@Override
public String toString() {
return mStringRepr;
}
public String generateLongStringRepresentation() {
final String repr = "Input: " + mInput + ", Settings: " + mSettings + ", Toolchain: " + mToolchain;
if (mServiceCallback != null && mServiceCallback.hasName()) {
return "Callback: " + mServiceCallback.getName() + ", " + repr;
}
return repr;
}
private String generateShortStringRepresentation() {
final StringBuilder sb = new StringBuilder();
final File settings = getSettings();
final File toolchain = getToolchain();
if (mServiceCallback != null && mServiceCallback.hasName()) {
sb.append("Callback:").append(mServiceCallback.getName()).append(" ");
}
sb.append("Input:").append(getInputFileNames());
sb.append(" Settings:");
if (settings == null) {
sb.append("default");
} else {
sb.append(removeTrunkExamplesPrefix(settings.getAbsolutePath()));
}
sb.append(" Toolchain:");
sb.append(removeTrunkExamplesPrefix(toolchain.getAbsolutePath()));
if (CoreUtil.OS_IS_WINDOWS) {
return sb.toString().replaceAll("\\\\", "/");
}
return sb.toString();
}
public static String removeTrunkExamplesPrefix(final String path) {
final String trunk = TestUtil.getPathFromTrunk("");
final String examples = trunk + File.separator + "examples" + File.separator;
final int lastIndexOf = path.lastIndexOf(examples);
if (lastIndexOf != -1) {
final String trunkated = path.substring(lastIndexOf + examples.length(), path.length());
return trunkated;
}
return path;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (mInput == null ? 0 : Arrays.hashCode(mInput));
result = prime * result + (mSettings == null ? 0 : mSettings.hashCode());
result = prime * result + (mToolchain == null ? 0 : mToolchain.hashCode());
result = prime * result + (mServiceCallback == null ? 0 : mServiceCallback.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final UltimateRunDefinition other = (UltimateRunDefinition) obj;
if (mInput == null) {
if (other.mInput != null) {
return false;
}
} else if (!Arrays.equals(mInput, other.mInput)) {
return false;
}
if (mSettings == null) {
if (other.mSettings != null) {
return false;
}
} else if (!mSettings.equals(other.mSettings)) {
return false;
}
if (mToolchain == null) {
if (other.mToolchain != null) {
return false;
}
} else if (!mToolchain.equals(other.mToolchain)) {
return false;
}
if (!Objects.equals(mServiceCallback, other.mServiceCallback)) {
return false;
}
return true;
}
@Override
public int compareTo(final UltimateRunDefinition other) {
if (other.mInput.length > mInput.length) {
return -1;
} else if (other.mInput.length < mInput.length) {
return 1;
} else {
for (int i = 0; i < mInput.length; ++i) {
final int inputCmp = mInput[i].compareTo(other.mInput[i]);
if (inputCmp != 0) {
return inputCmp;
}
}
}
final int tcCmp = mToolchain.compareTo(other.mToolchain);
if (tcCmp != 0) {
return tcCmp;
}
if (mSettings == null && other.mSettings == null) {
return 0;
} else if (mSettings == null) {
return 1;
} else if (other.mSettings == null) {
return -1;
} else {
return mSettings.compareTo(other.mSettings);
}
}
public static class NamedServiceCallback {
private final String mName;
private final UnaryOperator mCallback;
public NamedServiceCallback(final String name, final UnaryOperator callback) {
mName = name;
mCallback = Objects.requireNonNull(callback);
}
public boolean hasName() {
return mName != null;
}
public String getName() {
return mName;
}
public IUltimateServiceProvider processServices(final IUltimateServiceProvider services) {
return mCallback.apply(services);
}
@Override
public int hashCode() {
return Objects.hash(mCallback, mName);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final NamedServiceCallback other = (NamedServiceCallback) obj;
return mCallback == other.mCallback && Objects.equals(mName, other.mName);
}
}
}