+
Skip to content

Provide an extension point to allow filtering/skipping specification examples #267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/main/java/org/concordion/api/ExampleDefinition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.concordion.api;

/**
* An interface to access example element's name and attributes for use mainly in {@link ImplementationStatusModifier}.
*
* @author <a href="mailto:chiknrice@gmail.com">Ian Bondoc</a>
*/
public interface ExampleDefinition {

/**
* Accessor to get the example's name
*
* @return the example's name
*/
String getName();

/**
* Accessor to the example's attribute given the name
*
* @param name the name of the attribute
* @return the attribute value
*/
String getAttributeValue(String name);

/**
* Accessor to the example's attribute given the name and namespace
*
* @param localName the name of the attribute
* @param namespaceURI the namespace of the attribute
* @return the attribute value
*/
String getAttributeValue(String localName, String namespaceURI);

}
11 changes: 11 additions & 0 deletions src/main/java/org/concordion/api/IgnoredExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.concordion.api;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoredExample {
}
3 changes: 2 additions & 1 deletion src/main/java/org/concordion/api/ImplementationStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
public enum ImplementationStatus {
UNIMPLEMENTED("Unimplemented", Unimplemented.class),
EXPECTED_TO_FAIL("ExpectedToFail", ExpectedToFail.class),
EXPECTED_TO_PASS("ExpectedToPass", ExpectedToPass.class);
EXPECTED_TO_PASS("ExpectedToPass", ExpectedToPass.class),
IGNORED("Ignored", IgnoredExample.class);

private final String tag;
private final Class<? extends Annotation> annotation;
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/org/concordion/api/ImplementationStatusModifier.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.concordion.api;

/**
* Examples can be marked as {@code Unimplemented}, {@code ExpectedToFail}, or {@code Ignored} declaratively via
* c:status attribute. If the status needs to be determined at runtime, this extension point can be used.
*
* @author <a href="mailto:chiknrice@gmail.com">Ian Bondoc</a>
* @see ImplementationStatus
*/
public interface ImplementationStatusModifier {

/**
* Determine an example element's {@code ImplementationStatus}
*
* @param exampleDefinition the definition of the example to evaluate
* @return the status based on the exampleDefinition
*/
ImplementationStatus getStatusForExample(ExampleDefinition exampleDefinition);

}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ public interface ConcordionExtender {
*/
ConcordionExtender withExampleListener(ExampleListener listener);

/**
* Adds a status modifier which Concordion can apply to each specification example to override their status.
*
* @param statusModifier the status modifier
* @return this
*/
ConcordionExtender withImplementationStatusModifier(ImplementationStatusModifier statusModifier);

/**
* Adds a listener that is invoked before and after Concordion has processed the "outer" example (which includes
* all commands in a specification not inside an example command).
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/concordion/internal/ConcordionBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,12 @@ public ConcordionExtender withExampleListener(ExampleListener listener) {
return this;
}

@Override
public ConcordionExtender withImplementationStatusModifier(ImplementationStatusModifier statusModifier) {
exampleCommand.setImplementationStatusModifier(statusModifier);
return this;
}

public ConcordionExtender withOuterExampleListener(OuterExampleListener listener) {
specificationCommand.addOuterExampleListener(listener);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,35 @@ public ResultSummary convertForCache(ResultSummary rs) {
// if we're expected to pass, then just use the result summary.
return rs;
}
},
IGNORED(ImplementationStatus.IGNORED) {
@Override
public void assertIsSatisfied(ResultSummary rs, FailFastException ffe) {
if (rs.getIgnoredCount() != 1 || rs.getSuccessCount() + rs.getFailureCount() + rs.getExceptionCount() > 0 || ffe != null) {
throw new ConcordionAssertionError("Example is expected to be ignored but is currently reporting.", rs);
}
}

@Override
public String printNoteToString() {
return " <-- Note: This example has been marked as IGNORED";
}

@Override
public ResultSummary getMeaningfulResultSummary(ResultSummary rs, FailFastException ffe) {
assertIsSatisfied(rs, ffe);
return new SingleResultSummary(Result.IGNORED);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here we have skipped being processed as ignored for the results. So another indicator they are probably the same concept. I've read your comment about the conceptual mismatch with jUnit - it's a good point and I'll have a think about it (woke up early today and brain not yet working!)

}

@Override
public ResultSummary convertForCache(ResultSummary rs) {
try {
assertIsSatisfied(rs, null);
return new SingleResultSummary(Result.IGNORED, rs.getSpecificationDescription());
} catch (ConcordionAssertionError cce) {
return new SingleResultSummary(Result.FAILURE, rs.getSpecificationDescription());
}
}
};

private final ImplementationStatus implementationStatus;
Expand Down
77 changes: 58 additions & 19 deletions src/main/java/org/concordion/internal/command/ExampleCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class ExampleCommand extends AbstractCommand {

private List<ExampleListener> listeners = new ArrayList<ExampleListener>();
private SpecificationDescriber specificationDescriber;
private ImplementationStatusModifier implementationStatusModifier;

public List<CommandCall> getExamples(CommandCall command) {
return Arrays.asList(command);
Expand All @@ -34,18 +35,25 @@ public void execute(CommandCall node, Evaluator evaluator, ResultRecorder result
resultRecorder.setSpecificationDescription(
specificationDescriber.getDescription(node.getResource(), exampleName));

if (!isBeforeExample) {
ImplementationStatus status = getImplementationStatus(node);

if (!isBeforeExample && status != ImplementationStatus.IGNORED) {
announceBeforeExample(exampleName, node.getElement(), resultRecorder);
}

try {
node.getChildren().processSequentially(evaluator, resultRecorder);
resultRecorder.setImplementationStatus(status);
if (status == ImplementationStatus.IGNORED) {
resultRecorder.record(Result.IGNORED);
} else {
node.getChildren().processSequentially(evaluator, resultRecorder);
}
} catch (FailFastException f) {
// Ignore - it'll be re-thrown later by the implementation status checker if necessary.
}
setupCommandForExample(node, resultRecorder, exampleName);

if (!isBeforeExample) {
if (!isBeforeExample && status != ImplementationStatus.IGNORED) {
announceAfterExample(exampleName, node.getElement(), resultRecorder);
}
}
Expand Down Expand Up @@ -84,26 +92,33 @@ protected boolean isBeforeExample(CommandCall element) {
return element.getExpression().equals("before");
}

public static void setupCommandForExample(CommandCall node, ResultRecorder resultRecorder, String exampleName) {
node.getElement().addAttribute("id", exampleName);

private ImplementationStatus getImplementationStatus(CommandCall node) {
// by default the implementation status is expected to pass
ImplementationStatus implementationStatus = ImplementationStatus.EXPECTED_TO_PASS;
// if there's a status param, it overrides expected to pass
String params = node.getParameter("status");
if (params != null) {
ImplementationStatus implementationStatus = ImplementationStatus.implementationStatusFor(params);
resultRecorder.setImplementationStatus(implementationStatus);
// let's be really nice and add the implementation status text into the element itself.
ImplementationStatusChecker checker = ImplementationStatusChecker.implementationStatusCheckerFor(implementationStatus);

String note;
if (checker != null) {
note = checker.printNoteToString();
} else {
note = "Invalid status expression " + params;
implementationStatus = ImplementationStatus.implementationStatusFor(params);
}
// if there's a status modifier and there's a status for the example, it overrides status param
if (implementationStatusModifier != null) {
ImplementationStatus runtimeImplementation = implementationStatusModifier.getStatusForExample(exampleDefinition(node.getElement()));
if (runtimeImplementation != null) {
implementationStatus = runtimeImplementation;
}
Element fixtureNode = new Element("p");
fixtureNode.appendText(note);
node.getElement().prependChild(fixtureNode);
}
return implementationStatus;
}

public static void setupCommandForExample(CommandCall node, ResultRecorder resultRecorder, String exampleName) {
node.getElement().addAttribute("id", exampleName);

// let's be really nice and add the implementation status text into the element itself.
ImplementationStatusChecker checker = ImplementationStatusChecker.implementationStatusCheckerFor(resultRecorder.getImplementationStatus());

Element fixtureNode = new Element("p");
fixtureNode.appendText(checker.printNoteToString());
node.getElement().prependChild(fixtureNode);
}

public void setSpecificationDescriber(SpecificationDescriber specificationDescriber) {
Expand All @@ -121,4 +136,28 @@ private void announceAfterExample(String exampleName, Element element, ResultRec
listeners.get(i).afterExample(new ExampleEvent(exampleName, element, (SummarizingResultRecorder)resultRecorder));
}
}

public void setImplementationStatusModifier(ImplementationStatusModifier implementationStatusModifier) {
this.implementationStatusModifier = implementationStatusModifier;
}

private static ExampleDefinition exampleDefinition(final Element element) {
return new ExampleDefinition() {
@Override
public String getName() {
return element.getAttributeValue("example", ConcordionBuilder.NAMESPACE_CONCORDION_2007);
}

@Override
public String getAttributeValue(String name) {
return element.getAttributeValue(name);
}

@Override
public String getAttributeValue(String localName, String namespaceURI) {
return element.getAttributeValue(localName, namespaceURI);
}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package spec.concordion.common.extension;

import org.concordion.api.*;
import org.concordion.api.extension.ConcordionExtender;
import org.concordion.api.extension.ConcordionExtension;
import org.concordion.integration.junit4.ConcordionRunner;
import org.concordion.internal.ConcordionBuilder;
import org.junit.runner.RunWith;
import test.concordion.ProcessingResult;

import java.util.ArrayList;
import java.util.List;

/**
* Created by bondocaadmin on 10/05/2018.
*/
@RunWith(ConcordionRunner.class)
@FullOGNL
public class ImplementationStatusModifierTest extends AbstractExtensionTestCase {

public void addExtension() {
setExtension(new ConcordionExtension() {
@Override
public void addTo(ConcordionExtender concordionExtender) {
concordionExtender.withImplementationStatusModifier(new ImplementationStatusModifier() {
@Override
public ImplementationStatus getStatusForExample(ExampleDefinition exampleDefinition) {
if (exampleDefinition.getName().endsWith("Ignored")) {
return ImplementationStatus.IGNORED;
} else {
return null;
}
}
});
}
});
}

@Override
public ProcessingResult getProcessingResult() {
return super.getProcessingResult();
}

private final List<String> beforeExampleCapturedNames = new ArrayList<String>();
private final List<String> afterExampleCapturedNames = new ArrayList<String>();

@BeforeExample
public void saveNameBeforeExample(@ExampleName String name) {
beforeExampleCapturedNames.add(name);
}

@AfterExample
public void saveNameAfterExample(@ExampleName String name) {
afterExampleCapturedNames.add(name);
}

public List<String> getBeforeExampleCapturedNames() {
return beforeExampleCapturedNames;
}

public List<String> getAfterExampleCapturedNames() {
return afterExampleCapturedNames;
}


}
4 changes: 4 additions & 0 deletions src/test/java/test/concordion/ProcessingResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ public long getExceptionCount() {
return resultSummary.getExceptionCount();
}

public long getIgnoredCount() {
return resultSummary.getIgnoredCount();
}

public AssertFailureEvent getLastAssertFailureEvent() {
return (AssertFailureEvent) eventRecorder.getLast(AssertFailureEvent.class);
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/test/concordion/TestRig.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,12 @@ public ProcessingResult process(Resource resource) {

try {

ResultSummary resultSummary = null;
SummarizingResultRecorder resultSummary = new SummarizingResultRecorder();
concordion.override(resource);
List<String> examples = concordion.getExampleNames(fixture);
if (!examples.isEmpty()) {
for (String example : examples) {
resultSummary = concordion.processExample(fixture, example);
resultSummary.record(concordion.processExample(fixture, example));
}
}
concordion.finish();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ <h1>Extension</h1>
<li><a concordion:run="concordion" href="ResourceExtension.html">Create resources</a> in the Concordion output folder</li>
<li>Add <a concordion:run="concordion" href="CSSExtension.html">CSS</a> or <a concordion:run="concordion" href="JavaScriptExtension.html">JavaScript</a> to the Concordion output</li>
<li>Read and write to files using <a concordion:run="concordion" href="FileSuffixExtensions.html">different file suffixes</a></li>
<li><a concordion:run="concordion" href="ImplementationStatusModifier.html">Modify ImplementationStatus</a> of examples at runtime</li>
</ul>

<h3>Further Questions</h3>
Expand Down
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载