From 8804729561d8e5edd7179f494d89c620459d319f Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Mon, 22 Jun 2026 11:37:02 +0200 Subject: assert: represent string literals with backticks when appropriate --- assert/equal.go | 35 ++++++++++++++++++++++++----------- assert/equal_test.go | 13 +++++++++++++ 2 files changed, 37 insertions(+), 11 deletions(-) (limited to 'assert') diff --git a/assert/equal.go b/assert/equal.go index 2cafb44..1c8004c 100644 --- a/assert/equal.go +++ b/assert/equal.go @@ -8,6 +8,7 @@ import ( "reflect" "slices" "strings" + "unicode" "unicode/utf8" "go.xyrillian.de/gg/internal/path" @@ -76,6 +77,18 @@ func formatValue(v reflect.Value) string { return fmt.Sprintf("%#v", v) } +func formatString(str string) string { + if strings.ContainsRune(str, '"') && !strings.ContainsRune(str, '`') && !strings.ContainsFunc(str, isControlCharacter) { + return fmt.Sprintf("`%s`", str) + } else { + return fmt.Sprintf("%q", str) + } +} + +func isControlCharacter(r rune) bool { + return unicode.Is(unicode.C, r) +} + func findInequalities(p path.Path, actual, expected reflect.Value) (result []inequality) { // try to recurse into structured type to find the specific location of the inequality // (thus producing a more succinct error message esp. with large and deeply nested structures) @@ -98,6 +111,15 @@ func findInequalities(p path.Path, actual, expected reflect.Value) (result []ine result = findInequalities(subpath, actualElem, expectedElem) } } + case reflect.String: + // string types do not allow structured recursion, but they have a special case for formatting + actualStr := actual.Convert(reflect.TypeFor[string]()).Interface().(string) + expectedStr := expected.Convert(reflect.TypeFor[string]()).Interface().(string) + return []inequality{{ + Pointer: p.AsGoExpression("actual"), + Actual: formatString(actualStr), + Expected: formatString(expectedStr), + }} } // if we do not have a recursion method for the type in question, @@ -118,8 +140,8 @@ func findInequalitiesInArrayOrSlice(p path.Path, actual, expected reflect.Value) if utf8.Valid(actualPayload) && utf8.Valid(expectedPayload) { return []inequality{{ Pointer: p.AsGoExpression("actual"), - Actual: formatByteSliceViaString(actualPayload), - Expected: formatByteSliceViaString(expectedPayload), + Actual: fmt.Sprintf(`[]byte(%s)`, formatString(string(actualPayload))), + Expected: fmt.Sprintf(`[]byte(%s)`, formatString(string(expectedPayload))), }} } } @@ -166,15 +188,6 @@ func findInequalitiesInArrayOrSlice(p path.Path, actual, expected reflect.Value) return result } -func formatByteSliceViaString(buf []byte) string { - str := string(buf) - if strings.Contains(str, `"`) && !strings.Contains(str, "`") { - return fmt.Sprintf("[]byte(`%s`)", str) - } else { - return fmt.Sprintf("[]byte(%q)", str) - } -} - func buildSingleInequalityForArrayOrSlice(p path.Path, actual, expected reflect.Value) inequality { // This is a helper for findInequalitiesInArrayOrSlice() that reports only a single inequality for the entire thing. // But it still tries to be clever, and will omit the longest common prefix and suffix to shorten the output. diff --git a/assert/equal_test.go b/assert/equal_test.go index df6fd70..3872ab7 100644 --- a/assert/equal_test.go +++ b/assert/equal_test.go @@ -31,6 +31,19 @@ func TestEqual(t *testing.T) { assert.Equal(t, false, true) }, `expected true, but got false`) + // scalars: check that string values are formatted with backticks when it makes the output nicer + expectErrors(t, func(t assert.TestingTB) { + assert.Equal(t, "bar", "foo") + assert.Equal(t, "Please run `rm -rf /`.", "Please run `echo hello`.") + assert.Equal(t, `{"foo":1,"bar":3}`, `{"foo":1,"bar":2}`) + assert.Equal(t, "\x1B[1;31mError\x1B[0m", "\x1B[1;32mSuccess\x1B[0m") + }, strings.Join([]string{ + `expected "foo", but got "bar"`, + "expected \"Please run `echo hello`.\", but got \"Please run `rm -rf /`.\"", + "expected `{\"foo\":1,\"bar\":2}`, but got `{\"foo\":1,\"bar\":3}`", + `expected "\x1b[1;32mSuccess\x1b[0m", but got "\x1b[1;31mError\x1b[0m"`, + }, "\n")) + // basic test for slices correctSlice := []int{1, 2, 3} wrongSlice := []int{1, 42, 3} -- cgit v1.3.1