Skip to content

Commit

Permalink
Merge pull request #75 from iantmoore/multiple-exec-configs-breaks-re…
Browse files Browse the repository at this point in the history
…port

Multiple exec configs breaks report
  • Loading branch information
iantmoore committed Nov 15, 2017
2 parents 124c202 + 9bded6f commit 0b15b17
Show file tree
Hide file tree
Showing 46 changed files with 404 additions and 214 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Requirements
* Glossary changes - got rid of the noise when extracting substeps tag info. Enabled the migration to new qualified custom glossary tags 'org.substeps.step.example' and 'org.substeps.step.section'
* some sonar suggested fixes
* If a -Denvironment= variable is set, pass through to the forked VM process. Can be overriden from the parent process using the vmArgs parameter in config if required.
* Multiple execution configs cause issues with the report when running in forked mode, only the second set of results are visible. #74

1.1.2
-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,14 +461,45 @@ class ExecutionResultsCollector extends IExecutionResultsCollector {
case class FeatureSummary(nodeType: String, filename: String, result : String, id : Long,
executionDurationMillis : Option[Long], description : String, scenarios: List[ScenarioSummary], tags : List[String])

object FeatureSummary {
def sequenceIds(summary: FeatureSummary, toAdd: Long): FeatureSummary = {
summary.copy(id = summary.id + toAdd, scenarios = ScenarioSummary.sequenceIds(summary.scenarios, toAdd))
}
}

case class ScenarioSummary(nodeId : Long, filename : String, result: String, tags : List[String])

object ScenarioSummary {
def sequenceIds(scenarios: List[ScenarioSummary], toAdd : Long): scala.List[ScenarioSummary] = {

scenarios.map(s => {
s.copy(nodeId = s.nodeId + toAdd)
})
}
}


case class FeatureSummaryForRootNode(nodeId : Long, resultsDir: String, result : String)

object FeatureSummaryForRootNode {
def sequenceIds(features: List[FeatureSummaryForRootNode], toAdd: Long): scala.List[FeatureSummaryForRootNode] = {

features.map(f => {
f.copy(nodeId = f.nodeId + toAdd)
})
}
}

case class RootNodeSummary(nodeType: String, description: String, result : String, id : Long,
executionDurationMillis : Option[Long], features : List[FeatureSummaryForRootNode], tags : Option[String], nonFatalTags : Option[String], timestamp : Long, environment : String)

object RootNodeSummary {
def sequenceIds(rootNodeSummary: RootNodeSummary, toAdd: Long): RootNodeSummary = {

rootNodeSummary.copy(id = rootNodeSummary.id + toAdd, features = FeatureSummaryForRootNode.sequenceIds(rootNodeSummary.features, toAdd))
}
}


case class SubstepsNode(id : Long, nodeType: String, description: String, children : List[SubstepsNode])

Expand Down
9 changes: 9 additions & 0 deletions core/src/main/scala/org/substeps/report/NodeDetail.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ case class NodeDetail(nodeType: String, filename: String, lineNumber : Int, resu


case object NodeDetail {

def sequenceIds(nodeDetails: List[NodeDetail], toAdd: Long): scala.List[NodeDetail] = {

nodeDetails.map(n => {
n.copy(id = n.id + toAdd, children = sequenceIds(n.children, toAdd))
})
}


def basicScenarioNodeInError(scenarioNode: BasicScenarioNode, children: List[NodeDetail], tags: Option[List[String]]) = {

val stackTrace = scenarioNode.getResult.getFailure.getCause.getStackTrace.toList.map(elem => elem.toString)
Expand Down
43 changes: 36 additions & 7 deletions core/src/main/scala/org/substeps/report/ReportBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,40 @@ object ReportBuilder {

case class FeatureDetails(summary : FeatureSummary, nodeDetails : List[NodeDetail])

object FeatureDetails {
def sequenceIds(featuresList: List[FeatureDetails], toAdd: Long): scala.List[_root_.org.substeps.report.FeatureDetails] = {

featuresList.map(f => {
f.copy(summary = FeatureSummary.sequenceIds(f.summary, toAdd), nodeDetails = NodeDetail.sequenceIds(f.nodeDetails, toAdd))
})
}
}

case class SourceDataModel(rootNodeSummary : RootNodeSummary, featuresList : List[FeatureDetails], config : Config)

case object SourceDataModel{

def sequenceIds(srcDataList: List[SourceDataModel]) : List[SourceDataModel] = {



srcDataList match {
case sdm :: Nil => srcDataList
case head :: tail => {

var toAdd = head.rootNodeSummary.id
// renumber the tail list
val newTail =
tail.map(sd => {
val copied = sd.copy(rootNodeSummary = RootNodeSummary.sequenceIds(sd.rootNodeSummary, toAdd) , featuresList = FeatureDetails.sequenceIds(sd.featuresList, toAdd))
toAdd = copied.rootNodeSummary.id
copied
})
head :: newTail
}
}
}


}

Expand All @@ -101,7 +131,7 @@ case class UncalledStepImpl(value:String, implementedIn: String, keyword: String
/**
* Created by ian on 30/06/16.
*/
class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTreeTemplate with GlossaryTemplate {
class ReportBuilder extends IReportBuilder with IndexPageTemplate with UsageTreeTemplate with GlossaryTemplate {

// TODO need to leave here as legacy to ensure maven is able to inject in parameter, mojo fails otherwise
@BeanProperty
Expand Down Expand Up @@ -223,7 +253,7 @@ class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTr

processUncalledAndUnusedDataFiles( dataDir, executionConfigs)

val srcDataList: List[SourceDataModel] = readModels(dataDir, executionConfigs)
val srcDataList: List[SourceDataModel] = SourceDataModel.sequenceIds(readModels(dataDir, executionConfigs))

val detailData = createFile( "detail_data.js")

Expand Down Expand Up @@ -680,7 +710,7 @@ class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTr
f
}

def writeResultSummary(resultsFile: RootNodeSummary)(implicit reportDir : File): Unit = {
def writeResultSummaryX(resultsFile: RootNodeSummary)(implicit reportDir : File): Unit = {

val file = createFile("results-summary.js")

Expand Down Expand Up @@ -734,7 +764,7 @@ class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTr
resultsFileOption match {
case Some(resultsFile) => {

writeResultSummary(resultsFile)
//writeResultSummary(resultsFile)

resultsFile.features.flatMap(featureSummary => {

Expand Down Expand Up @@ -910,13 +940,12 @@ class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTr

val featureSummaries = srcData.featuresList.map(f => f.summary) //srcData._2.map(_._1)

srcData.featuresList.foreach(feature =>{
writeRootNode(writer, srcData.rootNodeSummary, featureSummaries, srcData.config)

srcData.featuresList.foreach(feature =>{

val nodeDetailList = feature.nodeDetails

writeRootNode(writer, srcData.rootNodeSummary, featureSummaries, srcData.config)

val dataSubdir = NewSubstepsExecutionConfig.getDataSubdir(srcData.config)

writeFeatureNode(writer, feature.summary, nodeDetailList, dataSubdir)
Expand Down
8 changes: 1 addition & 7 deletions core/src/main/scala/org/substeps/report/ReportFrame.scala
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package org.substeps.report

import java.time.{Instant, LocalDateTime, ZoneId}
import java.time.format.DateTimeFormatter

import com.typesafe.config.Config

/**
* Created by ian on 18/08/16.
*/


trait ReportFrameTemplate {
trait IndexPageTemplate {


def buildStatsBlock(name: String, counters : Counters) = {
Expand Down Expand Up @@ -87,9 +84,6 @@ trait ReportFrameTemplate {
| <link href="css/substeps.css" rel="stylesheet"/>
|
| <link rel="stylesheet" href="css/jstree/style.min.css" />
| <script type="text/javascript" src="results-summary.js"></script>
|
|
|
|</head>
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,77 +26,81 @@
import com.technophobia.substeps.runner.syntax.ClassAnalyser;
import com.technophobia.substeps.runner.syntax.SubStepDefinitionParser;
import com.technophobia.substeps.runner.syntax.SyntaxBuilder;
import com.technophobia.substeps.runner.syntax.validation.fake.FakeSyntaxErrorReporter;
import com.technophobia.substeps.runner.syntax.validation.fake.FakeSyntaxErrorReporter.SyntaxErrorData;
import com.technophobia.substeps.runner.syntax.SyntaxErrorReporter;
import com.technophobia.substeps.stepimplementations.MockStepImplementations;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;

import java.io.File;
import java.util.Arrays;
import java.util.List;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

@RunWith(MockitoJUnitRunner.class)
public class SyntaxAwareStepValidatorTest {

private static final String FEATURE_PATH = "./target/test-classes/features/";
private static final String SUBSTEPS_PATH = "./target/test-classes/substeps/";

private FakeSyntaxErrorReporter syntaxErrorReporter;
private FeatureFileParser featureFileParser;
private SubStepDefinitionParser substepsFileParser;

private SyntaxErrorReporter mock;

@Before
public void initialise() {

this.syntaxErrorReporter = new FakeSyntaxErrorReporter();
this.mock = Mockito.mock(SyntaxErrorReporter.class);
this.featureFileParser = new FeatureFileParser();
this.substepsFileParser = new SubStepDefinitionParser(this.syntaxErrorReporter);
this.substepsFileParser = new SubStepDefinitionParser(mock);
}


@Test
public void validatorReportsMissingStepsInScenario() {
final FeatureFile featureFile = this.featureFileParser.loadFeatureFile(createFeatureFile("error.feature"));
File ff = createFeatureFile("error.feature");
final FeatureFile featureFile = this.featureFileParser.loadFeatureFile(ff);

createStepValidatorWithSubsteps("simple.substeps").validateFeatureFile(featureFile, syntaxErrorReporter);
final List<SyntaxErrorData> errors = syntaxErrorReporter.syntaxErrors();
assertThat(Integer.valueOf(errors.size()), is(Integer.valueOf(2)));
createStepValidatorWithSubsteps("simple.substeps", mock).validateFeatureFile(featureFile, mock);

checkError(errors.get(0), 6, "Given step 1");
checkError(errors.get(1), 7, "Given step 2");
verify(mock).reportFeatureError(eq(ff), eq("Given step 1"), eq(6), anyInt(), any());
verify(mock).reportFeatureError(eq(ff), eq("Given step 2"), eq(7), anyInt(), any());
}


@Test
public void validatorReportsNoErrorsForFeatureWithValidSteps() {
final FeatureFile featureFile = this.featureFileParser.loadFeatureFile(createFeatureFile("error.feature"));

createStepValidatorWithSubsteps("error.substeps").validateFeatureFile(featureFile, syntaxErrorReporter);
final List<SyntaxErrorData> errors = syntaxErrorReporter.syntaxErrors();
assertTrue(errors.isEmpty());
createStepValidatorWithSubsteps("error.substeps",mock).validateFeatureFile(featureFile, mock);

verify(mock, never()).reportFeatureError(any(),any(),anyInt(),anyInt(),any());

verify(mock, never()).reportSubstepsError(any());
verify(mock, never()).reportStepImplError(any());
}


@Test
public void validatorReportsMissingSubstepsInDefinition() {
final PatternMap<ParentStep> substeps = substepsFileParser.loadSubSteps(createSubstepsFile("error.substeps"));

final StepValidator stepValidator = createStepValidatorWithSubsteps("simple.substeps");
File substepsFile = createSubstepsFile("error.substeps").getAbsoluteFile();

final PatternMap<ParentStep> substeps = substepsFileParser.loadSubSteps(substepsFile);

final StepValidator stepValidator = createStepValidatorWithSubsteps("simple.substeps", mock);
for (final ParentStep substep : substeps.values()) {
stepValidator.validateSubstep(substep, syntaxErrorReporter);
stepValidator.validateSubstep(substep, mock);
}

final List<SyntaxErrorData> errors = syntaxErrorReporter.syntaxErrors();
assertThat(Integer.valueOf(errors.size()), is(Integer.valueOf(3)));
verify(mock).reportFeatureError(eq(substepsFile), eq("SingleWord"), eq(5), eq(101), eq("Step \"SingleWord\" is not defined"));
verify(mock).reportFeatureError(eq(substepsFile), eq("Test_Then something else has happened"), eq(6), anyInt(), any());
verify(mock).reportFeatureError(eq(substepsFile), eq("Test_Then something has happened"), eq(9), anyInt(), any());

checkError(errors.get(0), 5, "SingleWord");
checkError(errors.get(1), 6, "Test_Then something else has happened");
checkError(errors.get(2), 9, "Test_Then something has happened");
}


Expand All @@ -105,38 +109,30 @@ public void validatorReportsNoErrorsForSubstepsWithValidSteps() {
final PatternMap<ParentStep> substeps = this.substepsFileParser
.loadSubSteps(createSubstepsFile("allFeatures.substeps"));

final StepValidator stepValidator = createStepValidatorWithSubsteps("simple.substeps",
final StepValidator stepValidator = createStepValidatorWithSubsteps("simple.substeps", mock,
MockStepImplementations.class);
for (final ParentStep substep : substeps.values()) {
stepValidator.validateSubstep(substep, syntaxErrorReporter);
stepValidator.validateSubstep(substep, mock);
}
final List<SyntaxErrorData> errors = syntaxErrorReporter.syntaxErrors();
assertTrue(errors.isEmpty());
}


private void checkError(final SyntaxErrorData error, final int lineNumber, final String line) {
assertThat(Integer.valueOf(error.getLineNumber()), is(Integer.valueOf(lineNumber)));
assertThat(error.getLine(), is(line));
assertThat(error.getDescription(), is("Step \"" + line + "\" is not defined"));
verify(mock, never()).reportFeatureError(any(),any(),anyInt(),anyInt(),any());
verify(mock, never()).reportSubstepsError(any());
verify(mock, never()).reportStepImplError(any());
}


private File createFeatureFile(final String name) {
return new File(FEATURE_PATH, name);
}


private File createSubstepsFile(final String name) {
return new File(SUBSTEPS_PATH, name);
}


private StepValidator createStepValidatorWithSubsteps(final String substepsFilename,
private StepValidator createStepValidatorWithSubsteps(final String substepsFilename, SyntaxErrorReporter errorReporter,
final Class<?>... stepImplClasses) {
final Syntax syntax = SyntaxBuilder.buildSyntax(Arrays.asList(stepImplClasses),
createSubstepsFile(substepsFilename), true, new String[0], new ClassAnalyser(), true,
this.syntaxErrorReporter);
errorReporter);

return new SyntaxAwareStepValidator(syntax);
}
Expand Down
Loading

0 comments on commit 0b15b17

Please sign in to comment.