blob: 5dee9c2c4d16293a385c2438db6935d10fd5be42 [file] [log] [blame]
/**
*
*/
package org.eclipse.emf.henshin.variability.test;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.henshin.interpreter.Change;
import org.eclipse.emf.henshin.interpreter.Match;
import org.eclipse.emf.henshin.interpreter.impl.EGraphImpl;
import org.eclipse.emf.henshin.interpreter.impl.EngineImpl;
import org.eclipse.emf.henshin.interpreter.impl.MatchImpl;
import org.eclipse.emf.henshin.model.Module;
import org.eclipse.emf.henshin.model.Rule;
import org.eclipse.emf.henshin.model.resource.HenshinResourceSet;
import org.eclipse.emf.henshin.variability.InconsistentRuleException;
import org.eclipse.emf.henshin.variability.VarRuleApplicationImpl;
import org.eclipse.emf.henshin.variability.matcher.VariabilityAwareEngine;
import org.eclipse.emf.henshin.variability.matcher.VariabilityAwareMatch;
import org.eclipse.emf.henshin.variability.util.RuleUtil;
import org.junit.Test;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.junit.runner.RunWith;
import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
/**
* @author speldszus
*
* A test for checking the Henshin engine. The tests are specified in
* JSON files described by inner classes.
*
*/
@RunWith(Parameterized.class)
public class VBEngineParameterizedTest {
private static final File dataFile = new File("data");
private static final boolean DEBUG = false;
private VBTestData data;
public VBEngineParameterizedTest(VBTestData data) {
this.data = data;
}
@Parameters(name = "{0}")
public static Collection<VBTestData> collectTests() {
Collection<VBTestData> tests = new LinkedList<>();
for (File folder : dataFile.listFiles()) {
if (folder.isDirectory()) {
List<File> expectFiles = new LinkedList<>();
List<File> metaModelFiles = new LinkedList<>();
for (File file : folder.listFiles()) {
if (file.isFile()) {
if (file.getName().endsWith(".json")) {
expectFiles.add(file);
} else if (file.getName().endsWith(".ecore")) {
metaModelFiles.add(file);
}
}
}
for (File expectFile : expectFiles) {
tests.addAll(createTests(metaModelFiles, expectFile));
}
}
}
return tests;
}
/**
* Parses the expect file and creates test data objects
*
* @param metaModelFiles The relevant meta model files
* @param expectFile The file containing the test description
* @return The tests
*/
private static Collection<VBTestData> createTests(List<File> metaModelFiles, File expectFile) {
TestDescription desc;
try {
desc = new Gson().fromJson(new FileReader(expectFile), TestDescription.class);
} catch (JsonSyntaxException | JsonIOException | FileNotFoundException e1) {
System.err.println("Skip " + expectFile);
e1.printStackTrace();
return Collections.emptyList();
}
try {
File folder = expectFile.getParentFile();
File ruleFile = new File(folder, desc.ruleFile);
if (!ruleFile.exists()) {
System.err.println("Skip " + ruleFile);
return Collections.emptyList();
}
Collection<VBTestData> tests = new LinkedList<>();
for (TestApplication application : desc.applications) {
HenshinResourceSet rs = initRS(folder, metaModelFiles);
Module module = rs.getModule(ruleFile.getAbsolutePath(), true);
File modelFile = new File(folder, application.model);
EObject model = rs.getEObject(modelFile.getAbsolutePath());
Rule rule;
if (application.rule == null) {
rule = module.getAllRules().get(0);
} else {
rule = module.getAllRules().parallelStream().filter(r -> application.rule.equals(r.getName()))
.findAny().orElse(null);
}
if (!RuleUtil.isVarRule(rule)) {
System.err.println("Skip as it is not a VB rule: " + rule);
}
tests.add(new VBTestData(expectFile.getName(), rule, model.eResource(), application.results));
}
return tests;
} catch (NullPointerException e) {
System.err.println("Skipping rule file: " + desc.ruleFile);
e.printStackTrace();
}
return Collections.emptyList();
}
/**
* Initializes the resource set
*
* @param folder The folder containing the test cases
* @param metaModelFiles The meta model files to be loaded
* @return The resource set
*/
protected static HenshinResourceSet initRS(File folder, List<File> metaModelFiles) {
HenshinResourceSet rs = new HenshinResourceSet(folder.getAbsolutePath());
for (File metaModel : metaModelFiles) {
Resource mmResource = rs.getResource(metaModel.getAbsolutePath());
EPackage ePackage = (EPackage) mmResource.getContents().get(0);
mmResource.setURI(URI.createURI(ePackage.getNsURI()));
rs.getPackageRegistry().put(ePackage.getNsURI(), ePackage);
}
return rs;
}
/**
* Executes the specified test
*
* @param dataFile The test specification
* @throws InconsistentRuleException If a inconsistent rule should be executed
*/
@Test
public void testVBEngine() throws InconsistentRuleException {
EGraphImpl graph = new EGraphImpl(data.resource);
VariabilityAwareEngine vbEngine = new VariabilityAwareEngine(data.rule, graph);
Set<VariabilityAwareMatch> matches = vbEngine.findMatches();
int numberOfMatches = matches.size();
for (VariabilityAwareMatch completeVarMatch : matches) {
applyRuleAtMatch(graph, vbEngine, completeVarMatch);
}
if (DEBUG) {
Path path = Paths.get("debug/" + data.resource.getURI().lastSegment());
path.getParent().toFile().mkdirs();
try (OutputStream outputStream = Files
.newOutputStream(path)) {
data.resource.save(outputStream, Collections.emptyMap());
} catch (IOException e) {
e.printStackTrace();
}
}
int modelSize = getModelSize(data);
for (TestResult check : data.expect) {
switch (check.kind) {
case MATCHES:
assertEquals("Number of matches not as expected!", ((Number) check.value).intValue(), numberOfMatches);
break;
case MODEL_SIZE:
assertEquals("Model size not as expected!", ((Number) check.value).intValue(), modelSize);
break;
}
}
}
/**
* Applies the match
*
* @param graph The graph
* @param engine The engine
* @param match The VB match to be applied
*/
private void applyRuleAtMatch(EGraphImpl graph, VariabilityAwareEngine engine, VariabilityAwareMatch match) {
VarRuleApplicationImpl app = new VarRuleApplicationImpl(new EngineImpl(), graph, match.getRule(), match);
app.execute(null);
// Match completeMatch = match.getMatch();
// Rule unit = match.getMatch().getRule();
// match.prepareRule();
// MatchImpl resultMatch = new MatchImpl((Rule) unit, true);
// Change change = engine.createChange((Rule) unit, graph, completeMatch, resultMatch);
// if (change == null) {
// fail("Creating change failed!");
// }
// change.applyAndReverse();
// if (match != null) {
// match.undoPreparation();
// }
}
/**
* Calculates the size of the test model in terms of model elements
*
* @param data The test data
* @return The size of the model
*/
protected int getModelSize(VBTestData data) {
int modelSize = 0;
EcoreUtil.resolveAll(data.resource);
TreeIterator<EObject> contents = data.resource.getAllContents();
while (contents.hasNext()) {
EObject next = contents.next();
modelSize++;
}
return modelSize;
}
/**
* A data class for storing loaded test data
*
* @author speldszus
*
*/
private static class VBTestData {
/**
* The rule to be applied
*/
private Rule rule;
/**
* The resource to which the rule should be applied
*/
private Resource resource;
/**
* The expected outcomes of the rule application
*/
private Collection<TestResult> expect;
private String description;
/**
* Creates a new instance
*
* @param description A short description of the test
* @param rule The rule
* @param resource The model
* @param expect The expected outcome
*/
private VBTestData(String description, Rule rule, Resource resource, Collection<TestResult> expect) {
this.description = description;
this.rule = rule;
this.resource = resource;
this.expect = expect;
}
/**
* Returns a human readable description of the test case
*/
@Override
public String toString() {
return description + ": Apply the rule \"" + rule.getName() + "\" to the model \""
+ resource.getURI().lastSegment() + "\".";
}
}
/**
* The test description as specified in a JSON file
*
* @author speldszus
*
*/
private static class TestDescription {
/**
* The location of the rule which should be applied
*/
String ruleFile;
/**
* A specification of the rule applications
*/
Collection<TestApplication> applications;
}
/**
* The specification of the rule applications
*
* @author speldszus
*
*/
private static class TestApplication {
/**
* The name of the rule that should be applied
*/
String rule;
/**
* The location of the model to which the rule should be applied
*/
String model;
/**
* The expected outcome of the rule application
*/
Collection<TestResult> results;
}
/**
* The specificatio of the outcome of a rule application
*
* @author speldszus
*
*/
private static class TestResult {
/**
* The constraint kind to check
*/
TestResultKind kind;
/**
* The expected value
*/
Object value;
}
/**
*
* The constraint kinds
*
* @author speldszus
*
*/
private static enum TestResultKind {
/**
* The number of expected rule matches
*/
MATCHES,
/**
* The expected model size after the rule application
*/
MODEL_SIZE
}
}