mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-19 09:52:49 +00:00
Merge pull request #113384 from pohly/e2e-formatting
e2e: formatting enhancements
This commit is contained in:
commit
a9f87ad6c8
@ -67,6 +67,7 @@ import (
|
||||
_ "k8s.io/kubernetes/test/e2e/framework/debug/init"
|
||||
_ "k8s.io/kubernetes/test/e2e/framework/metrics/init"
|
||||
_ "k8s.io/kubernetes/test/e2e/framework/node/init"
|
||||
_ "k8s.io/kubernetes/test/utils/format"
|
||||
)
|
||||
|
||||
// handleFlags sets up all flags and parses the command line.
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
"github.com/onsi/ginkgo/v2"
|
||||
"github.com/onsi/ginkgo/v2/reporters"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
gomegaformat "github.com/onsi/gomega/format"
|
||||
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
@ -307,6 +308,9 @@ func (tc TestContextType) ClusterIsIPv6() bool {
|
||||
// options themselves, copy flags from test/e2e/framework/config
|
||||
// as shown in HandleFlags.
|
||||
func RegisterCommonFlags(flags *flag.FlagSet) {
|
||||
// The default is too low for objects like pods, even when using YAML. We double the default.
|
||||
flags.IntVar(&gomegaformat.MaxLength, "gomega-max-length", 8000, "Sets the maximum size for the gomega formatter (= gomega.MaxLength). Use 0 to disable truncation.")
|
||||
|
||||
flags.StringVar(&TestContext.GatherKubeSystemResourceUsageData, "gather-resource-usage", "false", "If set to 'true' or 'all' framework will be monitoring resource usage of system all add-ons in (some) e2e tests, if set to 'master' framework will be monitoring master node only, if set to 'none' of 'false' monitoring will be turned off.")
|
||||
flags.BoolVar(&TestContext.GatherLogsSizes, "gather-logs-sizes", false, "If set to true framework will be monitoring logs sizes on all machines running e2e tests.")
|
||||
flags.IntVar(&TestContext.MaxNodesToGather, "max-nodes-to-gather-from", 20, "The maximum number of nodes to gather extended info from on test failure.")
|
||||
@ -526,6 +530,14 @@ func AfterReadingAllFlags(t *TestContextType) {
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// This is the traditional gomega.Format default of 4000 for an object
|
||||
// dump plus some extra room for the message.
|
||||
maxFailureMessageSize = 5000
|
||||
|
||||
truncatedMsg = "\n[... see output for full dump ...]\n"
|
||||
)
|
||||
|
||||
// writeJUnitReport generates a JUnit file in the e2e report directory that is
|
||||
// shorter than the one normally written by `ginkgo --junit-report`. This is
|
||||
// needed because the full report can become too large for tools like Spyglass
|
||||
@ -542,6 +554,18 @@ func writeJUnitReport(report ginkgo.Report) {
|
||||
if specReport.State != types.SpecStateFailed {
|
||||
specReport.CapturedGinkgoWriterOutput = ""
|
||||
specReport.CapturedStdOutErr = ""
|
||||
} else {
|
||||
// Truncate the failure message if it is too large.
|
||||
msgLen := len(specReport.Failure.Message)
|
||||
if msgLen > maxFailureMessageSize {
|
||||
// Insert full message at the beginning where it is easy to find.
|
||||
specReport.CapturedGinkgoWriterOutput =
|
||||
"Full failure message:\n" +
|
||||
specReport.Failure.Message + "\n\n" +
|
||||
strings.Repeat("=", 70) + "\n\n" +
|
||||
specReport.CapturedGinkgoWriterOutput
|
||||
specReport.Failure.Message = specReport.Failure.Message[0:maxFailureMessageSize/2] + truncatedMsg + specReport.Failure.Message[msgLen-maxFailureMessageSize/2:msgLen]
|
||||
}
|
||||
}
|
||||
|
||||
// Remove report entries generated by ginkgo.By("doing
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
_ "k8s.io/kubernetes/test/e2e/framework/debug/init"
|
||||
_ "k8s.io/kubernetes/test/e2e/framework/metrics/init"
|
||||
_ "k8s.io/kubernetes/test/e2e/framework/node/init"
|
||||
_ "k8s.io/kubernetes/test/utils/format"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
@ -57,6 +57,7 @@ import (
|
||||
_ "k8s.io/kubernetes/test/e2e/framework/debug/init"
|
||||
_ "k8s.io/kubernetes/test/e2e/framework/metrics/init"
|
||||
_ "k8s.io/kubernetes/test/e2e/framework/node/init"
|
||||
_ "k8s.io/kubernetes/test/utils/format"
|
||||
|
||||
"github.com/onsi/ginkgo/v2"
|
||||
"github.com/onsi/gomega"
|
||||
|
80
test/utils/format/format.go
Normal file
80
test/utils/format/format.go
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package format is an extension of Gomega's format package which
|
||||
// improves printing of objects that can be serialized well as YAML,
|
||||
// like the structs in the Kubernetes API.
|
||||
//
|
||||
// Just importing it is enough to activate this special YAML support
|
||||
// in Gomega.
|
||||
package format
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/gomega/format"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func init() {
|
||||
format.RegisterCustomFormatter(handleYAML)
|
||||
}
|
||||
|
||||
// Object makes Gomega's [format.Object] available without having to import that
|
||||
// package.
|
||||
func Object(object interface{}, indentation uint) string {
|
||||
return format.Object(object, indentation)
|
||||
}
|
||||
|
||||
// handleYAML formats all values as YAML where the result
|
||||
// is likely to look better as YAML:
|
||||
// - pointer to struct or struct where all fields
|
||||
// have `json` tags
|
||||
// - slices containing such a value
|
||||
// - maps where the key or value are such a value
|
||||
func handleYAML(object interface{}) (string, bool) {
|
||||
value := reflect.ValueOf(object)
|
||||
if !useYAML(value.Type()) {
|
||||
return "", false
|
||||
}
|
||||
y, err := yaml.Marshal(object)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
return "\n" + strings.TrimSpace(string(y)), true
|
||||
}
|
||||
|
||||
func useYAML(t reflect.Type) bool {
|
||||
switch t.Kind() {
|
||||
case reflect.Pointer, reflect.Slice, reflect.Array:
|
||||
return useYAML(t.Elem())
|
||||
case reflect.Map:
|
||||
return useYAML(t.Key()) || useYAML(t.Elem())
|
||||
case reflect.Struct:
|
||||
// All fields must have a `json` tag.
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if _, ok := field.Tag.Lookup("json"); !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
81
test/utils/format/format_test.go
Normal file
81
test/utils/format/format_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package format_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/onsi/gomega/format"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestGomegaFormatObject(t *testing.T) {
|
||||
for name, test := range map[string]struct {
|
||||
value interface{}
|
||||
expected string
|
||||
indentation uint
|
||||
}{
|
||||
"int": {value: 1, expected: `<int>: 1`},
|
||||
"string": {value: "hello world", expected: `<string>: "hello world"`},
|
||||
"struct": {value: myStruct{a: 1, b: 2}, expected: `<format_test.myStruct>: {a: 1, b: 2}`},
|
||||
"gomegastringer": {value: typeWithGomegaStringer(2), expected: `<format_test.typeWithGomegaStringer>: my stringer 2`},
|
||||
"pod": {value: v1.Pod{}, expected: `<v1.Pod>:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
spec:
|
||||
containers: null
|
||||
status: {}`},
|
||||
"pod-indented": {value: v1.Pod{}, indentation: 1, expected: ` <v1.Pod>:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
spec:
|
||||
containers: null
|
||||
status: {}`},
|
||||
"pod-ptr": {value: &v1.Pod{}, expected: `<*v1.Pod | <hex>>:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
spec:
|
||||
containers: null
|
||||
status: {}`},
|
||||
"pod-hash": {value: map[string]v1.Pod{}, expected: `<map[string]v1.Pod | len:0>:
|
||||
{}`},
|
||||
"podlist": {value: v1.PodList{}, expected: `<v1.PodList>:
|
||||
items: null
|
||||
metadata: {}`},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
actual := format.Object(test.value, test.indentation)
|
||||
actual = regexp.MustCompile(`\| 0x[a-z0-9]+`).ReplaceAllString(actual, `| <hex>`)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type typeWithGomegaStringer int
|
||||
|
||||
func (v typeWithGomegaStringer) GomegaString() string {
|
||||
return fmt.Sprintf("my stringer %d", v)
|
||||
}
|
||||
|
||||
type myStruct struct {
|
||||
a, b int
|
||||
}
|
Loading…
Reference in New Issue
Block a user