1 | |
package org.jbehave.core.embedder; |
2 | |
|
3 | |
import java.io.File; |
4 | |
import java.util.ArrayList; |
5 | |
import java.util.List; |
6 | |
import java.util.Properties; |
7 | |
|
8 | |
import org.apache.commons.lang.builder.ToStringBuilder; |
9 | |
import org.apache.commons.lang.builder.ToStringStyle; |
10 | |
import org.jbehave.core.Embeddable; |
11 | |
import org.jbehave.core.configuration.Configuration; |
12 | |
import org.jbehave.core.configuration.MostUsefulConfiguration; |
13 | |
import org.jbehave.core.failures.BatchFailures; |
14 | |
import org.jbehave.core.junit.AnnotatedEmbedderRunner; |
15 | |
import org.jbehave.core.model.Story; |
16 | |
import org.jbehave.core.reporters.StepdocReporter; |
17 | |
import org.jbehave.core.reporters.StoryReporterBuilder; |
18 | |
import org.jbehave.core.reporters.ViewGenerator; |
19 | |
import org.jbehave.core.steps.CandidateSteps; |
20 | |
import org.jbehave.core.steps.StepFinder; |
21 | |
import org.jbehave.core.steps.Stepdoc; |
22 | |
import org.jbehave.core.steps.StepCollector.Stage; |
23 | |
|
24 | |
|
25 | |
|
26 | |
|
27 | |
|
28 | |
public class Embedder { |
29 | |
|
30 | 69 | private Configuration configuration = new MostUsefulConfiguration(); |
31 | 69 | private List<CandidateSteps> candidateSteps = new ArrayList<CandidateSteps>(); |
32 | 69 | private EmbedderControls embedderControls = new EmbedderControls(); |
33 | |
private StoryRunner storyRunner; |
34 | |
private EmbedderMonitor embedderMonitor; |
35 | |
|
36 | |
public Embedder() { |
37 | 46 | this(new StoryRunner(), new PrintStreamEmbedderMonitor()); |
38 | 46 | } |
39 | |
|
40 | 69 | public Embedder(StoryRunner storyRunner, EmbedderMonitor embedderMonitor) { |
41 | 69 | this.storyRunner = storyRunner; |
42 | 69 | this.embedderMonitor = embedderMonitor; |
43 | 69 | } |
44 | |
|
45 | |
public void runStoriesAsEmbeddables(List<String> classNames, EmbedderClassLoader classLoader) { |
46 | 9 | EmbedderControls embedderControls = embedderControls(); |
47 | 9 | if (embedderControls.skip()) { |
48 | 1 | embedderMonitor.embeddablesSkipped(classNames); |
49 | 1 | return; |
50 | |
} |
51 | |
|
52 | 8 | BatchFailures batchFailures = new BatchFailures(); |
53 | 8 | for (Embeddable embeddable : embeddables(classNames, classLoader)) { |
54 | 14 | String name = embeddable.getClass().getName(); |
55 | |
try { |
56 | 14 | embedderMonitor.runningEmbeddable(name); |
57 | 14 | embeddable.useEmbedder(this); |
58 | 14 | embeddable.run(); |
59 | 4 | } catch (Throwable e) { |
60 | 4 | if (embedderControls.batch()) { |
61 | |
|
62 | 2 | batchFailures.put(name, e); |
63 | |
} else { |
64 | 2 | if (embedderControls.ignoreFailureInStories()) { |
65 | 1 | embedderMonitor.embeddableFailed(name, e); |
66 | |
} else { |
67 | 1 | throw new RunningStoriesFailed(name, e); |
68 | |
} |
69 | |
} |
70 | 10 | } |
71 | 13 | } |
72 | |
|
73 | 7 | if (embedderControls.batch() && batchFailures.size() > 0) { |
74 | 2 | if (embedderControls.ignoreFailureInStories()) { |
75 | 1 | embedderMonitor.batchFailed(batchFailures); |
76 | |
} else { |
77 | 1 | throw new RunningStoriesFailed(batchFailures); |
78 | |
} |
79 | |
} |
80 | |
|
81 | 6 | if (embedderControls.generateViewAfterStories()) { |
82 | 5 | generateStoriesView(); |
83 | |
} |
84 | |
|
85 | 6 | } |
86 | |
|
87 | |
private List<Embeddable> embeddables(List<String> classNames, EmbedderClassLoader classLoader) { |
88 | 8 | List<Embeddable> embeddables = new ArrayList<Embeddable>(); |
89 | 8 | for (String className : classNames) { |
90 | 16 | if ( !classLoader.isAbstract(className)){ |
91 | 15 | embeddables.add(classLoader.newInstance(Embeddable.class, className)); |
92 | |
} |
93 | |
} |
94 | 8 | return embeddables; |
95 | |
} |
96 | |
|
97 | |
public void runStoriesWithAnnotatedEmbedderRunner(String runnerClass, List<String> classNames, |
98 | |
EmbedderClassLoader classLoader) { |
99 | 4 | List<AnnotatedEmbedderRunner> runners = annotatedEmbedderRunners(runnerClass, classNames, classLoader); |
100 | 3 | for (AnnotatedEmbedderRunner runner : runners) { |
101 | |
try { |
102 | 3 | Object annotatedInstance = runner.createTest(); |
103 | 3 | if (annotatedInstance instanceof Embeddable) { |
104 | 2 | ((Embeddable) annotatedInstance).run(); |
105 | |
} else { |
106 | 1 | embedderMonitor.annotatedInstanceNotOfType(annotatedInstance, Embeddable.class); |
107 | |
} |
108 | 1 | } catch (Throwable e) { |
109 | 1 | throw new AnnotatedEmbedderRunFailed(runner, e); |
110 | 2 | } |
111 | |
} |
112 | 2 | } |
113 | |
|
114 | |
private List<AnnotatedEmbedderRunner> annotatedEmbedderRunners(String runnerClassName, List<String> classNames, |
115 | |
EmbedderClassLoader classLoader) { |
116 | 4 | Class<?> runnerClass = loadClass(runnerClassName, classLoader); |
117 | 4 | List<AnnotatedEmbedderRunner> runners = new ArrayList<AnnotatedEmbedderRunner>(); |
118 | 4 | for (String annotatedClassName : classNames) { |
119 | 4 | runners.add(newAnnotatedEmbedderRunner(runnerClass, annotatedClassName, classLoader)); |
120 | |
} |
121 | 3 | return runners; |
122 | |
} |
123 | |
|
124 | |
private AnnotatedEmbedderRunner newAnnotatedEmbedderRunner(Class<?> runnerClass, String annotatedClassName, |
125 | |
EmbedderClassLoader classLoader) { |
126 | |
try { |
127 | 4 | Class<?> annotatedClass = loadClass(annotatedClassName, classLoader); |
128 | 3 | return (AnnotatedEmbedderRunner) runnerClass.getConstructor(Class.class).newInstance(annotatedClass); |
129 | 1 | } catch (Exception e) { |
130 | 1 | throw new AnnotatedEmbedderRunnerInstantiationFailed(runnerClass, annotatedClassName, classLoader, e); |
131 | |
} |
132 | |
} |
133 | |
|
134 | |
private Class<?> loadClass(String className, EmbedderClassLoader classLoader) { |
135 | |
try { |
136 | 8 | return classLoader.loadClass(className); |
137 | 1 | } catch (ClassNotFoundException e) { |
138 | 1 | throw new ClassLoadingFailed(className, classLoader, e); |
139 | |
} |
140 | |
} |
141 | |
|
142 | |
public void runStoriesAsPaths(List<String> storyPaths) { |
143 | 18 | EmbedderControls embedderControls = embedderControls(); |
144 | 18 | if (embedderControls.skip()) { |
145 | 1 | embedderMonitor.storiesSkipped(storyPaths); |
146 | 1 | return; |
147 | |
} |
148 | |
|
149 | 17 | Configuration configuration = configuration(); |
150 | 17 | List<CandidateSteps> candidateSteps = candidateSteps(); |
151 | |
|
152 | 17 | storyRunner.runBeforeOrAfterStories(configuration, candidateSteps, Stage.BEFORE); |
153 | |
|
154 | 17 | BatchFailures batchFailures = new BatchFailures(); |
155 | 17 | buildReporters(configuration, storyPaths); |
156 | 17 | for (String storyPath : storyPaths) { |
157 | |
try { |
158 | 23 | embedderMonitor.runningStory(storyPath); |
159 | 23 | Story story = storyRunner.storyOfPath(configuration, storyPath); |
160 | 23 | storyRunner.run(configuration, candidateSteps, story); |
161 | 7 | } catch (Throwable e) { |
162 | 7 | if (embedderControls.batch()) { |
163 | |
|
164 | 4 | batchFailures.put(storyPath, e); |
165 | |
} else { |
166 | 3 | if (embedderControls.ignoreFailureInStories()) { |
167 | 2 | embedderMonitor.storyFailed(storyPath, e); |
168 | |
} else { |
169 | 1 | throw new RunningStoriesFailed(storyPath, e); |
170 | |
} |
171 | |
} |
172 | 38 | } |
173 | |
} |
174 | |
|
175 | 16 | storyRunner.runBeforeOrAfterStories(configuration, candidateSteps, Stage.AFTER); |
176 | |
|
177 | 16 | if (embedderControls.batch() && batchFailures.size() > 0) { |
178 | 2 | if (embedderControls.ignoreFailureInStories()) { |
179 | 1 | embedderMonitor.batchFailed(batchFailures); |
180 | |
} else { |
181 | 1 | throw new RunningStoriesFailed(batchFailures); |
182 | |
} |
183 | |
} |
184 | |
|
185 | 15 | if (embedderControls.generateViewAfterStories()) { |
186 | 12 | generateStoriesView(); |
187 | |
} |
188 | |
|
189 | 15 | } |
190 | |
|
191 | |
private void buildReporters(Configuration configuration, List<String> storyPaths) { |
192 | 17 | StoryReporterBuilder reporterBuilder = configuration.storyReporterBuilder(); |
193 | 17 | configuration.useStoryReporters(reporterBuilder.build(storyPaths)); |
194 | 17 | } |
195 | |
|
196 | |
public void generateStoriesView() { |
197 | 17 | StoryReporterBuilder builder = configuration().storyReporterBuilder(); |
198 | 17 | File outputDirectory = builder.outputDirectory(); |
199 | 17 | List<String> formatNames = builder.formatNames(true); |
200 | 17 | generateStoriesView(outputDirectory, formatNames, builder.viewResources()); |
201 | 17 | } |
202 | |
|
203 | |
public void generateStoriesView(File outputDirectory, List<String> formats, Properties viewResources) { |
204 | 22 | EmbedderControls embedderControls = embedderControls(); |
205 | |
|
206 | 22 | if (embedderControls.skip()) { |
207 | 1 | embedderMonitor.storiesViewNotGenerated(); |
208 | 1 | return; |
209 | |
} |
210 | 21 | ViewGenerator viewGenerator = configuration().viewGenerator(); |
211 | |
try { |
212 | 21 | embedderMonitor.generatingStoriesView(outputDirectory, formats, viewResources); |
213 | 21 | viewGenerator.generateView(outputDirectory, formats, viewResources); |
214 | 1 | } catch (RuntimeException e) { |
215 | 1 | embedderMonitor.storiesViewGenerationFailed(outputDirectory, formats, viewResources, e); |
216 | 1 | throw new ViewGenerationFailed(outputDirectory, formats, viewResources, e); |
217 | 20 | } |
218 | 20 | int stories = viewGenerator.countStories(); |
219 | 20 | int scenarios = viewGenerator.countScenarios(); |
220 | 20 | int failedScenarios = viewGenerator.countFailedScenarios(); |
221 | 20 | embedderMonitor.storiesViewGenerated(stories, scenarios, failedScenarios); |
222 | 20 | if (!embedderControls.ignoreFailureInView() && failedScenarios > 0) { |
223 | 1 | throw new RunningStoriesFailed(stories, scenarios, failedScenarios); |
224 | |
} |
225 | |
|
226 | 19 | } |
227 | |
|
228 | |
public void reportStepdocs() { |
229 | 1 | Configuration configuration = configuration(); |
230 | 1 | List<CandidateSteps> candidateSteps = candidateSteps(); |
231 | 1 | StepFinder finder = configuration.stepFinder(); |
232 | 1 | StepdocReporter reporter = configuration.stepdocReporter(); |
233 | 1 | List<Object> stepsInstances = finder.stepsInstances(candidateSteps); |
234 | 1 | reporter.stepdocs(finder.stepdocs(candidateSteps), stepsInstances); |
235 | 1 | } |
236 | |
|
237 | |
public void reportMatchingStepdocs(String stepAsString) { |
238 | 3 | Configuration configuration = configuration(); |
239 | 3 | List<CandidateSteps> candidateSteps = candidateSteps(); |
240 | 3 | StepFinder finder = configuration.stepFinder(); |
241 | 3 | StepdocReporter reporter = configuration.stepdocReporter(); |
242 | 3 | List<Stepdoc> matching = finder.findMatching(stepAsString, candidateSteps); |
243 | 3 | List<Object> stepsInstances = finder.stepsInstances(candidateSteps); |
244 | 3 | reporter.stepdocsMatching(stepAsString, matching, stepsInstances); |
245 | 3 | } |
246 | |
|
247 | |
public Configuration configuration() { |
248 | 96 | return configuration; |
249 | |
} |
250 | |
|
251 | |
public List<CandidateSteps> candidateSteps() { |
252 | 32 | return candidateSteps; |
253 | |
} |
254 | |
|
255 | |
public EmbedderControls embedderControls() { |
256 | 56 | return embedderControls; |
257 | |
} |
258 | |
|
259 | |
public EmbedderMonitor embedderMonitor() { |
260 | 2 | return embedderMonitor; |
261 | |
} |
262 | |
|
263 | |
public StoryRunner storyRunner() { |
264 | 2 | return storyRunner; |
265 | |
} |
266 | |
|
267 | |
public void useConfiguration(Configuration configuration) { |
268 | 18 | this.configuration = configuration; |
269 | 18 | } |
270 | |
|
271 | |
public void useCandidateSteps(List<CandidateSteps> candidateSteps) { |
272 | 22 | this.candidateSteps = candidateSteps; |
273 | 22 | } |
274 | |
|
275 | |
public void useEmbedderControls(EmbedderControls embedderControls) { |
276 | 23 | this.embedderControls = embedderControls; |
277 | 23 | } |
278 | |
|
279 | |
public void useEmbedderMonitor(EmbedderMonitor embedderMonitor) { |
280 | 1 | this.embedderMonitor = embedderMonitor; |
281 | 1 | } |
282 | |
|
283 | |
public void useStoryRunner(StoryRunner storyRunner) { |
284 | 1 | this.storyRunner = storyRunner; |
285 | 1 | } |
286 | |
|
287 | |
@Override |
288 | |
public String toString() { |
289 | 1 | return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); |
290 | |
} |
291 | |
|
292 | |
@SuppressWarnings("serial") |
293 | |
public static class ClassLoadingFailed extends RuntimeException { |
294 | |
|
295 | |
public ClassLoadingFailed(String className, EmbedderClassLoader classLoader, Throwable cause) { |
296 | 1 | super("Failed to load class "+className+" with classLoader "+classLoader, cause); |
297 | 1 | } |
298 | |
|
299 | |
} |
300 | |
|
301 | |
@SuppressWarnings("serial") |
302 | |
public static class AnnotatedEmbedderRunnerInstantiationFailed extends RuntimeException { |
303 | |
|
304 | |
public AnnotatedEmbedderRunnerInstantiationFailed(Class<?> runnerClass, String annotatedClassName, |
305 | |
EmbedderClassLoader classLoader, Throwable cause) { |
306 | 1 | super("Failed to instantiate annotated embedder runner " + runnerClass + " with annotatedClassName " |
307 | |
+ annotatedClassName + " and classLoader " + classLoader, cause); |
308 | 1 | } |
309 | |
|
310 | |
} |
311 | |
|
312 | |
|
313 | |
@SuppressWarnings("serial") |
314 | |
public static class AnnotatedEmbedderRunFailed extends RuntimeException { |
315 | |
|
316 | |
public AnnotatedEmbedderRunFailed(AnnotatedEmbedderRunner runner, Throwable cause) { |
317 | 1 | super("Annotated embedder run failed with runner "+runner.toString(), cause); |
318 | 1 | } |
319 | |
|
320 | |
|
321 | |
} |
322 | |
|
323 | |
@SuppressWarnings("serial") |
324 | |
public static class RunningStoriesFailed extends RuntimeException { |
325 | |
|
326 | |
public RunningStoriesFailed(int stories, int scenarios, int failedScenarios) { |
327 | 1 | super("Failures in running " + stories +" stories containing "+ scenarios + " scenarios (of which " + failedScenarios |
328 | |
+ " failed)"); |
329 | 1 | } |
330 | |
|
331 | |
public RunningStoriesFailed(BatchFailures failures) { |
332 | 2 | super("Failures in running stories in batch: " + failures); |
333 | 2 | } |
334 | |
|
335 | |
public RunningStoriesFailed(String name, Throwable cause) { |
336 | 2 | super("Failures in running story " + name, cause); |
337 | 2 | } |
338 | |
} |
339 | |
|
340 | |
@SuppressWarnings("serial") |
341 | |
public static class ViewGenerationFailed extends RuntimeException { |
342 | |
public ViewGenerationFailed(File outputDirectory, List<String> formats, Properties viewResources, |
343 | |
RuntimeException cause) { |
344 | 1 | super("View generation failed to "+outputDirectory+" for formats "+formats+" and resources "+viewResources, cause); |
345 | 1 | } |
346 | |
} |
347 | |
} |