Skip to content

Commit

Permalink
feat: show test results with timed out tests (#15)
Browse files Browse the repository at this point in the history
Test results that fail due to a test timing out are now displayed
similarly to passing/failing test results. This allows users to identify
the failing test and fix it without breaking the workflow.

Closes #14
  • Loading branch information
michenriksen authored May 18, 2023
1 parent ed17813 commit 7403d72
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 6 deletions.
10 changes: 10 additions & 0 deletions internal/pkg/runner/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,19 @@ type Test struct {
Package string `json:"package"`
Pass bool `json:"pass"`
Skip bool `json:"skip"`
Timeout bool `json:"timeout"`
Elapsed float64 `json:"elapsed"`
Output string `json:"output"`
}

func (t *Test) detectTimeout() bool {
if !t.Pass && strings.Contains(t.Output, "panic: test timed out") {
t.Timeout = true
}

return t.Timeout
}

type parser struct {
logger *log.Logger
}
Expand Down Expand Up @@ -113,6 +122,7 @@ func (*parser) makePkgSlice(pkgMap map[string]*Package) []*Package {
tests := make([]*Test, 0, len(pkg.testMap))

for _, test := range pkg.testMap {
test.detectTimeout()
tests = append(tests, test)
}

Expand Down
59 changes: 54 additions & 5 deletions internal/pkg/runner/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ func TestRunner_Run_Integration(t *testing.T) {
require.NotZero(t, result.Duration)
require.Empty(t, result.Error)
require.False(t, result.Pass)
require.Equal(t, 3, result.Tests)
require.Equal(t, 1, result.Passed)
require.Equal(t, 4, result.Tests)
require.Equal(t, 2, result.Passed)
require.Equal(t, 1, result.Skipped)
require.Equal(t, 1, result.Failed)
require.Len(t, result.Packages, 1)
Expand All @@ -115,12 +115,12 @@ func TestRunner_Run_Integration(t *testing.T) {

require.True(t, strings.HasSuffix(pkg.Name, "testdata/numbers"))
require.False(t, pkg.Pass)
require.Equal(t, 1, pkg.Passed)
require.Equal(t, 2, pkg.Passed)
require.Equal(t, 1, pkg.Skipped)
require.Equal(t, 1, pkg.Failed)
require.Len(t, pkg.Tests, 3)
require.Len(t, pkg.Tests, 4)
require.NotZero(t, pkg.Elapsed)
require.Equal(t, 66.7, pkg.Coverage)
require.Equal(t, 80.0, pkg.Coverage)

passed := testByName(t, "TestIntMinBasic", pkg.Tests)
skipped := testByName(t, "TestIntMinTableDriven", pkg.Tests)
Expand All @@ -142,6 +142,55 @@ func TestRunner_Run_Integration(t *testing.T) {
require.Equal(t, "=== RUN TestIntMinFailing\n numbers_test.go:41: failing test\n--- FAIL: TestIntMinFailing (0.00s)\n", failed.Output)
}

func TestRunner_Run_Timeout(t *testing.T) {
runnerFunc := mockRunnerFunc(t, testutil.ReadFile(t, "testdata", "testoutput-timeout.json"), 1, nil)
r := runner.New(context.Background(), "./testdata",
runner.WithRunnerFunc(runnerFunc),
runner.WithUUIDFunc(mockUUIDFunc),
)

result, err := r.Run(testTarget)

require.NoError(t, err)
require.NotNil(t, result)

defer func() {
if dir := result.Dir(); dir != "" {
os.RemoveAll(dir)
}
}()

require.Equal(t, "deadbeef", result.UUID)
require.Equal(t, []string{testTarget}, result.Targets)
require.NotZero(t, result.Duration)
require.Empty(t, result.Error)
require.False(t, result.Pass)
require.Equal(t, 2, result.Tests)
require.Equal(t, 1, result.Passed)
require.Equal(t, 0, result.Skipped)
require.Equal(t, 1, result.Failed)
require.Len(t, result.Packages, 1)

pkg := result.Packages[0]

require.True(t, strings.HasSuffix(pkg.Name, "testdata/numbers"))

passed := testByName(t, "TestCounterIncrement", pkg.Tests)
failed := testByName(t, "TestCounterTimesOut", pkg.Tests)

require.Equal(t, pkg.Name, passed.Package)
require.True(t, passed.Pass)
require.False(t, passed.Skip)
require.False(t, passed.Timeout)

require.Equal(t, pkg.Name, failed.Package)
require.False(t, failed.Pass)
require.False(t, failed.Skip)
require.True(t, failed.Timeout)
require.Contains(t, failed.Output, "panic: test timed out after 500ms")
require.Contains(t, failed.Output, "goroutine 135 [sleep]:\ntime.Sleep(0x3b9aca00)\n")
}

func TestRunner_Run_NoTests(t *testing.T) {
runnerFunc := mockRunnerFunc(t, testutil.ReadFile(t, "testdata", "testoutput-no-tests.json"), 1, nil)
r := runner.New(context.Background(), "./testdata",
Expand Down
7 changes: 7 additions & 0 deletions internal/pkg/runner/testdata/numbers/counter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,10 @@ func TestCounterIncrement(t *testing.T) {
t.Errorf("Expected counter value to be 100, got %d", c.Value())
}
}

// func TestCounterTimesOut was used to generate test data for a test run with
// a test timing out.
// func TestCounterTimesOut(t *testing.T) {
// time.Sleep(1 * time.Second)
// t.Error("Expected test to time out")
// }
4 changes: 4 additions & 0 deletions internal/pkg/runner/testdata/numbers/numbers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ func TestIntMinTableDriven(t *testing.T) {
})
}
}

func TestIntMinFailing(t *testing.T) {
t.Error("failing test")
}
42 changes: 42 additions & 0 deletions internal/pkg/runner/testdata/testoutput-timeout.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{"Time":"2023-05-18T14:31:35.776974Z","Action":"start","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers"}
{"Time":"2023-05-18T14:31:35.865669Z","Action":"run","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterIncrement"}
{"Time":"2023-05-18T14:31:35.865739Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterIncrement","Output":"=== RUN TestCounterIncrement\n"}
{"Time":"2023-05-18T14:31:35.865765Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterIncrement","Output":"--- PASS: TestCounterIncrement (0.00s)\n"}
{"Time":"2023-05-18T14:31:35.865776Z","Action":"pass","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterIncrement","Elapsed":0}
{"Time":"2023-05-18T14:31:35.865785Z","Action":"run","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut"}
{"Time":"2023-05-18T14:31:35.865787Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"=== RUN TestCounterTimesOut\n"}
{"Time":"2023-01-01T13:37:00.369815Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"panic: test timed out after 500ms\n"}
{"Time":"2023-01-01T13:37:00.369855Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"running tests:\n"}
{"Time":"2023-01-01T13:37:00.369889Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\tTestCounterTimesOut (1s)\n"}
{"Time":"2023-01-01T13:37:00.369898Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\n"}
{"Time":"2023-01-01T13:37:00.369903Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"goroutine 19 [running]:\n"}
{"Time":"2023-01-01T13:37:00.369909Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"testing.(*M).startAlarm.func1()\n"}
{"Time":"2023-01-01T13:37:00.369914Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t/usr/local/go/src/testing/testing.go:2241 +0x328\n"}
{"Time":"2023-01-01T13:37:00.369946Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"created by time.goFunc\n"}
{"Time":"2023-01-01T13:37:00.369963Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t/usr/local/go/src/time/sleep.go:176 +0x38\n"}
{"Time":"2023-01-01T13:37:00.369968Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\n"}
{"Time":"2023-01-01T13:37:00.369973Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"goroutine 1 [chan receive]:\n"}
{"Time":"2023-01-01T13:37:00.369978Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"testing.(*T).Run(0x1400011c680, {0x102ad9273?, 0x1f0b3ea1d12ae?}, 0x102b341c8)\n"}
{"Time":"2023-01-01T13:37:00.370098Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t/usr/local/go/src/testing/testing.go:1630 +0x37c\n"}
{"Time":"2023-01-01T13:37:00.37011Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"testing.runTests.func1(0x1400010e480?)\n"}
{"Time":"2023-01-01T13:37:00.370114Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t/usr/local/go/src/testing/testing.go:2036 +0x48\n"}
{"Time":"2023-01-01T13:37:00.370199Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"testing.tRunner(0x1400011c680, 0x14000106c68)\n"}
{"Time":"2023-01-01T13:37:00.370211Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t/usr/local/go/src/testing/testing.go:1576 +0x10c\n"}
{"Time":"2023-01-01T13:37:00.370217Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"testing.runTests(0x140001300a0?, {0x102bea300, 0x4, 0x4}, {0x1400011e800?, 0x40?, 0x102bee780?})\n"}
{"Time":"2023-01-01T13:37:00.370221Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t/usr/local/go/src/testing/testing.go:2034 +0x40c\n"}
{"Time":"2023-01-01T13:37:00.370225Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"testing.(*M).Run(0x140001300a0)\n"}
{"Time":"2023-01-01T13:37:00.370232Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t/usr/local/go/src/testing/testing.go:1906 +0x58c\n"}
{"Time":"2023-01-01T13:37:00.370236Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"main.main()\n"}
{"Time":"2023-01-01T13:37:00.37024Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t_testmain.go:53 +0x1d0\n"}
{"Time":"2023-01-01T13:37:00.370243Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\n"}
{"Time":"2023-01-01T13:37:00.370247Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"goroutine 135 [sleep]:\n"}
{"Time":"2023-01-01T13:37:00.370251Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"time.Sleep(0x3b9aca00)\n"}
{"Time":"2023-01-01T13:37:00.370255Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t/usr/local/go/src/runtime/time.go:195 +0x118\n"}
{"Time":"2023-01-01T13:37:00.370259Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers.TestCounterTimesOut(0x1400017d380)\n"}
{"Time":"2023-01-01T13:37:00.370263Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t/Users/michael/src/github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers/counter_test.go:29 +0x28\n"}
{"Time":"2023-01-01T13:37:00.370271Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"testing.tRunner(0x1400017d380, 0x102b341c8)\n"}
{"Time":"2023-01-01T13:37:00.370275Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t/usr/local/go/src/testing/testing.go:1576 +0x10c\n"}
{"Time":"2023-01-01T13:37:00.370279Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"created by testing.(*T).Run\n"}
{"Time":"2023-01-01T13:37:00.370283Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Test":"TestCounterTimesOut","Output":"\t/usr/local/go/src/testing/testing.go:1629 +0x368\n"}
{"Time":"2023-01-01T13:37:00.37106Z","Action":"output","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Output":"FAIL\tgithub.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers\t0.594s\n"}
{"Time":"2023-01-01T13:37:00.371136Z","Action":"fail","Package":"github.com/michenriksen/gokiburi/internal/pkg/runner/testdata/numbers","Elapsed":0.594}
1 change: 1 addition & 0 deletions web/app/src/lib/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface Test {
package: string;
pass: boolean;
skip: boolean;
timeout: boolean;
elapsed: number; // Seconds.
output: string;
}
Expand Down
11 changes: 10 additions & 1 deletion web/app/src/lib/components/ResultPackageTableRow.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
import { slide } from 'svelte/transition';
import { CodeBlock } from '../../skeleton';
import { mdiCheckCircleOutline, mdiClockStart, mdiCloseCircleOutline, mdiConsole, mdiDebugStepOver } from '@mdi/js';
import {
mdiCheckCircleOutline,
mdiClockStart,
mdiCloseCircleOutline,
mdiConsole,
mdiDebugStepOver,
mdiTimerRemoveOutline
} from '@mdi/js';
import { conditionalScale } from '$lib/conditionalTransitions';
import { format, formatDistanceToNow, isToday } from 'date-fns';
Expand Down Expand Up @@ -60,6 +67,8 @@
<SvgIcon path={mdiDebugStepOver} class="text-warning-500 inline-block" size="1.7em" title="Skipped" />
{:else if test.pass}
<SvgIcon path={mdiCheckCircleOutline} class="text-success-500 inline-block" size="1.7em" title="Passed" />
{:else if test.timeout}
<SvgIcon path={mdiTimerRemoveOutline} class="text-error-400 inline-block" size="1.7em" title="Timed out" />
{:else}
<SvgIcon path={mdiCloseCircleOutline} class="text-error-400 inline-block" size="1.7em" title="Failed" />
{/if}
Expand Down

0 comments on commit 7403d72

Please sign in to comment.