gotest.tools/gotestsum@v1.11.0/cmd/rerunfails_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"strings"
     9  	"testing"
    10  
    11  	"gotest.tools/gotestsum/testjson"
    12  	"gotest.tools/v3/assert"
    13  	"gotest.tools/v3/fs"
    14  	"gotest.tools/v3/golden"
    15  )
    16  
    17  func TestWriteRerunFailsReport(t *testing.T) {
    18  	reportFile := fs.NewFile(t, t.Name())
    19  	defer reportFile.Remove()
    20  
    21  	opts := &options{
    22  		rerunFailsReportFile:  reportFile.Path(),
    23  		rerunFailsMaxAttempts: 4,
    24  	}
    25  
    26  	exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
    27  		Stdout: bytes.NewReader(golden.Get(t, "go-test-json-flaky-rerun.out")),
    28  	})
    29  	assert.NilError(t, err)
    30  
    31  	err = writeRerunFailsReport(opts, exec)
    32  	assert.NilError(t, err)
    33  
    34  	raw, err := ioutil.ReadFile(reportFile.Path())
    35  	assert.NilError(t, err)
    36  	golden.Assert(t, string(raw), t.Name()+"-expected")
    37  }
    38  
    39  func TestWriteRerunFailsReport_HandlesMissingActionRunEvents(t *testing.T) {
    40  	reportFile := fs.NewFile(t, t.Name())
    41  	defer reportFile.Remove()
    42  
    43  	opts := &options{
    44  		rerunFailsReportFile:  reportFile.Path(),
    45  		rerunFailsMaxAttempts: 4,
    46  	}
    47  
    48  	exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
    49  		Stdout: bytes.NewReader(golden.Get(t, "go-test-missing-run-events.out")),
    50  	})
    51  	assert.NilError(t, err)
    52  
    53  	err = writeRerunFailsReport(opts, exec)
    54  	assert.NilError(t, err)
    55  
    56  	raw, err := ioutil.ReadFile(reportFile.Path())
    57  	assert.NilError(t, err)
    58  	golden.Assert(t, string(raw), t.Name()+"-expected")
    59  }
    60  
    61  func TestGoTestRunFlagFromTestCases(t *testing.T) {
    62  	type testCase struct {
    63  		input    string
    64  		expected string
    65  	}
    66  	fn := func(t *testing.T, tc testCase) {
    67  		actual := goTestRunFlagForTestCase(testjson.TestName(tc.input))
    68  		assert.Equal(t, actual, tc.expected)
    69  	}
    70  
    71  	var testCases = map[string]testCase{
    72  		"root test case": {
    73  			input:    "TestOne",
    74  			expected: "-test.run=^TestOne$",
    75  		},
    76  		"sub test case": {
    77  			input:    "TestOne/SubtestA",
    78  			expected: "-test.run=^TestOne$/^SubtestA$",
    79  		},
    80  		"sub test case with special characters": {
    81  			input:    "TestOne/Subtest(A)[100]",
    82  			expected: `-test.run=^TestOne$/^Subtest\(A\)\[100\]$`,
    83  		},
    84  		"nested sub test case": {
    85  			input:    "TestOne/Nested/SubtestA",
    86  			expected: `-test.run=^TestOne$/^Nested$/^SubtestA$`,
    87  		},
    88  	}
    89  
    90  	for name := range testCases {
    91  		t.Run(name, func(t *testing.T) {
    92  			fn(t, testCases[name])
    93  		})
    94  	}
    95  }
    96  
    97  func TestRerunFailed_ReturnsAnErrorWhenTheLastTestIsSuccessful(t *testing.T) {
    98  	type result struct {
    99  		out string
   100  		err error
   101  	}
   102  	jsonFailed := `{"Package": "pkg", "Action": "run"}
   103  {"Package": "pkg", "Test": "TestOne", "Action": "run"}
   104  {"Package": "pkg", "Test": "TestOne", "Action": "fail"}
   105  {"Package": "pkg", "Action": "fail"}
   106  `
   107  	events := []result{
   108  		{out: jsonFailed, err: newExitCode("run-failed-1", 1)},
   109  		{out: jsonFailed, err: newExitCode("run-failed-2", 1)},
   110  		{out: jsonFailed, err: newExitCode("run-failed-3", 1)},
   111  		{
   112  			out: `{"Package": "pkg", "Action": "run"}
   113  {"Package": "pkg", "Test": "TestOne", "Action": "run"}
   114  {"Package": "pkg", "Test": "TestOne", "Action": "pass"}
   115  {"Package": "pkg", "Action": "pass"}
   116  `,
   117  		},
   118  	}
   119  
   120  	fn := func(args []string) *proc {
   121  		next := events[0]
   122  		events = events[1:]
   123  		return &proc{
   124  			cmd:    fakeWaiter{result: next.err},
   125  			stdout: strings.NewReader(next.out),
   126  			stderr: bytes.NewReader(nil),
   127  		}
   128  	}
   129  	reset := patchStartGoTestFn(fn)
   130  	defer reset()
   131  
   132  	stdout := new(bytes.Buffer)
   133  	ctx := context.Background()
   134  	opts := &options{
   135  		rerunFailsMaxInitialFailures: 10,
   136  		rerunFailsMaxAttempts:        2,
   137  		stdout:                       stdout,
   138  	}
   139  	cfg := testjson.ScanConfig{
   140  		Execution: newExecutionWithTwoFailures(t),
   141  		Handler:   noopHandler{},
   142  	}
   143  	err := rerunFailed(ctx, opts, cfg)
   144  	assert.Error(t, err, "run-failed-3")
   145  }
   146  
   147  func patchStartGoTestFn(f func(args []string) *proc) func() {
   148  	orig := startGoTestFn
   149  	startGoTestFn = func(ctx context.Context, dir string, args []string) (*proc, error) {
   150  		return f(args), nil
   151  	}
   152  	return func() {
   153  		startGoTestFn = orig
   154  	}
   155  }
   156  
   157  func newExecutionWithTwoFailures(t *testing.T) *testjson.Execution {
   158  	t.Helper()
   159  
   160  	out := `{"Package": "pkg", "Action": "run"}
   161  {"Package": "pkg", "Test": "TestOne", "Action": "run"}
   162  {"Package": "pkg", "Test": "TestOne", "Action": "fail"}
   163  {"Package": "pkg", "Test": "TestTwo", "Action": "run"}
   164  {"Package": "pkg", "Test": "TestTwo", "Action": "fail"}
   165  {"Package": "pkg", "Action": "fail"}
   166  `
   167  	exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
   168  		Stdout: strings.NewReader(out),
   169  		Stderr: strings.NewReader(""),
   170  	})
   171  	assert.NilError(t, err)
   172  	return exec
   173  }
   174  
   175  type fakeWaiter struct {
   176  	result error
   177  }
   178  
   179  func (f fakeWaiter) Wait() error {
   180  	return f.result
   181  }
   182  
   183  type exitCodeError struct {
   184  	error
   185  	code int
   186  }
   187  
   188  func (e exitCodeError) ExitCode() int {
   189  	return e.code
   190  }
   191  
   192  func newExitCode(msg string, code int) error {
   193  	return exitCodeError{error: fmt.Errorf(msg), code: code}
   194  }
   195  
   196  type noopHandler struct{}
   197  
   198  func (s noopHandler) Event(testjson.TestEvent, *testjson.Execution) error {
   199  	return nil
   200  }
   201  
   202  func (s noopHandler) Err(string) error {
   203  	return nil
   204  }