From e9806d9e133c63ec62991189f08f929dd52e81d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Fri, 3 Jan 2014 22:27:48 +0100 Subject: [PATCH 1/2] Let the Java JSON Formatter handle output and embeddings in hooks --- java/src/main/java/gherkin/JSONParser.java | 15 +- .../java/gherkin/formatter/JSONFormatter.java | 69 +++++---- .../gherkin/formatter/JSONFormatterTest.java | 131 ++++++++++++++++-- .../gherkin/formatter/model/complicated.json | 64 ++++++--- 4 files changed, 224 insertions(+), 55 deletions(-) diff --git a/java/src/main/java/gherkin/JSONParser.java b/java/src/main/java/gherkin/JSONParser.java index 943fae11..8c85b2bf 100644 --- a/java/src/main/java/gherkin/JSONParser.java +++ b/java/src/main/java/gherkin/JSONParser.java @@ -43,10 +43,11 @@ public void parse(String src) { formatter.uri(getString(o, "uri")); new Feature(comments(o), tags(o), keyword(o), name(o), description(o), line(o), id(o)).replay(formatter); for (Map featureElement : (List) getList(o, "elements")) { - featureElement(featureElement).replay(formatter); + BasicStatement element = featureElement(featureElement); for (Map hook : (List) getList(featureElement, "before")) { before(hook); } + element.replay(formatter); for (Map step : (List) getList(featureElement, "steps")) { step(step); } @@ -75,6 +76,8 @@ private BasicStatement featureElement(Map o) { } private void before(Map o) { + handleEmbeddings(o); + handleOutput(o); Map m = (Map) o.get("match"); Match match = new Match(arguments(m), location(m)); Map r = (Map) o.get("result"); @@ -83,6 +86,8 @@ private void before(Map o) { } private void after(Map o) { + handleEmbeddings(o); + handleOutput(o); Map m = (Map) o.get("match"); Match match = new Match(arguments(m), location(m)); Map r = (Map) o.get("result"); @@ -110,11 +115,17 @@ private void step(Map o) { new Match(arguments(m), location(m)).replay(reporter); } + handleEmbeddings(o); + + handleOutput(o); + if (o.containsKey("result")) { Map r = (Map) o.get("result"); new Result(status(r), duration(r), errorMessage(r)).replay(reporter); } + } + private void handleEmbeddings(Map o) { if (o.containsKey("embeddings")) { List embeddings = (List) o.get("embeddings"); for (Map embedding : embeddings) { @@ -125,7 +136,9 @@ private void step(Map o) { } } } + } + private void handleOutput(Map o) { if (o.containsKey("output")) { List output = (List) o.get("output"); for (String text : output) { diff --git a/java/src/main/java/gherkin/formatter/JSONFormatter.java b/java/src/main/java/gherkin/formatter/JSONFormatter.java index 9aa40346..0fb59da6 100644 --- a/java/src/main/java/gherkin/formatter/JSONFormatter.java +++ b/java/src/main/java/gherkin/formatter/JSONFormatter.java @@ -23,7 +23,8 @@ public class JSONFormatter implements Reporter, Formatter { private Map featureMap; private String uri; - private List beforeHooks = new ArrayList(); + private Map> beforeHookScenario = new HashMap>(); + private Map currentHook = new HashMap(); private enum Phase {step, match, embedding, output, result}; @@ -61,15 +62,18 @@ private enum Phase {step, match, embedding, output, result}; */ private Map getCurrentStep(Phase phase) { String target = phase.ordinal() <= Phase.match.ordinal()?Phase.match.name():Phase.result.name(); - Map lastWithValue = null; - for (Map stepOrHook : getSteps()) { - if (stepOrHook.get(target) == null) { - return stepOrHook; - } else { - lastWithValue = stepOrHook; + Map> currentScenario = getFeatureElement(); + if (currentScenario != null) { + List steps = currentScenario.get("steps"); + if (steps != null) { + for (Map step : steps) { + if (step.get(target) == null) { + return step; + } + } } } - return lastWithValue; + return null; } @@ -97,9 +101,9 @@ public void background(Background background) { @Override public void scenario(Scenario scenario) { getFeatureElements().add(scenario.toMap()); - if (beforeHooks.size() > 0) { - getFeatureElement().put("before", beforeHooks); - beforeHooks = new ArrayList(); + if (beforeHookScenario.get("before") != null) { + getFeatureElement().put("before", beforeHookScenario.get("before")); + beforeHookScenario.remove("before"); } } @@ -128,12 +132,22 @@ public void embedding(String mimeType, byte[] data) { final Map embedding = new HashMap(); embedding.put("mime_type", mimeType); embedding.put("data", Base64.encodeBytes(data)); - getEmbeddings().add(embedding); + Map currentStep = getCurrentStep(Phase.embedding); + if (currentStep != null) { + getEmbeddings(currentStep).add(embedding); + } else { + getEmbeddings(currentHook).add(embedding); + } } @Override public void write(String text) { - getOutput().add(text); + Map currentStep = getCurrentStep(Phase.output); + if (currentStep != null) { + getOutput(currentStep).add(text); + } else { + getOutput(currentHook).add(text); + } } @Override @@ -143,23 +157,28 @@ public void result(Result result) { @Override public void before(Match match, Result result) { - beforeHooks.add(buildHookMap(match,result)); + addHook(beforeHookScenario, match, result, "before"); } @Override public void after(Match match, Result result) { - List hooks = getFeatureElement().get("after"); + addHook(getFeatureElement(), match, result, "after"); + } + + private void addHook(final Map> currentScenario, final Match match, final Result result, final String hook) { + List hooks = currentScenario.get(hook); if (hooks == null) { hooks = new ArrayList(); - getFeatureElement().put("after", hooks); + currentScenario.put(hook, hooks); } hooks.add(buildHookMap(match,result)); } private Map buildHookMap(final Match match, final Result result) { - final Map hookMap = new HashMap(); - hookMap.put("match", match.toMap()); - hookMap.put("result", result.toMap()); + currentHook.put("match", match.toMap()); + currentHook.put("result", result.toMap()); + final Map hookMap = currentHook; + currentHook = new HashMap(); return hookMap; } @@ -240,20 +259,20 @@ private List getSteps() { return steps; } - private List> getEmbeddings() { - List> embeddings = (List>) getCurrentStep(Phase.embedding).get("embeddings"); + private List> getEmbeddings(final Map currentStep) { + List> embeddings = (List>) currentStep.get("embeddings"); if (embeddings == null) { embeddings = new ArrayList>(); - getCurrentStep(Phase.embedding).put("embeddings", embeddings); + currentStep.put("embeddings", embeddings); } return embeddings; } - private List getOutput() { - List output = (List) getCurrentStep(Phase.output).get("output"); + private List getOutput(final Map currentStep) { + List output = (List) currentStep.get("output"); if (output == null) { output = new ArrayList(); - getCurrentStep(Phase.output).put("output", output); + currentStep.put("output", output); } return output; } diff --git a/java/src/test/java/gherkin/formatter/JSONFormatterTest.java b/java/src/test/java/gherkin/formatter/JSONFormatterTest.java index ab34d71e..64df651d 100644 --- a/java/src/test/java/gherkin/formatter/JSONFormatterTest.java +++ b/java/src/test/java/gherkin/formatter/JSONFormatterTest.java @@ -3,6 +3,7 @@ import static gherkin.util.FixJava.readResource; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -187,17 +188,28 @@ public void testBeforeHooks() { Feature feature = feature("Test Feature"); Scenario scenario1 = scenario("Test Scenario 1"); Scenario scenario2 = scenario("Test Scenario 2"); + final byte[] data1 = new byte[] {1, 2, 3}; + final byte[] data2 = new byte[] {4, 5, 6}; + String text1 = "text1"; + String text2 = "text2"; + Result hookResult = result("passed"); jsonFormatter.uri(uri()); jsonFormatter.feature(feature); jsonFormatter.startOfScenarioLifeCycle(scenario1); - jsonFormatter.before(match(), result("passed")); + jsonFormatter.embedding("mime-type", data1); + jsonFormatter.write(text1); + jsonFormatter.before(match(), hookResult); jsonFormatter.scenario(scenario1); jsonFormatter.endOfScenarioLifeCycle(scenario1); jsonFormatter.startOfScenarioLifeCycle(scenario2); - jsonFormatter.before(match(), result("passed")); + jsonFormatter.embedding("mime-type", data1); + jsonFormatter.write(text1); + jsonFormatter.embedding("mime-type", data2); + jsonFormatter.write(text2); + jsonFormatter.before(match(), hookResult); jsonFormatter.scenario(scenario2); jsonFormatter.endOfScenarioLifeCycle(scenario2); @@ -216,25 +228,111 @@ public void testBeforeHooks() { List beforeHooks = (List) scenarioJson.get("before"); assertEquals(1, beforeHooks.size()); + assertJsonHookData(hookResult, embeddingsCount(1), outputCount(1), (Map) beforeHooks.get(0)); scenarioJson = (Map) ((List) featureJson.get("elements")).get(1); assertEquals(scenario2.getName(), scenarioJson.get("name")); beforeHooks = (List) scenarioJson.get("before"); assertEquals(1, beforeHooks.size()); + assertJsonHookData(hookResult, embeddingsCount(2), outputCount(2), (Map) beforeHooks.get(0)); + } + + @SuppressWarnings("rawtypes") + @Test + public void testAfterHooks() { + StringBuilder stringBuilder = new StringBuilder(); + JSONFormatter jsonFormatter = new JSONPrettyFormatter(stringBuilder); + Feature feature = feature("Test Feature"); + Scenario scenario1 = scenario("Test Scenario 1"); + Scenario scenario2 = scenario("Test Scenario 2"); + final byte[] data1 = new byte[] {1, 2, 3}; + final byte[] data2 = new byte[] {4, 5, 6}; + String text1 = "text1"; + String text2 = "text2"; + Result hookResult = result("passed"); + + jsonFormatter.uri(uri()); + jsonFormatter.feature(feature); + + jsonFormatter.startOfScenarioLifeCycle(scenario1); + jsonFormatter.scenario(scenario1); + jsonFormatter.embedding("mime-type", data1); + jsonFormatter.write(text1); + jsonFormatter.after(match(), hookResult); + jsonFormatter.endOfScenarioLifeCycle(scenario1); + + jsonFormatter.startOfScenarioLifeCycle(scenario2); + jsonFormatter.scenario(scenario2); + jsonFormatter.embedding("mime-type", data1); + jsonFormatter.write(text1); + jsonFormatter.embedding("mime-type", data2); + jsonFormatter.write(text2); + jsonFormatter.after(match(), hookResult); + jsonFormatter.endOfScenarioLifeCycle(scenario2); + + jsonFormatter.eof(); + jsonFormatter.done(); + jsonFormatter.close(); + + Gson gson = new Gson(); + List result = gson.fromJson(stringBuilder.toString(), List.class); + + Map featureJson = (Map) result.get(0); + assertEquals(feature.getName(), featureJson.get("name")); + + Map scenarioJson = (Map) ((List) featureJson.get("elements")).get(0); + assertEquals(scenario1.getName(), scenarioJson.get("name")); + + List afterrHooks = (List) scenarioJson.get("after"); + assertEquals(1, afterrHooks.size()); + assertJsonHookData(hookResult, embeddingsCount(1), outputCount(1), (Map) afterrHooks.get(0)); + + scenarioJson = (Map) ((List) featureJson.get("elements")).get(1); + assertEquals(scenario2.getName(), scenarioJson.get("name")); + + afterrHooks = (List) scenarioJson.get("after"); + assertEquals(1, afterrHooks.size()); + assertJsonHookData(hookResult, embeddingsCount(2), outputCount(2), (Map) afterrHooks.get(0)); + } + + private void assertJsonStepData(Step step, Result stepResult, Map stepJson) { + assertEquals(step.getName(), stepJson.get("name")); + assertJsonEmbeddings(stepJson, embeddingsCount(1)); + assertJsonOutput(stepJson, outputCount(1)); + assertJsonResult(stepResult, stepJson); + } + + private void assertJsonHookData(Result hookResult, int embeddingsCount, int outputCount, Map hookJson) { + assertJsonEmbeddings(hookJson, embeddingsCount); + assertJsonOutput(hookJson, outputCount); + assertJsonResult(hookResult, hookJson); } - private void assertJsonStepData(Step step, Result stepResult, Map stepJson) { - assertEquals(step.getName(), stepJson.get("name")); - List embeddings1 = (List)stepJson.get("embeddings"); - assertNotNull("embeddings", embeddings1); - assertEquals("embeddings size", embeddings1.size(), 1); - List output1 = (List)stepJson.get("output"); - assertNotNull("output", output1); - assertEquals("output size", output1.size(), 1); - Map step1ResultJson = (Map) stepJson.get("result"); + private void assertJsonEmbeddings(Map jsonData, int embeddingsCount) { + List embeddings = (List)jsonData.get("embeddings"); + if (embeddingsCount != 0) { + assertNotNull("embeddings", embeddings); + assertEquals("embeddings size", embeddings.size(), embeddingsCount); + } else { + assertNull("embeddings", embeddings); + } + } + + private void assertJsonOutput(Map jsonData, int outputCount) { + List output = (List)jsonData.get("output"); + if (outputCount != 0) { + assertNotNull("output", output); + assertEquals("output size", output.size(), outputCount); + } else { + assertNull("output", output); + } + } + + private void assertJsonResult(Result stepResult, Map jsonData) { + Map step1ResultJson = (Map) jsonData.get("result"); assertEquals(stepResult.getStatus(), step1ResultJson.get("status")); - } + } private String uri() { return "uri"; @@ -245,7 +343,7 @@ private Feature feature(String featureName) { } private Scenario scenario(String scenarioName) { - return new Scenario(Collections.emptyList(), Collections.emptyList(), "", scenarioName, "", 13, ""); + return new Scenario(Collections.emptyList(), Collections.emptyList(), "", scenarioName, "", 13, ""); } private Step step(String keyword, String stepName) { @@ -260,4 +358,11 @@ private Result result(String status) { return new Result(status, 565L, ""); } + private int embeddingsCount(int count) { + return count; + } + + private int outputCount(int count) { + return count; + } } diff --git a/java/src/test/resources/gherkin/formatter/model/complicated.json b/java/src/test/resources/gherkin/formatter/model/complicated.json index 4e416d95..0dcae16c 100644 --- a/java/src/test/resources/gherkin/formatter/model/complicated.json +++ b/java/src/test/resources/gherkin/formatter/model/complicated.json @@ -16,6 +16,38 @@ { "id":"one/a-scenario", "type":"scenario", + "before":[ + { + "embeddings":[ + { + "mime_type":"text/plain", + "data":"Tm8sIEknbSBub3QgaW50ZXJlc3RlZCBpbiBkZXZlbG9waW5nIGEgcG93ZXJmdWwgYnJhaW4uIEFsbCBJJ20gYWZ0ZXIgaXMganVzdCBhIG1lZGlvY3JlIGJyYWluLCBzb21ldGhpbmcgbGlrZSB0aGUgUHJlc2lkZW50IG9mIHRoZSBBbWVyaWNhbiBUZWxlcGhvbmUgYW5kIFRlbGVncmFwaCBDb21wYW55Lg==" + } + ], + "output":[ + "Hello", + "World" + ], + "match":{ + "location":"features/step_definitions/hooks.rb:1" + }, + "result":{ + "status":"passed", + "error_message":"Passed hook", + "duration":3 + } + }, + { + "match":{ + "location":"features/step_definitions/hooks.rb:2" + }, + "result":{ + "status":"failed", + "error_message":"Failed hook", + "duration":22 + } + } + ], "steps":[ { "keyword":"Given ", @@ -30,11 +62,6 @@ ], "location":"features/step_definitions/steps.rb:1" }, - "result":{ - "status":"failed", - "error_message":"You suck", - "duration":-1 - }, "embeddings":[ { "mime_type":"text/plain", @@ -44,21 +71,26 @@ "output":[ "Hello", "World" - ] + ], + "result":{ + "status":"failed", + "error_message":"You suck", + "duration":-1 + } } ], "after":[ { - "match":{ - "location":"features/step_definitions/hooks.rb:1" - }, - "result":{ - "status":"passed", - "error_message":"Passed hook", - "duration":3 - } - }, - { + "embeddings":[ + { + "mime_type":"text/plain", + "data":"Tm8sIEknbSBub3QgaW50ZXJlc3RlZCBpbiBkZXZlbG9waW5nIGEgcG93ZXJmdWwgYnJhaW4uIEFsbCBJJ20gYWZ0ZXIgaXMganVzdCBhIG1lZGlvY3JlIGJyYWluLCBzb21ldGhpbmcgbGlrZSB0aGUgUHJlc2lkZW50IG9mIHRoZSBBbWVyaWNhbiBUZWxlcGhvbmUgYW5kIFRlbGVncmFwaCBDb21wYW55Lg==" + } + ], + "output":[ + "Hello", + "World" + ], "match":{ "location":"features/step_definitions/hooks.rb:3" }, From 02a4cc56055974207a34a94b20930ad69c95febd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Sun, 5 Jan 2014 15:39:24 +0100 Subject: [PATCH 2/2] Ensure that the Java JSON Formatter is compatible with the Rspec specs --- .../java/gherkin/formatter/JSONFormatter.java | 10 +++- .../gherkin/formatter/JSONFormatterTest.java | 51 ++++++++++++++++--- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/java/src/main/java/gherkin/formatter/JSONFormatter.java b/java/src/main/java/gherkin/formatter/JSONFormatter.java index 0fb59da6..25f2b9a8 100644 --- a/java/src/main/java/gherkin/formatter/JSONFormatter.java +++ b/java/src/main/java/gherkin/formatter/JSONFormatter.java @@ -183,7 +183,15 @@ private Map buildHookMap(final Match match, final Result result) { } public void appendDuration(final int timestamp) { - final Map result = (Map) getCurrentStep(Phase.result).get("result"); + Map result = null; + for (Map step : getFeatureElement().get("steps")) { + if (step.get("result") == null) { + break; + } else { + result = (Map) step.get("result"); + } + } + // check to make sure result exists (scenario outlines do not have results yet) if (result != null) { //convert to nanoseconds diff --git a/java/src/test/java/gherkin/formatter/JSONFormatterTest.java b/java/src/test/java/gherkin/formatter/JSONFormatterTest.java index 64df651d..25d71310 100644 --- a/java/src/test/java/gherkin/formatter/JSONFormatterTest.java +++ b/java/src/test/java/gherkin/formatter/JSONFormatterTest.java @@ -115,10 +115,10 @@ public void testAllStepsFirstOrderingOfCalls() { assertEquals(scenario.getName(), scenarioJson.get("name")); Map step1Json = (Map) ((List)scenarioJson.get("steps")).get(0); - assertJsonStepData(step1, step1Result, step1Json); + assertJsonStepData(step1, step1Result, embeddingsCount(1), outputCount(1), step1Json); Map step2Json = (Map) ((List)scenarioJson.get("steps")).get(1); - assertJsonStepData(step2, step2Result, step2Json); + assertJsonStepData(step2, step2Result, embeddingsCount(1), outputCount(1), step2Json); } @@ -172,10 +172,10 @@ public void testOneStepAtTheTimeOrderingOfCalls() { assertEquals(scenario.getName(), scenarioJson.get("name")); Map step1Json = (Map) ((List)scenarioJson.get("steps")).get(0); - assertJsonStepData(step1, step1Result, step1Json); + assertJsonStepData(step1, step1Result, embeddingsCount(1), outputCount(1), step1Json); Map step2Json = (Map) ((List)scenarioJson.get("steps")).get(1); - assertJsonStepData(step2, step2Result, step2Json); + assertJsonStepData(step2, step2Result, embeddingsCount(1), outputCount(1), step2Json); } @@ -296,10 +296,47 @@ public void testAfterHooks() { assertJsonHookData(hookResult, embeddingsCount(2), outputCount(2), (Map) afterrHooks.get(0)); } - private void assertJsonStepData(Step step, Result stepResult, Map stepJson) { + @SuppressWarnings("rawtypes") + @Test + public void testCompatibilityWithTheRspecSpecs() { + StringBuilder stringBuilder = new StringBuilder(); + JSONFormatter jsonFormatter = new JSONPrettyFormatter(stringBuilder); + Feature feature = feature("Test Feature"); + Scenario scenario = scenario("Test Scenario"); + Step step1 = step("Given", "Step 1"); + Result step1Result = result("passed"); + + jsonFormatter.uri(uri()); + jsonFormatter.feature(feature); + // StartOfScenarioLifeCycle() is not called when executing the Rspec specs + jsonFormatter.scenario(scenario); + + jsonFormatter.step(step1); + jsonFormatter.match(match()); + jsonFormatter.result(step1Result); + jsonFormatter.appendDuration(1); // appendDuration is used in the Rspec specs + + jsonFormatter.eof(); + jsonFormatter.done(); + jsonFormatter.close(); + + Gson gson = new Gson(); + List result = gson.fromJson(stringBuilder.toString(), List.class); + + Map featureJson = (Map) result.get(0); + assertEquals(feature.getName(), featureJson.get("name")); + + Map scenarioJson = (Map) ((List) featureJson.get("elements")).get(0); + assertEquals(scenario.getName(), scenarioJson.get("name")); + + Map step1Json = (Map) ((List)scenarioJson.get("steps")).get(0); + assertJsonStepData(step1, step1Result, embeddingsCount(0), outputCount(0), step1Json); + } + + private void assertJsonStepData(Step step, Result stepResult, int embeddingsCount, int outputCount, Map stepJson) { assertEquals(step.getName(), stepJson.get("name")); - assertJsonEmbeddings(stepJson, embeddingsCount(1)); - assertJsonOutput(stepJson, outputCount(1)); + assertJsonEmbeddings(stepJson, embeddingsCount); + assertJsonOutput(stepJson, outputCount); assertJsonResult(stepResult, stepJson); }