Skip to content

Commit c80e627

Browse files
committed
dummy jbehave integration
1 parent 847c171 commit c80e627

8 files changed

Lines changed: 365 additions & 18 deletions

File tree

allure-java-commons/src/main/java/io/qameta/allure/util/ResultsUtils.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public final class ResultsUtils {
5757
public static final String SEVERITY_LABEL_NAME = "severity";
5858
public static final String TAG_LABEL_NAME = "tag";
5959
public static final String OWNER_LABEL_NAME = "owner";
60+
public static final String HOST_LABEL_NAME = "host";
61+
public static final String THREAD_LABEL_NAME = "thread";
6062

6163
private static final Logger LOGGER = LoggerFactory.getLogger(ResultsUtils.class);
6264
private static final String ALLURE_DESCRIPTIONS_PACKAGE = "allureDescriptions/";
@@ -95,6 +97,14 @@ public static Label createSeverityLabel(final String severity) {
9597
return new Label().withName(SEVERITY_LABEL_NAME).withValue(severity);
9698
}
9799

100+
public static Label createHostLabel() {
101+
return new Label().withName(HOST_LABEL_NAME).withValue(getHostName());
102+
}
103+
104+
public static Label createThreadLabel() {
105+
return new Label().withName(THREAD_LABEL_NAME).withValue(getThreadName());
106+
}
107+
98108
public static Label createLabel(final Owner owner) {
99109
return createOwnerLabel(owner.value());
100110
}

allure-jbehave/build.gradle

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
description = 'Allure JBehave'
2+
3+
apply from: "${gradleScriptDir}/maven-publish.gradle"
4+
apply from: "${gradleScriptDir}/bintray.gradle"
5+
apply plugin: 'maven'
6+
7+
configurations {
8+
agent
9+
}
10+
11+
dependencies {
12+
agent 'org.aspectj:aspectjweaver'
13+
14+
compile project(':allure-java-commons')
15+
compile 'org.jbehave:jbehave-core'
16+
17+
testCompile 'org.slf4j:slf4j-simple'
18+
testCompile 'org.mockito:mockito-core'
19+
testCompile 'org.assertj:assertj-core'
20+
testCompile project(':allure-java-commons-test')
21+
}
22+
23+
test.doFirst {
24+
jvmArgs "-javaagent:${configurations.agent.singleFile}"
25+
}
26+
27+
test {
28+
useJUnit()
29+
systemProperty 'allure.results.indentOutput', true
30+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package io.qameta.allure.jbehave;
2+
3+
import io.qameta.allure.Allure;
4+
import io.qameta.allure.AllureLifecycle;
5+
import io.qameta.allure.model.Stage;
6+
import io.qameta.allure.model.Status;
7+
import io.qameta.allure.model.StepResult;
8+
import io.qameta.allure.model.TestResult;
9+
import io.qameta.allure.util.ResultsUtils;
10+
import org.jbehave.core.model.Story;
11+
import org.jbehave.core.reporters.NullStoryReporter;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
15+
import javax.xml.bind.DatatypeConverter;
16+
import java.nio.charset.StandardCharsets;
17+
import java.security.MessageDigest;
18+
import java.security.NoSuchAlgorithmException;
19+
import java.util.Map;
20+
import java.util.Objects;
21+
import java.util.Optional;
22+
import java.util.UUID;
23+
import java.util.concurrent.ConcurrentHashMap;
24+
import java.util.stream.Stream;
25+
26+
import static io.qameta.allure.util.ResultsUtils.createHostLabel;
27+
import static io.qameta.allure.util.ResultsUtils.createStoryLabel;
28+
import static io.qameta.allure.util.ResultsUtils.createThreadLabel;
29+
30+
/**
31+
* @author charlie (Dmitry Baev).
32+
*/
33+
public class AllureJbehave extends NullStoryReporter {
34+
35+
private static final String MD_5 = "md5";
36+
37+
private final AllureLifecycle lifecycle;
38+
39+
private final ThreadLocal<Story> stories = new InheritableThreadLocal<>();
40+
41+
private final ThreadLocal<String> scenarios
42+
= InheritableThreadLocal.withInitial(() -> UUID.randomUUID().toString());
43+
44+
private final Map<String, Status> scenarioStatusStorage = new ConcurrentHashMap<>();
45+
46+
public AllureJbehave() {
47+
this(Allure.getLifecycle());
48+
}
49+
50+
public AllureJbehave(final AllureLifecycle lifecycle) {
51+
this.lifecycle = lifecycle;
52+
}
53+
54+
@Override
55+
public void beforeStory(final Story story, final boolean givenStory) {
56+
stories.set(story);
57+
}
58+
59+
@Override
60+
public void afterStory(final boolean givenStory) {
61+
stories.remove();
62+
}
63+
64+
@Override
65+
public void beforeScenario(final String title) {
66+
final Story story = stories.get();
67+
final String uuid = scenarios.get();
68+
final String fullName = String.format("%s: %s", story.getName(), title);
69+
final TestResult result = new TestResult()
70+
.withUuid(uuid)
71+
.withName(title)
72+
.withFullName(fullName)
73+
.withStage(Stage.SCHEDULED)
74+
.withLabels(createStoryLabel(story.getName()), createHostLabel(), createThreadLabel())
75+
.withDescription(story.getDescription().asString())
76+
.withHistoryId(md5(fullName));
77+
getLifecycle().scheduleTestCase(result);
78+
getLifecycle().startTestCase(result.getUuid());
79+
}
80+
81+
@Override
82+
public void afterScenario() {
83+
final String uuid = scenarios.get();
84+
final Status status = scenarioStatusStorage.getOrDefault(uuid, Status.PASSED);
85+
86+
getLifecycle().updateTestCase(uuid, testResult -> testResult.withStatus(status));
87+
getLifecycle().stopTestCase(uuid);
88+
getLifecycle().writeTestCase(uuid);
89+
scenarios.remove();
90+
}
91+
92+
@Override
93+
public void beforeStep(final String step) {
94+
final String stepUuid = UUID.randomUUID().toString();
95+
getLifecycle().startStep(stepUuid, new StepResult().withName(step));
96+
}
97+
98+
@Override
99+
public void successful(final String step) {
100+
getLifecycle().updateStep(result -> result.withStatus(Status.PASSED));
101+
getLifecycle().stopStep();
102+
updateScenarioStatus(Status.PASSED);
103+
}
104+
105+
@Override
106+
public void ignorable(final String step) {
107+
getLifecycle().updateStep(result -> result.withStatus(Status.SKIPPED));
108+
getLifecycle().stopStep();
109+
updateScenarioStatus(Status.SKIPPED);
110+
}
111+
112+
@Override
113+
public void pending(final String step) {
114+
getLifecycle().updateStep(result -> result.withStatus(Status.SKIPPED));
115+
getLifecycle().stopStep();
116+
updateScenarioStatus(Status.SKIPPED);
117+
}
118+
119+
@Override
120+
public void failed(final String step, final Throwable cause) {
121+
ResultsUtils.getStatus(cause).ifPresent(status ->
122+
getLifecycle().updateStep(result -> result.withStatus(status))
123+
);
124+
getLifecycle().stopStep();
125+
updateScenarioStatus(Status.FAILED);
126+
}
127+
128+
public AllureLifecycle getLifecycle() {
129+
return lifecycle;
130+
}
131+
132+
protected void updateScenarioStatus(final Status passed) {
133+
final String scenarioUuid = scenarios.get();
134+
max(scenarioStatusStorage.get(scenarioUuid), passed)
135+
.ifPresent(status -> scenarioStatusStorage.put(scenarioUuid, status));
136+
}
137+
138+
private String md5(final String string) {
139+
return DatatypeConverter.printHexBinary(getMessageDigest()
140+
.digest(string.getBytes(StandardCharsets.UTF_8))
141+
);
142+
}
143+
144+
private MessageDigest getMessageDigest() {
145+
try {
146+
return MessageDigest.getInstance(MD_5);
147+
} catch (NoSuchAlgorithmException e) {
148+
throw new IllegalStateException("Could not find md5 hashing algorithm", e);
149+
}
150+
}
151+
152+
private Optional<Status> max(final Status first, final Status second) {
153+
return Stream.of(first, second)
154+
.filter(Objects::nonNull)
155+
.min(Status::compareTo);
156+
}
157+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package io.qameta.allure.jbehave;
2+
3+
import io.qameta.allure.AllureLifecycle;
4+
import io.qameta.allure.aspects.AttachmentsAspects;
5+
import io.qameta.allure.aspects.StepsAspects;
6+
import io.qameta.allure.jbehave.steps.StackSteps;
7+
import io.qameta.allure.model.Status;
8+
import io.qameta.allure.model.TestResult;
9+
import io.qameta.allure.test.AllureResultsWriterStub;
10+
import org.assertj.core.groups.Tuple;
11+
import org.jbehave.core.configuration.MostUsefulConfiguration;
12+
import org.jbehave.core.embedder.Embedder;
13+
import org.jbehave.core.embedder.EmbedderControls;
14+
import org.jbehave.core.io.LoadFromClasspath;
15+
import org.jbehave.core.reporters.StoryReporterBuilder;
16+
import org.jbehave.core.steps.InstanceStepsFactory;
17+
import org.junit.Before;
18+
import org.junit.Rule;
19+
import org.junit.Test;
20+
import org.junit.rules.TemporaryFolder;
21+
22+
import java.io.File;
23+
import java.util.List;
24+
25+
import static java.util.Collections.singletonList;
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
/**
29+
* @author charlie (Dmitry Baev).
30+
*/
31+
public class AllureJbehaveTest {
32+
33+
private AllureResultsWriterStub results;
34+
private Embedder embedder = new Embedder();
35+
36+
@Rule
37+
public TemporaryFolder folder = new TemporaryFolder();
38+
39+
@Before
40+
public void setUp() throws Exception {
41+
results = new AllureResultsWriterStub();
42+
final AllureLifecycle lifecycle = new AllureLifecycle(results);
43+
StepsAspects.setLifecycle(lifecycle);
44+
AttachmentsAspects.setLifecycle(lifecycle);
45+
embedder.useEmbedderControls(new EmbedderControls()
46+
.doGenerateViewAfterStories(false)
47+
.doVerboseFailures(true)
48+
);
49+
embedder.useConfiguration(new MostUsefulConfiguration()
50+
.useStoryLoader(new LoadFromClasspath(this.getClass()))
51+
.useStoryReporterBuilder(new ReportlessStoryReporterBuilder(folder.newFolder())
52+
.withReporters(new AllureJbehave(lifecycle))
53+
)
54+
);
55+
embedder.useCandidateSteps(new InstanceStepsFactory(embedder.configuration(), new StackSteps())
56+
.createCandidateSteps()
57+
);
58+
}
59+
60+
@Test
61+
public void shouldAddResults() throws Exception {
62+
embedder.runStoriesAsPaths(singletonList("stories/stack_story.story"));
63+
final List<TestResult> testResults = results.getTestResults();
64+
65+
assertThat(testResults)
66+
.hasSize(2)
67+
.extracting(TestResult::getFullName, TestResult::getStatus)
68+
.containsExactlyInAnyOrder(
69+
Tuple.tuple("stack_story.story: Add elements to empty stack", Status.FAILED),
70+
Tuple.tuple("stack_story.story: Clear stack", Status.PASSED)
71+
);
72+
}
73+
74+
static class ReportlessStoryReporterBuilder extends StoryReporterBuilder {
75+
76+
private final File outputDirectory;
77+
78+
ReportlessStoryReporterBuilder(final File outputDirectory) {
79+
this.outputDirectory = outputDirectory;
80+
}
81+
82+
@Override
83+
public File outputDirectory() {
84+
return outputDirectory;
85+
}
86+
}
87+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package io.qameta.allure.jbehave.steps;
2+
3+
import org.jbehave.core.annotations.Given;
4+
import org.jbehave.core.annotations.Then;
5+
import org.jbehave.core.annotations.UsingSteps;
6+
import org.jbehave.core.annotations.When;
7+
import org.jbehave.core.steps.CandidateSteps;
8+
9+
import java.util.Stack;
10+
11+
import static org.assertj.core.api.Assertions.assertThat;
12+
13+
/**
14+
* @author charlie (Dmitry Baev).
15+
*/
16+
public class StackSteps {
17+
18+
private Stack<String> stack = new Stack<>();
19+
20+
@Given("an empty stack")
21+
public void createEmptyStack() {
22+
stack.clear();
23+
}
24+
25+
@Given("a stack with elements")
26+
public void createStack() {
27+
stack.clear();
28+
stack.push("a");
29+
stack.push("b");
30+
stack.push("c");
31+
}
32+
33+
@When("I add $number elements")
34+
public void addElements(int elementCount) {
35+
for (int i = 0; i < elementCount; i++) {
36+
stack.add((new Integer(i)).toString());
37+
}
38+
}
39+
40+
@When("I clear stack")
41+
public void clearStack() {
42+
stack.clear();
43+
}
44+
45+
@Then("the stack should have $number elements")
46+
public void assertElementCount(int elementCount) {
47+
assertThat(stack)
48+
.hasSize(elementCount);
49+
}
50+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Scenario: Add elements to empty stack
2+
3+
Given an empty stack
4+
When I add 4 elements
5+
Then the stack should have 2 elements
6+
7+
Scenario: Clear stack
8+
9+
Given a stack with elements
10+
When I clear stack
11+
Then the stack should have 0 elements

0 commit comments

Comments
 (0)