Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 47 additions & 12 deletions execution/rerun/rerun.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,38 @@ func init() {
failedMeta = newFailedMetaData()
}

type failureKey struct {
filePath string
line int
specDataTableRow int
scenarioDataTableRow int
hasSpecDataTableRow bool
hasScenarioDataTableRow bool
}

func newScenarioFailureKey(filePath string, sce *gauge.Scenario) failureKey {
k := failureKey{filePath: filePath, line: sce.Span.Start}
if sce.HasSpecDataTable {
k.hasSpecDataTableRow = true
k.specDataTableRow = sce.SpecDataTableRowIndex
}
if sce.ScenarioDataTableRow.IsInitialized() {
k.hasScenarioDataTableRow = true
k.scenarioDataTableRow = sce.ScenarioDataTableRowIndex
}
return k
}

func (k failureKey) outputRef() string {
if k.line == 0 {
return k.filePath
}
return fmt.Sprintf("%s:%d", k.filePath, k.line)
}

type failedMetadata struct {
Args []string
failedItemsMap map[string]map[string]bool
failedItemsMap map[string]map[failureKey]bool
FailedItems []string
}

Expand All @@ -46,10 +75,16 @@ func (m *failedMetadata) args() []string {
}

func (m *failedMetadata) getFailedItems() []string {
seen := make(map[string]bool)
failedItems := []string{}
for _, v := range m.failedItemsMap {
for k := range v {
failedItems = append(failedItems, k)
ref := k.outputRef()
if seen[ref] {
continue
}
seen[ref] = true
failedItems = append(failedItems, ref)
}
}
return failedItems
Expand All @@ -60,17 +95,17 @@ func (m *failedMetadata) aggregateFailedItems() {
}

func newFailedMetaData() *failedMetadata {
return &failedMetadata{Args: make([]string, 0), failedItemsMap: make(map[string]map[string]bool), FailedItems: []string{}}
return &failedMetadata{Args: make([]string, 0), failedItemsMap: make(map[string]map[failureKey]bool), FailedItems: []string{}}
}

func (m *failedMetadata) addFailedItem(itemName string, item string) {
func (m *failedMetadata) addFailedItem(itemName string, item failureKey) {
if _, ok := m.failedItemsMap[itemName]; !ok {
m.failedItemsMap[itemName] = make(map[string]bool)
m.failedItemsMap[itemName] = make(map[failureKey]bool)
}
m.failedItemsMap[itemName][item] = true
}

func (m *failedMetadata) removeFailedItem(itemName string, item string) {
func (m *failedMetadata) removeFailedItem(itemName string, item failureKey) {
if _, ok := m.failedItemsMap[itemName]; !ok {
return
}
Expand Down Expand Up @@ -109,29 +144,29 @@ func ListenFailedScenarios(wg *sync.WaitGroup, specDirs []string) {
func prepareScenarioFailedMetadata(res *result.ScenarioResult, sce *gauge.Scenario, executionInfo *gauge_messages.ExecutionInfo) {
specPath := executionInfo.GetCurrentSpec().GetFileName()
failedScenario := util.RelPathToProjectRoot(specPath)
scenarioRef := fmt.Sprintf("%s:%v", failedScenario, sce.Span.Start)
key := newScenarioFailureKey(failedScenario, sce)
if res.GetFailed() {
failedMeta.addFailedItem(specPath, scenarioRef)
failedMeta.addFailedItem(specPath, key)
return
}
// A scenario can emit multiple ScenarioEnd events when retries are enabled.
// If a later retry passes, remove any failed entry captured from earlier attempts.
failedMeta.removeFailedItem(specPath, scenarioRef)
failedMeta.removeFailedItem(specPath, key)
}

func addSpecFailedMetadata(res result.Result, args []string) {
fileName := util.RelPathToProjectRoot(res.(*result.SpecResult).ProtoSpec.GetFileName())
delete(failedMeta.failedItemsMap, fileName)
failedMeta.addFailedItem(fileName, fileName)
failedMeta.addFailedItem(fileName, failureKey{filePath: fileName})
}

func addSuiteFailedMetadata(res result.Result, args []string) {
failedMeta.failedItemsMap = make(map[string]map[string]bool)
failedMeta.failedItemsMap = make(map[string]map[failureKey]bool)
for _, arg := range args {
path, err := filepath.Abs(arg)
path = util.RelPathToProjectRoot(path)
if err == nil {
failedMeta.addFailedItem(path, path)
failedMeta.addFailedItem(path, failureKey{filePath: path})
}
}
}
Expand Down
178 changes: 165 additions & 13 deletions execution/rerun/rerun_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (s *MySuite) TestGetScenarioFailedMetadata(c *C) {
prepareScenarioFailedMetadata(sr1, sce, &gauge_messages.ExecutionInfo{CurrentSpec: &gauge_messages.SpecInfo{FileName: spec1Abs}})

c.Assert(len(failedMeta.failedItemsMap[spec1Abs]), Equals, 1)
c.Assert(failedMeta.failedItemsMap[spec1Abs][spec1Rel+":2"], Equals, true)
c.Assert(failedMeta.failedItemsMap[spec1Abs][failureKey{filePath: spec1Rel, line: 2}], Equals, true)
}

func (s *MySuite) TestScenarioPassingOnRetryRemovesFailedMetadata(c *C) {
Expand All @@ -71,13 +71,153 @@ func (s *MySuite) TestScenarioPassingOnRetryRemovesFailedMetadata(c *C) {
passedResult := &result.ScenarioResult{ProtoScenario: &gauge_messages.ProtoScenario{ExecutionStatus: gauge_messages.ExecutionStatus_PASSED}}

prepareScenarioFailedMetadata(failedResult, sce, execInfo)
c.Assert(failedMeta.failedItemsMap[spec1Abs][spec1Rel+":2"], Equals, true)
c.Assert(failedMeta.failedItemsMap[spec1Abs][failureKey{filePath: spec1Rel, line: 2}], Equals, true)

prepareScenarioFailedMetadata(passedResult, sce, execInfo)
_, exists := failedMeta.failedItemsMap[spec1Abs]
c.Assert(exists, Equals, false)
}

func (s *MySuite) TestPassingTableRowDoesNotRemoveFailedTableRowMetadata(c *C) {
spec1Rel := filepath.Join("specs", "example1.spec")
spec1Abs := filepath.Join(config.ProjectRoot, spec1Rel)
specTableRow := *gauge.NewTable([]string{"Word"}, [][]gauge.TableCell{{{Value: "Snap", CellType: gauge.Static}}}, 0)
execInfo := &gauge_messages.ExecutionInfo{CurrentSpec: &gauge_messages.SpecInfo{FileName: spec1Abs}}
failedScenario := &gauge.Scenario{
Span: &gauge.Span{Start: 13},
HasSpecDataTable: true,
SpecDataTableRow: specTableRow,
SpecDataTableRowIndex: 2,
}
passedScenario := &gauge.Scenario{
Span: &gauge.Span{Start: 13},
HasSpecDataTable: true,
SpecDataTableRow: specTableRow,
SpecDataTableRowIndex: 3,
}
failedResult := &result.ScenarioResult{ProtoScenario: &gauge_messages.ProtoScenario{ExecutionStatus: gauge_messages.ExecutionStatus_FAILED}}
passedResult := &result.ScenarioResult{ProtoScenario: &gauge_messages.ProtoScenario{ExecutionStatus: gauge_messages.ExecutionStatus_PASSED}}

prepareScenarioFailedMetadata(failedResult, failedScenario, execInfo)
c.Assert(failedMeta.failedItemsMap[spec1Abs][failureKey{filePath: spec1Rel, line: 13, hasSpecDataTableRow: true, specDataTableRow: 2}], Equals, true)

prepareScenarioFailedMetadata(passedResult, passedScenario, execInfo)
c.Assert(failedMeta.failedItemsMap[spec1Abs][failureKey{filePath: spec1Rel, line: 13, hasSpecDataTableRow: true, specDataTableRow: 2}], Equals, true)
}

func (s *MySuite) TestScenarioFailureRefWithScenarioDataTableRow(c *C) {
scenarioTableRow := *gauge.NewTable([]string{"Color"}, [][]gauge.TableCell{{{Value: "Red", CellType: gauge.Static}}}, 0)
sce := &gauge.Scenario{
Span: &gauge.Span{Start: 5},
ScenarioDataTableRow: scenarioTableRow,
ScenarioDataTableRowIndex: 3,
}

key := newScenarioFailureKey("specs/example.spec", sce)

c.Assert(key.filePath, Equals, "specs/example.spec")
c.Assert(key.line, Equals, 5)
c.Assert(key.hasSpecDataTableRow, Equals, false)
c.Assert(key.hasScenarioDataTableRow, Equals, true)
c.Assert(key.scenarioDataTableRow, Equals, 3)
}

func (s *MySuite) TestScenarioFailureRefWithBothDataTableRows(c *C) {
specTableRow := *gauge.NewTable([]string{"Word"}, [][]gauge.TableCell{{{Value: "Snap", CellType: gauge.Static}}}, 0)
scenarioTableRow := *gauge.NewTable([]string{"Color"}, [][]gauge.TableCell{{{Value: "Red", CellType: gauge.Static}}}, 0)
sce := &gauge.Scenario{
Span: &gauge.Span{Start: 5},
HasSpecDataTable: true,
SpecDataTableRow: specTableRow,
SpecDataTableRowIndex: 2,
ScenarioDataTableRow: scenarioTableRow,
ScenarioDataTableRowIndex: 1,
}

key := newScenarioFailureKey("specs/example.spec", sce)

c.Assert(key.filePath, Equals, "specs/example.spec")
c.Assert(key.line, Equals, 5)
c.Assert(key.hasSpecDataTableRow, Equals, true)
c.Assert(key.specDataTableRow, Equals, 2)
c.Assert(key.hasScenarioDataTableRow, Equals, true)
c.Assert(key.scenarioDataTableRow, Equals, 1)
}

func (s *MySuite) TestSameTableRowPassingOnRetryRemovesFailedMetadata(c *C) {
spec1Rel := filepath.Join("specs", "example1.spec")
spec1Abs := filepath.Join(config.ProjectRoot, spec1Rel)
specTableRow := *gauge.NewTable([]string{"Word"}, [][]gauge.TableCell{{{Value: "Snap", CellType: gauge.Static}}}, 0)
execInfo := &gauge_messages.ExecutionInfo{CurrentSpec: &gauge_messages.SpecInfo{FileName: spec1Abs}}
sce := &gauge.Scenario{
Span: &gauge.Span{Start: 13},
HasSpecDataTable: true,
SpecDataTableRow: specTableRow,
SpecDataTableRowIndex: 2,
}
failedResult := &result.ScenarioResult{ProtoScenario: &gauge_messages.ProtoScenario{ExecutionStatus: gauge_messages.ExecutionStatus_FAILED}}
passedResult := &result.ScenarioResult{ProtoScenario: &gauge_messages.ProtoScenario{ExecutionStatus: gauge_messages.ExecutionStatus_PASSED}}

prepareScenarioFailedMetadata(failedResult, sce, execInfo)
c.Assert(failedMeta.failedItemsMap[spec1Abs][failureKey{filePath: spec1Rel, line: 13, hasSpecDataTableRow: true, specDataTableRow: 2}], Equals, true)

prepareScenarioFailedMetadata(passedResult, sce, execInfo)
_, exists := failedMeta.failedItemsMap[spec1Abs]
c.Assert(exists, Equals, false)
}

func (s *MySuite) TestGetFailedItemsUsesFileLineForTableDrivenScenarios(c *C) {
spec1Rel := filepath.Join("specs", "example1.spec")
metaData := newFailedMetaData()
metaData.failedItemsMap[spec1Rel] = map[failureKey]bool{
{filePath: spec1Rel, line: 13, hasSpecDataTableRow: true, specDataTableRow: 2}: true,
{filePath: spec1Rel, line: 13, hasSpecDataTableRow: true, specDataTableRow: 4, hasScenarioDataTableRow: true, scenarioDataTableRow: 1}: true,
}

failedItems := metaData.getFailedItems()
sort.Strings(failedItems)

c.Assert(failedItems, DeepEquals, []string{spec1Rel + ":13"})
}

func (s *MySuite) TestScenarioFailureRefDistinguishesSpecRowsForNestedTable(c *C) {
scenarioTableRow := *gauge.NewTable([]string{"Color"}, [][]gauge.TableCell{{{Value: "Red", CellType: gauge.Static}}}, 0)
sce0 := &gauge.Scenario{
Span: &gauge.Span{Start: 13},
HasSpecDataTable: true,
SpecDataTableRowIndex: 0,
ScenarioDataTableRow: scenarioTableRow,
ScenarioDataTableRowIndex: 1,
}
sce1 := &gauge.Scenario{
Span: &gauge.Span{Start: 13},
HasSpecDataTable: true,
SpecDataTableRowIndex: 1,
ScenarioDataTableRow: scenarioTableRow,
ScenarioDataTableRowIndex: 1,
}

key0 := newScenarioFailureKey("specs/example.spec", sce0)
key1 := newScenarioFailureKey("specs/example.spec", sce1)

c.Assert(key0, Not(Equals), key1)
c.Assert(key0.outputRef(), Equals, "specs/example.spec:13")
c.Assert(key1.outputRef(), Equals, "specs/example.spec:13")
}

func (s *MySuite) TestGetFailedItemsDeduplicatesNestedTableScenarios(c *C) {
spec1Rel := filepath.Join("specs", "example1.spec")
metaData := newFailedMetaData()
metaData.failedItemsMap[spec1Rel] = map[failureKey]bool{
{filePath: spec1Rel, line: 13, hasSpecDataTableRow: true, specDataTableRow: 0, hasScenarioDataTableRow: true, scenarioDataTableRow: 1}: true,
{filePath: spec1Rel, line: 13, hasSpecDataTableRow: true, specDataTableRow: 1, hasScenarioDataTableRow: true, scenarioDataTableRow: 1}: true,
}

failedItems := metaData.getFailedItems()

c.Assert(failedItems, DeepEquals, []string{spec1Rel + ":13"})
}

func (s *MySuite) TestAddSpecPreHookFailedMetadata(c *C) {
spec1Rel := filepath.Join("specs", "example1.spec")
spec1Abs := filepath.Join(config.ProjectRoot, spec1Rel)
Expand All @@ -86,7 +226,7 @@ func (s *MySuite) TestAddSpecPreHookFailedMetadata(c *C) {
addFailedMetadata(spec1, []string{}, addSpecFailedMetadata)

c.Assert(len(failedMeta.failedItemsMap[spec1Rel]), Equals, 1)
c.Assert(failedMeta.failedItemsMap[spec1Rel][spec1Rel], Equals, true)
c.Assert(failedMeta.failedItemsMap[spec1Rel][failureKey{filePath: spec1Rel}], Equals, true)
}

func (s *MySuite) TestAddSpecPostHookFailedMetadata(c *C) {
Expand All @@ -97,21 +237,21 @@ func (s *MySuite) TestAddSpecPostHookFailedMetadata(c *C) {
addFailedMetadata(spec1, []string{}, addSpecFailedMetadata)

c.Assert(len(failedMeta.failedItemsMap[spec1Rel]), Equals, 1)
c.Assert(failedMeta.failedItemsMap[spec1Rel][spec1Rel], Equals, true)
c.Assert(failedMeta.failedItemsMap[spec1Rel][failureKey{filePath: spec1Rel}], Equals, true)
}

func (s *MySuite) TestAddSpecFailedMetadataOverwritesPreviouslyAddedValues(c *C) {
spec1Rel := filepath.Join("specs", "example1.spec")
spec1Abs := filepath.Join(config.ProjectRoot, spec1Rel)
spec1 := &result.SpecResult{ProtoSpec: &gauge_messages.ProtoSpec{PreHookFailures: []*gauge_messages.ProtoHookFailure{{ErrorMessage: "error"}}, FileName: spec1Abs}}
failedMeta.failedItemsMap[spec1Rel] = make(map[string]bool)
failedMeta.failedItemsMap[spec1Rel]["scn1"] = true
failedMeta.failedItemsMap[spec1Rel]["scn2"] = true
failedMeta.failedItemsMap[spec1Rel] = make(map[failureKey]bool)
failedMeta.failedItemsMap[spec1Rel][failureKey{filePath: spec1Rel, line: 1}] = true
failedMeta.failedItemsMap[spec1Rel][failureKey{filePath: spec1Rel, line: 2}] = true

addSpecFailedMetadata(spec1, []string{})

c.Assert(len(failedMeta.failedItemsMap[spec1Rel]), Equals, 1)
c.Assert(failedMeta.failedItemsMap[spec1Rel][spec1Rel], Equals, true)
c.Assert(failedMeta.failedItemsMap[spec1Rel][failureKey{filePath: spec1Rel}], Equals, true)
}

func (s *MySuite) TestGetRelativePath(c *C) {
Expand All @@ -123,15 +263,27 @@ func (s *MySuite) TestGetRelativePath(c *C) {
c.Assert(path, Equals, spec1Rel)
}

func (s *MySuite) TestGetFailedItemsWithCustomSpecExtension(c *C) {
spec1Rel := filepath.Join("specs", "example1.foo")
metaData := newFailedMetaData()
metaData.failedItemsMap[spec1Rel] = map[failureKey]bool{
{filePath: spec1Rel, line: 13, hasSpecDataTableRow: true, specDataTableRow: 2}: true,
}

failedItems := metaData.getFailedItems()

c.Assert(failedItems, DeepEquals, []string{spec1Rel + ":13"})
}

func (s *MySuite) TestGetAllFailedItems(c *C) {
spec1Rel := filepath.Join("specs", "example1.spec")
spec2Rel := filepath.Join("specs", "example2.spec")
metaData := newFailedMetaData()
metaData.failedItemsMap[spec1Rel] = make(map[string]bool)
metaData.failedItemsMap[spec2Rel] = make(map[string]bool)
metaData.failedItemsMap[spec1Rel]["scn1"] = true
metaData.failedItemsMap[spec1Rel]["scn2"] = true
metaData.failedItemsMap[spec2Rel]["scn3"] = true
metaData.failedItemsMap[spec1Rel] = make(map[failureKey]bool)
metaData.failedItemsMap[spec2Rel] = make(map[failureKey]bool)
metaData.failedItemsMap[spec1Rel][failureKey{filePath: "scn1"}] = true
metaData.failedItemsMap[spec1Rel][failureKey{filePath: "scn2"}] = true
metaData.failedItemsMap[spec2Rel][failureKey{filePath: "scn3"}] = true

failedItems := metaData.getFailedItems()
sort.Strings(failedItems)
Expand Down
1 change: 1 addition & 0 deletions gauge/scenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Scenario struct {
DataTable DataTable
SpecDataTableRow Table
SpecDataTableRowIndex int
HasSpecDataTable bool
ScenarioDataTableRow Table
ScenarioDataTableRowIndex int
Span *Span
Expand Down
5 changes: 4 additions & 1 deletion parser/dataTableSpecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ func copyScenarios(scenarios []*gauge.Scenario, table gauge.Table, i int, errMap
Comments: scn.Comments,
Span: scn.Span,
}
if table.IsInitialized() {
newScn.HasSpecDataTable = true
newScn.SpecDataTableRowIndex = i
}
if assignSpecTable {
newScn.SpecDataTableRow = table
newScn.SpecDataTableRowIndex = i
}
if scnTableRow.IsInitialized() {
newScn.ScenarioDataTableRow = scnTableRow
Expand Down
Loading