SyncValidationListener: collect errors for JUnit tests
diff --git a/plugins/org.hawk.graph.syncValidationListener/src/org/hawk/graph/syncValidationListener/SyncValidationListener.java b/plugins/org.hawk.graph.syncValidationListener/src/org/hawk/graph/syncValidationListener/SyncValidationListener.java
index 5173d70..254421b 100644
--- a/plugins/org.hawk.graph.syncValidationListener/src/org/hawk/graph/syncValidationListener/SyncValidationListener.java
+++ b/plugins/org.hawk.graph.syncValidationListener/src/org/hawk/graph/syncValidationListener/SyncValidationListener.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011-2016 The University of York.
+ * Copyright (c) 2011-2019 The University of York, Aston University.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
@@ -13,6 +13,7 @@
*
* Contributors:
* Konstantinos Barmpis - initial API and implementation
+ * Antonio Garcia-Dominguez - collect errors in list instead of printing them
******************************************************************************/
package org.hawk.graph.syncValidationListener;
@@ -20,10 +21,12 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -50,8 +53,24 @@
public class SyncValidationListener implements IGraphChangeListener {
+ public class ValidationError {
+ private final String message;
+
+ public ValidationError(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+ }
+
private ModelIndexerImpl hawk;
- private int removedProxies, totalErrors, deleted, malformed, singletonCount, totalGraphSize, totalResourceSizes;
+
+ // stats
+ private List<ValidationError> errors = new ArrayList<>();
+ private int removedProxies, deleted, malformed, singletonCount, totalGraphSize, totalResourceSizes;
+
private Set<String> changed = new HashSet<>();
private IGraphNodeIndex singletonIndex;
@@ -95,8 +114,12 @@
changed.clear();
}
+ public List<ValidationError> getErrors() {
+ return errors;
+ }
+
public int getTotalErrors() {
- return totalErrors;
+ return errors.size();
}
private void validateChanges() throws URISyntaxException {
@@ -113,8 +136,8 @@
System.err.println("validating changes...");
+ errors.clear();
removedProxies = 0;
- totalErrors = 0;
malformed = 0;
singletonCount = 0;
totalResourceSizes = 0;
@@ -130,19 +153,20 @@
}
System.err.println("changed resource size: " + totalResourceSizes);
-
System.err.println("relevant graph size: "
+ totalGraphSize
+ (singletonCount > 0 ? (" + singleton count: " + singletonCount) : ""));
if (totalGraphSize + singletonCount != totalResourceSizes) {
- totalErrors++;
+ errors.add(new ValidationError(
+ String.format("Mismatched resource size: %d + %d != %d", totalGraphSize, singletonCount, totalResourceSizes)
+ ));
}
System.err.println("validated changes... "
- + (totalErrors == 0 ? "true"
- : ((totalErrors == malformed) + " (with "
- + totalErrors + " total and "
+ + (errors.isEmpty() ? "true"
+ : ((errors.size() == malformed) + " (with "
+ + errors.size() + " total and "
+ malformed + " malformed errors)"))
+ (removedProxies == 0 ? "" : " [" + removedProxies
+ "] unresolved hawk proxies matched"));
@@ -174,33 +198,36 @@
file = repository + GraphModelUpdater.FILEINDEX_REPO_SEPARATOR + c.getPath();
filenode = graph.getFileIndex().get("id", file).getSingle();
} catch (Exception ee) {
- System.err.println("expected file " + file
- + " but it did not exist (maybe metamodel not registered, if so expect +1 errors)");
- totalErrors++;
+ errors.add(new ValidationError(
+ String.format(
+ "Expected file %s but it did not exist "
+ + "(maybe metamodel not registered, if so expect +1 errors)",
+ file)
+ ));
return;
}
// cache model elements in current resource
- Map<String, IHawkObject> eobjectCache = new HashMap<>();
+ Map<String, IHawkObject> eObjectCache = new HashMap<>();
// cache of malformed object identifiers (to ignore references)
Set<String> malformedObjectCache = new HashSet<>();
- cacheModelElements(c, r, eobjectCache, malformedObjectCache);
+ cacheModelElements(c, r, eObjectCache, malformedObjectCache);
// go through all nodes in graph from the file the resource is in
for (IGraphEdge instanceEdge : filenode.getIncomingWithType(ModelElementNode.EDGE_LABEL_FILE)) {
final IGraphNode instance = instanceEdge.getStartNode();
totalGraphSize++;
- final IHawkObject eobject = eobjectCache.get(instance.getProperty(IModelIndexer.IDENTIFIER_PROPERTY));
+ final IHawkObject eobject = eObjectCache.get(instance.getProperty(IModelIndexer.IDENTIFIER_PROPERTY));
// if a node cannot be found in the model cache
if (eobject == null) {
- System.err.println("error in validating: graph contains node with identifier:"
- + instance.getProperty(IModelIndexer.IDENTIFIER_PROPERTY) + " but resource does not!");
// this triggers when a malformed model has 2 identical identifiers
- totalErrors++;
+ errors.add(new ValidationError(
+ String.format("Graph contains node with identifier: %s but resource does not!", instance.getProperty(IModelIndexer.IDENTIFIER_PROPERTY))
+ ));
} else {
- eobjectCache.remove(instance.getProperty(IModelIndexer.IDENTIFIER_PROPERTY));
+ eObjectCache.remove(instance.getProperty(IModelIndexer.IDENTIFIER_PROPERTY));
if (!malformedObjectCache.contains(eobject.getUri())) {
compareAttributes(instance, eobject);
@@ -210,10 +237,10 @@
}
// if there are model elements not found in nodes
- if (eobjectCache.size() > 0) {
- System.err.println("error in validating: the following objects were not found in the graph:");
- System.err.println(eobjectCache.keySet());
- totalErrors++;
+ if (eObjectCache.size() > 0) {
+ errors.add(new ValidationError(
+ String.format("The following objects were not found in the graph:\n%s", eObjectCache.keySet())
+ ));
}
t.success();
@@ -279,21 +306,25 @@
filterFragmentBasedReferences(noderefvaluesclone, modelrefvaluesclone);
if (noderefvaluesclone.size() > 0) {
- System.err.println("error in validating: reference " + modelRefName + " of node: "
- + instance.getProperty(IModelIndexer.IDENTIFIER_PROPERTY) + "\nlocated: "
- + instance.getOutgoingWithType(ModelElementNode.EDGE_LABEL_FILE).iterator().next().getEndNode()
- .getProperty(IModelIndexer.IDENTIFIER_PROPERTY));
- System.err.println(noderefvaluesclone);
- System.err.println("the above ids were found in the graph but not the model");
- totalErrors++;
+ final IGraphNode fileNode = instance.getOutgoingWithType(ModelElementNode.EDGE_LABEL_FILE).iterator().next().getEndNode();
+ errors.add(new ValidationError(
+ String.format(
+ "Reference %s of node: %s\n"
+ + "Located: %s\n%s\n"
+ + "The above IDs were found in the graph but not the model",
+ modelRefName, instance.getProperty(IModelIndexer.IDENTIFIER_PROPERTY),
+ fileNode, noderefvaluesclone)
+ ));
}
if (modelrefvaluesclone.size() > 0) {
- System.err.println("error in validating: reference " + modelRefName + " of model element: "
- + eobject.getUriFragment() + "\nlocated: " + eobject.getUri());
- System.err.println(modelrefvaluesclone);
- System.err.println("the above ids were found in the model but not the graph");
- totalErrors++;
+ errors.add(new ValidationError(
+ String.format("Reference %s of model element: %s\n"
+ + "Located: %s\n%s\n"
+ + "The above IDs were found in the model but not the graph",
+ modelRefName, eobject.getUriFragment(),
+ eobject.getUri(), modelrefvaluesclone)
+ ));
}
nodereferences.remove(modelRefName);
@@ -301,10 +332,10 @@
}
if (nodereferences.size() > 0) {
- System.err.println("error in validating: references " + nodereferences.keySet()
- + " had targets in the graph but not in the model: ");
- System.err.println(nodereferences);
- totalErrors++;
+ errors.add(new ValidationError(String.format(
+ "References %s had targets in the graph but not in the model:\n%s",
+ nodereferences.keySet(), nodereferences)
+ ));
}
}
@@ -409,29 +440,28 @@
Object attr = modelAttributes.get(propertykey);
if (!flattenedStringEquals(dbattr, attr)) {
- totalErrors++;
- System.err.println("error in validating, attribute: " + propertykey + " has values:");
- final String cla1 = dbattr != null ? dbattr.getClass().toString() : "null attr";
- System.err.println(String.format("database:\t%s JAVATYPE: %s IN NODE: %s WITH ID: %s",
- (dbattr instanceof Object[] ? (Arrays.asList((Object[]) dbattr)) : dbattr),
- cla1, node.getId(), node.getProperty(IModelIndexer.IDENTIFIER_PROPERTY)));
+ final String dbJavaType = dbattr != null ? dbattr.getClass().toString() : "null attr";
+ final Object dbValue = dbattr instanceof Object[] ? (Arrays.asList((Object[]) dbattr)) : dbattr;
+ final String modelJavaType = attr != null ? attr.getClass().toString() : "null attr";
+ final Object modelValue = attr instanceof Object[] ? (Arrays.asList((Object[]) attr)) : attr;
- String cla2 = attr != null ? attr.getClass().toString() : "null attr";
- System.err.println(String.format("model:\t\t%s JAVATYPE: %s IN ELEMENT WITH ID %s",
- (attr instanceof Object[] ? (Arrays.asList((Object[]) attr)) : attr),
- cla2, modelElement.getUriFragment()));
+ errors.add(new ValidationError(String.format(
+ "Attribute %s has mismatched values:\n"
+ + " * database:\t%s JAVATYPE: %s IN NODE: %s WITH ID: %s\n"
+ + " * model:\t\t%s JAVATYPE: %s IN ELEMENT WITH ID %s",
+ propertykey,
+ dbValue, dbJavaType, node.getId(), node.getProperty(IModelIndexer.IDENTIFIER_PROPERTY),
+ modelValue, modelJavaType, modelElement.getUriFragment()
+ )));
}
modelAttributes.remove(propertykey);
}
}
if (modelAttributes.size() > 0) {
- System.err.println(String.format(
- "error in validating, the following attributes were "
- + "not found in the graph node %s: %s",
- node.getId(), modelAttributes.keySet()));
-
- totalErrors++;
+ errors.add(new ValidationError(String.format(
+ "The following attributes were not found in the graph node %s: %s", node.getId(), modelAttributes.keySet()
+ )));
}
}
diff --git a/tests/org.hawk.integration.tests/src/org/hawk/integration/tests/emf/SubtreeContextTest.java b/tests/org.hawk.integration.tests/src/org/hawk/integration/tests/emf/SubtreeContextTest.java
index 339da71..8314342 100644
--- a/tests/org.hawk.integration.tests/src/org/hawk/integration/tests/emf/SubtreeContextTest.java
+++ b/tests/org.hawk.integration.tests/src/org/hawk/integration/tests/emf/SubtreeContextTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import java.io.File;
@@ -27,6 +28,7 @@
import java.util.List;
import java.util.Map;
import java.util.Random;
+import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
@@ -43,6 +45,7 @@
import org.hawk.core.query.IQueryEngine;
import org.hawk.epsilon.emc.EOLQueryEngine;
import org.hawk.graph.syncValidationListener.SyncValidationListener;
+import org.hawk.graph.syncValidationListener.SyncValidationListener.ValidationError;
import org.hawk.integration.tests.ModelIndexingTest;
import org.junit.Before;
import org.junit.Rule;
@@ -176,15 +179,25 @@
protected void prepareFragmented() throws Exception, Throwable {
requestFolderIndex(folderFragmented);
scheduleAndWait(() -> {
- assertEquals(0, syncValidation.getListener().getTotalErrors());
+ assertNoValidationErrors("There should be no validation errors on the fragmented model");
return null;
});
}
+ private void assertNoValidationErrors(String message) {
+ List<ValidationError> errors = syncValidation.getListener().getErrors();
+ if (!errors.isEmpty()) {
+ List<String> messages = errors.stream()
+ .map(e -> e.getMessage())
+ .collect(Collectors.toList());
+ fail(message + ":\n" + String.join("\n\n", messages));
+ }
+ }
+
protected void prepareOriginal() throws Exception, Throwable {
requestFolderIndex(folderOriginal);
scheduleAndWait(() -> {
- assertEquals(0, syncValidation.getListener().getTotalErrors());
+ assertNoValidationErrors("There should be no validation errors on the original model");
return null;
});
}