test_class.file: step-plans/step-plans-base-artefacts/src/test/java/step/reporting/JUnit4ReportWriterTest.java
test_class.identifier: JUnit4ReportWriterTest
test_case.body: @Test
  	public void testSimpleSequence() throws IOException {
  		Plan plan = PlanBuilder.create()
  				.startBlock(sequence())
  					.add(checkArtefact(ReportNodeStatus.PASSED))
  					.startBlock(sequence())
  						.add(checkArtefact(ReportNodeStatus.PASSED))
  						.add(checkArtefact(ReportNodeStatus.PASSED))
  					.endBlock()
  					.add(checkArtefact(ReportNodeStatus.FAILED))
  					.add(checkArtefact(ReportNodeStatus.FAILED, "my message"))
  					.add(checkArtefact(ReportNodeStatus.TECHNICAL_ERROR))
  					.startBlock(testCase("TC - TECH_ERROR with message"))
  						.add(checkArtefact(ReportNodeStatus.TECHNICAL_ERROR, "My error message"))
  					.endBlock()
  					.add(checkArtefact(ReportNodeStatus.SKIPPED))
  					.add(checkArtefact(ReportNodeStatus.NORUN))
  					.add(checkArtefact(ReportNodeStatus.INTERRUPTED))
  				.endBlock().build();
  		
  		File report = new File("TEST-JUnit4ReportWriterTest-testSimpleSequence.xml");
  		report.deleteOnExit();
  		
  		ExecutionEngine engine = ExecutionEngine.builder().withPlugin(new BaseArtefactPlugin()).build();
  		engine.execute(plan).writeReport(new JUnit4ReportWriter(), report);
  		
  		PlanRunnerResultAssert.assertEquals(this.getClass(), "TEST-JUnit4ReportWriterTest-testSimpleSequence-expected.xml", report, "time=\".+?\"");
  	}
test_case.class_method_signature: JUnit4ReportWriterTest.testSimpleSequence()
test_case.full_signature: @Test public void testSimpleSequence()
test_case.identifier: testSimpleSequence
test_case.invocations: build endBlock add add add endBlock add startBlock add add add endBlock add add startBlock add startBlock create sequence checkArtefact sequence checkArtefact checkArtefact checkArtefact checkArtefact checkArtefact testCase checkArtefact checkArtefact checkArtefact checkArtefact deleteOnExit build withPlugin builder writeReport execute assertEquals getClass
test_case.modifiers: @Test public
test_case.parameters: ()
test_case.return: void
test_case.signature: void testSimpleSequence()
focal_class.file: step-plans/step-plans-base-artefacts/src/main/java/step/reporting/JUnit4ReportWriter.java
focal_class.identifier: JUnit4ReportWriter
focal_class.interfaces: implements ReportWriter
focal_class.class_method_signature: JUnit4ReportWriter.writeReport(ReportTreeAccessor reportTreeAccessor, String executionId, File outputFile)
focal_class.full_signature: @Override public void writeReport(ReportTreeAccessor reportTreeAccessor, String executionId, File outputFile)
focal_class.identifier: writeReport
focal_class.modifiers: @Override public
focal_class.parameters: (ReportTreeAccessor reportTreeAccessor, String executionId, File outputFile)
focal_class.return: void
focal_class.signature: void writeReport(ReportTreeAccessor reportTreeAccessor, String executionId, File outputFile)
focal_class.class_method_signature: JUnit4ReportWriter.writeErrorOrFailure(BufferedWriter writer, ReportNode node, AtomicBoolean errorWritten)
focal_class.full_signature: protected void writeErrorOrFailure(BufferedWriter writer, ReportNode node, AtomicBoolean errorWritten)
focal_class.identifier: writeErrorOrFailure
focal_class.modifiers: protected
focal_class.parameters: (BufferedWriter writer, ReportNode node, AtomicBoolean errorWritten)
focal_class.return: void
focal_class.signature: void writeErrorOrFailure(BufferedWriter writer, ReportNode node, AtomicBoolean errorWritten)
focal_class.class_method_signature: JUnit4ReportWriter.formatTime(long duration)
focal_class.full_signature: protected String formatTime(long duration)
focal_class.identifier: formatTime
focal_class.modifiers: protected
focal_class.parameters: (long duration)
focal_class.return: String
focal_class.signature: String formatTime(long duration)
focal_method.body: @Override
  	public void writeReport(ReportTreeAccessor reportTreeAccessor, String executionId, File outputFile) throws IOException {
  		ReportTreeVisitor visitor = new ReportTreeVisitor(reportTreeAccessor);
  		
  		try(BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile))) {
  			// Using AtomicInteger and StringBuilder because of the "final limitation" in lambdas...
  			AtomicInteger numberOfTests = new AtomicInteger(0);
  			AtomicInteger numberOfFailures = new AtomicInteger(0);
  			AtomicInteger numberOfErrors = new AtomicInteger(0);
  			AtomicInteger numberOfSkipped = new AtomicInteger(0);
  			AtomicLong duration = new AtomicLong();
  			StringBuilder name = new StringBuilder();
  			
  			// First visit the report tree to get the root node informations and the different counts
  			visitor.visit(executionId, e->{
  				if(e.getStack().size()==0) {
  					name.append(e.getNode().getName());
  					duration.set(e.getNode().getDuration());
  				}
  				if(e.getStack().size()==1) {
  					numberOfTests.incrementAndGet();
  					ReportNode node = e.getNode();
  					if(node.getStatus() == ReportNodeStatus.FAILED) {
  						numberOfFailures.incrementAndGet();
  					} else if(node.getStatus() == ReportNodeStatus.TECHNICAL_ERROR) {
  						numberOfErrors.incrementAndGet();
  					} else if(node.getStatus() == ReportNodeStatus.SKIPPED) {
  						numberOfSkipped.incrementAndGet();
  					}
  				}
  			});
  			
  			
  			writer.write("<testsuite name=\""+name.toString()+"\" time=\""+formatTime(duration.get())+"\" tests=\""+numberOfTests.get()+"\" skipped=\""+numberOfSkipped.get()+"\" failures=\""+numberOfFailures.get()+"\" errors=\""+numberOfErrors.get()+"\">");
  			writer.newLine();
  			
  			AtomicBoolean errorWritten = new AtomicBoolean(false);
  			// visit the tree again and write the <testcase> blocks
  			visitor.visit(executionId, new ReportNodeVisitorEventHandler() {
  				@Override
  				public void startReportNode(ReportNodeEvent event) {
  					ReportNode node = event.getNode();
  					try {
  						// as a convention report the children of the first level as testcases
  						if(event.getStack().size()==1) {
  							if(!skipReportNode(node)) {
  								writer.write("<testcase classname=\""+node.getClass().getName()+"\" name=\""+node.getName()+"\" time=\""+formatTime(node.getDuration())+"\">");
  								errorWritten.set(false);
  							}
  						} else if (event.getStack().size()>1) {
  							// report all the errors of the sub nodes (level > 1)
  							if(node.getError() != null) {
  								writeErrorOrFailure(writer, node, errorWritten);
  							}							
  						}
  					} catch (IOException e1) {
  						throw new RuntimeException(e1);
  					}
  				}
  				
  				@Override
  				public void endReportNode(ReportNodeEvent event) {
  					if(event.getStack().size()==1) {
  						ReportNode node = event.getNode();
  						if(!skipReportNode(node)) {
  							try {
  								// if no error has been found in the sub-nodes, report the error for this node
  								if(node.getStatus()!=ReportNodeStatus.PASSED && !errorWritten.get()) {
  									writeErrorOrFailure(writer, node, errorWritten);
  								}
  								// close the <testcase> block
  								writer.write("</testcase>");
  								writer.newLine();
  							} catch (IOException e1) {
  								throw new RuntimeException(e1);
  							}
  						}
  					}
  				}
  
  				protected boolean skipReportNode(ReportNode node) {
  					return node.getStatus()==ReportNodeStatus.SKIPPED || node.getStatus()==ReportNodeStatus.NORUN;
  				}
  			});
  			
  			writer.write("</testsuite>");
  			writer.newLine();
  			
  			writer.flush();
  		}
  	}
focal_method.class_method_signature: JUnit4ReportWriter.writeReport(ReportTreeAccessor reportTreeAccessor, String executionId, File outputFile)
focal_method.full_signature: @Override public void writeReport(ReportTreeAccessor reportTreeAccessor, String executionId, File outputFile)
focal_method.identifier: writeReport
focal_method.invocations: visit size getStack append getName getNode set getDuration getNode size getStack incrementAndGet getNode getStatus incrementAndGet getStatus incrementAndGet getStatus incrementAndGet write toString formatTime get get get get get newLine visit getNode size getStack skipReportNode write getName getClass getName formatTime getDuration set size getStack getError writeErrorOrFailure size getStack getNode skipReportNode getStatus get writeErrorOrFailure write newLine getStatus getStatus write newLine flush
focal_method.modifiers: @Override public
focal_method.parameters: (ReportTreeAccessor reportTreeAccessor, String executionId, File outputFile)
focal_method.return: void
focal_method.signature: void writeReport(ReportTreeAccessor reportTreeAccessor, String executionId, File outputFile)