github.com/getgauge/gauge@v1.6.9/execution/specExecutor_test.go (about)

     1  /*----------------------------------------------------------------
     2   *  Copyright (c) ThoughtWorks, Inc.
     3   *  Licensed under the Apache License, Version 2.0
     4   *  See LICENSE in the project root for license information.
     5   *----------------------------------------------------------------*/
     6  
     7  package execution
     8  
     9  import (
    10  	"fmt"
    11  	"net"
    12  	"testing"
    13  
    14  	"github.com/getgauge/gauge/runner"
    15  
    16  	"sync"
    17  
    18  	"github.com/getgauge/gauge-proto/go/gauge_messages"
    19  	"github.com/getgauge/gauge/execution/event"
    20  	"github.com/getgauge/gauge/execution/result"
    21  	"github.com/getgauge/gauge/gauge"
    22  	"github.com/getgauge/gauge/parser"
    23  	"github.com/getgauge/gauge/validation"
    24  	. "gopkg.in/check.v1"
    25  )
    26  
    27  type specBuilder struct {
    28  	lines []string
    29  }
    30  
    31  func newSpecBuilder() *specBuilder {
    32  	return &specBuilder{lines: make([]string, 0)}
    33  }
    34  
    35  func (specBuilder *specBuilder) addPrefix(prefix string, line string) string {
    36  	return fmt.Sprintf("%s%s\n", prefix, line)
    37  }
    38  
    39  func (specBuilder *specBuilder) String() string {
    40  	var specResult string
    41  	for _, line := range specBuilder.lines {
    42  		specResult = fmt.Sprintf("%s%s", specResult, line)
    43  	}
    44  	return specResult
    45  }
    46  
    47  func (specBuilder *specBuilder) specHeading(heading string) *specBuilder {
    48  	line := specBuilder.addPrefix("#", heading)
    49  	specBuilder.lines = append(specBuilder.lines, line)
    50  	return specBuilder
    51  }
    52  
    53  func (specBuilder *specBuilder) scenarioHeading(heading string) *specBuilder {
    54  	line := specBuilder.addPrefix("##", heading)
    55  	specBuilder.lines = append(specBuilder.lines, line)
    56  	return specBuilder
    57  }
    58  
    59  func (specBuilder *specBuilder) step(stepText string) *specBuilder {
    60  	line := specBuilder.addPrefix("* ", stepText)
    61  	specBuilder.lines = append(specBuilder.lines, line)
    62  	return specBuilder
    63  }
    64  
    65  func (specBuilder *specBuilder) tableHeader(cells ...string) *specBuilder {
    66  	return specBuilder.tableRow(cells...)
    67  }
    68  func (specBuilder *specBuilder) tableRow(cells ...string) *specBuilder {
    69  	rowInMarkdown := "|"
    70  	for _, cell := range cells {
    71  		rowInMarkdown = fmt.Sprintf("%s%s|", rowInMarkdown, cell)
    72  	}
    73  	specBuilder.lines = append(specBuilder.lines, fmt.Sprintf("%s\n", rowInMarkdown))
    74  	return specBuilder
    75  }
    76  
    77  type tableRow struct {
    78  	name   string
    79  	input  string // input by user for data table rows
    80  	output []int  // data table indexes to be executed
    81  }
    82  
    83  var tableRowTests = []*tableRow{
    84  	{"Valid single row number", "2", []int{1}},
    85  	{"Valid row numbers list", "2,3,4", []int{1, 2, 3}},
    86  	{"Valid table rows range", "2-5", []int{1, 2, 3, 4}},
    87  	{"Empty table rows range", "", []int(nil)},
    88  	{"Table rows list with spaces", "2, 4 ", []int{1, 3}},
    89  }
    90  
    91  func (s *MySuite) TestToGetDataTableRowsRangeFromInputFlag(c *C) {
    92  	for _, test := range tableRowTests {
    93  		got := getDataTableRows(test.input)
    94  		want := test.output
    95  		c.Assert(got, DeepEquals, want, Commentf(test.name))
    96  	}
    97  }
    98  
    99  func (s *MySuite) TestCreateSkippedSpecResult(c *C) {
   100  	spec := &gauge.Specification{Heading: &gauge.Heading{LineNo: 0, Value: "SPEC_HEADING"}, FileName: "FILE"}
   101  	r := &mockRunner{}
   102  	se := newSpecExecutor(spec, r, nil, nil, 0)
   103  	se.errMap = getValidationErrorMap()
   104  	se.specResult = &result.SpecResult{}
   105  	se.skipSpecForError(fmt.Errorf("ERROR"))
   106  
   107  	c.Assert(se.specResult.IsFailed, Equals, false)
   108  	c.Assert(se.specResult.Skipped, Equals, true)
   109  	c.Assert(len(se.errMap.SpecErrs[spec]), Equals, 1)
   110  }
   111  
   112  func (s *MySuite) TestCreateSkippedSpecResultWithScenarios(c *C) {
   113  	r := &mockRunner{}
   114  	se := newSpecExecutor(anySpec(), r, nil, nil, 0)
   115  	se.errMap = getValidationErrorMap()
   116  	se.specResult = &result.SpecResult{ProtoSpec: &gauge_messages.ProtoSpec{}}
   117  	se.skipSpecForError(fmt.Errorf("ERROR"))
   118  
   119  	c.Assert(len(se.errMap.ScenarioErrs[se.specification.Scenarios[0]]), Equals, 1)
   120  	c.Assert(len(se.errMap.SpecErrs[se.specification]), Equals, 1)
   121  }
   122  
   123  func anySpec() *gauge.Specification {
   124  
   125  	specText := newSpecBuilder().specHeading("A spec heading").
   126  		scenarioHeading("First scenario").
   127  		step("create user \"456\" \"foo\" and \"9900\"").
   128  		String()
   129  
   130  	spec, _, _ := new(parser.SpecParser).Parse(specText, gauge.NewConceptDictionary(), "")
   131  	spec.FileName = "FILE"
   132  	return spec
   133  }
   134  
   135  func (s *MySuite) TestSpecIsSkippedIfDataRangeIsInvalid(c *C) {
   136  	errMap := &gauge.BuildErrors{
   137  		SpecErrs:     make(map[*gauge.Specification][]error),
   138  		ScenarioErrs: make(map[*gauge.Scenario][]error),
   139  		StepErrs:     make(map[*gauge.Step]error),
   140  	}
   141  	r := &mockRunner{}
   142  	spec := anySpec()
   143  	errMap.SpecErrs[spec] = []error{validation.NewSpecValidationError("Table row number out of range", spec.FileName)}
   144  	se := newSpecExecutor(spec, r, nil, errMap, 0)
   145  
   146  	specResult := se.execute(true, false, false)
   147  	c.Assert(specResult.Skipped, Equals, true)
   148  }
   149  
   150  func (s *MySuite) TestDataTableRowsAreSkippedForUnimplemetedStep(c *C) {
   151  	MaxRetriesCount = 1
   152  	stepText := "Unimplememted step"
   153  
   154  	specText := newSpecBuilder().specHeading("A spec heading").
   155  		tableHeader("id", "name", "phone").
   156  		tableRow("123", "foo", "8800").
   157  		tableRow("666", "bar", "9900").
   158  		scenarioHeading("First scenario").
   159  		step(stepText).
   160  		step("create user <id> <name> and <phone>").
   161  		String()
   162  
   163  	spec, _, _ := new(parser.SpecParser).Parse(specText, gauge.NewConceptDictionary(), "")
   164  
   165  	errMap := &gauge.BuildErrors{
   166  		SpecErrs:     make(map[*gauge.Specification][]error),
   167  		ScenarioErrs: make(map[*gauge.Scenario][]error),
   168  		StepErrs:     make(map[*gauge.Step]error),
   169  	}
   170  	r := &mockRunner{}
   171  	errMap.SpecErrs[spec] = []error{validation.NewSpecValidationError("Step implementation not found", spec.FileName)}
   172  	errMap.ScenarioErrs[spec.Scenarios[0]] = []error{validation.NewSpecValidationError("Step implementation not found", spec.FileName)}
   173  	se := newSpecExecutor(spec, r, nil, errMap, 0)
   174  
   175  	specResult := se.execute(true, true, true)
   176  	c.Assert(specResult.ProtoSpec.GetIsTableDriven(), Equals, true)
   177  	c.Assert(specResult.Skipped, Equals, true)
   178  }
   179  
   180  func (s *MySuite) TestConvertParseErrorToGaugeMessagesError(c *C) {
   181  	spec := &gauge.Specification{Heading: &gauge.Heading{LineNo: 0, Value: "SPEC_HEADING"}, FileName: "FILE"}
   182  	e := parser.ParseError{Message: "Message", LineNo: 5, FileName: "filename"}
   183  	r := &mockRunner{}
   184  	se := newSpecExecutor(spec, r, nil, nil, 0)
   185  
   186  	errs := se.convertErrors([]error{e})
   187  
   188  	expected := &gauge_messages.Error{
   189  		Type:       gauge_messages.Error_PARSE_ERROR,
   190  		Message:    "filename:5 Message => ''",
   191  		LineNumber: 5,
   192  		Filename:   "filename",
   193  	}
   194  
   195  	c.Assert(len(errs), DeepEquals, 1)
   196  	c.Assert(errs[0], DeepEquals, expected)
   197  }
   198  
   199  func (s *MySuite) TestConvertSpecValidationErrorToGaugeMessagesError(c *C) {
   200  	spec := &gauge.Specification{Heading: &gauge.Heading{LineNo: 0, Value: "SPEC_HEADING"}, FileName: "FILE"}
   201  	e := validation.NewSpecValidationError("Message", "filename")
   202  	r := &mockRunner{}
   203  	se := newSpecExecutor(spec, r, nil, nil, 0)
   204  
   205  	errs := se.convertErrors([]error{e})
   206  
   207  	expected := &gauge_messages.Error{
   208  		Type:    gauge_messages.Error_VALIDATION_ERROR,
   209  		Message: "filename Message",
   210  	}
   211  
   212  	c.Assert(len(errs), DeepEquals, 1)
   213  	c.Assert(errs[0], DeepEquals, expected)
   214  }
   215  
   216  func (s *MySuite) TestConvertStepValidationErrorToGaugeMessagesError(c *C) {
   217  	spec := &gauge.Specification{Heading: &gauge.Heading{LineNo: 0, Value: "SPEC_HEADING"}, FileName: "FILE"}
   218  	e := validation.NewStepValidationError(&gauge.Step{LineText: "step", LineNo: 3}, "Step Message", "filename", nil, "")
   219  	r := &mockRunner{}
   220  	se := newSpecExecutor(spec, r, nil, nil, 0)
   221  
   222  	errs := se.convertErrors([]error{e})
   223  
   224  	expected := &gauge_messages.Error{
   225  		Type:    gauge_messages.Error_VALIDATION_ERROR,
   226  		Message: "filename:3 Step Message => 'step'",
   227  	}
   228  
   229  	c.Assert(len(errs), DeepEquals, 1)
   230  	c.Assert(errs[0], DeepEquals, expected)
   231  }
   232  
   233  type mockRunner struct {
   234  	ExecuteAndGetStatusFunc func(m *gauge_messages.Message) *gauge_messages.ProtoExecutionResult
   235  }
   236  
   237  func (r *mockRunner) ExecuteMessageWithTimeout(m *gauge_messages.Message) (*gauge_messages.Message, error) {
   238  	return nil, nil
   239  }
   240  func (r *mockRunner) ExecuteAndGetStatus(m *gauge_messages.Message) *gauge_messages.ProtoExecutionResult {
   241  	return r.ExecuteAndGetStatusFunc(m)
   242  }
   243  
   244  func (r *mockRunner) Alive() bool {
   245  	return false
   246  }
   247  
   248  func (r *mockRunner) Kill() error {
   249  	return nil
   250  }
   251  
   252  func (r *mockRunner) Connection() net.Conn {
   253  	return nil
   254  }
   255  
   256  func (r *mockRunner) IsMultithreaded() bool {
   257  	return false
   258  }
   259  
   260  func (r *mockRunner) Info() *runner.RunnerInfo {
   261  	return &runner.RunnerInfo{Killed: false}
   262  }
   263  
   264  func (r *mockRunner) Pid() int {
   265  	return -1
   266  }
   267  
   268  type mockPluginHandler struct {
   269  	NotifyPluginsfunc         func(*gauge_messages.Message)
   270  	GracefullyKillPluginsfunc func()
   271  }
   272  
   273  func (h *mockPluginHandler) NotifyPlugins(m *gauge_messages.Message) {
   274  	h.NotifyPluginsfunc(m)
   275  }
   276  
   277  func (h *mockPluginHandler) GracefullyKillPlugins() {
   278  	h.GracefullyKillPluginsfunc()
   279  }
   280  
   281  func (h *mockPluginHandler) ExtendTimeout(id string) {
   282  
   283  }
   284  
   285  var exampleSpec = &gauge.Specification{Heading: &gauge.Heading{Value: "Example Spec"}, FileName: "example.spec", Tags: &gauge.Tags{}}
   286  
   287  var exampleSpecWithScenarios = &gauge.Specification{
   288  	Heading:  &gauge.Heading{Value: "Example Spec"},
   289  	FileName: "example.spec",
   290  	Tags:     &gauge.Tags{},
   291  	Scenarios: []*gauge.Scenario{
   292  		{Heading: &gauge.Heading{Value: "Example Scenario 1"}, Items: make([]gauge.Item, 0), Tags: &gauge.Tags{}, Span: &gauge.Span{}},
   293  		{Heading: &gauge.Heading{Value: "Example Scenario 2"}, Items: make([]gauge.Item, 0), Tags: &gauge.Tags{}, Span: &gauge.Span{}},
   294  	},
   295  }
   296  
   297  func TestExecuteFailsWhenSpecHasParseErrors(t *testing.T) {
   298  	errs := gauge.NewBuildErrors()
   299  	r := &mockRunner{}
   300  	errs.SpecErrs[exampleSpec] = append(errs.SpecErrs[exampleSpec], parser.ParseError{Message: "some error"})
   301  	se := newSpecExecutor(exampleSpec, r, nil, errs, 0)
   302  
   303  	res := se.execute(false, true, false)
   304  
   305  	if !res.GetFailed() {
   306  		t.Errorf("Expected result.Failed=true, got %t", res.GetFailed())
   307  	}
   308  
   309  	c := len(res.Errors)
   310  	if c != 1 {
   311  		t.Errorf("Expected result to contain 1 error, got %d", c)
   312  	}
   313  }
   314  
   315  func TestExecuteSkipsWhenSpecHasErrors(t *testing.T) {
   316  	errs := gauge.NewBuildErrors()
   317  	r := &mockRunner{}
   318  	errs.SpecErrs[exampleSpec] = append(errs.SpecErrs[exampleSpec], fmt.Errorf("some error"))
   319  	se := newSpecExecutor(exampleSpec, r, nil, errs, 0)
   320  
   321  	res := se.execute(false, true, false)
   322  
   323  	if !res.Skipped {
   324  		t.Errorf("Expected result.Skipped=true, got %t", res.Skipped)
   325  	}
   326  }
   327  
   328  func TestExecuteInitSpecDatastore(t *testing.T) {
   329  	errs := gauge.NewBuildErrors()
   330  	r := &mockRunner{}
   331  	h := &mockPluginHandler{NotifyPluginsfunc: func(m *gauge_messages.Message) {}, GracefullyKillPluginsfunc: func() {}}
   332  	dataStoreInitCalled := false
   333  	r.ExecuteAndGetStatusFunc = func(m *gauge_messages.Message) *gauge_messages.ProtoExecutionResult {
   334  		if m.MessageType == gauge_messages.Message_SpecDataStoreInit {
   335  			dataStoreInitCalled = true
   336  		}
   337  		return &gauge_messages.ProtoExecutionResult{}
   338  	}
   339  	se := newSpecExecutor(exampleSpecWithScenarios, r, h, errs, 0)
   340  	se.execute(true, false, false)
   341  
   342  	if !dataStoreInitCalled {
   343  		t.Error("Expected runner to be called with SpecDataStoreInit")
   344  	}
   345  }
   346  
   347  func TestExecuteShouldNotInitSpecDatastoreWhenBeforeIsFalse(t *testing.T) {
   348  	errs := gauge.NewBuildErrors()
   349  	r := &mockRunner{}
   350  
   351  	dataStoreInitCalled := false
   352  	r.ExecuteAndGetStatusFunc = func(m *gauge_messages.Message) *gauge_messages.ProtoExecutionResult {
   353  		if m.MessageType == gauge_messages.Message_SpecDataStoreInit {
   354  			dataStoreInitCalled = true
   355  		}
   356  		return &gauge_messages.ProtoExecutionResult{}
   357  	}
   358  	se := newSpecExecutor(exampleSpec, r, nil, errs, 0)
   359  	se.execute(false, false, false)
   360  
   361  	if dataStoreInitCalled {
   362  		t.Error("Expected SpecDataStoreInit to not be called")
   363  	}
   364  }
   365  
   366  func TestExecuteSkipsWhenSpecDatastoreInitFails(t *testing.T) {
   367  	errs := gauge.NewBuildErrors()
   368  	r := &mockRunner{}
   369  
   370  	r.ExecuteAndGetStatusFunc = func(m *gauge_messages.Message) *gauge_messages.ProtoExecutionResult {
   371  		return &gauge_messages.ProtoExecutionResult{Failed: true, ErrorMessage: "datastore init error"}
   372  	}
   373  	se := newSpecExecutor(exampleSpecWithScenarios, r, nil, errs, 0)
   374  	res := se.execute(true, false, false)
   375  
   376  	if !res.Skipped {
   377  		t.Errorf("Expected result.Skipped=true, got %t", res.Skipped)
   378  	}
   379  
   380  	e := res.Errors[0]
   381  	expected := "example.spec:0 Failed to initialize spec datastore. Error: datastore init error => 'Example Spec'"
   382  	if e.Message != expected {
   383  		t.Errorf("Expected error = '%s', got '%s'", expected, e.Message)
   384  	}
   385  }
   386  
   387  func TestExecuteBeforeSpecHook(t *testing.T) {
   388  	errs := gauge.NewBuildErrors()
   389  	r := &mockRunner{}
   390  	h := &mockPluginHandler{NotifyPluginsfunc: func(m *gauge_messages.Message) {}, GracefullyKillPluginsfunc: func() {}}
   391  
   392  	beforeSpecHookCalled := false
   393  	r.ExecuteAndGetStatusFunc = func(m *gauge_messages.Message) *gauge_messages.ProtoExecutionResult {
   394  		if m.MessageType == gauge_messages.Message_SpecExecutionStarting {
   395  			beforeSpecHookCalled = true
   396  		}
   397  		return &gauge_messages.ProtoExecutionResult{}
   398  	}
   399  	se := newSpecExecutor(exampleSpecWithScenarios, r, h, errs, 0)
   400  	se.execute(true, false, false)
   401  
   402  	if !beforeSpecHookCalled {
   403  		t.Error("Expected runner to be called with SpecExecutionStarting")
   404  	}
   405  }
   406  
   407  func TestExecuteShouldNotifyBeforeSpecEvent(t *testing.T) {
   408  	errs := gauge.NewBuildErrors()
   409  	r := &mockRunner{}
   410  	h := &mockPluginHandler{NotifyPluginsfunc: func(m *gauge_messages.Message) {}, GracefullyKillPluginsfunc: func() {}}
   411  
   412  	eventRaised := false
   413  	r.ExecuteAndGetStatusFunc = func(m *gauge_messages.Message) *gauge_messages.ProtoExecutionResult {
   414  		return &gauge_messages.ProtoExecutionResult{}
   415  	}
   416  
   417  	ch := make(chan event.ExecutionEvent)
   418  	event.InitRegistry()
   419  	event.Register(ch, event.SpecStart)
   420  	wg := &sync.WaitGroup{}
   421  	wg.Add(1)
   422  	go func() {
   423  		for {
   424  			e := <-ch
   425  			t.Log(e.Topic)
   426  			if e.Topic == event.SpecStart {
   427  				eventRaised = true
   428  				wg.Done()
   429  			}
   430  		}
   431  	}()
   432  	se := newSpecExecutor(exampleSpecWithScenarios, r, h, errs, 0)
   433  	se.execute(true, false, false)
   434  
   435  	wg.Wait()
   436  	if !eventRaised {
   437  		t.Error("Expected SpecStart event to be raised")
   438  	}
   439  	event.InitRegistry()
   440  }
   441  func TestExecuteAfterSpecHook(t *testing.T) {
   442  	errs := gauge.NewBuildErrors()
   443  	r := &mockRunner{}
   444  	h := &mockPluginHandler{NotifyPluginsfunc: func(m *gauge_messages.Message) {}, GracefullyKillPluginsfunc: func() {}}
   445  
   446  	afterSpecHookCalled := false
   447  	r.ExecuteAndGetStatusFunc = func(m *gauge_messages.Message) *gauge_messages.ProtoExecutionResult {
   448  		if m.MessageType == gauge_messages.Message_SpecExecutionEnding {
   449  			afterSpecHookCalled = true
   450  		}
   451  		return &gauge_messages.ProtoExecutionResult{}
   452  	}
   453  	se := newSpecExecutor(exampleSpecWithScenarios, r, h, errs, 0)
   454  	se.execute(false, false, true)
   455  
   456  	if !afterSpecHookCalled {
   457  		t.Error("Expected runner to be called with SpecExecutionAfter")
   458  	}
   459  }
   460  
   461  func TestExecuteAddsSpecHookExecutionMessages(t *testing.T) {
   462  	errs := gauge.NewBuildErrors()
   463  	mockRunner := &mockRunner{}
   464  	mockHandler := &mockPluginHandler{NotifyPluginsfunc: func(m *gauge_messages.Message) {}, GracefullyKillPluginsfunc: func() {}}
   465  
   466  	mockRunner.ExecuteAndGetStatusFunc = func(m *gauge_messages.Message) *gauge_messages.ProtoExecutionResult {
   467  		if m.MessageType == gauge_messages.Message_SpecExecutionEnding {
   468  			return &gauge_messages.ProtoExecutionResult{
   469  				Message:       []string{"After Spec Called"},
   470  				Failed:        false,
   471  				ExecutionTime: 10,
   472  			}
   473  		} else if m.MessageType == gauge_messages.Message_SpecExecutionStarting {
   474  			return &gauge_messages.ProtoExecutionResult{
   475  				Message:       []string{"Before Spec Called"},
   476  				Failed:        false,
   477  				ExecutionTime: 10,
   478  			}
   479  		}
   480  		return &gauge_messages.ProtoExecutionResult{}
   481  	}
   482  	se := newSpecExecutor(exampleSpec, mockRunner, mockHandler, errs, 0)
   483  	se.execute(true, false, true)
   484  
   485  	gotPreHookMessages := se.specResult.ProtoSpec.PreHookMessages
   486  	gotPostHookMessages := se.specResult.ProtoSpec.PostHookMessages
   487  
   488  	if len(gotPreHookMessages) != 1 {
   489  		t.Errorf("Expected 1 message, got : %d", len(gotPreHookMessages))
   490  	}
   491  	if gotPreHookMessages[0] != "Before Spec Called" {
   492  		t.Errorf("Expected `Before Spec Called` message, got : %s", gotPreHookMessages[0])
   493  	}
   494  	if len(gotPostHookMessages) != 1 {
   495  		t.Errorf("Expected 1 message, got : %d", len(gotPostHookMessages))
   496  	}
   497  	if gotPostHookMessages[0] != "After Spec Called" {
   498  		t.Errorf("Expected `After Spec Called` message, got : %s", gotPostHookMessages[0])
   499  	}
   500  }
   501  
   502  func TestExecuteAddsSpecHookExecutionScreenshots(t *testing.T) {
   503  	errs := gauge.NewBuildErrors()
   504  	mockRunner := &mockRunner{}
   505  	mockHandler := &mockPluginHandler{NotifyPluginsfunc: func(m *gauge_messages.Message) {}, GracefullyKillPluginsfunc: func() {}}
   506  
   507  	mockRunner.ExecuteAndGetStatusFunc = func(m *gauge_messages.Message) *gauge_messages.ProtoExecutionResult {
   508  		if m.MessageType == gauge_messages.Message_SpecExecutionEnding {
   509  			return &gauge_messages.ProtoExecutionResult{
   510  				ScreenshotFiles: []string{"screenshot1.png", "screenshot2.png"},
   511  				Failed:          false,
   512  				ExecutionTime:   10,
   513  			}
   514  		} else if m.MessageType == gauge_messages.Message_SpecExecutionStarting {
   515  			return &gauge_messages.ProtoExecutionResult{
   516  				ScreenshotFiles: []string{"screenshot3.png", "screenshot4.png"},
   517  				Failed:          false,
   518  				ExecutionTime:   10,
   519  			}
   520  		}
   521  		return &gauge_messages.ProtoExecutionResult{}
   522  	}
   523  	se := newSpecExecutor(exampleSpec, mockRunner, mockHandler, errs, 0)
   524  	se.execute(true, false, true)
   525  
   526  	beforeSpecScreenshots := se.specResult.ProtoSpec.PreHookScreenshotFiles
   527  	afterSpecScreenshots := se.specResult.ProtoSpec.PostHookScreenshotFiles
   528  	expectedAfterSpecScreenshots := []string{"screenshot1.png", "screenshot2.png"}
   529  	expectedBeforeSpecScreenshots := []string{"screenshot3.png", "screenshot4.png"}
   530  
   531  	if len(beforeSpecScreenshots) != len(expectedBeforeSpecScreenshots) {
   532  		t.Errorf("Expected 2 screenshots, got : %d", len(beforeSpecScreenshots))
   533  	}
   534  	for i, e := range expectedBeforeSpecScreenshots {
   535  		if string(beforeSpecScreenshots[i]) != e {
   536  			t.Errorf("Expected `%s` screenshot, got : %s", e, beforeSpecScreenshots[i])
   537  		}
   538  	}
   539  	if len(afterSpecScreenshots) != len(expectedAfterSpecScreenshots) {
   540  		t.Errorf("Expected 2 screenshots, got : %d", len(afterSpecScreenshots))
   541  	}
   542  	for i, e := range expectedAfterSpecScreenshots {
   543  		if string(afterSpecScreenshots[i]) != e {
   544  			t.Errorf("Expected `%s` screenshot, got : %s", e, afterSpecScreenshots[i])
   545  		}
   546  	}
   547  }
   548  
   549  func TestExecuteShouldNotifyAfterSpecEvent(t *testing.T) {
   550  	errs := gauge.NewBuildErrors()
   551  	r := &mockRunner{}
   552  	h := &mockPluginHandler{NotifyPluginsfunc: func(m *gauge_messages.Message) {}, GracefullyKillPluginsfunc: func() {}}
   553  
   554  	eventRaised := false
   555  	r.ExecuteAndGetStatusFunc = func(m *gauge_messages.Message) *gauge_messages.ProtoExecutionResult {
   556  		return &gauge_messages.ProtoExecutionResult{}
   557  	}
   558  
   559  	ch := make(chan event.ExecutionEvent)
   560  	event.InitRegistry()
   561  	event.Register(ch, event.SpecEnd)
   562  	wg := &sync.WaitGroup{}
   563  	wg.Add(1)
   564  	go func() {
   565  		for {
   566  			e := <-ch
   567  			t.Log(e.Topic)
   568  			if e.Topic == event.SpecEnd {
   569  				eventRaised = true
   570  				wg.Done()
   571  			}
   572  		}
   573  	}()
   574  	se := newSpecExecutor(exampleSpecWithScenarios, r, h, errs, 0)
   575  	se.execute(false, false, true)
   576  
   577  	wg.Wait()
   578  	if !eventRaised {
   579  		t.Error("Expected SpecEnd event to be raised")
   580  	}
   581  	event.InitRegistry()
   582  }
   583  
   584  type mockExecutor struct {
   585  	executeFunc func(i gauge.Item, r result.Result)
   586  }
   587  
   588  func (e *mockExecutor) execute(i gauge.Item, r result.Result) {
   589  	e.executeFunc(i, r)
   590  }
   591  
   592  func TestExecuteScenario(t *testing.T) {
   593  	MaxRetriesCount = 1
   594  	errs := gauge.NewBuildErrors()
   595  	r := &mockRunner{}
   596  	se := newSpecExecutor(exampleSpecWithScenarios, r, nil, errs, 0)
   597  	executedScenarios := make([]string, 0)
   598  	se.scenarioExecutor = &mockExecutor{
   599  		executeFunc: func(i gauge.Item, r result.Result) {
   600  			executedScenarios = append(executedScenarios, i.(*gauge.Scenario).Heading.Value)
   601  		},
   602  	}
   603  	se.execute(false, true, false)
   604  	got := len(executedScenarios)
   605  	if got != 2 {
   606  		t.Errorf("Expected 2 scenarios to be executed, got %d", got)
   607  	}
   608  
   609  	expected := []string{"Example Scenario 1", "Example Scenario 2"}
   610  	for i, s := range executedScenarios {
   611  		if s != expected[i] {
   612  			t.Errorf("Expected '%s' scenario to be executed. Got %s", s, executedScenarios)
   613  		}
   614  	}
   615  }
   616  
   617  func TestExecuteScenarioWithRetries(t *testing.T) {
   618  	MaxRetriesCount = 3
   619  	errs := gauge.NewBuildErrors()
   620  	r := &mockRunner{}
   621  	se := newSpecExecutor(exampleSpecWithScenarios, r, nil, errs, 0)
   622  
   623  	count := 1
   624  	se.scenarioExecutor = &mockExecutor{
   625  		executeFunc: func(i gauge.Item, r result.Result) {
   626  			if count < MaxRetriesCount {
   627  				r.SetFailure()
   628  			} else {
   629  				r.(*result.ScenarioResult).ProtoScenario.ExecutionStatus = gauge_messages.ExecutionStatus_PASSED
   630  			}
   631  
   632  			count++
   633  		},
   634  	}
   635  
   636  	sceResult, _ := se.executeScenario(exampleSpecWithScenarios.Scenarios[0])
   637  
   638  	if sceResult.GetFailed() {
   639  		t.Errorf("Expect sceResult.GetFailed() = false, got true")
   640  	}
   641  }
   642  
   643  var exampleSpecWithTags = &gauge.Specification{
   644  	Heading:  &gauge.Heading{Value: "Example Spec"},
   645  	FileName: "example.spec",
   646  	Tags:     &gauge.Tags{RawValues: [][]string{{"tagSpec"}}},
   647  	Scenarios: []*gauge.Scenario{
   648  		{Heading: &gauge.Heading{Value: "Example Scenario 1"}, Items: make([]gauge.Item, 0), Tags: &gauge.Tags{RawValues: [][]string{{"tagSce"}}}, Span: &gauge.Span{}},
   649  	},
   650  }
   651  
   652  func TestExecuteScenarioShouldNotRetryIfNotMatchTags(t *testing.T) {
   653  	MaxRetriesCount = 2
   654  	RetryOnlyTags = "tagN"
   655  
   656  	se := newSpecExecutorForTestsWithRetry()
   657  	sceResult, _ := se.executeScenario(exampleSpecWithTags.Scenarios[0])
   658  
   659  	if !sceResult.GetFailed() {
   660  		t.Errorf("Expect sceResult.GetFailed() = true, got false")
   661  	}
   662  }
   663  
   664  func TestExecuteScenarioShouldRetryIfSpecificationMatchTags(t *testing.T) {
   665  	MaxRetriesCount = 2
   666  	RetryOnlyTags = "tagSpec"
   667  
   668  	se := newSpecExecutorForTestsWithRetry()
   669  
   670  	sceResult, _ := se.executeScenario(exampleSpecWithTags.Scenarios[0])
   671  
   672  	if sceResult.GetFailed() {
   673  		t.Errorf("Expect sceResult.GetFailed() = false, got true")
   674  	}
   675  }
   676  
   677  func TestExecuteScenarioShouldRetryIfScenarioMatchTags(t *testing.T) {
   678  	MaxRetriesCount = 2
   679  	RetryOnlyTags = "tagSce"
   680  
   681  	se := newSpecExecutorForTestsWithRetry()
   682  
   683  	sceResult, _ := se.executeScenario(exampleSpecWithTags.Scenarios[0])
   684  
   685  	if sceResult.GetFailed() {
   686  		t.Errorf("Expect sceResult.GetFailed() = false, got true")
   687  	}
   688  }
   689  
   690  func newSpecExecutorForTestsWithRetry() *specExecutor {
   691  	errs := gauge.NewBuildErrors()
   692  	r := &mockRunner{}
   693  	se := newSpecExecutor(exampleSpecWithTags, r, nil, errs, 0)
   694  
   695  	count := 1
   696  	se.scenarioExecutor = &mockExecutor{
   697  		executeFunc: func(i gauge.Item, r result.Result) {
   698  			if count < MaxRetriesCount {
   699  				r.SetFailure()
   700  			} else {
   701  				r.(*result.ScenarioResult).ProtoScenario.ExecutionStatus = gauge_messages.ExecutionStatus_PASSED
   702  			}
   703  
   704  			count++
   705  		},
   706  	}
   707  
   708  	return se
   709  }
   710  
   711  func TestExecuteShouldMarkSpecAsSkippedWhenAllScenariosSkipped(t *testing.T) {
   712  	errs := gauge.NewBuildErrors()
   713  	r := &mockRunner{}
   714  	se := newSpecExecutor(exampleSpecWithScenarios, r, nil, errs, 0)
   715  	se.scenarioExecutor = &mockExecutor{
   716  		executeFunc: func(i gauge.Item, r result.Result) {
   717  			r.(*result.ScenarioResult).ProtoScenario.ExecutionStatus = gauge_messages.ExecutionStatus_SKIPPED
   718  		},
   719  	}
   720  	res := se.execute(false, true, false)
   721  	if !res.Skipped {
   722  		t.Error("Expect SpecResult.Skipped = true, got false")
   723  	}
   724  }
   725  
   726  func TestExecuteScenarioShoulHaveRetriesInfo(t *testing.T) {
   727  	MaxRetriesCount = 3
   728  	RetryOnlyTags = "tagSce"
   729  
   730  	se := newSpecExecutorForTestsWithRetry()
   731  	sceResult, _ := se.executeScenario(exampleSpecWithTags.Scenarios[0])
   732  
   733  	if sceResult.GetFailed() {
   734  		t.Errorf("Expect sceResult.GetFailed() = false, got true")
   735  	}
   736  	if se.currentExecutionInfo.CurrentScenario.Retries.MaxRetries != int32(MaxRetriesCount - 1) {
   737  		t.Errorf("Expected MaxRetries %d, got %d", 
   738  			int32(MaxRetriesCount - 1), 
   739  			se.currentExecutionInfo.CurrentScenario.Retries.MaxRetries)
   740  	}
   741  	if se.currentExecutionInfo.CurrentScenario.Retries.CurrentRetry != int32(MaxRetriesCount - 1) {
   742  		t.Errorf("Expected CurrentRetry %d, got %d", 
   743  			int32(MaxRetriesCount - 1), 
   744  			se.currentExecutionInfo.CurrentScenario.Retries.CurrentRetry)
   745  	}
   746  }
   747