package pebl.benchmark.test;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.XmlList;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import pebl.HasExtensions;
import pebl.HasId;
import pebl.HasName;
import pebl.MapAdapter;
import pebl.benchmark.feature.Feature;
import pebl.benchmark.feature.FeatureDimension;
import pebl.benchmark.feature.HasMetrics;
import pebl.benchmark.feature.Metric;
import pebl.benchmark.feature.MetricType;
@XmlAccessorType(XmlAccessType.NONE)
public class Test implements Comparable<Test>, HasName, HasId, FeatureDimension, HasExtensions, HasMetrics {
@XmlIDREF
@XmlAttribute(required = true)
private final Feature feature;
@XmlElement(required = true)
private Path process;
@XmlElement(required = true)
private final String description;
@XmlElement(name = "testCase")
@XmlElementWrapper(name = "testCases")
private final List<TestCase> testCases;
@XmlElement(name = "files", type = String.class)
@XmlList
private final List<Path> files;
@XmlElement
@XmlElementRef
@XmlElementWrapper(name = "testPartners")
private final List<TestPartner> partners;
@XmlJavaTypeAdapter(MapAdapter.class)
private final Map<String, String> extensions;
@XmlElement(name = "metric")
@XmlElementWrapper(name = "metrics")
private final List<Metric> metrics = new LinkedList<>();
public Test addMetric(MetricType type) {
metrics.add(new Metric(type, getId()));
return this;
}
@XmlID
@XmlAttribute(required = true)
private final String id;
public Test() {
this(Paths.get(""), "", new ArrayList<>(), new Feature());
}
public Test(Path process,
String description,
List<? extends TestCase> testCases,
Feature feature) {
this(process, description, testCases, feature, new ArrayList<>(), new ArrayList<>());
}
public Test(Path process,
String description,
List<? extends TestCase> testCases,
Feature feature,
List<TestPartner> partners) {
this(process, description, testCases, feature, new ArrayList<>(), partners);
}
public Test(Path process,
String description,
List<? extends TestCase> testCases,
Feature feature,
List<Path> files,
List<TestPartner> partners) {
this.partners = new ArrayList<>(Objects.requireNonNull(partners));
this.process = Objects.requireNonNull(process);
this.feature = Objects.requireNonNull(feature);
this.description = Objects.requireNonNull(description);
this.testCases = uniqueifyTestCaseNames(new ArrayList<>(Objects.requireNonNull(testCases)));
this.files = new ArrayList<>(Objects.requireNonNull(files));
this.extensions = new HashMap<>();
this.id = String.join(HasId.SEPARATOR, getFeature().getId(), "test");
}
public Test(Path process, List<TestCase> testCases,
Feature feature,
List<Path> files,
String description,
List<TestPartner> partners,
Map<String, String> extensions) {
this.process = process;
this.testCases = testCases;
this.feature = feature;
this.files = files;
this.description = description;
this.partners = partners;
this.extensions = extensions;
this.id = "";
}
public Test withNewProcessAndFeature(Path process, Feature feature) {
return new Test(process, description, testCases, feature, files, partners);
}
public Test withNewTestCases(List<TestCase> testCases) {
return new Test(process, description, testCases, feature, files, partners);
}
public List<Path> getFiles() {
return files;
}
private List<TestCase> uniqueifyTestCaseNames(List<TestCase> testCases) {
// make sure that the tests have different numbers
for (int counter = 1; counter <= testCases.size(); counter++) {
TestCase testCase = testCases.get(counter - 1);
testCase.setNumber(counter);
}
// only when not already unique
if (!hasUniqueTestCaseNames(testCases)) {
return testCases;
}
// make test case names unique
for (TestCase testCase : testCases) {
testCase.setName(testCase.getName() + "-" + testCase.getNumber());
}
return testCases;
}
private boolean hasUniqueTestCaseNames(List<TestCase> testCases) {
return testCases.stream().map(TestCase::getName).distinct().count() == testCases.size();
}
public String getProcessLanguage() {
return getLanguage().getName();
}
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!getClass().equals(o.getClass())) {
return false;
}
Test that = (Test) o;
return process.toString().equals(that.process.toString());
}
public int hashCode() {
return process.hashCode();
}
@Override
public int compareTo(Test o) {
return process.compareTo(o.process);
}
public Path getProcess() {
return this.process;
}
public String getDescription() {
return description;
}
public List<TestCase> getTestCases() {
return Collections.unmodifiableList(testCases);
}
private void setTestCases(List<TestCase> testCases) {
uniqueifyTestCaseNames(testCases);
this.testCases.clear();
this.testCases.addAll(testCases);
}
@Override
public String getName() {
return getFeature().getName();
}
public Feature getFeature() {
return feature;
}
public List<Path> getFiles(Predicate<Path> predicate) {
return getFiles().stream().filter(predicate).collect(Collectors.toList());
}
public List<TestPartner> getTestPartners() {
return Collections.unmodifiableList(partners);
}
public void addTestPartner(TestPartner testPartner) {
this.partners.add(testPartner);
}
public Map<String, String> getExtensions() {
return extensions;
}
public Test addExtension(String key, String value) {
extensions.put(key, value);
return this;
}
@Override
public String getId() {
return id;
}
public List<Metric> getMetrics() {
return metrics;
}
public void setProcess(Path process) {
this.process = process;
}
@Override
public String toString() {
return getId();
}
}