gitee.com/mirrors/gauge@v1.0.6/execution/specExecutor_test.go (about)

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