Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

merge in master

  • Loading branch information...
commit f51e19b36c2a13d3d11fe9744381a0c910d61c6f 2 parents a171a4a + c4c3277
Ian Moore authored
Showing with 1,562 additions and 1,332 deletions.
  1. +11 −0 Developer_Notes.md
  2. +697 −707 src/main/java/com/technophobia/substeps/execution/ExecutionNode.java
  3. +3 −1 src/main/java/com/technophobia/substeps/model/Configuration.java
  4. +0 −26 src/main/java/com/technophobia/substeps/model/DuplicatePatternException.java
  5. +14 −3 src/main/java/com/technophobia/substeps/model/PatternMap.java
  6. +24 −22 src/main/java/com/technophobia/substeps/model/Syntax.java
  7. +29 −0 src/main/java/com/technophobia/substeps/model/exception/DuplicatePatternException.java
  8. +22 −0 src/main/java/com/technophobia/substeps/model/exception/DuplicateStepImplementationException.java
  9. +27 −0 src/main/java/com/technophobia/substeps/model/exception/StepImplementationException.java
  10. +22 −5 ...ia/substeps/model/{SubStepConfigurationException.java → exception/SubstepsConfigurationException.java}
  11. +23 −0 src/main/java/com/technophobia/substeps/model/exception/SubstepsException.java
  12. +39 −0 src/main/java/com/technophobia/substeps/model/exception/SubstepsParsingException.java
  13. +11 −0 src/main/java/com/technophobia/substeps/model/exception/SubstepsRuntimeException.java
  14. +12 −0 src/main/java/com/technophobia/substeps/model/exception/UnimplementedStepException.java
  15. +49 −57 src/main/java/com/technophobia/substeps/runner/BuildFailureManager.java
  16. +2 −2 src/main/java/com/technophobia/substeps/runner/ExecutionConfig.java
  17. +204 −192 src/main/java/com/technophobia/substeps/runner/ExecutionNodeRunner.java
  18. +6 −6 src/main/java/com/technophobia/substeps/runner/ExecutionNodeTreeBuilder.java
  19. +2 −0  src/main/java/com/technophobia/substeps/runner/syntax/ClassAnalyser.java
  20. +8 −13 src/main/java/com/technophobia/substeps/runner/syntax/DefaultSyntaxErrorReporter.java
  21. +43 −73 src/main/java/com/technophobia/substeps/runner/syntax/SubStepDefinitionParser.java
  22. +5 −3 src/main/java/com/technophobia/substeps/runner/syntax/SyntaxBuilder.java
  23. +5 −6 src/main/java/com/technophobia/substeps/runner/syntax/SyntaxErrorReporter.java
  24. +3 −13 src/main/java/com/technophobia/substeps/runner/syntax/validation/SyntaxAwareStepValidator.java
  25. +34 −0 src/test/java/com/technophobia/substeps/model/exception/DuplicateStepImplementationExceptionTest.java
  26. +2 −2 src/test/java/com/technophobia/substeps/runner/ExecutionConfigTest.java
  27. +63 −83 src/test/java/com/technophobia/substeps/runner/ExecutionNodeRunnerTest.java
  28. +3 −3 src/test/java/com/technophobia/substeps/runner/JunitFeatureRunnerTest.java
  29. +99 −0 src/test/java/com/technophobia/substeps/runner/syntax/SubStepDefinitionParserTest.java
  30. +0 −43 src/test/java/com/technophobia/substeps/runner/syntax/SubstepDefinitionParserTest.java
  31. +3 −6 src/test/java/com/technophobia/substeps/runner/syntax/SyntaxBuilderTest.java
  32. +16 −24 src/test/java/com/technophobia/substeps/runner/syntax/validation/SyntaxAwareStepValidatorTest.java
  33. +64 −42 src/test/java/com/technophobia/substeps/runner/syntax/validation/fake/FakeSyntaxErrorReporter.java
  34. +10 −0 src/test/resources/com/technophobia/substeps/runner/syntax/duplicate-definition.substeps
  35. +7 −0 src/test/resources/com/technophobia/substeps/runner/syntax/empty-definition.substeps
View
11 Developer_Notes.md
@@ -0,0 +1,11 @@
+substeps-core Developer Notes
+=============
+
+Please use the susbteps formatter which can be found in the root Substeps project.
+
+In addition, please use the following save actions:
+- format the source code
+- organise imports
+- Always use 'this'
+- Add final
+- Convert control statements bodies to block
View
1,404 src/main/java/com/technophobia/substeps/execution/ExecutionNode.java
@@ -38,714 +38,704 @@
*/
public class ExecutionNode {
- private static AtomicLong counter = new AtomicLong(1);
-
- private final long id; // for uniqueness
-
- private Feature feature = null;
-
- /**
- * An {@link ExecutionNode} can be seen as compiled substeps code - ready to
- * run. We include the fileUri and line number to tie this back to the
- * substeps source - the files from which the compiled substeps were
- * generated. This information could be seen as debug information - however
- * it is useful in other places - for example in editor plugins where we
- * have the compiled code but need to show where it came from to the user.
- * Note that these values won't always exist, for example the root node.
- *
- */
- private String fileUri;
- private int lineNumber;
-
- private String scenarioName = null;
-
- private int depth = 0;
- private int rowNumber = -1;
-
- private List<ExecutionNode> children = null;
- private List<ExecutionNode> backgrounds = null;
-
- private ExecutionNode parent = null;
-
- private Class<?> targetClass = null;
- private Method targetMethod = null;
- private Object[] methodArgs = null;
-
- private String line;
- private boolean background = false;
-
- private boolean outline = false;
-
- private final ExecutionNodeResult result = new ExecutionNodeResult();
-
- private Set<String> tags; // used for analysis
-
- public ExecutionNode() {
- id = counter.getAndIncrement();
- }
-
- public void addChild(final ExecutionNode child) {
- child.setParent(this);
- child.setDepth(depth + 1);
-
- if (children == null) {
- children = new ArrayList<ExecutionNode>();
- }
- children.add(child);
- }
-
- public void addBackground(final ExecutionNode backgroundNode) {
- if (backgrounds == null) {
- backgrounds = new ArrayList<ExecutionNode>();
- }
- backgroundNode.background = true;
- backgrounds.add(backgroundNode);
-
- backgroundNode.setDepth(depth + 1);
-
- }
-
- /**
- * @return the feature
- */
- public Feature getFeature() {
- return feature;
- }
-
- /**
- * @param feature
- * the feature to set
- */
- public void setFeature(final Feature feature) {
- this.feature = feature;
- }
-
- /**
- * @return the depth
- */
- public int getDepth() {
- return depth;
- }
-
- /**
- * @param depth
- * the depth to set
- */
- public void setDepth(final int depth) {
- this.depth = depth;
- }
-
- /**
- * @return the rowNumber
- */
- public int getRowNumber() {
- return rowNumber;
- }
-
- /**
- * @param rowNumber
- * the rowNumber to set
- */
- public void setRowNumber(final int rowNumber) {
- this.rowNumber = rowNumber;
- }
-
- /**
- * @return the parent
- */
- public ExecutionNode getParent() {
- return parent;
- }
-
- /**
- * @param parent
- * the parent to set
- */
- public void setParent(final ExecutionNode parent) {
-
- if (parent.getId() == getId()) {
- throw new IllegalStateException("don't think so");
- }
-
- this.parent = parent;
- }
-
- /**
- * @return the id
- */
- public long getId() {
- return id;
- }
-
- /**
- * @return the targetClass
- */
- public Class<?> getTargetClass() {
- return targetClass;
- }
-
- /**
- * @param targetClass
- * the targetClass to set
- */
- public void setTargetClass(final Class<?> targetClass) {
- this.targetClass = targetClass;
- }
-
- /**
- * @return the methodArgs
- */
- public Object[] getMethodArgs() {
- return methodArgs;
- }
-
- /**
- * @param methodArgs
- * the methodArgs to set
- */
- public void setMethodArgs(final Object[] methodArgs) {
- this.methodArgs = methodArgs;
- }
-
- /**
- * @return the scenarioName
- */
- public String getScenarioName() {
- return scenarioName;
- }
-
- /**
- * @param scenarioName
- * the scenarioName to set
- */
- public void setScenarioName(final String scenarioName) {
- this.scenarioName = scenarioName;
- }
-
- /**
- * @return the line
- */
- public String getLine() {
- return line;
- }
-
- /**
- * @param line
- * the line to set
- */
- public void setLine(final String line) {
- this.line = line;
- }
-
- /**
- * @return the targetMethod
- */
- public Method getTargetMethod() {
- return targetMethod;
- }
-
- /**
- * @param targetMethod
- * the targetMethod to set
- */
- public void setTargetMethod(final Method targetMethod) {
- this.targetMethod = targetMethod;
- }
-
- /**
- * @return the children
- */
- public List<ExecutionNode> getChildren() {
- return children;
- }
-
- /**
- * @return the backgrounds
- */
- public List<ExecutionNode> getBackgrounds() {
- return backgrounds;
- }
-
- public String printTree() {
- // traverse the tree
- final StringBuilder buf = new StringBuilder();
-
- buf.append("Execution tree:\n");
-
- buf.append(toDebugString()).append("\n");
-
- if (children != null) {
- for (final ExecutionNode child : children) {
- buf.append(child.toDebugString());
- }
- }
-
- return buf.toString();
- }
-
- @Override
- public String toString() {
- return id + ":" + getDescription() + " children size: "
- + getChildrenSize();
- }
-
- public String treeToString() {
- final StringBuilder buf = new StringBuilder();
-
- buf.append(toString());
- buf.append("\n");
- if (hasChildren()) {
-
- for (final ExecutionNode child : getChildren()) {
-
- buf.append(Strings.repeat("\t", depth));
- buf.append(child.treeToString());
- buf.append("\n");
- }
- }
-
- return buf.toString();
- }
-
- public String getDebugStringForThisNode() {
- final StringBuilder buf = new StringBuilder();
-
- buf.append(id);
-
- if (parent != null) {
- buf.append(Strings.repeat("\t", depth));
-
- if (feature != null) {
- buf.append(feature.getName()).append(" in ")
- .append(feature.getFilename()).append("\n");
- } else if (scenarioName != null) {
- buf.append(scenarioName).append("\n");
- }
-
- if (rowNumber > -1) {
- buf.append("outline #: ").append(rowNumber).append("\n");
- }
-
- if (background) {
- buf.append("BACKGROUND\n");
- }
-
- if (line != null) {
- buf.append(line);
- }
-
- appendMethodInfo(" - ", buf);
-
- } else {
- buf.append(": Root");
- }
- return buf.toString();
- }
-
- /**
- * @return
- */
- public String toDebugString() {
- final StringBuilder buf = new StringBuilder();
-
- if (parent != null) {
-
- buf.append(id).append(Strings.repeat("\t", depth)).append("file: ")
- .append(getFilename()).append(" ").append(parent.getId())
- .append(" ");
-
- if (feature != null) {
- buf.append(feature.getName()).append("\n");
- } else if (scenarioName != null) {
- buf.append(scenarioName).append("\n");
- }
-
- if (rowNumber > -1) {
- buf.append(" outline #: ").append(rowNumber).append("\n");
- }
-
- if (background) {
- buf.append("BACKGROUND\n");
- }
-
- if (backgrounds != null) {
- for (final ExecutionNode backgroundNode : backgrounds) {
- buf.append(backgroundNode.toDebugString());
- }
- }
-
- boolean printedLine = false;
- if (line != null) {
- buf.append(line);
- printedLine = true;
- }
-
- appendMethodInfo(" - ", buf);
-
- if (printedLine) {
- buf.append("\n");
- }
- }
-
- if (children != null) {
- for (final ExecutionNode child : children) {
- buf.append(child.toDebugString());
- }
- }
-
- // else we're root
- return buf.toString();
- }
-
- public void appendMethodInfo(final StringBuilder buf) {
- appendMethodInfo(null, buf);
- }
-
- /**
- * @param buf
- */
- public void appendMethodInfo(final String prefix, final StringBuilder buf) {
- if (targetClass != null && targetMethod != null) {
-
- if (prefix != null) {
- buf.append(prefix);
- }
-
- buf.append(targetClass.getSimpleName()).append(".")
- .append(targetMethod.getName()).append("(");
-
- if (methodArgs != null) {
- boolean commaRequired = false;
- for (final Object arg : methodArgs) {
- if (commaRequired) {
- buf.append(", ");
- }
-
- boolean quotes = false;
- if (arg instanceof String) {
- quotes = true;
- buf.append("\"");
- }
- buf.append(arg.toString());
- if (quotes) {
- buf.append("\"");
- }
- commaRequired = true;
- }
- }
-
- buf.append(")");
- }
- }
-
- /**
- * @return
- */
- public boolean isOutlineScenario() {
- return outline;
- }
-
- /**
- * @param b
- */
- public void setOutline(final boolean isOutline) {
- outline = isOutline;
-
- }
-
- /**
- * @return
- */
- public boolean hasBackground() {
- return backgrounds != null && !backgrounds.isEmpty();
- }
-
- /**
- * @return
- */
- public boolean isExecutable() {
- return getTargetClass() != null && getTargetMethod() != null;
- }
-
- /**
- * @return
- */
- public boolean hasChildren() {
- return children != null && !children.isEmpty();
- }
-
- /**
- * @return
- */
- public Long getLongId() {
- return Long.valueOf(id);
- }
-
- /**
- * @return
- */
- public boolean shouldHaveChildren() {
- return isFeature() || isScenario() || isOutlineScenario();
- }
-
- /**
- * @return
- */
- public boolean isScenario() {
- return scenarioName != null;
- }
-
- /**
- * @return
- */
- public boolean isFeature() {
- return feature != null;
- }
-
- /**
- * @return the result
- */
- public ExecutionNodeResult getResult() {
- return result;
- }
-
- /**
- * @return the tags
- */
- public Set<String> getTags() {
- return tags;
- }
-
- /**
- * @param tags
- * the tags to set
- */
- public void setTags(final Set<String> tags) {
- this.tags = tags;
- }
-
- /**
+ private static AtomicLong counter = new AtomicLong(1);
+
+ private final long id; // for uniqueness
+
+ private Feature feature = null;
+
+ /**
+ * An {@link ExecutionNode} can be seen as compiled substeps code - ready to
+ * run. We include the fileUri and line number to tie this back to the
+ * substeps source - the files from which the compiled substeps were
+ * generated. This information could be seen as debug information - however
+ * it is useful in other places - for example in editor plugins where we
+ * have the compiled code but need to show where it came from to the user.
+ * Note that these values won't always exist, for example the root node.
*
*/
- public boolean isStep() {
-
- return depth == 3 && !isOutlineScenario() || depth == 4
- && parent.isOutlineScenario();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#hashCode()
- */
- @Override
- public int hashCode() {
- final int prime = 31;
- int res = 1;
- res = prime * res + (int) (id ^ (id >>> 32));
- return res;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#equals(java.lang.Object)
- */
- @Override
- public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final ExecutionNode other = (ExecutionNode) obj;
- if (id != other.id) {
- return false;
- }
- return true;
- }
-
- /**
- * @return the filename
- */
- public String getFilename() {
- return new File(getFileUri()).getName();
- }
-
- public String getFileUri() {
- // Use this filename if specified, or go up through the tree till we get
- // an answer
- if (fileUri != null) {
- return fileUri;
- } else if (parent != null) {
- fileUri = parent.getFileUri();
- return fileUri;
- } else {
- fileUri = "";
- }
-
- return fileUri;
- }
-
- public void setFileUri(final String fileUri) {
- this.fileUri = fileUri;
- }
-
- public int getLineNumber() {
- return lineNumber;
- }
-
- public void setLineNumber(final int lineNumber) {
- this.lineNumber = lineNumber;
- }
-
- public String getType() {
-
- String rtn = null;
- if (parent == null) {
- rtn = "Root node";
- } else if (isFeature()) {
- rtn = "Feature";
- } else if (isScenario()) {
- rtn = "Scenario";
- } else if (isOutlineScenario()) {
- rtn = "Scenario Outline";
- } else if (isStep()) {
- rtn = "Step";
- } else if (targetMethod != null) {
- rtn = "Step Implementation";
- }
- return rtn;
-
- }
-
- public String getDescription() {
-
- // return a string that represents what this is
- String rtn = null;
-
- if (line != null) {
- rtn = line;
- } else {
- if (isFeature()) {
-
- rtn = feature.getName();
- } else if (isScenario()) {
-
- rtn = scenarioName;
- } else if (parent != null && parent.isOutlineScenario()) {
- rtn = parent.scenarioName + " [" + rowNumber + "]";
- }
- }
- return rtn;
- }
-
- public boolean hasError() {
- return result.getResult() == ExecutionResult.FAILED
- || result.getResult() == ExecutionResult.PARSE_FAILURE;
- }
-
- public boolean hasPassed() {
- return result.getResult() == ExecutionResult.PASSED;
- }
-
- public Set<String> getTagsFromHierarchy() {
- Set<String> allTags = null;
-
- ExecutionNode node = this;
-
- while (node != null) {
-
- if (node.tags != null) {
-
- if (allTags == null) {
- allTags = new HashSet<String>();
- }
- allTags.addAll(node.tags);
- }
-
- node = node.parent;
- }
-
- return allTags;
- }
-
- public List<ExecutionNode> getFailedChildNodes() {
-
- // TODO - how should we handle background or setup and tear down
- // failures ?
-
- final List<ExecutionNode> failed = filterNodes(children,
- new Predicate<ExecutionNode>() {
-
- public boolean apply(final ExecutionNode input) {
-
- return input.hasFailed();
- }
- });
-
- return failed;
- }
-
- private List<ExecutionNode> filterNodes(
- final List<ExecutionNode> sourceList,
- final Predicate<ExecutionNode> predicate) {
-
- List<ExecutionNode> filtered = null;
- if (sourceList != null) {
-
- for (final ExecutionNode node : sourceList) {
-
- if (predicate.apply(node)) {
- if (filtered == null) {
- filtered = new ArrayList<ExecutionNode>();
- }
- filtered.add(node);
- }
- }
- }
- return filtered;
- }
-
- /**
- * @return
- */
- private boolean hasFailed() {
-
- // this node has failed if any of this node's backgrounds have failed,
- // this node's state is failed, or any of this node's children's state
- // has failed
- // TODO include backgrounds
- return result.getResult() == ExecutionResult.FAILED
- || getFailedChildNodes() != null;
- }
-
- /**
- * @param i
- */
- public ExecutionNode getChild(final int i) {
- ExecutionNode rtn = null;
- if (children != null && children.size() > i) {
- rtn = children.get(i);
- }
- return rtn;
- }
-
- /**
- * @return
- */
- public int getChildrenSize() {
- int rtn = 0;
-
- if (children != null) {
- rtn = children.size();
- }
-
- return rtn;
- }
+ private String fileUri;
+ private int lineNumber;
+
+ private String scenarioName = null;
+
+ private int depth = 0;
+ private int rowNumber = -1;
+
+ private List<ExecutionNode> children = null;
+ private List<ExecutionNode> backgrounds = null;
+
+ private ExecutionNode parent = null;
+
+ private Class<?> targetClass = null;
+ private Method targetMethod = null;
+ private Object[] methodArgs = null;
+
+ private String line;
+ private boolean background = false;
+
+ private boolean outline = false;
+
+ private final ExecutionNodeResult result = new ExecutionNodeResult();
+
+ private Set<String> tags; // used for analysis
+
+ public ExecutionNode() {
+ id = counter.getAndIncrement();
+ }
+
+ public void addChild(final ExecutionNode child) {
+ child.setParent(this);
+ child.setDepth(depth + 1);
+
+ if (children == null) {
+ children = new ArrayList<ExecutionNode>();
+ }
+ children.add(child);
+ }
+
+ public void addBackground(final ExecutionNode backgroundNode) {
+ if (backgrounds == null) {
+ backgrounds = new ArrayList<ExecutionNode>();
+ }
+ backgroundNode.background = true;
+ backgrounds.add(backgroundNode);
+
+ backgroundNode.setDepth(depth + 1);
+
+ }
+
+ /**
+ * @return the feature
+ */
+ public Feature getFeature() {
+ return feature;
+ }
+
+ /**
+ * @param feature
+ * the feature to set
+ */
+ public void setFeature(final Feature feature) {
+ this.feature = feature;
+ }
+
+ /**
+ * @return the depth
+ */
+ public int getDepth() {
+ return depth;
+ }
+
+ /**
+ * @param depth
+ * the depth to set
+ */
+ public void setDepth(final int depth) {
+ this.depth = depth;
+ }
+
+ /**
+ * @return the rowNumber
+ */
+ public int getRowNumber() {
+ return rowNumber;
+ }
+
+ /**
+ * @param rowNumber
+ * the rowNumber to set
+ */
+ public void setRowNumber(final int rowNumber) {
+ this.rowNumber = rowNumber;
+ }
+
+ /**
+ * @return the parent
+ */
+ public ExecutionNode getParent() {
+ return parent;
+ }
+
+ /**
+ * @param parent
+ * the parent to set
+ */
+ public void setParent(final ExecutionNode parent) {
+
+ if (parent.getId() == getId()) {
+ throw new IllegalStateException("don't think so");
+ }
+
+ this.parent = parent;
+ }
+
+ /**
+ * @return the id
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * @return the targetClass
+ */
+ public Class<?> getTargetClass() {
+ return targetClass;
+ }
+
+ /**
+ * @param targetClass
+ * the targetClass to set
+ */
+ public void setTargetClass(final Class<?> targetClass) {
+ this.targetClass = targetClass;
+ }
+
+ /**
+ * @return the methodArgs
+ */
+ public Object[] getMethodArgs() {
+ return methodArgs;
+ }
+
+ /**
+ * @param methodArgs
+ * the methodArgs to set
+ */
+ public void setMethodArgs(final Object[] methodArgs) {
+ this.methodArgs = methodArgs;
+ }
+
+ /**
+ * @return the scenarioName
+ */
+ public String getScenarioName() {
+ return scenarioName;
+ }
+
+ /**
+ * @param scenarioName
+ * the scenarioName to set
+ */
+ public void setScenarioName(final String scenarioName) {
+ this.scenarioName = scenarioName;
+ }
+
+ /**
+ * @return the line
+ */
+ public String getLine() {
+ return line;
+ }
+
+ /**
+ * @param line
+ * the line to set
+ */
+ public void setLine(final String line) {
+ this.line = line;
+ }
+
+ /**
+ * @return the targetMethod
+ */
+ public Method getTargetMethod() {
+ return targetMethod;
+ }
+
+ /**
+ * @param targetMethod
+ * the targetMethod to set
+ */
+ public void setTargetMethod(final Method targetMethod) {
+ this.targetMethod = targetMethod;
+ }
+
+ /**
+ * @return the children
+ */
+ public List<ExecutionNode> getChildren() {
+ return children;
+ }
+
+ /**
+ * @return the backgrounds
+ */
+ public List<ExecutionNode> getBackgrounds() {
+ return backgrounds;
+ }
+
+ public String printTree() {
+ // traverse the tree
+ final StringBuilder buf = new StringBuilder();
+
+ buf.append("Execution tree:\n");
+
+ buf.append(toDebugString()).append("\n");
+
+ if (children != null) {
+ for (final ExecutionNode child : children) {
+ buf.append(child.toDebugString());
+ }
+ }
+
+ return buf.toString();
+ }
+
+ @Override
+ public String toString() {
+ return id + ":" + getDescription() + " children size: " + getChildrenSize();
+ }
+
+ public String treeToString() {
+ final StringBuilder buf = new StringBuilder();
+
+ buf.append(toString());
+ buf.append("\n");
+ if (hasChildren()) {
+
+ for (final ExecutionNode child : getChildren()) {
+
+ buf.append(Strings.repeat("\t", depth));
+ buf.append(child.treeToString());
+ buf.append("\n");
+ }
+ }
+
+ return buf.toString();
+ }
+
+ public String getDebugStringForThisNode() {
+ final StringBuilder buf = new StringBuilder();
+
+ buf.append(id);
+
+ if (parent != null) {
+ buf.append(Strings.repeat("\t", depth));
+
+ if (feature != null) {
+ buf.append(feature.getName()).append(" in ").append(feature.getFilename()).append("\n");
+ } else if (scenarioName != null) {
+ buf.append(scenarioName).append("\n");
+ }
+
+ if (rowNumber > -1) {
+ buf.append("outline #: ").append(rowNumber).append("\n");
+ }
+
+ if (background) {
+ buf.append("BACKGROUND\n");
+ }
+
+ if (line != null) {
+ buf.append(line).append("\n");
+ }
+
+ appendMethodInfo(" - ", buf);
+ } else {
+ buf.append(": Root");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * @return
+ */
+ public String toDebugString() {
+ final StringBuilder buf = new StringBuilder();
+
+ if (parent != null) {
+
+ buf.append(id).append(Strings.repeat("\t", depth)).append("file: ").append(getFilename()).append(" ")
+ .append(parent.getId()).append(" ");
+
+ if (feature != null) {
+ buf.append(feature.getName()).append("\n");
+ } else if (scenarioName != null) {
+ buf.append(scenarioName).append("\n");
+ }
+
+ if (rowNumber > -1) {
+ buf.append(" outline #: ").append(rowNumber).append("\n");
+ }
+
+ if (background) {
+ buf.append("BACKGROUND\n");
+ }
+
+ if (backgrounds != null) {
+ for (final ExecutionNode backgroundNode : backgrounds) {
+ buf.append(backgroundNode.toDebugString());
+ }
+ }
+
+ boolean printedLine = false;
+ if (line != null) {
+ buf.append(line);
+ printedLine = true;
+ }
+
+ appendMethodInfo(" - ", buf);
+
+ if (printedLine) {
+ buf.append("\n");
+ }
+ }
+
+ if (children != null) {
+ for (final ExecutionNode child : children) {
+ buf.append(child.toDebugString());
+ }
+ }
+
+ // else we're root
+ return buf.toString();
+ }
+
+ public void appendMethodInfo(final StringBuilder buf) {
+ appendMethodInfo(null, buf);
+ }
+
+ /**
+ * @param buf
+ */
+ public void appendMethodInfo(final String prefix, final StringBuilder buf) {
+ if (targetClass != null && targetMethod != null) {
+
+ if (prefix != null) {
+ buf.append(prefix);
+ }
+
+ buf.append(targetClass.getSimpleName()).append(".").append(targetMethod.getName()).append("(");
+
+ if (methodArgs != null) {
+ boolean commaRequired = false;
+ for (final Object arg : methodArgs) {
+ if (commaRequired) {
+ buf.append(", ");
+ }
+
+ boolean quotes = false;
+ if (arg instanceof String) {
+ quotes = true;
+ buf.append("\"");
+ }
+ buf.append(arg.toString());
+ if (quotes) {
+ buf.append("\"");
+ }
+ commaRequired = true;
+ }
+ }
+
+ buf.append(")").append("\n");
+ }
+ }
+
+ /**
+ * @return
+ */
+ public boolean isOutlineScenario() {
+ return outline;
+ }
+
+ /**
+ * @param b
+ */
+ public void setOutline(final boolean isOutline) {
+ outline = isOutline;
+
+ }
+
+ /**
+ * @return
+ */
+ public boolean hasBackground() {
+ return backgrounds != null && !backgrounds.isEmpty();
+ }
+
+ /**
+ * @return
+ */
+ public boolean isExecutable() {
+ return getTargetClass() != null && getTargetMethod() != null;
+ }
+
+ /**
+ * @return
+ */
+ public boolean hasChildren() {
+ return children != null && !children.isEmpty();
+ }
+
+ /**
+ * @return
+ */
+ public Long getLongId() {
+ return Long.valueOf(id);
+ }
+
+ /**
+ * @return
+ */
+ public boolean shouldHaveChildren() {
+ return isFeature() || isScenario() || isOutlineScenario();
+ }
+
+ /**
+ * @return
+ */
+ public boolean isScenario() {
+ return scenarioName != null;
+ }
+
+ /**
+ * @return
+ */
+ public boolean isFeature() {
+ return feature != null;
+ }
+
+ /**
+ * @return the result
+ */
+ public ExecutionNodeResult getResult() {
+ return result;
+ }
+
+ /**
+ * @return the tags
+ */
+ public Set<String> getTags() {
+ return tags;
+ }
+
+ /**
+ * @param tags
+ * the tags to set
+ */
+ public void setTags(final Set<String> tags) {
+ this.tags = tags;
+ }
+
+ /**
+ *
+ */
+ public boolean isStep() {
+
+ return depth == 3 && !isOutlineScenario() || depth == 4 && parent.isOutlineScenario();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int res = 1;
+ res = prime * res + (int) (id ^ (id >>> 32));
+ return res;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final ExecutionNode other = (ExecutionNode) obj;
+ if (id != other.id) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return the filename
+ */
+ public String getFilename() {
+ return new File(getFileUri()).getName();
+ }
+
+ public String getFileUri() {
+ // Use this filename if specified, or go up through the tree till we get
+ // an answer
+ if (fileUri != null) {
+ return fileUri;
+ } else if (parent != null) {
+ fileUri = parent.getFileUri();
+ return fileUri;
+ } else {
+ fileUri = "";
+ }
+
+ return fileUri;
+ }
+
+ public void setFileUri(final String fileUri) {
+ this.fileUri = fileUri;
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public void setLineNumber(final int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ public String getType() {
+
+ String rtn = null;
+ if (parent == null) {
+ rtn = "Root node";
+ } else if (isFeature()) {
+ rtn = "Feature";
+ } else if (isScenario()) {
+ rtn = "Scenario";
+ } else if (isOutlineScenario()) {
+ rtn = "Scenario Outline";
+ } else if (isStep()) {
+ rtn = "Step";
+ } else if (targetMethod != null) {
+ rtn = "Step Implementation";
+ }
+ return rtn;
+
+ }
+
+ public String getDescription() {
+
+ // return a string that represents what this is
+ String rtn = null;
+
+ if (line != null) {
+ rtn = line;
+ } else {
+ if (isFeature()) {
+
+ rtn = feature.getName();
+ } else if (isScenario()) {
+
+ rtn = scenarioName;
+ } else if (parent != null && parent.isOutlineScenario()) {
+ rtn = parent.scenarioName + " [" + rowNumber + "]";
+ }
+ }
+ return rtn;
+ }
+
+ public boolean hasError() {
+ return result.getResult() == ExecutionResult.FAILED || result.getResult() == ExecutionResult.PARSE_FAILURE;
+ }
+
+ public boolean hasPassed() {
+ return result.getResult() == ExecutionResult.PASSED;
+ }
+
+ public Set<String> getTagsFromHierarchy() {
+ Set<String> allTags = null;
+
+ ExecutionNode node = this;
+
+ while (node != null) {
+
+ if (node.tags != null) {
+
+ if (allTags == null) {
+ allTags = new HashSet<String>();
+ }
+ allTags.addAll(node.tags);
+ }
+
+ node = node.parent;
+ }
+
+ return allTags;
+ }
+
+ public List<ExecutionNode> getFailedChildNodes() {
+
+ // TODO - how should we handle background or setup and tear down
+ // failures ?
+
+ final List<ExecutionNode> failed = filterNodes(children, new Predicate<ExecutionNode>() {
+
+ public boolean apply(final ExecutionNode input) {
+
+ return input.hasFailed();
+ }
+ });
+
+ return failed;
+ }
+
+ private List<ExecutionNode> filterNodes(final List<ExecutionNode> sourceList,
+ final Predicate<ExecutionNode> predicate) {
+
+ List<ExecutionNode> filtered = null;
+ if (sourceList != null) {
+
+ for (final ExecutionNode node : sourceList) {
+
+ if (predicate.apply(node)) {
+ if (filtered == null) {
+ filtered = new ArrayList<ExecutionNode>();
+ }
+ filtered.add(node);
+ }
+ }
+ }
+ return filtered;
+ }
+
+ /**
+ * @return
+ */
+ private boolean hasFailed() {
+
+ // this node has failed if any of this node's backgrounds have failed,
+ // this node's state is failed, or any of this node's children's state
+ // has failed
+ // TODO include backgrounds
+ return result.getResult() == ExecutionResult.FAILED || getFailedChildNodes() != null;
+ }
+
+ /**
+ * @param i
+ */
+ public ExecutionNode getChild(final int i) {
+ ExecutionNode rtn = null;
+ if (children != null && children.size() > i) {
+ rtn = children.get(i);
+ }
+ return rtn;
+ }
+
+ /**
+ * @return
+ */
+ public int getChildrenSize() {
+ int rtn = 0;
+
+ if (children != null) {
+ rtn = children.size();
+ }
+
+ return rtn;
+ }
}
View
4 src/main/java/com/technophobia/substeps/model/Configuration.java
@@ -30,6 +30,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.technophobia.substeps.model.exception.SubstepsConfigurationException;
+
/**
* @author ian
*
@@ -88,7 +90,7 @@ public void addDefaultProperties(final URL url, final String name) {
combinedConfig.addConfiguration(defaultProps, name);
} catch (final ConfigurationException e) {
logger.error("error loading default properties", e);
- throw new SubStepConfigurationException(e);
+ throw new SubstepsConfigurationException(e);
}
}
}
View
26 src/main/java/com/technophobia/substeps/model/DuplicatePatternException.java
@@ -1,26 +0,0 @@
-package com.technophobia.substeps.model;
-
-public class DuplicatePatternException extends RuntimeException {
-
- private static final long serialVersionUID = 2426674698756596968L;
-
-
- public DuplicatePatternException() {
- super();
- }
-
-
- public DuplicatePatternException(final String message, final Throwable cause) {
- super(message, cause);
- }
-
-
- public DuplicatePatternException(final String message) {
- super(message);
- }
-
-
- public DuplicatePatternException(final Throwable cause) {
- super(cause);
- }
-}
View
17 src/main/java/com/technophobia/substeps/model/PatternMap.java
@@ -48,11 +48,23 @@ public V getNullVale() {
}
- public void put(final String pattern, final V value) {
+ /**
+ * Adds a new pattern to the map.
+ *
+ * Users of this class <em>must</em> check if the pattern has already been added
+ * by using {@link containsPattern} to avoid IllegalStateException in the event that
+ * the pattern has already been added to the map.
+ *
+ * @param pattern
+ * @param value
+ *
+ * @throws IllegalStateException - if the map already contains the specified patter.
+ */
+ public void put(final String pattern, final V value) throws IllegalStateException {
if (pattern != null) {
if (keys.containsKey(pattern)) {
- throw new DuplicatePatternException("duplicate patterns detected: " + pattern);
+ throw new IllegalStateException("");
}
keys.put(pattern, value);
@@ -66,7 +78,6 @@ public void put(final String pattern, final V value) {
public int size() {
-
return patternMap.size();
}
View
46 src/main/java/com/technophobia/substeps/model/Syntax.java
@@ -27,6 +27,11 @@
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
+import com.technophobia.substeps.model.exception.DuplicateStepImplementationException;
+import com.technophobia.substeps.model.exception.StepImplementationException;
+import com.technophobia.substeps.model.exception.UnimplementedStepException;
+import com.technophobia.substeps.runner.syntax.DefaultSyntaxErrorReporter;
+import com.technophobia.substeps.runner.syntax.SyntaxErrorReporter;
/**
*
@@ -35,6 +40,7 @@
*
*/
public class Syntax {
+
// These two will always be populated
private final Map<String, PatternMap<StepImplementation>> stepImplementationMap = new HashMap<String, PatternMap<StepImplementation>>();
@@ -45,12 +51,20 @@
private boolean failOnDuplicateStepImplementations = true;
private String[] nonStrictKeywordPrecedence;
+ private final SyntaxErrorReporter syntaxErrorReporter;
+
+ public Syntax() {
+ this(new DefaultSyntaxErrorReporter());
+ }
+
+ public Syntax(final SyntaxErrorReporter syntaxErrorReporter) {
+ this.syntaxErrorReporter = syntaxErrorReporter;
+ }
public Map<String, PatternMap<StepImplementation>> getStepImplementationMap() {
return stepImplementationMap;
}
-
public List<StepImplementation> getStepImplementations() {
// build a list of the impls in the order of the annotations
final List<StepImplementation> allImpls = new ArrayList<StepImplementation>();
@@ -74,7 +88,6 @@
return allImpls;
}
-
/**
* @param keyWord
* @return
@@ -83,7 +96,6 @@
return stepImplementationMap.get(keyWord);
}
-
/**
* @param loadSubSteps
*/
@@ -91,12 +103,10 @@ public void setSubStepsMap(final PatternMap<ParentStep> loadSubSteps) {
subStepsMap = loadSubSteps;
}
-
public PatternMap<ParentStep> getSubStepsMap() {
return subStepsMap;
}
-
/**
* @return
*/
@@ -111,7 +121,6 @@ public void setSubStepsMap(final PatternMap<ParentStep> loadSubSteps) {
return sortedList;
}
-
/**
* @param impl
*/
@@ -124,16 +133,19 @@ public void addStepImplementation(final StepImplementation impl) {
stepImplementationMap.put(impl.getKeyword(), patternMap);
}
- try {
- patternMap.put(impl.getValue(), impl);
- } catch (final DuplicatePatternException ex) {
+ final String pattern = impl.getValue();
+ if (!patternMap.containsPattern(pattern)) {
+ patternMap.put(pattern, impl);
+ } else {
+ final StepImplementationException ex = new DuplicateStepImplementationException(pattern,
+ patternMap.getValueForPattern(pattern), impl);
+ syntaxErrorReporter.reportStepImplError(ex);
if (failOnDuplicateStepImplementations) {
throw ex;
}
-
}
- }
+ }
/**
* @param strict
@@ -148,12 +160,10 @@ public void setStrict(final boolean strict, final String[] nonStrictKeywordPrece
}
}
-
public void setFailOnDuplicateStepImplementations(final boolean failOnDuplicateStepImplementations) {
this.failOnDuplicateStepImplementations = failOnDuplicateStepImplementations;
}
-
/**
* @param parameterLine
* @return
@@ -162,12 +172,10 @@ public void setFailOnDuplicateStepImplementations(final boolean failOnDuplicateS
return getStepImplementationsInternal(keyword, parameterLine, false);
}
-
public List<StepImplementation> checkForStepImplementations(final String keyword, final String parameterLine) {
return getStepImplementationsInternal(keyword, parameterLine, true);
}
-
/**
* @param keyword
* @param parameterLine
@@ -200,7 +208,6 @@ public void setFailOnDuplicateStepImplementations(final boolean failOnDuplicateS
return list;
}
-
/**
* @param keyword
* @param parameterLine
@@ -218,8 +225,7 @@ public void setFailOnDuplicateStepImplementations(final boolean failOnDuplicateS
}
else if (!okNotTofindAnything) {
- throw new SubStepConfigurationException(parameterLine
- + " is not a recognised substep or step implementation");
+ throw new UnimplementedStepException(parameterLine);
}
return list;
@@ -230,18 +236,15 @@ else if (!okNotTofindAnything) {
private final String keyword;
-
public CloneStepImplementationsWithNewKeywordFunction(final String keyword) {
this.keyword = keyword;
}
-
public StepImplementation apply(final StepImplementation stepImplementation) {
return stepImplementation.cloneWithKeyword(keyword);
}
}
-
/**
* @return the strict
*/
@@ -249,7 +252,6 @@ public boolean isStrict() {
return strict;
}
-
/**
* @return the nonStrictKeywordPrecedence
*/
View
29 src/main/java/com/technophobia/substeps/model/exception/DuplicatePatternException.java
@@ -0,0 +1,29 @@
+package com.technophobia.substeps.model.exception;
+
+import java.io.File;
+
+import com.technophobia.substeps.model.ParentStep;
+
+public class DuplicatePatternException extends SubstepsParsingException {
+
+ private static final long serialVersionUID = 2426674698756596968L;
+
+ public DuplicatePatternException(final String pattern, final ParentStep originalSource,
+ final ParentStep duplicatingSource) {
+ // TODO - offset
+ super(new File(duplicatingSource.getSubStepFile()), originalSource.getParent().getSourceLineNumber(),
+ originalSource.getParent().getLine(), 0L, messageFrom(pattern, originalSource, duplicatingSource));
+
+ }
+
+ private static String messageFrom(final String pattern, final ParentStep originalSource,
+ final ParentStep duplicatingSource) {
+ // TODO: is 'pattern' actually a end-user friendly word?
+ return String.format("Duplicate pattern detected: Pattern [%s] is defined in %s and %s", pattern,
+ patternLocationFrom(originalSource), patternLocationFrom(duplicatingSource));
+ }
+
+ private static String patternLocationFrom(final ParentStep parentStep) {
+ return parentStep.getSubStepFile() + "::" + parentStep.getParent().getSourceLineNumber();
+ }
+}
View
22 .../java/com/technophobia/substeps/model/exception/DuplicateStepImplementationException.java
@@ -0,0 +1,22 @@
+package com.technophobia.substeps.model.exception;
+
+import com.technophobia.substeps.model.StepImplementation;
+
+public class DuplicateStepImplementationException extends StepImplementationException {
+
+ private static final long serialVersionUID = 6851509341143564326L;
+
+ public DuplicateStepImplementationException(final String pattern, final StepImplementation originalSource,
+ final StepImplementation duplicatingSource) {
+
+ super(duplicatingSource.getImplementedIn(), duplicatingSource.getMethod(), String.format(
+ "Duplicate step implementation detected: Pattern [%s] is implemented in %s and %s", pattern,
+ fullMethodNameFrom(originalSource), fullMethodNameFrom(duplicatingSource)));
+
+ }
+
+ private static String fullMethodNameFrom(final StepImplementation stepImplementation) {
+ return stepImplementation.getImplementedIn().getName() + "." + stepImplementation.getMethod().getName();
+ }
+
+}
View
27 src/main/java/com/technophobia/substeps/model/exception/StepImplementationException.java
@@ -0,0 +1,27 @@
+package com.technophobia.substeps.model.exception;
+
+import java.lang.reflect.Method;
+
+public class StepImplementationException extends SubstepsException {
+
+ private static final long serialVersionUID = 2361658683635343317L;
+
+ private final Class<?> implementingClass;
+ private final Method implementingMethod;
+
+ public StepImplementationException(final Class<?> implementingClass, final Method implementingMethod,
+ final String message) {
+ super(message);
+ this.implementingClass = implementingClass;
+ this.implementingMethod = implementingMethod;
+ }
+
+ public Class<?> getImplementingClass() {
+ return implementingClass;
+ }
+
+ public Method getImplementingMethod() {
+ return implementingMethod;
+ }
+
+}
View
27 .../model/SubStepConfigurationException.java → ...ption/SubstepsConfigurationException.java
@@ -16,22 +16,39 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Substeps. If not, see <http://www.gnu.org/licenses/>.
*/
-package com.technophobia.substeps.model;
+package com.technophobia.substeps.model.exception;
/**
* @author ian
*
*/
-public class SubStepConfigurationException extends RuntimeException {
+public class SubstepsConfigurationException extends SubstepsException {
+
private static final long serialVersionUID = -7081029538420278487L;
- public SubStepConfigurationException(final Throwable cause) {
+ public SubstepsConfigurationException(final Throwable cause) {
super(cause);
}
- public SubStepConfigurationException(final String msg) {
- super(msg);
+ /*
+ * TODO: The following probably shouldn't be required - but we use this
+ * exception a bit hap-hazzardly - review after error handling overhaul.
+ */
+
+ public SubstepsConfigurationException() {
+ super();
+ }
+
+
+ public SubstepsConfigurationException(final String message, final Throwable cause) {
+ super(message, cause);
}
+
+
+ public SubstepsConfigurationException(final String message) {
+ super(message);
+ }
+
}
View
23 src/main/java/com/technophobia/substeps/model/exception/SubstepsException.java
@@ -0,0 +1,23 @@
+package com.technophobia.substeps.model.exception;
+
+public class SubstepsException extends RuntimeException {
+
+ private static final long serialVersionUID = 4647698987295633906L;
+
+ public SubstepsException() {
+ super();
+ }
+
+ public SubstepsException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ public SubstepsException(final String message) {
+ super(message);
+ }
+
+ public SubstepsException(final Throwable cause) {
+ super(cause);
+ }
+
+}
View
39 src/main/java/com/technophobia/substeps/model/exception/SubstepsParsingException.java
@@ -0,0 +1,39 @@
+package com.technophobia.substeps.model.exception;
+
+import java.io.File;
+
+public class SubstepsParsingException extends SubstepsException {
+
+ private static final long serialVersionUID = 3310663144363390571L;
+
+ private final File file;
+ private final long offset;
+ private final String line;
+ private final int lineNumber;
+
+ public SubstepsParsingException(final File file, final int lineNumber, final String line, final long offset,
+ final String message) {
+ super(message);
+ this.file = file;
+ this.line = line;
+ this.offset = offset;
+ this.lineNumber = lineNumber;
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public String getLine() {
+ return line;
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public long getOffset() {
+ return offset;
+ }
+
+}
View
11 src/main/java/com/technophobia/substeps/model/exception/SubstepsRuntimeException.java
@@ -0,0 +1,11 @@
+package com.technophobia.substeps.model.exception;
+
+public class SubstepsRuntimeException extends SubstepsException {
+
+ private static final long serialVersionUID = 4050361783327727693L;
+
+ public SubstepsRuntimeException(final String msg) {
+ super(msg);
+ }
+
+}
View
12 src/main/java/com/technophobia/substeps/model/exception/UnimplementedStepException.java
@@ -0,0 +1,12 @@
+package com.technophobia.substeps.model.exception;
+
+//TODO: we should be able to identify these at parse time
+public class UnimplementedStepException extends SubstepsRuntimeException {
+
+ private static final long serialVersionUID = 6807673963152251297L;
+
+ public UnimplementedStepException(final String pattern) {
+ super(pattern + " is not a recognised step or substep implementation");
+ }
+
+}
View
106 src/main/java/com/technophobia/substeps/runner/BuildFailureManager.java
@@ -27,34 +27,31 @@
import com.technophobia.substeps.execution.ExecutionNode;
-
/**
* @author ian
- *
+ *
*/
-public class BuildFailureManager
-{
+public class BuildFailureManager {
+
private List<SubstepExecutionFailure> criticalFailures = null;
private List<SubstepExecutionFailure> nonCriticalFailures = null;
-
- public String getBuildFailureInfo()
- {
+
+ public String getBuildFailureInfo() {
return getBuildInfoString("NON CRITICAL FAILURES:\n\n", this.nonCriticalFailures)
- + getBuildInfoString("\n\nCRITICAL FAILURES:\n\n", this.criticalFailures);
- }
+ + getBuildInfoString("\n\nCRITICAL FAILURES:\n\n", this.criticalFailures);
+ }
- private String getBuildInfoString(final String msg, final List<SubstepExecutionFailure> failures)
- {
+ private String getBuildInfoString(final String msg, final List<SubstepExecutionFailure> failures) {
final StringBuilder buf = new StringBuilder();
final Set<ExecutionNode> dealtWith = new HashSet<ExecutionNode>();
if (failures != null && !failures.isEmpty()) {
-
- buf.append(msg);
-
+
+ buf.append(msg);
+
for (final SubstepExecutionFailure fail : failures) {
- final ExecutionNode node = fail.getExeccutionNode();
+ final ExecutionNode node = fail.getExeccutionNode();
if (!dealtWith.contains(node)) {
final List<ExecutionNode> hierarchy = new ArrayList<ExecutionNode>();
@@ -74,55 +71,50 @@ private String getBuildInfoString(final String msg, final List<SubstepExecutionF
dealtWith.add(node2);
}
}
+ buf.append("\n");
}
}
return buf.toString();
- }
-
-
- /**
- * @param failures
- */
- public void sortFailures(final List<SubstepExecutionFailure> failures)
- {
-
- for (final SubstepExecutionFailure fail : failures){
-
- if (fail.isNonCritical()){
-
- if (this.nonCriticalFailures == null){
- this.nonCriticalFailures = new ArrayList<SubstepExecutionFailure>();
- }
-
- this.nonCriticalFailures.add(fail);
- }
- else {
+ }
+
+ /**
+ * @param failures
+ */
+ public void sortFailures(final List<SubstepExecutionFailure> failures) {
+
+ for (final SubstepExecutionFailure fail : failures) {
+
+ if (fail.isNonCritical()) {
+
+ if (this.nonCriticalFailures == null) {
+ this.nonCriticalFailures = new ArrayList<SubstepExecutionFailure>();
+ }
+
+ this.nonCriticalFailures.add(fail);
+ } else {
if (this.criticalFailures == null) {
this.criticalFailures = new ArrayList<SubstepExecutionFailure>();
}
criticalFailures.add(fail);
- }
- }
- }
-
- public boolean testSuiteCompletelyPassed()
- {
- return (this.criticalFailures == null && this.nonCriticalFailures == null) ||
-
- (this.criticalFailures != null && this.criticalFailures.isEmpty() &&
- this.nonCriticalFailures != null && this.nonCriticalFailures.isEmpty() );
- }
-
- public boolean testSuiteSomeFailures()
- {
- return (testSuiteFailed()) ||
- (this.nonCriticalFailures != null && !this.nonCriticalFailures.isEmpty() );
- }
-
- public boolean testSuiteFailed()
- {
- return (this.criticalFailures != null && !this.criticalFailures.isEmpty());
- }
+ }
+ }
+ }
+
+ public boolean testSuiteCompletelyPassed() {
+ return (this.criticalFailures == null && this.nonCriticalFailures == null)
+ ||
+
+ (this.criticalFailures != null && this.criticalFailures.isEmpty() && this.nonCriticalFailures != null && this.nonCriticalFailures
+ .isEmpty());
+ }
+
+ public boolean testSuiteSomeFailures() {
+ return (testSuiteFailed()) || (this.nonCriticalFailures != null && !this.nonCriticalFailures.isEmpty());
+ }
+
+ public boolean testSuiteFailed() {
+ return (this.criticalFailures != null && !this.criticalFailures.isEmpty());
+ }
}
View
4 src/main/java/com/technophobia/substeps/runner/ExecutionConfig.java
@@ -27,8 +27,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.technophobia.substeps.model.SubStepConfigurationException;
import com.technophobia.substeps.model.SubSteps.StepImplementations;
+import com.technophobia.substeps.model.exception.SubstepsConfigurationException;
/**
* @author ian
@@ -412,7 +412,7 @@ public void setFastFailParseErrors(final boolean fastFailParseErrors) {
// TODO - build up a message
- throw new SubStepConfigurationException(
+ throw new SubstepsConfigurationException(
"Incompatible initialisation sequence");
}
} else {
View
396 src/main/java/com/technophobia/substeps/runner/ExecutionNodeRunner.java
@@ -42,6 +42,7 @@
*
*/
public class ExecutionNodeRunner {
+
private final Logger log = LoggerFactory.getLogger(ExecutionNodeRunner.class);
private boolean noTestsRun = true;
@@ -56,13 +57,11 @@
private final MethodExecutor methodExecutor = new ImplementationCache();
-
public void addNotifier(final INotifier notifier) {
notificationDistributor.addListener(notifier);
}
-
public ExecutionNode prepareExecutionConfig(final ExecutionConfig theConfig) {
config = theConfig;
@@ -70,28 +69,26 @@ public ExecutionNode prepareExecutionConfig(final ExecutionConfig theConfig) {
setupAndTearDown = new SetupAndTearDown(config.getInitialisationClasses(), methodExecutor);
- final String loggingConfigName = config.getDescription() != null ? config.getDescription()
- : "SubStepsMojo";
+ final String loggingConfigName = config.getDescription() != null ? config.getDescription() : "SubStepsMojo";
setupAndTearDown.setLoggingConfigName(loggingConfigName);
final TagManager tagmanager = new TagManager(config.getTags());
- if (config.getNonFatalTags() != null){
- nonFatalTagmanager = new TagManager(config.getNonFatalTags());
+ if (config.getNonFatalTags() != null) {
+ nonFatalTagmanager = new TagManager(config.getNonFatalTags());
}
-
+
File subStepsFile = null;
if (config.getSubStepsFileName() != null) {
subStepsFile = new File(config.getSubStepsFileName());
}
- final Syntax syntax = SyntaxBuilder.buildSyntax(config.getStepImplementationClasses(),
- subStepsFile, config.isStrict(), config.getNonStrictKeywordPrecedence());
+ final Syntax syntax = SyntaxBuilder.buildSyntax(config.getStepImplementationClasses(), subStepsFile,
+ config.isStrict(), config.getNonStrictKeywordPrecedence());
- final TestParameters parameters = new TestParameters(tagmanager, syntax,
- config.getFeatureFile());
+ final TestParameters parameters = new TestParameters(tagmanager, syntax, config.getFeatureFile());
parameters.setFailParseErrorsImmediately(config.isFastFailParseErrors());
parameters.init();
@@ -101,8 +98,7 @@ public ExecutionNode prepareExecutionConfig(final ExecutionConfig theConfig) {
// building the tree can throw critical failures if exceptions are found
rootNode = nodeTreeBuilder.buildExecutionNodeTree();
- ExecutionContext.put(Scope.SUITE, INotificationDistributor.NOTIFIER_DISTRIBUTOR_KEY,
- notificationDistributor);
+ ExecutionContext.put(Scope.SUITE, INotificationDistributor.NOTIFIER_DISTRIBUTOR_KEY, notificationDistributor);
final String dryRunProperty = System.getProperty("dryRun");
if (dryRunProperty != null && Boolean.parseBoolean(dryRunProperty)) {
@@ -116,53 +112,48 @@ public ExecutionNode prepareExecutionConfig(final ExecutionConfig theConfig) {
return rootNode;
}
-
public List<SubstepExecutionFailure> run() {
log.debug("run root node");
noTestsRun = true;
// TODO - why is this here twice?
- ExecutionContext.put(Scope.SUITE, INotificationDistributor.NOTIFIER_DISTRIBUTOR_KEY,
- notificationDistributor);
-
- final List<SubstepExecutionFailure> failures =
- runExecutionNodeHierarchy(Scope.SUITE, rootNode);
+ ExecutionContext.put(Scope.SUITE, INotificationDistributor.NOTIFIER_DISTRIBUTOR_KEY, notificationDistributor);
+
+ final List<SubstepExecutionFailure> failures = runExecutionNodeHierarchy(Scope.SUITE, rootNode);
if (noTestsRun) {
final Throwable t = new IllegalStateException("No tests executed");
rootNode.getResult().setFailed(t);
notificationDistributor.notifyNodeFailed(rootNode, t);
-
- addFailure(failures, new SubstepExecutionFailure(t, rootNode));
+ addFailure(failures, new SubstepExecutionFailure(t, rootNode));
}
return failures;
}
- private void addFailure(final List<SubstepExecutionFailure> failures, final SubstepExecutionFailure failure){
+ private void addFailure(final List<SubstepExecutionFailure> failures, final SubstepExecutionFailure failure) {
failures.add(failure);
logFailure(failure);
-
+
// set the criticality of this failure
-
- if (!failure.isSetupOrTearDown() && this.nonFatalTagmanager != null &&
- nonFatalTagmanager.acceptTaggedScenario(failure.getExeccutionNode().getTagsFromHierarchy())) {
- failure.setNonCritical(true);
+ if (!failure.isSetupOrTearDown() && this.nonFatalTagmanager != null
+ && nonFatalTagmanager.acceptTaggedScenario(failure.getExeccutionNode().getTagsFromHierarchy())) {
+
+ failure.setNonCritical(true);
}
-
+
}
-
- private List<SubstepExecutionFailure> runExecutionNodeHierarchy(final Scope scope,
- final ExecutionNode node){
+
+ private List<SubstepExecutionFailure> runExecutionNodeHierarchy(final Scope scope, final ExecutionNode node) {
log.info("run Node Hierarchy @ " + scope.name() + ":" + node.getDebugStringForThisNode());
final List<SubstepExecutionFailure> failures = new ArrayList<SubstepExecutionFailure>();
-
+
notificationDistributor.notifyNodeStarted(node);
// node may have parsing error, in which case, bail immediately
@@ -170,8 +161,7 @@ private void addFailure(final List<SubstepExecutionFailure> failures, final Subs
notificationDistributor.notifyNodeFailed(node, node.getResult().getThrown());
addFailure(failures, new SubstepExecutionFailure(node.getResult().getThrown(), node));
- }
- else {
+ } else {
node.getResult().setStarted();
// run setup if necessary for this depth and step
@@ -187,187 +177,210 @@ private void addFailure(final List<SubstepExecutionFailure> failures, final Subs
}
if (failures.isEmpty() && node.hasBackground() && !node.isOutlineScenario()) {
- // any of these fail then bail & mark this node as failed
- for (final ExecutionNode backgroundNode : node.getBackgrounds()) {
-// try {
-
-// runExecutionNodeHierarchy(Scope.SCENARIO_BACKGROUND, backgroundNode);
-
- final List<SubstepExecutionFailure> backgroundScenarioFailures = runExecutionNodeHierarchy(Scope.SCENARIO_BACKGROUND, backgroundNode);
-
- if (!backgroundScenarioFailures.isEmpty()){
- log.debug("running background scenarios failed");
- failures.addAll(backgroundScenarioFailures);
- }
-
-
-// }
-// catch (final Throwable t) {
-// log.debug("scenario background failed", t);
-//
-// failures.add(new SubstepExecutionFailure(t, backgroundNode));
-// }
-
- if (!failures.isEmpty()){
- break;
- }
- }
- }
- if (failures.isEmpty() && node.isExecutable()) {
-
- final SubstepExecutionFailure methodInvocationFailure = executeNodeMethod(node);
- if (methodInvocationFailure != null){
-
- addFailure(failures, methodInvocationFailure);
- }
- }
-
- // if children, run children
-
- else if (failures.isEmpty() && node.shouldHaveChildren() && !node.hasChildren()) {
-
- // TODO - better error message required
-
- addFailure(failures, new SubstepExecutionFailure(new IllegalStateException("node should have children but doesn't"), node));
-
- }
-
- else if (failures.isEmpty() && node.hasChildren()) {
-
- log.debug("node has children");
- // if any fail, mark this as failed. if current scope is
- // suite, feature then continue even if failure
- // if scenario or outline or step, bail
- for (final ExecutionNode child : node.getChildren()) {
-
- final Scope childScope = getChildScope(node, scope);
-
- final List<SubstepExecutionFailure> childFailures = runExecutionNodeHierarchy(childScope,child);
-
- if (!childFailures.isEmpty()){
- log.debug("running children failed");
- failures.addAll(childFailures);
- }
-
- // bail out if current scope is Step or SCENARIO_OUTLINE_ROW
-
- // bail if current scope is scenario and childscope is step
- if (!failures.isEmpty()
- && (scope == Scope.STEP || scope == Scope.SCENARIO_OUTLINE_ROW || (scope == Scope.SCENARIO && childScope == Scope.STEP))) {
- log.debug("bailing out of execution");
- break;
- }
- }
- }
-
- try {
- // run tear down if necessary for this depth and step
- if (!node.isOutlineScenario()) {
- setupAndTearDown.runTearDown(scope);
- ExecutionContext.clear(scope);
-
- }
- }
- catch (final Throwable t) {
- log.debug("tear down failed", t);
-
- failures.add(new SubstepExecutionFailure(t, node, true));
- }
-
- if (failures.isEmpty()) {
- log.debug("node success");
- notificationDistributor.notifyNodeFinished(node);
-
- node.getResult().setFinished();
-
- }
- else {
-
- log.debug("node failures");
- // just notify on the last one in..?
- final Throwable lastException = failures.get(failures.size()-1).getCause();
- notificationDistributor.notifyNodeFailed(node, lastException);
-
- // TODO should this have been set earlier...?
- node.getResult().setFailed(lastException);
- }
+ // any of these fail then bail & mark this node as failed
+ for (final ExecutionNode backgroundNode : node.getBackgrounds()) {
<