From: Mathieu Baudier Date: Sat, 10 Nov 2007 21:43:39 +0000 (+0000) Subject: Introduce diff framework X-Git-Tag: argeo-slc-2.1.7~3182 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=c6ce3b0f4d81b0fb92a3b3b724dff4e496283edb;p=gpl%2Fargeo-slc.git Introduce diff framework git-svn-id: https://svn.argeo.org/slc/trunk@698 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/ant/test/SlcTestTask.java b/org.argeo.slc/src/main/java/org/argeo/slc/ant/test/SlcTestTask.java index 1d5943f58..1d9132003 100644 --- a/org.argeo.slc/src/main/java/org/argeo/slc/ant/test/SlcTestTask.java +++ b/org.argeo.slc/src/main/java/org/argeo/slc/ant/test/SlcTestTask.java @@ -9,6 +9,7 @@ import org.argeo.slc.ant.spring.AbstractSpringArg; import org.argeo.slc.ant.structure.SAwareTask; import org.argeo.slc.core.deploy.DeployedSystem; import org.argeo.slc.core.structure.StructureAware; +import org.argeo.slc.core.test.ExecutableTestRun; import org.argeo.slc.core.test.TestData; import org.argeo.slc.core.test.TestDefinition; import org.argeo.slc.core.test.TestResult; @@ -65,7 +66,7 @@ public class SlcTestTask extends SAwareTask { getPath()); } - testRun.execute(); + ((ExecutableTestRun)testRun).execute(); } /** diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/core/UnsupportedException.java b/org.argeo.slc/src/main/java/org/argeo/slc/core/UnsupportedException.java new file mode 100644 index 000000000..b113b2cb0 --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/core/UnsupportedException.java @@ -0,0 +1,18 @@ +package org.argeo.slc.core; + +public class UnsupportedException extends SlcException { + static final long serialVersionUID = 1l; + + public UnsupportedException(String message) { + super(message); + } + + public UnsupportedException(String nature, Object obj) { + super("Unsupported " + nature + ": " + obj.getClass()); + } + + public UnsupportedException(String nature, String value) { + super("Unsupported " + nature + ": " + value); + } + +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/ExecutableTestRun.java b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/ExecutableTestRun.java new file mode 100644 index 000000000..bd1526412 --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/ExecutableTestRun.java @@ -0,0 +1,9 @@ +package org.argeo.slc.core.test; + +/** A test run that can be executed*/ +public interface ExecutableTestRun extends TestRun{ + + /** Executes this test run. */ + public void execute(); + +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/SimpleResultPart.java b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/SimpleResultPart.java index 19fd2d5c2..6ea2d9dbf 100644 --- a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/SimpleResultPart.java +++ b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/SimpleResultPart.java @@ -16,21 +16,27 @@ package org.argeo.slc.core.test; *

*/ public class SimpleResultPart implements TestResultPart { - - /** The flag for a passed test: 1 */ - public final static int PASSED = 1; - /** The flag for a failed test: 2 */ - public final static int FAILED = 2; - /** The flag for a test which could not properly run because of an error: 3 */ - public final static int ERROR = 3; - /** For ORM */ private Long tid; - private Integer status; + private TestStatus status; private String message; private Throwable exception; + + /** Empty constructor for ORM */ + public SimpleResultPart(){ + + } + + public SimpleResultPart(TestStatus status, String message, + Throwable exception) { + super(); + this.status = status; + this.message = message; + this.exception = exception; + } + public String getMessage() { return message; } @@ -39,11 +45,11 @@ public class SimpleResultPart implements TestResultPart { this.message = message; } - public void setStatus(Integer status) { + public void setStatus(TestStatus status) { this.status = status; } - public Integer getStatus() { + public TestStatus getStatus() { return status; } @@ -58,13 +64,7 @@ public class SimpleResultPart implements TestResultPart { @Override public String toString() { StringBuffer buf = new StringBuffer(""); - if (status == PASSED) { - buf.append("PASSED "); - } else if (status == FAILED) { - buf.append("FAILED "); - } else if (status == ERROR) { - buf.append("ERROR "); - } + buf.append(status).append(" "); buf.append(message); if (exception != null) { buf.append("(").append(exception.getMessage()).append(")"); diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/SimpleTestResult.java b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/SimpleTestResult.java new file mode 100644 index 000000000..88bf8b394 --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/SimpleTestResult.java @@ -0,0 +1,37 @@ +package org.argeo.slc.core.test; + +import java.util.List; +import java.util.Vector; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class SimpleTestResult implements TestResult { + private static Log log = LogFactory.getLog(SimpleTestResult.class); + + private TestResultId testResultId; + private List parts = new Vector(); + + public void addResultPart(TestResultPart part) { + parts.add(part); + if (log.isDebugEnabled()) + log.debug(part); + } + + public void close() { + parts.clear(); + } + + public TestResultId getTestResultId() { + return testResultId; + } + + public void setTestResultId(TestResultId testResultId) { + this.testResultId = testResultId; + } + + public List getParts() { + return parts; + } + +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/SimpleTestRun.java b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/SimpleTestRun.java index 9cb14ac04..c548f9d59 100644 --- a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/SimpleTestRun.java +++ b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/SimpleTestRun.java @@ -6,7 +6,7 @@ import org.argeo.slc.core.deploy.DeployedSystem; * A basic bean implementation of a WritableTestRun, holding * references to the various parts of a test run. */ -public class SimpleTestRun implements WritableTestRun { +public class SimpleTestRun implements WritableTestRun, ExecutableTestRun { private DeployedSystem deployedSystem; private TestData testData; private TestDefinition testDefinition; diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/TestResultPart.java b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/TestResultPart.java index ecac72f72..844f6c419 100644 --- a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/TestResultPart.java +++ b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/TestResultPart.java @@ -6,5 +6,7 @@ package org.argeo.slc.core.test; * @see TestResult */ public interface TestResultPart { + public TestStatus getStatus(); + public String getMessage(); } diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/TestRun.java b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/TestRun.java index 270785eb3..073acb5b2 100644 --- a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/TestRun.java +++ b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/TestRun.java @@ -15,7 +15,4 @@ public interface TestRun { /** Gets the related result where to record results. */ public TestResult getTestResult(); - - /** Executes this test run. */ - public void execute(); } diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/TestStatus.java b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/TestStatus.java new file mode 100644 index 000000000..bda5a74ab --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/TestStatus.java @@ -0,0 +1,14 @@ +package org.argeo.slc.core.test; + +/** Simple statuses. */ +public enum TestStatus { + /** Test passed */ + PASSED, + /** Test failed: the behavior was not the expected one */ + FAILED, + /** + * Test could not run properly because of an unexpected issue: their can be + * no feedback on the behavior of the tested component + */ + ERROR +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/tree/FullHtmlTreeReport.java b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/tree/FullHtmlTreeReport.java index fea9765da..7aec7c1b3 100644 --- a/org.argeo.slc/src/main/java/org/argeo/slc/core/test/tree/FullHtmlTreeReport.java +++ b/org.argeo.slc/src/main/java/org/argeo/slc/core/test/tree/FullHtmlTreeReport.java @@ -11,6 +11,7 @@ import org.argeo.slc.core.structure.StructurePath; import org.argeo.slc.core.structure.StructureRegistry; import org.argeo.slc.core.structure.tree.TreeSPath; import org.argeo.slc.core.test.SimpleResultPart; +import org.argeo.slc.core.test.TestStatus; import org.argeo.slc.core.test.TestReport; import org.argeo.slc.core.test.TestResult; import org.argeo.slc.core.test.TestResultPart; @@ -108,7 +109,7 @@ public class FullHtmlTreeReport implements TestReport, StructureAware { for (TestResultPart part : subList.getParts()) { SimpleResultPart sPart = (SimpleResultPart) part; String color = "yellow"; - if (sPart.getStatus() == SimpleResultPart.PASSED) { + if (sPart.getStatus() == TestStatus.PASSED) { color = "green"; } else { color = "red"; diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/DataInterpreter.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DataInterpreter.java new file mode 100644 index 000000000..7a57b0552 --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DataInterpreter.java @@ -0,0 +1,20 @@ +package org.argeo.slc.diff; + +/** + * Converts data into a format better adapted for comparison. It is typically + * used to convert String into typed format such as + * BigDecimal + */ +public interface DataInterpreter { + /** + * Converts data + * + * @param key + * any object used to differentiate the type of data (e.g. + * column, path) + * @param value + * the data to convert + * @return the converted object + */ + public Object convert(Object key, Object value); +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/Diff.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/Diff.java new file mode 100644 index 000000000..0b28996e9 --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/Diff.java @@ -0,0 +1,9 @@ +package org.argeo.slc.diff; + +import org.springframework.core.io.Resource; + +/** A comparator providing structured information about the differences found. */ +public interface Diff { + /** Performs the comparison. */ + public DiffResult compare(Resource expected, Resource reached); +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffIssue.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffIssue.java new file mode 100644 index 000000000..14d3fe02e --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffIssue.java @@ -0,0 +1,20 @@ +package org.argeo.slc.diff; + +/** The root class for issues which happened during a diff. */ +public abstract class DiffIssue implements Comparable { + protected final DiffPosition position; + + public DiffIssue(DiffPosition position) { + super(); + this.position = position; + } + + public int compareTo(DiffIssue o) { + return position.compareTo(o.position); + } + + /** The position of this issue within the test file */ + public DiffPosition getPosition() { + return position; + } +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffKey.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffKey.java new file mode 100644 index 000000000..9d34f720a --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffKey.java @@ -0,0 +1,9 @@ +package org.argeo.slc.diff; + +/** + * Object able to uniquely identify an atomic diff part. Used to identify + * missings and left-overs. + */ +public interface DiffKey { + public String toString(); +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffMissing.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffMissing.java new file mode 100644 index 000000000..1b3e01fa4 --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffMissing.java @@ -0,0 +1,33 @@ +package org.argeo.slc.diff; + + +/** + * A value missing in one of the file. If its position is related to expected, + * this means it is a left over in the reached, if its position is related to + * the reached it means that it is missing from the reached. If the value is + * null it means that the entire line is missing. + */ +public class DiffMissing extends DiffIssue { + private final DiffKey key; + + public DiffMissing(DiffPosition position, DiffKey key) { + super(position); + this.key = key; + } + + public Object getKey() { + return key; + } + + @Override + public String toString() { + if (position.relatedFile == RelatedFile.EXPECTED) { + return position + ": left over " + key; + } + else if (position.relatedFile == RelatedFile.REACHED) { + return position + ": missing " + key; + } + return super.toString(); + } + +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffNotMatched.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffNotMatched.java new file mode 100644 index 000000000..04f7acff1 --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffNotMatched.java @@ -0,0 +1,27 @@ +package org.argeo.slc.diff; + +/** Diff issue where reached and expected values are different. */ +public class DiffNotMatched extends DiffIssue { + private final Object expected; + private final Object reached; + + public DiffNotMatched(DiffPosition position, Object expected, Object reached) { + super(position); + this.expected = expected; + this.reached = reached; + } + + public Object getExpected() { + return expected; + } + + public Object getReached() { + return reached; + } + + @Override + public String toString() { + return position + ": not matched " + expected + " <> " + reached; + } + +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffPosition.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffPosition.java new file mode 100644 index 000000000..302c0afa9 --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffPosition.java @@ -0,0 +1,16 @@ +package org.argeo.slc.diff; + +/** The position of a diff issue within the test resource. */ +public abstract class DiffPosition implements Comparable { + protected RelatedFile relatedFile; + + public DiffPosition(RelatedFile relatedFile) { + super(); + this.relatedFile = relatedFile; + } + + public RelatedFile getRelatedFile() { + return relatedFile; + } + +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffResult.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffResult.java new file mode 100644 index 000000000..0e29fdd05 --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/DiffResult.java @@ -0,0 +1,12 @@ +package org.argeo.slc.diff; + +import java.util.List; + +/** + * The result of a diff. Can be subclassed to provided more structured + * information. + */ +public interface DiffResult { + /** The list of issues, a zero size meaning that the diff succeeded. */ + public List getIssues(); +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/RelatedFile.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/RelatedFile.java new file mode 100644 index 000000000..757968355 --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/RelatedFile.java @@ -0,0 +1,9 @@ +package org.argeo.slc.diff; + +/** Enumeration of the types of resource tested. */ +public enum RelatedFile { + /** The expected resource */ + EXPECTED, + /** The reached resource */ + REACHED +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/SimpleDiffResult.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/SimpleDiffResult.java new file mode 100644 index 000000000..accd71834 --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/SimpleDiffResult.java @@ -0,0 +1,14 @@ +package org.argeo.slc.diff; + +import java.util.List; +import java.util.Vector; + +/** A basic implementation of DiffResult.*/ +public class SimpleDiffResult implements DiffResult { + private List issues = new Vector(); + + public List getIssues() { + return issues; + } + +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/TableDiffPosition.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/TableDiffPosition.java new file mode 100644 index 000000000..d8a3d785d --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/TableDiffPosition.java @@ -0,0 +1,66 @@ +package org.argeo.slc.diff; + +import org.argeo.slc.core.UnsupportedException; + +/** + * A diff position within a table structure such a CSV file or an SQL result + * set. + */ +public class TableDiffPosition extends DiffPosition { + private Integer line; + /** Can be null */ + private Integer column; + /** Can be null */ + private String columnName; + + public TableDiffPosition(RelatedFile relatedFile, Integer line, + Integer column, String columnName) { + super(relatedFile); + this.line = line; + this.column = column; + this.columnName = columnName; + } + + public Integer getLine() { + return line; + } + + public Integer getColumn() { + return column; + } + + public String getColumnName() { + return columnName; + } + + public int compareTo(DiffPosition dp) { + if (!(dp instanceof TableDiffPosition)) + throw new UnsupportedException("position", dp); + + TableDiffPosition o = (TableDiffPosition) dp; + if (relatedFile.equals(o.relatedFile)) { + if (line == o.line) { + return column.compareTo(o.column); + } else { + return line.compareTo(o.line); + } + } else { + return relatedFile.compareTo(o.relatedFile); + } + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(""); + buf.append(relatedFile).append('[').append(line); + if (column != null) { + buf.append(',').append(column); + if (columnName != null) { + buf.append('-').append(columnName); + } + } + buf.append(']'); + return buf.toString(); + } + +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/Tolerance.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/Tolerance.java new file mode 100644 index 000000000..b44a7123a --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/Tolerance.java @@ -0,0 +1,18 @@ +package org.argeo.slc.diff; + +/** Compares objects, eventually using tolerance mechanisms. */ +public interface Tolerance { + /** + * Compares objects + * + * @param key + * any object used to differentiate the type of data (e.g. + * column, path) + * @param expected + * the expected value + * @param reached + * the reached value + * @return the converted object + */ + public Boolean compare(Object key, Object expected, Object reached); +} diff --git a/org.argeo.slc/src/main/java/org/argeo/slc/diff/XPathDiffPosition.java b/org.argeo.slc/src/main/java/org/argeo/slc/diff/XPathDiffPosition.java new file mode 100644 index 000000000..9069011cd --- /dev/null +++ b/org.argeo.slc/src/main/java/org/argeo/slc/diff/XPathDiffPosition.java @@ -0,0 +1,27 @@ +package org.argeo.slc.diff; + +import org.argeo.slc.core.UnsupportedException; + +/** A diff position within an Xml file. NOT YET IMPLEMENTED.*/ +public class XPathDiffPosition extends DiffPosition { + + private String xPath; + + public XPathDiffPosition(RelatedFile relatedFile, String path) { + super(relatedFile); + xPath = path; + } + + public int compareTo(DiffPosition dp) { + if (!(dp instanceof XPathDiffPosition)) + throw new UnsupportedException("position", dp); + + XPathDiffPosition o = (XPathDiffPosition) dp; + if (relatedFile.equals(o.relatedFile)) { + return xPath.compareTo(o.xPath); + } else { + return relatedFile.compareTo(o.relatedFile); + } + } + +} diff --git a/org.argeo.slc/src/test/java/org/argeo/slc/example/ExampleTask.java b/org.argeo.slc/src/test/java/org/argeo/slc/example/ExampleTask.java index c709f259c..52e06bd59 100644 --- a/org.argeo.slc/src/test/java/org/argeo/slc/example/ExampleTask.java +++ b/org.argeo.slc/src/test/java/org/argeo/slc/example/ExampleTask.java @@ -6,6 +6,7 @@ import org.argeo.slc.core.structure.StructurePath; import org.argeo.slc.core.structure.StructureRegistry; import org.argeo.slc.core.structure.tree.TreeSPath; import org.argeo.slc.core.test.SimpleResultPart; +import org.argeo.slc.core.test.TestStatus; import org.argeo.slc.core.test.TestDefinition; import org.argeo.slc.core.test.TestResult; import org.argeo.slc.core.test.TestRun; @@ -17,7 +18,7 @@ public class ExampleTask implements StructureAware, TestDefinition, public void execute(TestRun testRun) { SimpleResultPart part = new SimpleResultPart(); - part.setStatus(SimpleResultPart.PASSED); + part.setStatus(TestStatus.PASSED); part.setMessage("Sub task with path " + path + " executed"); TestResult result = testRun.getTestResult(); diff --git a/org.argeo.slc/src/test/java/org/argeo/slc/example/SimpleExampleTestDef.java b/org.argeo.slc/src/test/java/org/argeo/slc/example/SimpleExampleTestDef.java index 6dcbb6e93..505623c7f 100644 --- a/org.argeo.slc/src/test/java/org/argeo/slc/example/SimpleExampleTestDef.java +++ b/org.argeo.slc/src/test/java/org/argeo/slc/example/SimpleExampleTestDef.java @@ -9,6 +9,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.slc.core.test.SimpleResultPart; +import org.argeo.slc.core.test.TestStatus; import org.argeo.slc.core.test.TestDefinition; import org.argeo.slc.core.test.TestResult; import org.argeo.slc.core.test.TestResultPart; @@ -36,13 +37,13 @@ public class SimpleExampleTestDef implements TestDefinition { // execute appli.filter(args); - executePart.setStatus(SimpleResultPart.PASSED); + executePart.setStatus(TestStatus.PASSED); } catch (Exception e) { - executePart.setStatus(SimpleResultPart.ERROR); + executePart.setStatus(TestStatus.ERROR); executePart.setException(e); } result.addResultPart(executePart); - if (executePart.getStatus() == SimpleResultPart.ERROR) { + if (executePart.getStatus() == TestStatus.ERROR) { return; } @@ -56,10 +57,10 @@ public class SimpleExampleTestDef implements TestDefinition { try { String expected = loadFile(data.getExpectedFile()); String reached = loadFile(data.getReachedFile()); - part.setStatus(expected.equals(reached) ? SimpleResultPart.PASSED - : SimpleResultPart.FAILED); + part.setStatus(expected.equals(reached) ? TestStatus.PASSED + : TestStatus.FAILED); } catch (Exception e) { - part.setStatus(SimpleResultPart.ERROR); + part.setStatus(TestStatus.ERROR); part.setException(e); log.error("Error while asserting files", e); }