gotest.tools/gotestsum@v1.11.0/testjson/execution_test.go (about)

     1  package testjson
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/google/go-cmp/cmp"
    11  	"github.com/google/go-cmp/cmp/cmpopts"
    12  	"gotest.tools/v3/assert"
    13  	"gotest.tools/v3/golden"
    14  )
    15  
    16  func TestExecution_Add_PackageCoverage(t *testing.T) {
    17  	exec := newExecution()
    18  	exec.add(TestEvent{
    19  		Package: "mytestpkg",
    20  		Action:  ActionOutput,
    21  		Output:  "coverage: 33.1% of statements\n",
    22  	})
    23  
    24  	pkg := exec.Package("mytestpkg")
    25  	expected := &Package{
    26  		coverage: "coverage: 33.1% of statements",
    27  		output: map[int][]string{
    28  			0: {"coverage: 33.1% of statements\n"},
    29  		},
    30  		running: map[string]TestCase{},
    31  	}
    32  	assert.DeepEqual(t, pkg, expected, cmpPackage)
    33  }
    34  
    35  var cmpPackage = cmp.Options{
    36  	cmp.AllowUnexported(Package{}),
    37  	cmpopts.EquateEmpty(),
    38  }
    39  
    40  func TestScanTestOutput_MinimalConfig(t *testing.T) {
    41  	in := bytes.NewReader(golden.Get(t, "input/go-test-json.out"))
    42  	exec, err := ScanTestOutput(ScanConfig{Stdout: in})
    43  	assert.NilError(t, err)
    44  	// a weak check to show that all the stdout was scanned
    45  	assert.Equal(t, exec.Total(), 59)
    46  }
    47  
    48  func TestScanTestOutput_CallsStopOnError(t *testing.T) {
    49  	var called bool
    50  	stop := func() {
    51  		called = true
    52  	}
    53  	cfg := ScanConfig{
    54  		Stdout:  bytes.NewReader(golden.Get(t, "input/go-test-json.out")),
    55  		Handler: &handlerFails{},
    56  		Stop:    stop,
    57  	}
    58  	_, err := ScanTestOutput(cfg)
    59  	assert.Error(t, err, "something failed")
    60  	assert.Assert(t, called)
    61  }
    62  
    63  type handlerFails struct {
    64  	count int
    65  }
    66  
    67  func (s *handlerFails) Event(_ TestEvent, _ *Execution) error {
    68  	if s.count > 1 {
    69  		return fmt.Errorf("something failed")
    70  	}
    71  	s.count++
    72  	return nil
    73  }
    74  
    75  func (s *handlerFails) Err(_ string) error {
    76  	return nil
    77  }
    78  
    79  func TestParseEvent(t *testing.T) {
    80  	// nolint: lll
    81  	raw := `{"Time":"2018-03-22T22:33:35.168308334Z","Action":"output","Package":"example.com/good","Test": "TestOk","Output":"PASS\n"}`
    82  	event, err := parseEvent([]byte(raw))
    83  	assert.NilError(t, err)
    84  	expected := TestEvent{
    85  		Time:    time.Date(2018, 3, 22, 22, 33, 35, 168308334, time.UTC),
    86  		Action:  "output",
    87  		Package: "example.com/good",
    88  		Test:    "TestOk",
    89  		Output:  "PASS\n",
    90  		raw:     []byte(raw),
    91  	}
    92  	cmpTestEvent := cmp.AllowUnexported(TestEvent{})
    93  	assert.DeepEqual(t, event, expected, cmpTestEvent)
    94  }
    95  
    96  func TestPackage_AddEvent(t *testing.T) {
    97  	type testCase struct {
    98  		name     string
    99  		event    string
   100  		expected Package
   101  	}
   102  
   103  	run := func(t *testing.T, tc testCase) {
   104  		te, err := parseEvent([]byte(tc.event))
   105  		assert.NilError(t, err)
   106  
   107  		p := newPackage()
   108  		p.addEvent(te)
   109  		assert.DeepEqual(t, p, &tc.expected, cmpPackage)
   110  	}
   111  
   112  	var testCases = []testCase{
   113  		{
   114  			name:  "coverage with -cover",
   115  			event: `{"Action":"output","Package":"gotest.tools/testing","Output":"coverage: 4.2% of statements\n"}`,
   116  			expected: Package{
   117  				coverage: "coverage: 4.2% of statements",
   118  				output:   pkgOutput(0, "coverage: 4.2% of statements\n"),
   119  			},
   120  		},
   121  		{
   122  			name:  "coverage with -coverpkg",
   123  			event: `{"Action":"output","Package":"gotest.tools/testing","Output":"coverage: 7.5% of statements in ./testing\n"}`,
   124  			expected: Package{
   125  				coverage: "coverage: 7.5% of statements in ./testing",
   126  				output:   pkgOutput(0, "coverage: 7.5% of statements in ./testing\n"),
   127  			},
   128  		},
   129  		{
   130  			name:     "package failed",
   131  			event:    `{"Action":"fail","Package":"gotest.tools/testing","Elapsed":0.012}`,
   132  			expected: Package{action: ActionFail, elapsed: 12 * time.Millisecond},
   133  		},
   134  		{
   135  			name:  "package is cached",
   136  			event: `{"Action":"output","Package":"gotest.tools/testing","Output":"ok  \tgotest.tools/testing\t(cached)\n"}`,
   137  			expected: Package{
   138  				cached: true,
   139  				output: pkgOutput(0, "ok  \tgotest.tools/testing\t(cached)\n"),
   140  			},
   141  		},
   142  		{
   143  			name:     "package pass",
   144  			event:    `{"Action":"pass","Package":"gotest.tools/testing","Elapsed":0.012}`,
   145  			expected: Package{action: ActionPass, elapsed: 12 * time.Millisecond},
   146  		},
   147  	}
   148  
   149  	for _, tc := range testCases {
   150  		t.Run(tc.name, func(t *testing.T) {
   151  			run(t, tc)
   152  		})
   153  	}
   154  }
   155  
   156  func pkgOutput(id int, line string) map[int][]string {
   157  	return map[int][]string{id: {line}}
   158  }
   159  
   160  func TestScanTestOutput_WithMissingEvents(t *testing.T) {
   161  	source := golden.Get(t, "go-test-json-missing-test-events.out")
   162  	handler := &captureHandler{}
   163  	cfg := ScanConfig{
   164  		Stdout:  bytes.NewReader(source),
   165  		Handler: handler,
   166  	}
   167  	_, err := ScanTestOutput(cfg)
   168  	assert.NilError(t, err)
   169  
   170  	var cmpTestEventShallow = cmp.Options{
   171  		cmp.Comparer(func(x, y TestEvent) bool {
   172  			return x.Test == y.Test && x.Action == y.Action && x.Elapsed == y.Elapsed
   173  		}),
   174  		cmpopts.SortSlices(func(x, y TestEvent) bool {
   175  			return x.Test < y.Test
   176  		}),
   177  	}
   178  
   179  	// the package end event should come immediately before the artificial events
   180  	expected := []TestEvent{
   181  		{Action: ActionPass},
   182  		{Action: ActionFail, Test: "TestMissing", Elapsed: -1},
   183  		{Action: ActionFail, Test: "TestMissing/a", Elapsed: -1},
   184  		{Action: ActionFail, Test: "TestMissingEvent", Elapsed: -1},
   185  		{Action: ActionFail, Test: "TestFailed/a", Elapsed: -1},
   186  		{Action: ActionFail, Test: "TestFailed/a/sub", Elapsed: -1},
   187  	}
   188  	assert.Assert(t, len(handler.events) > len(expected))
   189  	start := len(handler.events) - len(expected)
   190  	assert.DeepEqual(t, expected, handler.events[start:], cmpTestEventShallow)
   191  }
   192  
   193  func TestScanTestOutput_WithNonJSONLines(t *testing.T) {
   194  	source := golden.Get(t, "go-test-json-with-nonjson-stdout.out")
   195  	nonJSONLine := "|||This line is not valid test2json output.|||"
   196  
   197  	// Test that when we ignore non-JSON lines, scanning completes, and test
   198  	// that when we don't ignore non-JSON lines, scanning fails.
   199  	for _, ignore := range []bool{true, false} {
   200  		t.Run(fmt.Sprintf("ignore-non-json=%v", ignore), func(t *testing.T) {
   201  			handler := &captureHandler{}
   202  			cfg := ScanConfig{
   203  				Stdout:                   bytes.NewReader(source),
   204  				Handler:                  handler,
   205  				IgnoreNonJSONOutputLines: ignore,
   206  			}
   207  			_, err := ScanTestOutput(cfg)
   208  			if ignore {
   209  				assert.DeepEqual(t, handler.errs, []string{nonJSONLine})
   210  				assert.NilError(t, err)
   211  				return
   212  			}
   213  			assert.DeepEqual(t, handler.errs, []string{}, cmpopts.EquateEmpty())
   214  			expected := "failed to parse test output: " +
   215  				nonJSONLine + ": invalid character '|' looking for beginning of value"
   216  			assert.Error(t, err, expected)
   217  		})
   218  	}
   219  }
   220  
   221  func TestScanTestOutput_WithGODEBUG(t *testing.T) {
   222  	goDebugSource := `HASH[moduleIndex]
   223  HASH[moduleIndex]: "go1.20.4"
   224  HASH /usr/lib/go/src/runtime/debuglog_off.go: d6f147198
   225  testcache: package: input list not found: ...`
   226  
   227  	handler := &captureHandler{}
   228  	cfg := ScanConfig{
   229  		Stdout:  bytes.NewReader(nil),
   230  		Stderr:  strings.NewReader(goDebugSource),
   231  		Handler: handler,
   232  	}
   233  	exec, err := ScanTestOutput(cfg)
   234  	assert.NilError(t, err)
   235  	assert.DeepEqual(t, handler.errs, strings.Split(goDebugSource, "\n"))
   236  	assert.DeepEqual(t, exec.Errors(), []string(nil))
   237  }
   238  
   239  type captureHandler struct {
   240  	events []TestEvent
   241  	errs   []string
   242  }
   243  
   244  func (s *captureHandler) Event(event TestEvent, _ *Execution) error {
   245  	s.events = append(s.events, event)
   246  	return nil
   247  }
   248  
   249  func (s *captureHandler) Err(text string) error {
   250  	s.errs = append(s.errs, text)
   251  	return nil
   252  }
   253  
   254  func TestFilterFailedUnique_MultipleNested(t *testing.T) {
   255  	source := []byte(`{"Package": "pkg", "Action": "run"}
   256  	{"Package": "pkg", "Test": "TestParent", "Action": "run"}
   257  	{"Package": "pkg", "Test": "TestParent/TestNested", "Action": "run"}
   258  	{"Package": "pkg", "Test": "TestParent/TestNested/TestOne", "Action": "run"}
   259  	{"Package": "pkg", "Test": "TestParent/TestNested/TestOne", "Action": "fail"}
   260  	{"Package": "pkg", "Test": "TestParent/TestNested/TestOnePrefix", "Action": "run"}
   261  	{"Package": "pkg", "Test": "TestParent/TestNested/TestOnePrefix", "Action": "fail"}
   262  	{"Package": "pkg", "Test": "TestParent/TestNested", "Action": "fail"}
   263  	{"Package": "pkg", "Test": "TestParent", "Action": "fail"}
   264  	{"Package": "pkg", "Test": "TestTop", "Action": "run"}
   265  	{"Package": "pkg", "Test": "TestTop", "Action": "fail"}
   266  	{"Package": "pkg", "Test": "TestTopPrefix", "Action": "run"}
   267  	{"Package": "pkg", "Test": "TestTopPrefix", "Action": "fail"}
   268  	{"Package": "pkg", "Action": "fail"}
   269  	{"Package": "pkg2", "Action": "run"}
   270  	{"Package": "pkg2", "Test": "TestParent", "Action": "run"}
   271  	{"Package": "pkg2", "Test": "TestParent/TestNested", "Action": "run"}
   272  	{"Package": "pkg2", "Test": "TestParent/TestNested", "Action": "fail"}
   273  	{"Package": "pkg2", "Test": "TestParent/TestNestedPrefix", "Action": "run"}
   274  	{"Package": "pkg2", "Test": "TestParent/TestNestedPrefix", "Action": "fail"}
   275  	{"Package": "pkg2", "Test": "TestParent", "Action": "fail"}
   276  	{"Package": "pkg2", "Test": "TestParentPrefix", "Action": "run"}
   277  	{"Package": "pkg2", "Test": "TestParentPrefix", "Action": "fail"}
   278  	{"Package": "pkg2", "Action": "fail"}`)
   279  
   280  	handler := &captureHandler{}
   281  	cfg := ScanConfig{
   282  		Stdout:  bytes.NewReader(source),
   283  		Handler: handler,
   284  	}
   285  	exec, err := ScanTestOutput(cfg)
   286  	assert.NilError(t, err)
   287  	actual := FilterFailedUnique(exec.Failed())
   288  
   289  	expected := []TestCase{
   290  		{ID: 3, Package: "pkg", Test: TestName("TestParent/TestNested/TestOne")},
   291  		{ID: 4, Package: "pkg", Test: TestName("TestParent/TestNested/TestOnePrefix")},
   292  		{ID: 5, Package: "pkg", Test: TestName("TestTop")},
   293  		{ID: 6, Package: "pkg", Test: TestName("TestTopPrefix")},
   294  		{ID: 2, Package: "pkg2", Test: TestName("TestParent/TestNested")},
   295  		{ID: 3, Package: "pkg2", Test: TestName("TestParent/TestNestedPrefix")},
   296  		{ID: 4, Package: "pkg2", Test: TestName("TestParentPrefix")},
   297  	}
   298  	cmpTestCase := cmp.AllowUnexported(TestCase{})
   299  	assert.DeepEqual(t, expected, actual, cmpTestCase)
   300  }