Skip to content

Commit 259bb3b

Browse files
committed
e2e: consistent failure logging
All failures are worth logging immediately, not just unexpected errors. That helps understand tests that have long-running cleanup operations with their own logging, because the failure will be visible inside the test output. The logging in framework.ExpectNoError also was rather poor, because it only showed the error, but not the additional information about it. Tests suites now should use log.Fail as Gomega failure handler instead of ginkgowrapper.Fail. log.Fail will handle the logging for all failures before proceeding to record the failure in Ginkgo. Because logging is always done also after a test failure, additional failures during cleanup are now visible. Ginkgo itself just ignores them.
1 parent 311b66a commit 259bb3b

File tree

4 files changed

+43
-26
lines changed

4 files changed

+43
-26
lines changed

test/e2e/e2e.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import (
3232
"k8s.io/component-base/logs"
3333
commontest "k8s.io/kubernetes/test/e2e/common"
3434
"k8s.io/kubernetes/test/e2e/framework"
35-
"k8s.io/kubernetes/test/e2e/framework/ginkgowrapper"
35+
e2elog "k8s.io/kubernetes/test/e2e/framework/log"
3636

3737
// ensure auth plugins are loaded
3838
_ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -71,7 +71,7 @@ func RunE2ETests(t *testing.T) {
7171
logs.InitLogs()
7272
defer logs.FlushLogs()
7373

74-
gomega.RegisterFailHandler(ginkgowrapper.Fail)
74+
gomega.RegisterFailHandler(e2elog.Fail)
7575
// Disable skipped tests unless they are explicitly requested.
7676
if config.GinkgoConfig.FocusString == "" && config.GinkgoConfig.SkipString == "" {
7777
config.GinkgoConfig.SkipString = `\[Flaky\]|\[Feature:.+\]`

test/e2e/framework/log/logger.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@ func Failf(format string, args ...interface{}) {
4848
// (for example, for call chain f -> g -> FailfWithOffset(1, ...) error would be logged for "f").
4949
func FailfWithOffset(offset int, format string, args ...interface{}) {
5050
msg := fmt.Sprintf(format, args...)
51-
log("INFO", msg)
51+
log("FAIL", msg)
5252
ginkgowrapper.Fail(nowStamp()+": "+msg, 1+offset)
5353
}
54+
55+
// Fail is a replacement for ginkgo.Fail which logs the problem as it occurs
56+
// and then calls ginkgowrapper.Fail.
57+
func Fail(msg string, callerSkip ...int) {
58+
skip := 1
59+
if len(callerSkip) > 0 {
60+
skip += callerSkip[0]
61+
}
62+
log("FAIL", msg)
63+
ginkgowrapper.Fail(nowStamp()+": "+msg, skip)
64+
}

test/e2e/framework/log/logger_test.go

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"github.com/onsi/gomega"
3030

3131
"k8s.io/kubernetes/test/e2e/framework"
32-
"k8s.io/kubernetes/test/e2e/framework/ginkgowrapper"
3332
"k8s.io/kubernetes/test/e2e/framework/log"
3433
)
3534

@@ -46,7 +45,7 @@ var _ = ginkgo.Describe("log", func() {
4645
gomega.Expect(false).To(gomega.Equal(true), "false is never true")
4746
})
4847
ginkgo.It("error", func() {
49-
err := errors.New("I'm an error, nice to meet to.")
48+
err := errors.New("an error with a long, useless description")
5049
framework.ExpectNoError(err, "hard-coded error")
5150
})
5251
ginkgo.AfterEach(func() {
@@ -60,39 +59,50 @@ func TestFailureOutput(t *testing.T) {
6059
// reporter in adddition to the default one. To see what the full
6160
// Ginkgo report looks like, run this test with "go test -v".
6261
config.DefaultReporterConfig.FullTrace = true
63-
gomega.RegisterFailHandler(ginkgowrapper.Fail)
62+
gomega.RegisterFailHandler(log.Fail)
6463
fakeT := &testing.T{}
6564
reporter := reporters.NewFakeReporter()
6665
ginkgo.RunSpecsWithDefaultAndCustomReporters(fakeT, "Logging Suite", []ginkgo.Reporter{reporter})
6766

6867
// Now check the output.
68+
// TODO: all of the stacks are currently broken because Ginkgo doesn't properly skip
69+
// over the initial entries returned by runtime.Stack. Fix is pending in
70+
// https://github.com/onsi/ginkgo/pull/590, "stack" texts need to be updated
71+
// when updating to a fixed Ginkgo.
6972
g := gomega.NewGomegaWithT(t)
70-
g.Expect(normalizeReport(*reporter)).To(gomega.Equal(suiteResults{
73+
actual := normalizeReport(*reporter)
74+
expected := suiteResults{
7175
testResult{
72-
name: "[Top Level] log asserts",
73-
// TODO: also log the failed assertion as it happens
74-
output: "INFO: before\nINFO: after\n",
76+
name: "[Top Level] log asserts",
77+
output: "INFO: before\nFAIL: false is never true\nExpected\n <bool>: false\nto equal\n <bool>: true\nINFO: after\nFAIL: true is never false either\nExpected\n <bool>: true\nto equal\n <bool>: false\n",
7578
failure: "false is never true\nExpected\n <bool>: false\nto equal\n <bool>: true",
76-
// TODO: ginkowrapper.Fail should also prune this stack.
77-
stack: "\tassertion.go:75\nk8s.io/kubernetes/vendor/github.com/onsi/gomega/internal/assertion.(*Assertion).To()\n\tassertion.go:38\nk8s.io/kubernetes/test/e2e/framework/log_test.glob..func1.3()\n\tlogger_test.go:46\nk8s.io/kubernetes/vendor/github.com/onsi/ginkgo/internal/leafnodes.(*runner).runSync()\n\tlogger_test.go:66\n",
79+
// TODO: should start with k8s.io/kubernetes/test/e2e/framework/log_test.glob..func1.3()
80+
stack: "\tassertion.go:75\nk8s.io/kubernetes/vendor/github.com/onsi/gomega/internal/assertion.(*Assertion).To()\n\tassertion.go:38\nk8s.io/kubernetes/test/e2e/framework/log_test.glob..func1.3()\n\tlogger_test.go:45\nk8s.io/kubernetes/vendor/github.com/onsi/ginkgo/internal/leafnodes.(*runner).runSync()\n\tlogger_test.go:65\n",
7881
},
7982
testResult{
80-
name: "[Top Level] log error",
81-
// TODO: the additional information about the error should be logged
82-
output: "INFO: before\nINFO: Unexpected error occurred: I'm an error, nice to meet to.\nINFO: after\n",
83-
failure: "hard-coded error\nUnexpected error:\n <*errors.errorString>: {\n s: \"I'm an error, nice to meet to.\",\n }\n I'm an error, nice to meet to.\noccurred",
84-
// TODO: ginkowrapper.Fail should also prune this stack.
83+
name: "[Top Level] log error",
84+
output: "INFO: before\nFAIL: hard-coded error\nUnexpected error:\n <*errors.errorString>: {\n s: \"an error with a long, useless description\",\n }\n an error with a long, useless description\noccurred\nINFO: after\nFAIL: true is never false either\nExpected\n <bool>: true\nto equal\n <bool>: false\n",
85+
failure: "hard-coded error\nUnexpected error:\n <*errors.errorString>: {\n s: \"an error with a long, useless description\",\n }\n an error with a long, useless description\noccurred",
86+
// TODO: should start with k8s.io/kubernetes/test/e2e/framework/log_test.glob..func1.4()
8587
stack: "\tutil.go:1362\nk8s.io/kubernetes/test/e2e/framework.ExpectNoError()\n\tutil.go:1356\nk8s.io/kubernetes/test/e2e/framework/log_test.glob..func1.4()\n\tlogger_test.go:49\nk8s.io/kubernetes/vendor/github.com/onsi/ginkgo/internal/leafnodes.(*runner).runSync()\n\tlogger_test.go:65\n",
8688
},
8789
testResult{
88-
name: "[Top Level] log fails",
89-
// TODO: why is the failure log as "INFO"?
90-
output: "INFO: before\nINFO: I'm failing.\nINFO: after\n",
90+
name: "[Top Level] log fails",
91+
output: "INFO: before\nFAIL: I'm failing.\nINFO: after\nFAIL: true is never false either\nExpected\n <bool>: true\nto equal\n <bool>: false\n",
9192
failure: "I'm failing.",
92-
// TODO: ginkowrapper.Fail should also prune this stack.
93+
// TODO: should start with k8s.io/kubernetes/test/e2e/framework/log_test.glob..func1.2.1(...)
9394
stack: "\tlogger.go:52\nk8s.io/kubernetes/test/e2e/framework/log.Failf()\n\tlogger.go:44\nk8s.io/kubernetes/test/e2e/framework/log_test.glob..func1.2.1(...)\n\tlogger_test.go:41\nk8s.io/kubernetes/test/e2e/framework/log_test.glob..func1.2()\n\tlogger_test.go:42\nk8s.io/kubernetes/vendor/github.com/onsi/ginkgo/internal/leafnodes.(*runner).runSync()\n\tlogger_test.go:65\n",
9495
},
95-
}))
96+
}
97+
// Compare individual fields. Comparing the slices leads to unreadable error output when there is any mismatch.
98+
g.Expect(len(actual)).To(gomega.Equal(len(expected)), "%d entries in %v", len(expected), actual)
99+
for i, a := range actual {
100+
b := expected[i]
101+
g.Expect(a.name).To(gomega.Equal(b.name), "name in %d", i)
102+
g.Expect(a.output).To(gomega.Equal(b.output), "output in %d", i)
103+
g.Expect(a.failure).To(gomega.Equal(b.failure), "failure in %d", i)
104+
g.Expect(a.stack).To(gomega.Equal(b.stack), "stack in %d", i)
105+
}
96106
}
97107

98108
type testResult struct {

test/e2e/framework/util.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,10 +1359,6 @@ func ExpectNoError(err error, explain ...interface{}) {
13591359
// ExpectNoErrorWithOffset checks if "err" is set, and if so, fails assertion while logging the error at "offset" levels above its caller
13601360
// (for example, for call chain f -> g -> ExpectNoErrorWithOffset(1, ...) error would be logged for "f").
13611361
func ExpectNoErrorWithOffset(offset int, err error, explain ...interface{}) {
1362-
if err != nil {
1363-
e2elog.Logf("Unexpected error occurred: %v", err)
1364-
debug.PrintStack()
1365-
}
13661362
gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...)
13671363
}
13681364

0 commit comments

Comments
 (0)