diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7deea6e..891b206 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,9 +18,8 @@ jobs: - ubuntu-latest - windows-latest go-version: - - "1.22" - - "1.23" - - "1.24" + - "1.25" + - "1.26" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -43,8 +42,8 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.24" + go-version: "1.26" - name: Lint uses: golangci/golangci-lint-action@v7 with: - version: v2.0.2 + version: v2.12.1 diff --git a/README.md b/README.md index c0e2f52..17985fd 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,9 @@ func BeThirteen(i int) ghost.Result { return ghost.Result{ Ok: i == 13, - Message: fmt.Sprintf("%v is %d", args[0], i), + Message: func() string { + return fmt.Sprintf("%v is %d", args.Get(0), i) + } } } ``` diff --git a/be/assertions.go b/be/assertions.go index cdab1cc..a6c8944 100644 --- a/be/assertions.go +++ b/be/assertions.go @@ -18,12 +18,13 @@ import ( // The target must be a non-nil pointer. func AssignedAs[T any](value any, target *T) ghost.Result { args := ghostlib.ArgsFromAST(value, target) - argValue, argTarget := args[0], args[1] if target == nil { return ghost.Result{ - Ok: false, - Message: fmt.Sprintf("target %s cannot be nil", argTarget), + Ok: false, + Message: func() string { + return fmt.Sprintf("target %s cannot be nil", args.Get(1)) + }, } } @@ -31,15 +32,19 @@ func AssignedAs[T any](value any, target *T) ghost.Result { if !ok { return ghost.Result{ Ok: false, - Message: fmt.Sprintf( - `%s (%T) could not be assigned to %s (%T) + Message: func() string { + argValue, argTarget := args.Get(0), args.Get(1) + + return fmt.Sprintf( + `%s (%T) could not be assigned to %s (%T) value: %v`, - argValue, - value, - argTarget, - target, - value, - ), + argValue, + value, + argTarget, + target, + value, + ) + }, } } @@ -47,156 +52,168 @@ value: %v`, return ghost.Result{ Ok: true, - Message: fmt.Sprintf( - `%s (%T) was assigned to %s (%T) + Message: func() string { + argValue, argTarget := args.Get(0), args.Get(1) + + return fmt.Sprintf( + `%s (%T) was assigned to %s (%T) value: %v`, - argValue, - value, - argTarget, - target, - value, - ), + argValue, + value, + argTarget, + target, + value, + ) + }, } } // Close asserts that a value is within a delta of another. func Close[T constraints.Integer | constraints.Float](got, want, delta T) ghost.Result { args := ghostlib.ArgsFromAST(got, want, delta) - argGot, argWant := args[0], args[1] - gotDelta := want - got if gotDelta < 0 { gotDelta = 0 - gotDelta } - if _, err := strconv.ParseFloat(argGot, 64); err != nil { - argGot = fmt.Sprintf("%s (%v)", argGot, got) - } - - if _, err := strconv.ParseFloat(argWant, 64); err != nil { - argWant = fmt.Sprintf("%s (%v)", argWant, want) - } - if gotDelta <= delta { return ghost.Result{ Ok: true, - Message: fmt.Sprintf( - `delta %v between %s and %s is within %v + Message: func() string { + argGot, argWant := args.Get(0), args.Get(1) + argGot = formatNumeric(argGot, got) + argWant = formatNumeric(argWant, want) + + return fmt.Sprintf( + `delta %v between %s and %s is within %v got: %v want: %v delta: %v`, - gotDelta, argGot, argWant, delta, - got, - want, - gotDelta, - ), + gotDelta, argGot, argWant, delta, + got, + want, + gotDelta, + ) + }, } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf( - `delta %v between %s and %s is not within %v + Message: func() string { + argGot, argWant := args.Get(0), args.Get(1) + argGot = formatNumeric(argGot, got) + argWant = formatNumeric(argWant, want) + + return fmt.Sprintf( + `delta %v between %s and %s is not within %v got: %v want: %v delta: %v`, - gotDelta, argGot, argWant, delta, - got, - want, - gotDelta, - ), + gotDelta, argGot, argWant, delta, + got, + want, + gotDelta, + ) + }, } } +// formatNumeric with the arg and value, or simply the value if the arg is +// a numeric literal. +func formatNumeric[T constraints.Integer | constraints.Float](arg string, value T) string { + if _, err := strconv.ParseFloat(arg, 64); err != nil { + return fmt.Sprintf("%s (%v)", arg, value) + } + + return arg +} + // DeepEqual asserts that two elements are deeply equal. func DeepEqual[T any](got, want T) ghost.Result { args := ghostlib.ArgsFromAST(got, want) - argGot, argWant := args[0], args[1] - if diff := colorDiff(want, got); diff != "" { return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v != %v -%v`, argGot, argWant, diff), + Message: func() string { + argGot, argWant := args.Get(0), args.Get(1) + return fmt.Sprintf(`%v != %v +%v`, argGot, argWant, diff) + }, } } return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v == %v + Message: func() string { + argGot, argWant := args.Get(0), args.Get(1) + return fmt.Sprintf(`%v == %v value: %v -`, argGot, argWant, want), +`, argGot, argWant, want) + }, } } // Equal asserts that two elements are equal. func Equal[T comparable](got T, want T) ghost.Result { args := ghostlib.ArgsFromAST(got, want) - argGot, argWant := args[0], args[1] - if got == want { - switch fmt.Sprint(want) { - case argGot, argWant: - return ghost.Result{ - Ok: true, - Message: fmt.Sprintf(`%v == %v`, argGot, argWant), - } - default: - return ghost.Result{ - Ok: true, - Message: fmt.Sprintf(`%v == %v + return ghost.Result{ + Ok: true, + Message: func() string { + argGot, argWant := args.Get(0), args.Get(1) + switch fmt.Sprint(want) { + case argGot, argWant: + return fmt.Sprintf(`%v == %v`, argGot, argWant) + default: + return fmt.Sprintf(`%v == %v value: %v -`, argGot, argWant, want), - } +`, argGot, argWant, want) + } + }, } } - switch v := reflect.ValueOf(want); v.Kind() { - // These are types cmp tends to do particularly well - case - reflect.Array, - reflect.Interface, - reflect.Map, - reflect.Pointer, - reflect.Slice, - reflect.Struct: - - return ghost.Result{ - Ok: false, - Message: fmt.Sprintf(`%v != %v -%v`, argGot, argWant, colorDiff(want, got)), - } - case reflect.String: - if strings.ContainsAny(v.String(), "\n\r") || - strings.ContainsAny(reflect.ValueOf(got).String(), "\n\r") { - - return ghost.Result{ - Ok: false, - Message: fmt.Sprintf(`%v != %v -%v`, argGot, argWant, colorDiff(want, got)), - } - } - - return ghost.Result{ - Ok: false, - Message: fmt.Sprintf(`%v != %v + return ghost.Result{ + Ok: false, + Message: func() string { + argGot, argWant := args.Get(0), args.Get(1) + switch v := reflect.ValueOf(want); v.Kind() { + // These are types cmp tends to do particularly well + case + reflect.Array, + reflect.Interface, + reflect.Map, + reflect.Pointer, + reflect.Slice, + reflect.Struct: + + return fmt.Sprintf(`%v != %v +%v`, argGot, argWant, colorDiff(want, got)) + case reflect.String: + if strings.ContainsAny(v.String(), "\n\r") || + strings.ContainsAny(reflect.ValueOf(got).String(), "\n\r") { + + return fmt.Sprintf(`%v != %v +%v`, argGot, argWant, colorDiff(want, got)) + } + + return fmt.Sprintf(`%v != %v got: %v want: %v `, - argGot, - argWant, - quoteString(reflect.ValueOf(got).String()), - quoteString(reflect.ValueOf(want).String()), - ), - } - } + argGot, + argWant, + quoteString(reflect.ValueOf(got).String()), + quoteString(reflect.ValueOf(want).String()), + ) + } - return ghost.Result{ - Ok: false, - Message: fmt.Sprintf(`%v != %v + return fmt.Sprintf(`%v != %v got: %v want: %v -`, argGot, argWant, got, want), +`, argGot, argWant, got, want) + }, } } @@ -215,77 +232,87 @@ func quoteString(s string) string { // False asserts that a value is false. func False(b bool) ghost.Result { args := ghostlib.ArgsFromAST(b) - argB := args[0] - return ghost.Result{ - Ok: !b, - Message: fmt.Sprintf("%v is %t", argB, b), + Ok: !b, + Message: func() string { + return fmt.Sprintf("%v is %t", args.Get(0), b) + }, } } // JSONEqual asserts that two sets of JSON-encoded data are equivalent. func JSONEqual[T ~string | ~[]byte](got, want T) ghost.Result { args := ghostlib.ArgsFromAST(got, want) - argGot, argWant := args[0], args[1] - diff, kind := colorJSONDiff(got, want) switch kind { case jsondiff.Match: return ghost.Result{ - Ok: true, - Message: fmt.Sprintf("%v and %v are JSON equal", argGot, argWant), + Ok: true, + Message: func() string { + return fmt.Sprintf("%v and %v are JSON equal", args.Get(0), args.Get(1)) + }, } case jsondiff.GotInvalid: return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v is not valid JSON -value: %s`, argGot, got), + Message: func() string { + return fmt.Sprintf(`%v is not valid JSON +value: %s`, args.Get(0), got) + }, } case jsondiff.WantInvalid: return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v is not valid JSON -value: %s`, argWant, want), + Message: func() string { + return fmt.Sprintf(`%v is not valid JSON +value: %s`, args.Get(1), want) + }, } case jsondiff.BothInvalid: return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v and %v are not valid JSON + Message: func() string { + return fmt.Sprintf(`%v and %v are not valid JSON got: %s want: -%s`, argGot, argWant, got, want), +%s`, args.Get(0), args.Get(1), got, want) + }, } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v and %v are not JSON equal -%s`, argGot, argWant, diff), + Message: func() string { + return fmt.Sprintf(`%v and %v are not JSON equal +%s`, args.Get(0), args.Get(1), diff) + }, } } // MapLen asserts that the length of a map is a particular size. func MapLen[K comparable, V any](got map[K]V, want int) ghost.Result { args := ghostlib.ArgsFromAST(got, want) - argGot := args[0] - if len(got) == want { return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v is length %d + Message: func() string { + return fmt.Sprintf(`%v is length %d map: %v -`, argGot, len(got), mapToString(got)), +`, args.Get(0), len(got), mapToString(got)) + }, } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v is length %d, not %d + Message: func() string { + return fmt.Sprintf(`%v is length %d, not %d map: %v -`, argGot, len(got), want, mapToString(got)), +`, args.Get(0), len(got), want, mapToString(got)) + }, } } @@ -305,18 +332,20 @@ func mapToString[K comparable, V any](m map[K]V) string { // Nil asserts that the given value is nil. func Nil(v any) ghost.Result { args := ghostlib.ArgsFromAST(v) - argV := args[0] - if isNil(v) { return ghost.Result{ - Ok: true, - Message: fmt.Sprintf("%v is nil", argV), + Ok: true, + Message: func() string { + return fmt.Sprintf("%v is nil", args.Get(0)) + }, } } return ghost.Result{ - Ok: false, - Message: fmt.Sprintf("%v is %v, not nil", argV, v), + Ok: false, + Message: func() string { + return fmt.Sprintf("%v is %v, not nil", args.Get(0), v) + }, } } @@ -344,36 +373,38 @@ func isNil(v any) bool { // SliceContaining asserts that an element exists in a given slice. func SliceContaining[T comparable](slice []T, element T) ghost.Result { args := ghostlib.ArgsFromAST(slice, element) - argSlice, argElement := args[0], args[1] - for _, x := range slice { if x == element { return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v contains %v + Message: func() string { + return fmt.Sprintf(`%v contains %v slice: %v element: %v `, - argSlice, - argElement, - sliceElementToString(slice, element), - element, - ), + args.Get(0), + args.Get(1), + sliceElementToString(slice, element), + element, + ) + }, } } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v does not contain %v + Message: func() string { + return fmt.Sprintf(`%v does not contain %v slice: %v element: %v `, - argSlice, - argElement, - sliceElementToString(slice, element), - element, - ), + args.Get(0), + args.Get(1), + sliceElementToString(slice, element), + element, + ) + }, } } @@ -401,22 +432,24 @@ func sliceElementToString[T comparable](slice []T, element T) string { // SliceLen asserts that the length of a slice is a particular size. func SliceLen[T any](got []T, want int) ghost.Result { args := ghostlib.ArgsFromAST(got, want) - argGot := args[0] - if len(got) == want { return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v is length %d + Message: func() string { + return fmt.Sprintf(`%v is length %d slice: %v -`, argGot, len(got), sliceToString(got)), +`, args.Get(0), len(got), sliceToString(got)) + }, } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v is length %d, not %d + Message: func() string { + return fmt.Sprintf(`%v is length %d, not %d slice: %v -`, argGot, len(got), want, sliceToString(got)), +`, args.Get(0), len(got), want, sliceToString(got)) + }, } } @@ -440,105 +473,110 @@ func sliceToString[T any](slice []T) string { // StringContaining asserts that a substring exists in a given string. func StringContaining(str, substr string) ghost.Result { args := ghostlib.ArgsFromAST(str, substr) - argStr, argSubstr := args[0], args[1] - if strings.Contains(str, substr) { return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v contains %v + Message: func() string { + return fmt.Sprintf(`%v contains %v str: %s substr: %s -`, argStr, argSubstr, quoteString(str), quoteString(substr)), +`, args.Get(0), args.Get(1), quoteString(str), quoteString(substr)) + }, } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v does not contain %v + Message: func() string { + return fmt.Sprintf(`%v does not contain %v str: %s substr: %s -`, argStr, argSubstr, quoteString(str), quoteString(substr)), +`, args.Get(0), args.Get(1), quoteString(str), quoteString(substr)) + }, } } // StringMatching asserts that a given string matches a regular expression. func StringMatching(str string, expr string) ghost.Result { args := ghostlib.ArgsFromAST(str, expr) - argStr, argExpr := args[0], args[1] - re, err := regexp.Compile(expr) if err != nil { return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v is not a valid regular expression + Message: func() string { + return fmt.Sprintf(`%v is not a valid regular expression %v `, - argExpr, - err, - ), + args.Get(1), + err, + ) + }, } } if re.MatchString(str) { return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v matches regular expression %v + Message: func() string { + return fmt.Sprintf(`%v matches regular expression %v str: %s expr: %s `, - argStr, argExpr, - quoteString(str), - re.String(), - ), + args.Get(0), args.Get(1), + quoteString(str), + re.String(), + ) + }, } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v does not match regular expression %v + Message: func() string { + return fmt.Sprintf(`%v does not match regular expression %v str: %s expr: %s `, - argStr, argExpr, - quoteString(str), - re.String(), - ), + args.Get(0), args.Get(1), + quoteString(str), + re.String(), + ) + }, } } // True asserts that a value is true. func True(b bool) ghost.Result { args := ghostlib.ArgsFromAST(b) - argB := args[0] - return ghost.Result{ - Ok: b, - Message: fmt.Sprintf("%v is %t", argB, b), + Ok: b, + Message: func() string { + return fmt.Sprintf("%v is %t", args.Get(0), b) + }, } } // Zero asserts that the given value equals its zero value. func Zero[T comparable](v T) ghost.Result { args := ghostlib.ArgsFromAST(v) - argV := args[0] - var zero T if v == zero { return ghost.Result{ - Ok: true, - Message: fmt.Sprintf("%v is the zero value", argV), - } - } - - if argV != fmt.Sprint(v) { - return ghost.Result{ - Ok: false, - Message: fmt.Sprintf("%v is non-zero\nvalue: %v", argV, v), + Ok: true, + Message: func() string { + return fmt.Sprintf("%v is the zero value", args.Get(0)) + }, } } return ghost.Result{ - Ok: false, - Message: fmt.Sprintf("%v is non-zero", argV), + Ok: false, + Message: func() string { + argV := args.Get(0) + if argV != fmt.Sprint(v) { + return fmt.Sprintf("%v is non-zero\nvalue: %v", argV, v) + } + return fmt.Sprintf("%v is non-zero", argV) + }, } } diff --git a/be/assertions_test.go b/be/assertions_test.go index 4af55c5..5739962 100644 --- a/be/assertions_test.go +++ b/be/assertions_test.go @@ -20,12 +20,12 @@ func TestAssignedAs(t *testing.T) { result := be.AssignedAs(got, &want) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `got (string) was assigned to &want (*string) + g.Should(be.Equal(result.Message(), `got (string) was assigned to &want (*string) value: some-value`)) result = be.AssignedAs("some-value", new(string)) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `"some-value" (string) was assigned to new(string) (*string) + g.Should(be.Equal(result.Message(), `"some-value" (string) was assigned to new(string) (*string) value: some-value`)) }) @@ -37,12 +37,12 @@ value: some-value`)) result := be.AssignedAs(got, &want) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `got (int) could not be assigned to &want (*string) + g.Should(be.Equal(result.Message(), `got (int) could not be assigned to &want (*string) value: 15`)) result = be.AssignedAs(15, new(string)) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `15 (int) could not be assigned to new(string) (*string) + g.Should(be.Equal(result.Message(), `15 (int) could not be assigned to new(string) (*string) value: 15`, )) }) @@ -55,13 +55,13 @@ value: 15`, result := be.AssignedAs(got, &want) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `got (*bytes.Buffer) was assigned to &want (*io.Reader) + g.Should(be.Equal(result.Message(), `got (*bytes.Buffer) was assigned to &want (*io.Reader) value: `)) result = be.AssignedAs(new(bytes.Buffer), new(io.Reader)) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `new(bytes.Buffer) (*bytes.Buffer) was assigned to new(io.Reader) (*io.Reader) value: `)) }) @@ -74,12 +74,16 @@ value: `)) result := be.AssignedAs(got, &want) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `got (int) could not be assigned to &want (*io.Reader) + g.Should(be.Equal( + result.Message(), + `got (int) could not be assigned to &want (*io.Reader) value: 15`)) result = be.AssignedAs(15, new(io.Reader)) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `15 (int) could not be assigned to new(io.Reader) (*io.Reader) + g.Should(be.Equal( + result.Message(), + `15 (int) could not be assigned to new(io.Reader) (*io.Reader) value: 15`)) }) @@ -91,7 +95,7 @@ value: 15`)) result := be.AssignedAs(got, want) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, "target want cannot be nil")) + g.Should(be.Equal(result.Message(), "target want cannot be nil")) }) t.Run("panic", func(t *testing.T) { @@ -117,7 +121,7 @@ func TestClose(t *testing.T) { result := be.Close(got, want, 1) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `delta 0.5 between got (32) and want (32.5) is within 1 got: 32 want: 32.5 @@ -127,7 +131,7 @@ delta: 0.5`, result = be.Close(32.0, 32.5, 1.0) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `delta 0.5 between 32.0 and 32.5 is within 1 got: 32 want: 32.5 @@ -137,7 +141,7 @@ delta: 0.5`, result = be.Close(32.5, 32.0, 1.0) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `delta 0.5 between 32.5 and 32.0 is within 1 got: 32.5 want: 32 @@ -154,7 +158,7 @@ delta: 0.5`, result := be.Close(got, want, 0.3) g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `delta 0.5 between got (32) and want (32.5) is not within 0.3 got: 32 want: 32.5 @@ -164,7 +168,7 @@ delta: 0.5`, result = be.Close(32.0, 32.5, 0.3) g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `delta 0.5 between 32.0 and 32.5 is not within 0.3 got: 32 want: 32.5 @@ -187,13 +191,13 @@ func TestDeepEqual(t *testing.T) { result := be.DeepEqual(got, want) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `got == want + g.Should(be.Equal(result.Message(), `got == want value: {foo [1 2]} `)) result = be.DeepEqual(T{"foo", []int{1}}, T{"foo", []int{1}}) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `T{"foo", []int{1}} == T{"foo", []int{1}} + g.Should(be.Equal(result.Message(), `T{"foo", []int{1}} == T{"foo", []int{1}} value: {foo [1]} `)) }) @@ -222,8 +226,8 @@ diff (-want +got): + b: 0, } ` - result.Message = strings.ReplaceAll(result.Message, "\u00a0", " ") - g.Should(be.Equal(result.Message, wantText)) + gotMsg := strings.ReplaceAll(result.Message(), "\u00a0", " ") + g.Should(be.Equal(gotMsg, wantText)) result = be.DeepEqual(T{"bar", 0}, T{"foo", 1}) g.Check(!result.Ok) @@ -237,8 +241,8 @@ diff (-want +got): + b: 0, } ` - result.Message = strings.ReplaceAll(result.Message, "\u00a0", " ") - g.Should(be.Equal(result.Message, wantText)) + gotMsg = strings.ReplaceAll(result.Message(), "\u00a0", " ") + g.Should(be.Equal(gotMsg, wantText)) }) } @@ -256,13 +260,13 @@ func TestEqual(t *testing.T) { result := be.Equal(got, want) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `got == want + g.Should(be.Equal(result.Message(), `got == want value: {foo 1} `)) result = be.Equal(T{"foo", 1}, T{"foo", 1}) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `T{"foo", 1} == T{"foo", 1} + g.Should(be.Equal(result.Message(), `T{"foo", 1} == T{"foo", 1} value: {foo 1} `)) }) @@ -274,7 +278,7 @@ value: {foo 1} result := be.Equal(got, 3) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `got == 3`)) + g.Should(be.Equal(result.Message(), `got == 3`)) }) t.Run("unequal int", func(t *testing.T) { @@ -290,7 +294,7 @@ value: {foo 1} got: 0 want: 1 ` - g.Should(be.Equal(result.Message, wantText)) + g.Should(be.Equal(result.Message(), wantText)) result = be.Equal(0, 1) g.Check(!result.Ok) @@ -299,7 +303,7 @@ want: 1 got: 0 want: 1 ` - g.Should(be.Equal(result.Message, wantText)) + g.Should(be.Equal(result.Message(), wantText)) }) t.Run("unequal string short", func(t *testing.T) { @@ -315,7 +319,7 @@ want: 1 got: "bar" want: "foo" ` - g.Should(be.Equal(result.Message, wantText)) + g.Should(be.Equal(result.Message(), wantText)) result = be.Equal("bar", "foo") g.Check(!result.Ok) @@ -324,7 +328,7 @@ want: "foo" got: "bar" want: "foo" ` - g.Should(be.Equal(result.Message, wantText)) + g.Should(be.Equal(result.Message(), wantText)) }) t.Run("unequal string long", func(t *testing.T) { @@ -343,8 +347,8 @@ diff (-want +got): + "bar", ) ` - result.Message = strings.ReplaceAll(result.Message, "\u00a0", " ") - g.Should(be.Equal(result.Message, wantText)) + got = strings.ReplaceAll(result.Message(), "\u00a0", " ") + g.Should(be.Equal(got, wantText)) result = be.Equal("bar", "foo\nbar\nbaz") g.Check(!result.Ok) @@ -356,8 +360,8 @@ diff (-want +got): + "bar", ) ` - result.Message = strings.ReplaceAll(result.Message, "\u00a0", " ") - g.Should(be.Equal(result.Message, wantText)) + got = strings.ReplaceAll(result.Message(), "\u00a0", " ") + g.Should(be.Equal(got, wantText)) }) t.Run("unequal struct", func(t *testing.T) { @@ -384,8 +388,8 @@ diff (-want +got): + B: 0, } ` - result.Message = strings.ReplaceAll(result.Message, "\u00a0", " ") - g.Should(be.Equal(result.Message, wantText)) + gotMsg := strings.ReplaceAll(result.Message(), "\u00a0", " ") + g.Should(be.Equal(gotMsg, wantText)) result = be.Equal(T{"bar", 0}, T{"foo", 1}) g.Check(!result.Ok) @@ -399,8 +403,8 @@ diff (-want +got): + B: 0, } ` - result.Message = strings.ReplaceAll(result.Message, "\u00a0", " ") - g.Should(be.Equal(result.Message, wantText)) + gotMsg = strings.ReplaceAll(result.Message(), "\u00a0", " ") + g.Should(be.Equal(gotMsg, wantText)) }) t.Run("custom string type", func(t *testing.T) { @@ -413,14 +417,14 @@ diff (-want +got): result := be.Equal(got, want) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `got != want + g.Should(be.Equal(result.Message(), `got != want got: "foo" want: "bar" `)) result = be.Equal(CustomString("foo"), CustomString("bar")) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `CustomString("foo") != CustomString("bar") + g.Should(be.Equal(result.Message(), `CustomString("foo") != CustomString("bar") got: "foo" want: "bar" `)) @@ -434,11 +438,11 @@ func TestFalse(t *testing.T) { v := true result := be.False(v) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, "v is true")) + g.Should(be.Equal(result.Message(), "v is true")) result = be.False(true) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, "true is true")) + g.Should(be.Equal(result.Message(), "true is true")) }) t.Run("false", func(t *testing.T) { @@ -447,11 +451,11 @@ func TestFalse(t *testing.T) { v := false result := be.False(v) g.Check(result.Ok) - g.Should(be.Equal(result.Message, "v is false")) + g.Should(be.Equal(result.Message(), "v is false")) result = be.False(false) g.Check(result.Ok) - g.Should(be.Equal(result.Message, "false is false")) + g.Should(be.Equal(result.Message(), "false is false")) }) } @@ -464,12 +468,12 @@ func TestJSONEqual(t *testing.T) { result := be.JSONEqual(got, want) g.Check(result.Ok) - g.Should(be.Equal(result.Message, "got and want are JSON equal")) + g.Should(be.Equal(result.Message(), "got and want are JSON equal")) result = be.JSONEqual(`{"bar": [1, 2], "foo": "value"}`, `{"foo": "value", "bar": [1, 2]}`) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), "`{\"bar\": [1, 2], \"foo\": \"value\"}` and "+ "`{\"foo\": \"value\", \"bar\": [1, 2]}` are JSON equal", )) @@ -483,12 +487,12 @@ func TestJSONEqual(t *testing.T) { result := be.JSONEqual(got, want) g.Check(!result.Ok) - g.Should(be.StringContaining(result.Message, "got and want are not JSON equal")) + g.Should(be.StringContaining(result.Message(), "got and want are not JSON equal")) result = be.JSONEqual(`{"bar": [2, 1], "foo": "other"}`, `{"foo": "value", "bar": [1, 2]}`) g.Check(!result.Ok) g.Should(be.StringContaining( - result.Message, + result.Message(), "`{\"bar\": [2, 1], \"foo\": \"other\"}` and "+ "`{\"foo\": \"value\", \"bar\": [1, 2]}` are not JSON equal", )) @@ -503,17 +507,17 @@ func TestJSONEqual(t *testing.T) { result := be.JSONEqual(valid, invalid) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `invalid is not valid JSON + g.Should(be.Equal(result.Message(), `invalid is not valid JSON value: {{`)) result = be.JSONEqual(invalid, valid) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `invalid is not valid JSON + g.Should(be.Equal(result.Message(), `invalid is not valid JSON value: {{`)) result = be.JSONEqual(invalid, invalid2) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `invalid and invalid2 are not valid JSON + g.Should(be.Equal(result.Message(), `invalid and invalid2 are not valid JSON got: {{ @@ -523,7 +527,7 @@ want: result = be.JSONEqual(`{"bar": [1, 2], "foo": "value"}`, `{"foo": "value", "bar": [1, 2]}`) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), "`{\"bar\": [1, 2], \"foo\": \"value\"}` and "+ "`{\"foo\": \"value\", \"bar\": [1, 2]}` are JSON equal", )) @@ -539,12 +543,12 @@ func TestMapLen(t *testing.T) { result := be.MapLen(m, wantLen) g.Check(result.Ok) - g.Should(be.StringContaining(result.Message, `m is length 3`)) + g.Should(be.StringContaining(result.Message(), `m is length 3`)) result = be.MapLen(map[string]int{"a": 1, "b": 2, "c": 3}, 3) g.Check(result.Ok) g.Should(be.StringContaining( - result.Message, + result.Message(), `map[string]int{"a": 1, "b": 2, "c": 3} is length 3`, )) }) @@ -557,12 +561,12 @@ func TestMapLen(t *testing.T) { result := be.MapLen(m, wantLen) g.Check(result.Ok) - g.Should(be.StringContaining(result.Message, `m is length 4`)) + g.Should(be.StringContaining(result.Message(), `m is length 4`)) result = be.MapLen(map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}, 4) g.Check(result.Ok) g.Should(be.StringContaining( - result.Message, + result.Message(), `map[string]int{"a": 1, "b": 2, "c": 3, "d": 4} is length 4`, )) }) @@ -575,12 +579,12 @@ func TestMapLen(t *testing.T) { result := be.MapLen(m, wantLen) g.Check(!result.Ok) - g.Should(be.StringContaining(result.Message, `m is length 3, not 2`)) + g.Should(be.StringContaining(result.Message(), `m is length 3, not 2`)) result = be.MapLen(map[string]int{"a": 1, "b": 2, "c": 3}, 2) g.Check(!result.Ok) g.Should(be.StringContaining( - result.Message, + result.Message(), `map[string]int{"a": 1, "b": 2, "c": 3} is length 3, not 2`, )) }) @@ -593,12 +597,12 @@ func TestMapLen(t *testing.T) { result := be.MapLen(m, wantLen) g.Check(!result.Ok) - g.Should(be.StringContaining(result.Message, `m is length 4, not 3`)) + g.Should(be.StringContaining(result.Message(), `m is length 4, not 3`)) result = be.MapLen(map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}, 3) g.Check(!result.Ok) g.Should(be.StringContaining( - result.Message, + result.Message(), `map[string]int{"a": 1, "b": 2, "c": 3, "d": 4} is length 4, not 3`, )) }) @@ -612,11 +616,11 @@ func TestNil(t *testing.T) { result := be.Nil(v) g.Check(result.Ok) - g.Should(be.Equal(result.Message, "v is nil")) + g.Should(be.Equal(result.Message(), "v is nil")) result = be.Nil(nil) g.Check(result.Ok) - g.Should(be.Equal(result.Message, "nil is nil")) + g.Should(be.Equal(result.Message(), "nil is nil")) }) t.Run("typed nil", func(t *testing.T) { @@ -627,11 +631,11 @@ func TestNil(t *testing.T) { result := be.Nil(i) g.Check(result.Ok) - g.Should(be.Equal(result.Message, "i is nil")) + g.Should(be.Equal(result.Message(), "i is nil")) result = be.Nil((*int)(nil)) g.Check(result.Ok) - g.Should(be.Equal(result.Message, "(*int)(nil) is nil")) + g.Should(be.Equal(result.Message(), "(*int)(nil) is nil")) }) t.Run("non-nil", func(t *testing.T) { @@ -641,11 +645,11 @@ func TestNil(t *testing.T) { result := be.Nil(v) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, "v is 0, not nil")) + g.Should(be.Equal(result.Message(), "v is 0, not nil")) result = be.Nil(-1 + 1) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, "-1 + 1 is 0, not nil")) + g.Should(be.Equal(result.Message(), "-1 + 1 is 0, not nil")) }) } @@ -658,14 +662,14 @@ func TestSliceContaining(t *testing.T) { result := be.SliceContaining(slice, elem) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `slice contains elem + g.Should(be.Equal(result.Message(), `slice contains elem slice: [1 2 3] element: 2 `)) result = be.SliceContaining([]int{1, 2, 3}, 2) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `[]int{1, 2, 3} contains 2 + g.Should(be.Equal(result.Message(), `[]int{1, 2, 3} contains 2 slice: [1 2 3] element: 2 `)) @@ -679,7 +683,7 @@ element: 2 result := be.SliceContaining(slice, elem) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `slice contains elem + g.Should(be.Equal(result.Message(), `slice contains elem slice: [ 1 > 2 @@ -691,7 +695,7 @@ element: 2 result = be.SliceContaining([]int{1, 2, 3, 4}, 2) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `[]int{1, 2, 3, 4} contains 2 + g.Should(be.Equal(result.Message(), `[]int{1, 2, 3, 4} contains 2 slice: [ 1 > 2 @@ -710,14 +714,14 @@ element: 2 result := be.SliceContaining(slice, elem) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `slice does not contain elem + g.Should(be.Equal(result.Message(), `slice does not contain elem slice: [1 2 3] element: 5 `)) result = be.SliceContaining([]int{1, 2, 3}, 5) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `[]int{1, 2, 3} does not contain 5 + g.Should(be.Equal(result.Message(), `[]int{1, 2, 3} does not contain 5 slice: [1 2 3] element: 5 `)) @@ -731,7 +735,7 @@ element: 5 result := be.SliceContaining(slice, elem) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `slice does not contain elem + g.Should(be.Equal(result.Message(), `slice does not contain elem slice: [ 1 2 @@ -743,7 +747,7 @@ element: 5 result = be.SliceContaining([]int{1, 2, 3, 4}, 5) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `[]int{1, 2, 3, 4} does not contain 5 + g.Should(be.Equal(result.Message(), `[]int{1, 2, 3, 4} does not contain 5 slice: [ 1 2 @@ -764,13 +768,13 @@ func TestSliceLen(t *testing.T) { result := be.SliceLen(slice, wantLen) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `slice is length 3 + g.Should(be.Equal(result.Message(), `slice is length 3 slice: [a b c] `)) result = be.SliceLen([]string{"a", "b", "c"}, 3) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `[]string{"a", "b", "c"} is length 3 + g.Should(be.Equal(result.Message(), `[]string{"a", "b", "c"} is length 3 slice: [a b c] `)) }) @@ -783,7 +787,7 @@ slice: [a b c] result := be.SliceLen(slice, wantLen) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `slice is length 4 + g.Should(be.Equal(result.Message(), `slice is length 4 slice: [ a b @@ -794,7 +798,7 @@ slice: [ result = be.SliceLen([]string{"a", "b", "c", "d"}, 4) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `[]string{"a", "b", "c", "d"} is length 4 + g.Should(be.Equal(result.Message(), `[]string{"a", "b", "c", "d"} is length 4 slice: [ a b @@ -812,13 +816,13 @@ slice: [ result := be.SliceLen(slice, wantLen) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `slice is length 3, not 2 + g.Should(be.Equal(result.Message(), `slice is length 3, not 2 slice: [a b c] `)) result = be.SliceLen([]string{"a", "b", "c"}, 2) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `[]string{"a", "b", "c"} is length 3, not 2 + g.Should(be.Equal(result.Message(), `[]string{"a", "b", "c"} is length 3, not 2 slice: [a b c] `)) }) @@ -831,7 +835,7 @@ slice: [a b c] result := be.SliceLen(slice, wantLen) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `slice is length 4, not 3 + g.Should(be.Equal(result.Message(), `slice is length 4, not 3 slice: [ a b @@ -842,7 +846,7 @@ slice: [ result = be.SliceLen([]string{"a", "b", "c", "d"}, 3) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `[]string{"a", "b", "c", "d"} is length 4, not 3 + g.Should(be.Equal(result.Message(), `[]string{"a", "b", "c", "d"} is length 4, not 3 slice: [ a b @@ -862,14 +866,14 @@ func TestStringContaining(t *testing.T) { result := be.StringContaining(outer, inner) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `outer contains inner + g.Should(be.Equal(result.Message(), `outer contains inner str: "foobar" substr: "oob" `)) result = be.StringContaining("foobar", "oob") g.Check(result.Ok) - g.Should(be.Equal(result.Message, `"foobar" contains "oob" + g.Should(be.Equal(result.Message(), `"foobar" contains "oob" str: "foobar" substr: "oob" `)) @@ -883,14 +887,14 @@ substr: "oob" result := be.StringContaining(outer, inner) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `outer does not contain inner + g.Should(be.Equal(result.Message(), `outer does not contain inner str: "foobar" substr: "boo" `)) result = be.StringContaining("foobar", "boo") g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `"foobar" does not contain "boo" + g.Should(be.Equal(result.Message(), `"foobar" does not contain "boo" str: "foobar" substr: "boo" `)) @@ -906,7 +910,7 @@ three result := be.StringContaining(outer, "two") g.Check(result.Ok) - g.Should(be.Equal(result.Message, `outer contains "two" + g.Should(be.Equal(result.Message(), `outer contains "two" str: `+` """ one @@ -928,14 +932,14 @@ func TestStringMatching(t *testing.T) { result := be.StringMatching(str, expr) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `str matches regular expression expr + g.Should(be.Equal(result.Message(), `str matches regular expression expr str: "foobar" expr: ^foo `)) result = be.StringMatching("foobar", "^foo") g.Check(result.Ok) - g.Should(be.Equal(result.Message, `"foobar" matches regular expression "^foo" + g.Should(be.Equal(result.Message(), `"foobar" matches regular expression "^foo" str: "foobar" expr: ^foo `)) @@ -949,14 +953,14 @@ expr: ^foo result := be.StringMatching(str, expr) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `str does not match regular expression expr + g.Should(be.Equal(result.Message(), `str does not match regular expression expr str: "foobar" expr: ^foo$ `)) result = be.StringMatching("foobar", "^foo$") g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `"foobar" does not match regular expression "^foo$" + g.Should(be.Equal(result.Message(), `"foobar" does not match regular expression "^foo$" str: "foobar" expr: ^foo$ `)) @@ -970,12 +974,12 @@ expr: ^foo$ result := be.StringMatching(str, expr) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `expr is not a valid regular expression + g.Should(be.Equal(result.Message(), `expr is not a valid regular expression error parsing regexp: invalid escape sequence: `+"`\\j`\n")) result = be.StringMatching("foobar", "^foo\\j") g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `"^foo\\j" is not a valid regular expression + g.Should(be.Equal(result.Message(), `"^foo\\j" is not a valid regular expression error parsing regexp: invalid escape sequence: `+"`\\j`\n")) }) } @@ -987,11 +991,11 @@ func TestTrue(t *testing.T) { v := true result := be.True(v) g.Check(result.Ok) - g.Should(be.Equal(result.Message, "v is true")) + g.Should(be.Equal(result.Message(), "v is true")) result = be.True(true) g.Check(result.Ok) - g.Should(be.Equal(result.Message, "true is true")) + g.Should(be.Equal(result.Message(), "true is true")) }) t.Run("false", func(t *testing.T) { @@ -1000,11 +1004,11 @@ func TestTrue(t *testing.T) { v := false result := be.True(v) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, "v is false")) + g.Should(be.Equal(result.Message(), "v is false")) result = be.True(false) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, "false is false")) + g.Should(be.Equal(result.Message(), "false is false")) }) } @@ -1015,11 +1019,11 @@ func TestZero(t *testing.T) { var v int result := be.Zero(v) g.Check(result.Ok) - g.Should(be.Equal(result.Message, "v is the zero value")) + g.Should(be.Equal(result.Message(), "v is the zero value")) result = be.Zero(0) g.Check(result.Ok) - g.Should(be.Equal(result.Message, "0 is the zero value")) + g.Should(be.Equal(result.Message(), "0 is the zero value")) }) t.Run("non-zero", func(t *testing.T) { @@ -1028,10 +1032,10 @@ func TestZero(t *testing.T) { v := 1 result := be.Zero(v) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, "v is non-zero\nvalue: 1")) + g.Should(be.Equal(result.Message(), "v is non-zero\nvalue: 1")) result = be.Zero(1) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, "1 is non-zero")) + g.Should(be.Equal(result.Message(), "1 is non-zero")) }) } diff --git a/be/compose.go b/be/compose.go index d33406d..de323ae 100644 --- a/be/compose.go +++ b/be/compose.go @@ -3,7 +3,6 @@ package be import ( "fmt" "regexp" - "strconv" "strings" "time" @@ -13,7 +12,12 @@ import ( // All asserts that every one of the provided assertions is true. func All(results ...ghost.Result) ghost.Result { - args := ghostlib.ArgsFromAST(results) + argsOfAny := make([]any, 0, len(results)) + for _, result := range results { + argsOfAny = append(argsOfAny, result) + } + args := ghostlib.ArgsFromAST(argsOfAny...) + return applyVariadicBooleanLogic( true, func(acc, val bool) bool { @@ -26,7 +30,12 @@ func All(results ...ghost.Result) ghost.Result { // Any asserts that at least one of the provided assertions is true. func Any(results ...ghost.Result) ghost.Result { - args := ghostlib.ArgsFromAST(results) + argsOfAny := make([]any, 0, len(results)) + for _, result := range results { + argsOfAny = append(argsOfAny, result) + } + args := ghostlib.ArgsFromAST(argsOfAny...) + return applyVariadicBooleanLogic( false, func(acc, val bool) bool { @@ -41,40 +50,35 @@ func applyVariadicBooleanLogic( initial bool, apply func(acc, val bool) bool, results []ghost.Result, - args []string, + args ghostlib.Args, ) ghost.Result { if len(results) == 0 { return ghost.Result{ Ok: initial, - Message: "no assertions were provided", + Message: func() string { return "no assertions were provided" }, } } - out := ghost.Result{Ok: initial} - for i, result := range results { - out.Ok = apply(out.Ok, result.Ok) - - // Not sure why AST parsing would fail, but sometimes it does. Seems to be - // environment dependent rather than code dependent. - var arg string - if len(args) > i { - arg = fmt.Sprintf("`%s`", args[i]) - } else { - arg = strconv.Itoa(i) - } - - var b strings.Builder - if i != 0 { - b.WriteString("\n\n") - } - fmt.Fprintf(&b, "assertion %s is %t", arg, result.Ok) - b.WriteString("\n\t") - b.WriteString(indentString(result.Message)) - - out.Message += b.String() + ok := initial + for _, result := range results { + ok = apply(ok, result.Ok) } - return out + return ghost.Result{ + Ok: ok, + Message: func() string { + var b strings.Builder + for i, result := range results { + if i != 0 { + b.WriteString("\n\n") + } + fmt.Fprintf(&b, "assertion `%s` is %t", args.Get(i), result.Ok) + b.WriteString("\n\t") + b.WriteString(indentString(result.Message())) + } + return b.String() + }, + } } var reWhitespaceLine = regexp.MustCompile(`\n[ \t]+\n`) @@ -93,7 +97,6 @@ func Eventually( interval time.Duration, ) ghost.Result { args := ghostlib.ArgsFromAST(f, timeout, interval) - argF := args[0] timer := time.NewTimer(timeout) defer timer.Stop() @@ -102,8 +105,10 @@ func Eventually( defer ticker.Stop() lastRun := ghost.Result{ - Ok: false, - Message: fmt.Sprintf("%s did not return value within %s timeout", argF, timeout), + Ok: false, + Message: func() string { + return fmt.Sprintf("%s did not return value within %s timeout", args.Get(0), timeout) + }, } ch := make(chan ghost.Result, 1) diff --git a/be/compose_test.go b/be/compose_test.go index 9c28b2e..763ab97 100644 --- a/be/compose_test.go +++ b/be/compose_test.go @@ -16,7 +16,7 @@ func TestAll(t *testing.T) { result := be.All() g.Check(result.Ok) - g.Should(be.Equal(result.Message, "no assertions were provided")) + g.Should(be.Equal(result.Message(), "no assertions were provided")) }) t.Run("one valid", func(t *testing.T) { @@ -30,7 +30,7 @@ func TestAll(t *testing.T) { g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), fmt.Sprintf(`assertion %s is false 1 != 0 got: 1 @@ -60,7 +60,7 @@ assertion %s is false g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), fmt.Sprintf("assertion %s is true"+` 1 == 1 @@ -84,7 +84,7 @@ assertion %s is true g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), fmt.Sprintf(`assertion %s is false assertion %s is false 1 != 0 @@ -109,7 +109,7 @@ func TestAny(t *testing.T) { result := be.Any() g.Check(!result.Ok) - g.Should(be.Equal(result.Message, "no assertions were provided")) + g.Should(be.Equal(result.Message(), "no assertions were provided")) }) t.Run("one valid", func(t *testing.T) { @@ -123,7 +123,7 @@ func TestAny(t *testing.T) { g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), fmt.Sprintf(`assertion %s is false 1 != 0 got: 1 @@ -153,7 +153,7 @@ assertion %s is false g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), fmt.Sprintf("assertion %s is false"+` 1 != 0 got: 1 @@ -181,7 +181,7 @@ assertion %s is false g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), fmt.Sprintf(`assertion %s is false assertion %s is false 1 != 0 @@ -211,7 +211,7 @@ func TestEventually(t *testing.T) { }, 100*time.Millisecond, 5*time.Millisecond) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `count == 3`)) + g.Should(be.Equal(result.Message(), `count == 3`)) }) t.Run("not ok", func(t *testing.T) { @@ -228,7 +228,7 @@ func TestEventually(t *testing.T) { matched, err := regexp.MatchString(`count != -1 got: \d+ want: -1 -`, result.Message) +`, result.Message()) g.NoError(err) g.Check(matched) }) @@ -242,7 +242,7 @@ want: -1 }, 10*time.Millisecond, 100*time.Millisecond) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `func() ghost.Result { + g.Should(be.Equal(result.Message(), `func() ghost.Result { time.Sleep(100 * time.Millisecond) return be.True(true) } did not return value within 10ms timeout`)) @@ -256,13 +256,14 @@ func TestNot(t *testing.T) { result := ghost.Result{ Ok: true, - Message: message, + Message: func() string { return message }, } negated := be.Not(result) g.Check(!negated.Ok) - g.Should(be.Equal(negated.Message, message)) + g.Should(be.Equal(negated.Message(), message)) doubleNegated := be.Not(negated) - g.Should(be.Equal(doubleNegated, result)) + g.Should(be.Equal(doubleNegated.Ok, result.Ok)) + g.Should(be.Equal(doubleNegated.Message(), result.Message())) } diff --git a/be/errors.go b/be/errors.go index 2e2a9ad..f357155 100644 --- a/be/errors.go +++ b/be/errors.go @@ -12,71 +12,78 @@ import ( // Error asserts that an error is non-nil. func Error(err error) ghost.Result { args := ghostlib.ArgsFromAST(err) - argErr := args[0] - if err == nil { return ghost.Result{ - Ok: false, - Message: argErr + " is nil", + Ok: false, + Message: func() string { + return args.Get(0) + " is nil" + }, } } return ghost.Result{ - Ok: true, - Message: fmt.Sprintf("%s has error value: %s", argErr, err), + Ok: true, + Message: func() string { + return fmt.Sprintf("%s has error value: %s", args.Get(0), err) + }, } } // ErrorContaining asserts that an error string contains a particular substring. func ErrorContaining(err error, msg string) ghost.Result { args := ghostlib.ArgsFromAST(err, msg) - argErr, argMsg := args[0], args[1] - switch { - case err == nil && argMsg == fmt.Sprintf("%q", msg): + case err == nil: return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`error %v is nil, does not contain message + Message: func() string { + argErr, argMsg := args.Get(0), args.Get(1) + if argMsg == fmt.Sprintf("%q", msg) { + return fmt.Sprintf(`error %v is nil, does not contain message got: want: %v`, - argErr, - msg, - ), - } - case err == nil: - return ghost.Result{ - Ok: false, - Message: fmt.Sprintf(`error %v is nil, does not contain %v + argErr, + msg, + ) + } + return fmt.Sprintf(`error %v is nil, does not contain %v got: want: %v`, - argErr, - argMsg, - msg, - ), + argErr, + argMsg, + msg, + ) + }, } case strings.Contains(err.Error(), msg): return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`error %v contains message %v + Message: func() string { + argErr, argMsg := args.Get(0), args.Get(1) + return fmt.Sprintf(`error %v contains message %v got: %v want: %v`, - argErr, - argMsg, - err, - msg, - ), + argErr, + argMsg, + err, + msg, + ) + }, } default: return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`error %v does not contain message %v + Message: func() string { + argErr, argMsg := args.Get(0), args.Get(1) + return fmt.Sprintf(`error %v does not contain message %v got: %v want: %v`, - argErr, - argMsg, - err, - msg, - ), + argErr, + argMsg, + err, + msg, + ) + }, } } } @@ -84,119 +91,131 @@ want: %v`, // ErrorEqual asserts that an error string equals a particular message. func ErrorEqual(err error, msg string) ghost.Result { args := ghostlib.ArgsFromAST(err, msg) - argErr, argMsg := args[0], args[1] - if err == nil { return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`error %v is nil + Message: func() string { + return fmt.Sprintf(`error %v is nil got: want: %v`, - argErr, - msg, - ), + args.Get(0), + msg, + ) + }, } } if err.Error() == msg { return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`error %v has message %v + Message: func() string { + return fmt.Sprintf(`error %v has message %v value: %v`, - argErr, - argMsg, - err, - ), + args.Get(0), + args.Get(1), + err, + ) + }, } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`error %v does not have message %v + Message: func() string { + return fmt.Sprintf(`error %v does not have message %v got: %v want: %v`, - argErr, - argMsg, - err, - msg, - ), + args.Get(0), + args.Get(1), + err, + msg, + ) + }, } } // ErrorIs asserts that an error matches another using [errors.Is]. func ErrorIs(err error, target error) ghost.Result { args := ghostlib.ArgsFromAST(err, target) - argErr, argTarget := args[0], args[1] - if errors.Is(err, target) { return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`error %v is target %v + Message: func() string { + return fmt.Sprintf(`error %v is target %v error: %v target: %v`, - argErr, - argTarget, - err, - target, - ), + args.Get(0), + args.Get(1), + err, + target, + ) + }, } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`error %v is not target %v + Message: func() string { + return fmt.Sprintf(`error %v is not target %v error: %v target: %v`, - argErr, - argTarget, - err, - target, - ), + args.Get(0), + args.Get(1), + err, + target, + ) + }, } } // ErrorAs asserts that an error matches another using [errors.As]. func ErrorAs[T any](err error, target *T) ghost.Result { args := ghostlib.ArgsFromAST(err, target) - argErr, argTarget := args[0], args[1] - if err == nil { return ghost.Result{ - Ok: false, - Message: fmt.Sprintf("error %v was nil", argErr), + Ok: false, + Message: func() string { + return fmt.Sprintf("error %v was nil", args.Get(0)) + }, } } if target == nil { return ghost.Result{ - Ok: false, - Message: fmt.Sprintf("target %v cannot be nil", argTarget), + Ok: false, + Message: func() string { + return fmt.Sprintf("target %v cannot be nil", args.Get(1)) + }, } } if errors.As(err, target) { return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`error %v set as target %v + Message: func() string { + return fmt.Sprintf(`error %v set as target %v error: %v target: %T`, - argErr, - argTarget, - err, - *target, - ), + args.Get(0), + args.Get(1), + err, + *target, + ) + }, } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`error %v cannot be set as target %v + Message: func() string { + return fmt.Sprintf(`error %v cannot be set as target %v error: %v target: %T`, - argErr, - argTarget, - err, - *target, - ), + args.Get(0), + args.Get(1), + err, + *target, + ) + }, } } diff --git a/be/errors_test.go b/be/errors_test.go index f18c3e3..7b34e27 100644 --- a/be/errors_test.go +++ b/be/errors_test.go @@ -18,11 +18,11 @@ func TestError(t *testing.T) { result := be.Error(err) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `err has error value: oopsie`)) + g.Should(be.Equal(result.Message(), `err has error value: oopsie`)) result = be.Error(errors.New("oopsie")) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `errors.New("oopsie") has error value: oopsie`)) + g.Should(be.Equal(result.Message(), `errors.New("oopsie") has error value: oopsie`)) }) t.Run("nil", func(t *testing.T) { @@ -32,11 +32,11 @@ func TestError(t *testing.T) { result := be.Error(err) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `err is nil`)) + g.Should(be.Equal(result.Message(), `err is nil`)) result = be.Error(nil) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `nil is nil`)) + g.Should(be.Equal(result.Message(), `nil is nil`)) }) } @@ -50,7 +50,7 @@ func TestErrorContaining(t *testing.T) { result := be.ErrorContaining(err, msg) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error err contains message msg got: foobar want: oob`, @@ -59,7 +59,7 @@ want: oob`, result = be.ErrorContaining(errors.New("foobar"), "oob") g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error errors.New("foobar") contains message "oob" got: foobar want: oob`, @@ -75,7 +75,7 @@ want: oob`, result := be.ErrorContaining(err, msg) g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error err does not contain message msg got: foobar want: boo`, @@ -84,7 +84,7 @@ want: boo`, result = be.ErrorContaining(errors.New("foobar"), "boo") g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error errors.New("foobar") does not contain message "boo" got: foobar want: boo`, @@ -99,13 +99,13 @@ want: boo`, result := be.ErrorContaining(err, msg) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `error err is nil, does not contain msg + g.Should(be.Equal(result.Message(), `error err is nil, does not contain msg got: want: boo`)) result = be.ErrorContaining(nil, "boo") g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `error nil is nil, does not contain message + g.Should(be.Equal(result.Message(), `error nil is nil, does not contain message got: want: boo`)) }) @@ -121,7 +121,7 @@ func TestErrorEqual(t *testing.T) { result := be.ErrorEqual(err, msg) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error err has message msg value: foobar`, )) @@ -129,7 +129,7 @@ value: foobar`, result = be.ErrorEqual(errors.New("foobar"), "foobar") g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error errors.New("foobar") has message "foobar" value: foobar`, )) @@ -144,7 +144,7 @@ value: foobar`, result := be.ErrorEqual(err, msg) g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error err does not have message msg got: foobar want: boo`, @@ -153,7 +153,7 @@ want: boo`, result = be.ErrorEqual(errors.New("foobar"), "boo") g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error errors.New("foobar") does not have message "boo" got: foobar want: boo`, @@ -168,13 +168,13 @@ want: boo`, result := be.ErrorEqual(err, msg) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `error err is nil + g.Should(be.Equal(result.Message(), `error err is nil got: want: boo`)) result = be.ErrorEqual(nil, "boo") g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `error nil is nil + g.Should(be.Equal(result.Message(), `error nil is nil got: want: boo`)) }) @@ -190,7 +190,7 @@ func TestErrorIs(t *testing.T) { result := be.ErrorIs(err, target) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error err is target target error: wrapping: foobar target: foobar`, @@ -199,7 +199,7 @@ target: foobar`, result = be.ErrorIs(fmt.Errorf("wrapping: %w", target), target) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error fmt.Errorf("wrapping: %w", target) is target target error: wrapping: foobar target: foobar`, @@ -215,7 +215,7 @@ target: foobar`, result := be.ErrorIs(err, target) g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error err is not target target error: wrapping: foobar target: foobar`, @@ -224,7 +224,7 @@ target: foobar`, result = be.ErrorIs(fmt.Errorf("wrapping: %v", target), target) //nolint:errorlint // test case g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error fmt.Errorf("wrapping: %v", target) is not target target error: wrapping: foobar target: foobar`, @@ -239,13 +239,13 @@ target: foobar`, result := be.ErrorIs(err, target) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `error err is target target + g.Should(be.Equal(result.Message(), `error err is target target error: target: `)) result = be.ErrorIs(nil, nil) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `error nil is target nil + g.Should(be.Equal(result.Message(), `error nil is target nil error: target: `)) }) @@ -261,7 +261,7 @@ func TestErrorAs(t *testing.T) { result := be.ErrorAs(err, &target) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error err set as target &target error: strconv.Atoi: parsing "bad input": invalid syntax target: *strconv.NumError`, @@ -270,7 +270,7 @@ target: *strconv.NumError`, result = be.ErrorAs(fmt.Errorf("wrapping: %w", err), &target) g.Check(result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error fmt.Errorf("wrapping: %w", err) set as target &target error: wrapping: strconv.Atoi: parsing "bad input": invalid syntax target: *strconv.NumError`, @@ -286,7 +286,7 @@ target: *strconv.NumError`, result := be.ErrorAs(err, &target) g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error err cannot be set as target &target error: oh no target: *strconv.NumError`, @@ -295,7 +295,7 @@ target: *strconv.NumError`, result = be.ErrorAs(errors.New("oh no"), &target) g.Check(!result.Ok) g.Should(be.Equal( - result.Message, + result.Message(), `error errors.New("oh no") cannot be set as target &target error: oh no target: *strconv.NumError`, @@ -310,11 +310,11 @@ target: *strconv.NumError`, result := be.ErrorAs(err, &target) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `error err was nil`)) + g.Should(be.Equal(result.Message(), `error err was nil`)) result = be.ErrorAs(nil, new(error)) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `error nil was nil`)) + g.Should(be.Equal(result.Message(), `error nil was nil`)) }) t.Run("nil target", func(t *testing.T) { @@ -325,10 +325,10 @@ target: *strconv.NumError`, result := be.ErrorAs(err, target) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `target target cannot be nil`)) + g.Should(be.Equal(result.Message(), `target target cannot be nil`)) result = be.ErrorAs[error](errors.New("oh no"), nil) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `target cannot be nil`)) + g.Should(be.Equal(result.Message(), `target cannot be nil`)) }) } diff --git a/be/ordered.go b/be/ordered.go index d6a4f12..5ca9741 100644 --- a/be/ordered.go +++ b/be/ordered.go @@ -11,56 +11,62 @@ import ( // Greater asserts that the first value provided is strictly greater than the second. func Greater[T constraints.Ordered](a, b T) ghost.Result { args := ghostlib.ArgsFromAST(a, b) - argA, argB := args[0], args[1] - if a > b { return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v is greater than %v`, - inline(a, argA), - inline(b, argB), - ), + Message: func() string { + return fmt.Sprintf(`%v is greater than %v`, + inline(a, args.Get(0)), + inline(b, args.Get(1)), + ) + }, } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v is not greater than %v`, - inline(a, argA), - inline(b, argB), - ), + Message: func() string { + return fmt.Sprintf(`%v is not greater than %v`, + inline(a, args.Get(0)), + inline(b, args.Get(1)), + ) + }, } } // GreaterOrEqual asserts that the first value provided is greater than or equal to the second. func GreaterOrEqual[T constraints.Ordered](a, b T) ghost.Result { args := ghostlib.ArgsFromAST(a, b) - argA, argB := args[0], args[1] - switch { case a > b: return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v is greater than %v`, - inline(a, argA), - inline(b, argB), - ), + Message: func() string { + return fmt.Sprintf(`%v is greater than %v`, + inline(a, args.Get(0)), + inline(b, args.Get(1)), + ) + }, } case a == b: return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v is equal to %v`, - inline(a, argA), - inline(b, argB), - ), + Message: func() string { + return fmt.Sprintf(`%v is equal to %v`, + inline(a, args.Get(0)), + inline(b, args.Get(1)), + ) + }, } default: return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v is not greater than or equal to %v`, - inline(a, argA), - inline(b, argB), - ), + Message: func() string { + return fmt.Sprintf(`%v is not greater than or equal to %v`, + inline(a, args.Get(0)), + inline(b, args.Get(1)), + ) + }, } } } @@ -68,56 +74,62 @@ func GreaterOrEqual[T constraints.Ordered](a, b T) ghost.Result { // Less asserts that the first value provided is strictly less than the second. func Less[T constraints.Ordered](a, b T) ghost.Result { args := ghostlib.ArgsFromAST(a, b) - argA, argB := args[0], args[1] - if a < b { return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v is less than %v`, - inline(a, argA), - inline(b, argB), - ), + Message: func() string { + return fmt.Sprintf(`%v is less than %v`, + inline(a, args.Get(0)), + inline(b, args.Get(1)), + ) + }, } } return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v is not less than %v`, - inline(a, argA), - inline(b, argB), - ), + Message: func() string { + return fmt.Sprintf(`%v is not less than %v`, + inline(a, args.Get(0)), + inline(b, args.Get(1)), + ) + }, } } // LessOrEqual asserts that the first value provided is less than or equal to the second. func LessOrEqual[T constraints.Ordered](a, b T) ghost.Result { args := ghostlib.ArgsFromAST(a, b) - argA, argB := args[0], args[1] - switch { case a < b: return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v is less than %v`, - inline(a, argA), - inline(b, argB), - ), + Message: func() string { + return fmt.Sprintf(`%v is less than %v`, + inline(a, args.Get(0)), + inline(b, args.Get(1)), + ) + }, } case a == b: return ghost.Result{ Ok: true, - Message: fmt.Sprintf(`%v is equal to %v`, - inline(a, argA), - inline(b, argB), - ), + Message: func() string { + return fmt.Sprintf(`%v is equal to %v`, + inline(a, args.Get(0)), + inline(b, args.Get(1)), + ) + }, } default: return ghost.Result{ Ok: false, - Message: fmt.Sprintf(`%v is not less than or equal to %v`, - inline(a, argA), - inline(b, argB), - ), + Message: func() string { + return fmt.Sprintf(`%v is not less than or equal to %v`, + inline(a, args.Get(0)), + inline(b, args.Get(1)), + ) + }, } } } diff --git a/be/ordered_test.go b/be/ordered_test.go index 32cea85..5d1b37f 100644 --- a/be/ordered_test.go +++ b/be/ordered_test.go @@ -16,11 +16,11 @@ func TestGreater(t *testing.T) { result := be.Greater(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (3) is not greater than b (4)`)) + g.Should(be.Equal(result.Message(), `a (3) is not greater than b (4)`)) result = be.Greater(3, 4) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `3 is not greater than 4`)) + g.Should(be.Equal(result.Message(), `3 is not greater than 4`)) }) t.Run("int greater", func(t *testing.T) { @@ -31,11 +31,11 @@ func TestGreater(t *testing.T) { result := be.Greater(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (4) is greater than b (3)`)) + g.Should(be.Equal(result.Message(), `a (4) is greater than b (3)`)) result = be.Greater(4, 3) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `4 is greater than 3`)) + g.Should(be.Equal(result.Message(), `4 is greater than 3`)) }) t.Run("int equal", func(t *testing.T) { @@ -46,11 +46,11 @@ func TestGreater(t *testing.T) { result := be.Greater(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (3) is not greater than b (3)`)) + g.Should(be.Equal(result.Message(), `a (3) is not greater than b (3)`)) result = be.Greater(3, 3) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `3 is not greater than 3`)) + g.Should(be.Equal(result.Message(), `3 is not greater than 3`)) }) t.Run("float less", func(t *testing.T) { @@ -61,11 +61,11 @@ func TestGreater(t *testing.T) { result := be.Greater(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (3.1) is not greater than b (4.1)`)) + g.Should(be.Equal(result.Message(), `a (3.1) is not greater than b (4.1)`)) result = be.Greater(3.1, 4.1) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `3.1 is not greater than 4.1`)) + g.Should(be.Equal(result.Message(), `3.1 is not greater than 4.1`)) }) t.Run("float greater", func(t *testing.T) { @@ -76,11 +76,11 @@ func TestGreater(t *testing.T) { result := be.Greater(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (4.1) is greater than b (3.1)`)) + g.Should(be.Equal(result.Message(), `a (4.1) is greater than b (3.1)`)) result = be.Greater(4.1, 3.1) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `4.1 is greater than 3.1`)) + g.Should(be.Equal(result.Message(), `4.1 is greater than 3.1`)) }) t.Run("float equal", func(t *testing.T) { @@ -91,11 +91,11 @@ func TestGreater(t *testing.T) { result := be.Greater(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (3.1) is not greater than b (3.1)`)) + g.Should(be.Equal(result.Message(), `a (3.1) is not greater than b (3.1)`)) result = be.Greater(3.1, 3.1) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `3.1 is not greater than 3.1`)) + g.Should(be.Equal(result.Message(), `3.1 is not greater than 3.1`)) }) t.Run("string less", func(t *testing.T) { @@ -106,11 +106,11 @@ func TestGreater(t *testing.T) { result := be.Greater(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a ("bar") is not greater than b ("foo")`)) + g.Should(be.Equal(result.Message(), `a ("bar") is not greater than b ("foo")`)) result = be.Greater("bar", "foo") g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `"bar" is not greater than "foo"`)) + g.Should(be.Equal(result.Message(), `"bar" is not greater than "foo"`)) }) t.Run("string greater", func(t *testing.T) { @@ -121,11 +121,11 @@ func TestGreater(t *testing.T) { result := be.Greater(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a ("foo") is greater than b ("bar")`)) + g.Should(be.Equal(result.Message(), `a ("foo") is greater than b ("bar")`)) result = be.Greater("foo", "bar") g.Check(result.Ok) - g.Should(be.Equal(result.Message, `"foo" is greater than "bar"`)) + g.Should(be.Equal(result.Message(), `"foo" is greater than "bar"`)) }) t.Run("string equal", func(t *testing.T) { @@ -136,11 +136,11 @@ func TestGreater(t *testing.T) { result := be.Greater(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a ("foo") is not greater than b ("foo")`)) + g.Should(be.Equal(result.Message(), `a ("foo") is not greater than b ("foo")`)) result = be.Greater("foo", "foo") g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `"foo" is not greater than "foo"`)) + g.Should(be.Equal(result.Message(), `"foo" is not greater than "foo"`)) }) } @@ -153,11 +153,11 @@ func TestGreaterOrEqual(t *testing.T) { result := be.GreaterOrEqual(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (3) is not greater than or equal to b (4)`)) + g.Should(be.Equal(result.Message(), `a (3) is not greater than or equal to b (4)`)) result = be.GreaterOrEqual(3, 4) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `3 is not greater than or equal to 4`)) + g.Should(be.Equal(result.Message(), `3 is not greater than or equal to 4`)) }) t.Run("int greater", func(t *testing.T) { @@ -168,11 +168,11 @@ func TestGreaterOrEqual(t *testing.T) { result := be.GreaterOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (4) is greater than b (3)`)) + g.Should(be.Equal(result.Message(), `a (4) is greater than b (3)`)) result = be.GreaterOrEqual(4, 3) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `4 is greater than 3`)) + g.Should(be.Equal(result.Message(), `4 is greater than 3`)) }) t.Run("int equal", func(t *testing.T) { @@ -183,11 +183,11 @@ func TestGreaterOrEqual(t *testing.T) { result := be.GreaterOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (3) is equal to b (3)`)) + g.Should(be.Equal(result.Message(), `a (3) is equal to b (3)`)) result = be.GreaterOrEqual(3, 3) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `3 is equal to 3`)) + g.Should(be.Equal(result.Message(), `3 is equal to 3`)) }) t.Run("float less", func(t *testing.T) { @@ -198,11 +198,11 @@ func TestGreaterOrEqual(t *testing.T) { result := be.GreaterOrEqual(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (3.1) is not greater than or equal to b (4.1)`)) + g.Should(be.Equal(result.Message(), `a (3.1) is not greater than or equal to b (4.1)`)) result = be.GreaterOrEqual(3.1, 4.1) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `3.1 is not greater than or equal to 4.1`)) + g.Should(be.Equal(result.Message(), `3.1 is not greater than or equal to 4.1`)) }) t.Run("float greater", func(t *testing.T) { @@ -213,11 +213,11 @@ func TestGreaterOrEqual(t *testing.T) { result := be.GreaterOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (4.1) is greater than b (3.1)`)) + g.Should(be.Equal(result.Message(), `a (4.1) is greater than b (3.1)`)) result = be.GreaterOrEqual(4.1, 3.1) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `4.1 is greater than 3.1`)) + g.Should(be.Equal(result.Message(), `4.1 is greater than 3.1`)) }) t.Run("float equal", func(t *testing.T) { @@ -228,11 +228,11 @@ func TestGreaterOrEqual(t *testing.T) { result := be.GreaterOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (3.1) is equal to b (3.1)`)) + g.Should(be.Equal(result.Message(), `a (3.1) is equal to b (3.1)`)) result = be.GreaterOrEqual(3.1, 3.1) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `3.1 is equal to 3.1`)) + g.Should(be.Equal(result.Message(), `3.1 is equal to 3.1`)) }) t.Run("string less", func(t *testing.T) { @@ -243,11 +243,11 @@ func TestGreaterOrEqual(t *testing.T) { result := be.GreaterOrEqual(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a ("bar") is not greater than or equal to b ("foo")`)) + g.Should(be.Equal(result.Message(), `a ("bar") is not greater than or equal to b ("foo")`)) result = be.GreaterOrEqual("bar", "foo") g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `"bar" is not greater than or equal to "foo"`)) + g.Should(be.Equal(result.Message(), `"bar" is not greater than or equal to "foo"`)) }) t.Run("string greater", func(t *testing.T) { @@ -258,11 +258,11 @@ func TestGreaterOrEqual(t *testing.T) { result := be.GreaterOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a ("foo") is greater than b ("bar")`)) + g.Should(be.Equal(result.Message(), `a ("foo") is greater than b ("bar")`)) result = be.GreaterOrEqual("foo", "bar") g.Check(result.Ok) - g.Should(be.Equal(result.Message, `"foo" is greater than "bar"`)) + g.Should(be.Equal(result.Message(), `"foo" is greater than "bar"`)) }) t.Run("string equal", func(t *testing.T) { @@ -273,11 +273,11 @@ func TestGreaterOrEqual(t *testing.T) { result := be.GreaterOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a ("foo") is equal to b ("foo")`)) + g.Should(be.Equal(result.Message(), `a ("foo") is equal to b ("foo")`)) result = be.GreaterOrEqual("foo", "foo") g.Check(result.Ok) - g.Should(be.Equal(result.Message, `"foo" is equal to "foo"`)) + g.Should(be.Equal(result.Message(), `"foo" is equal to "foo"`)) }) } @@ -290,11 +290,11 @@ func TestLess(t *testing.T) { result := be.Less(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (3) is less than b (4)`)) + g.Should(be.Equal(result.Message(), `a (3) is less than b (4)`)) result = be.Less(3, 4) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `3 is less than 4`)) + g.Should(be.Equal(result.Message(), `3 is less than 4`)) }) t.Run("int greater", func(t *testing.T) { @@ -305,11 +305,11 @@ func TestLess(t *testing.T) { result := be.Less(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (4) is not less than b (3)`)) + g.Should(be.Equal(result.Message(), `a (4) is not less than b (3)`)) result = be.Less(4, 3) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `4 is not less than 3`)) + g.Should(be.Equal(result.Message(), `4 is not less than 3`)) }) t.Run("int equal", func(t *testing.T) { @@ -320,11 +320,11 @@ func TestLess(t *testing.T) { result := be.Less(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (3) is not less than b (3)`)) + g.Should(be.Equal(result.Message(), `a (3) is not less than b (3)`)) result = be.Less(3, 3) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `3 is not less than 3`)) + g.Should(be.Equal(result.Message(), `3 is not less than 3`)) }) t.Run("float less", func(t *testing.T) { @@ -335,11 +335,11 @@ func TestLess(t *testing.T) { result := be.Less(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (3.1) is less than b (4.1)`)) + g.Should(be.Equal(result.Message(), `a (3.1) is less than b (4.1)`)) result = be.Less(3.1, 4.1) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `3.1 is less than 4.1`)) + g.Should(be.Equal(result.Message(), `3.1 is less than 4.1`)) }) t.Run("float greater", func(t *testing.T) { @@ -350,11 +350,11 @@ func TestLess(t *testing.T) { result := be.Less(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (4.1) is not less than b (3.1)`)) + g.Should(be.Equal(result.Message(), `a (4.1) is not less than b (3.1)`)) result = be.Less(4.1, 3.1) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `4.1 is not less than 3.1`)) + g.Should(be.Equal(result.Message(), `4.1 is not less than 3.1`)) }) t.Run("float equal", func(t *testing.T) { @@ -365,11 +365,11 @@ func TestLess(t *testing.T) { result := be.Less(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (3.1) is not less than b (3.1)`)) + g.Should(be.Equal(result.Message(), `a (3.1) is not less than b (3.1)`)) result = be.Less(3.1, 3.1) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `3.1 is not less than 3.1`)) + g.Should(be.Equal(result.Message(), `3.1 is not less than 3.1`)) }) t.Run("string less", func(t *testing.T) { @@ -380,11 +380,11 @@ func TestLess(t *testing.T) { result := be.Less(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a ("bar") is less than b ("foo")`)) + g.Should(be.Equal(result.Message(), `a ("bar") is less than b ("foo")`)) result = be.Less("bar", "foo") g.Check(result.Ok) - g.Should(be.Equal(result.Message, `"bar" is less than "foo"`)) + g.Should(be.Equal(result.Message(), `"bar" is less than "foo"`)) }) t.Run("string greater", func(t *testing.T) { @@ -395,11 +395,11 @@ func TestLess(t *testing.T) { result := be.Less(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a ("foo") is not less than b ("bar")`)) + g.Should(be.Equal(result.Message(), `a ("foo") is not less than b ("bar")`)) result = be.Less("foo", "bar") g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `"foo" is not less than "bar"`)) + g.Should(be.Equal(result.Message(), `"foo" is not less than "bar"`)) }) t.Run("string equal", func(t *testing.T) { @@ -410,11 +410,11 @@ func TestLess(t *testing.T) { result := be.Less(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a ("foo") is not less than b ("foo")`)) + g.Should(be.Equal(result.Message(), `a ("foo") is not less than b ("foo")`)) result = be.Less("foo", "foo") g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `"foo" is not less than "foo"`)) + g.Should(be.Equal(result.Message(), `"foo" is not less than "foo"`)) }) } @@ -427,11 +427,11 @@ func TestLessOrEqual(t *testing.T) { result := be.LessOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (3) is less than b (4)`)) + g.Should(be.Equal(result.Message(), `a (3) is less than b (4)`)) result = be.LessOrEqual(3, 4) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `3 is less than 4`)) + g.Should(be.Equal(result.Message(), `3 is less than 4`)) }) t.Run("int greater", func(t *testing.T) { @@ -442,11 +442,11 @@ func TestLessOrEqual(t *testing.T) { result := be.LessOrEqual(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (4) is not less than or equal to b (3)`)) + g.Should(be.Equal(result.Message(), `a (4) is not less than or equal to b (3)`)) result = be.LessOrEqual(4, 3) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `4 is not less than or equal to 3`)) + g.Should(be.Equal(result.Message(), `4 is not less than or equal to 3`)) }) t.Run("int equal", func(t *testing.T) { @@ -457,11 +457,11 @@ func TestLessOrEqual(t *testing.T) { result := be.LessOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (3) is equal to b (3)`)) + g.Should(be.Equal(result.Message(), `a (3) is equal to b (3)`)) result = be.LessOrEqual(3, 3) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `3 is equal to 3`)) + g.Should(be.Equal(result.Message(), `3 is equal to 3`)) }) t.Run("float less", func(t *testing.T) { @@ -472,11 +472,11 @@ func TestLessOrEqual(t *testing.T) { result := be.LessOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (3.1) is less than b (4.1)`)) + g.Should(be.Equal(result.Message(), `a (3.1) is less than b (4.1)`)) result = be.LessOrEqual(3.1, 4.1) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `3.1 is less than 4.1`)) + g.Should(be.Equal(result.Message(), `3.1 is less than 4.1`)) }) t.Run("float greater", func(t *testing.T) { @@ -487,11 +487,11 @@ func TestLessOrEqual(t *testing.T) { result := be.LessOrEqual(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a (4.1) is not less than or equal to b (3.1)`)) + g.Should(be.Equal(result.Message(), `a (4.1) is not less than or equal to b (3.1)`)) result = be.LessOrEqual(4.1, 3.1) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `4.1 is not less than or equal to 3.1`)) + g.Should(be.Equal(result.Message(), `4.1 is not less than or equal to 3.1`)) }) t.Run("float equal", func(t *testing.T) { @@ -502,11 +502,11 @@ func TestLessOrEqual(t *testing.T) { result := be.LessOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a (3.1) is equal to b (3.1)`)) + g.Should(be.Equal(result.Message(), `a (3.1) is equal to b (3.1)`)) result = be.LessOrEqual(3.1, 3.1) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `3.1 is equal to 3.1`)) + g.Should(be.Equal(result.Message(), `3.1 is equal to 3.1`)) }) t.Run("string less", func(t *testing.T) { @@ -517,11 +517,11 @@ func TestLessOrEqual(t *testing.T) { result := be.LessOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a ("bar") is less than b ("foo")`)) + g.Should(be.Equal(result.Message(), `a ("bar") is less than b ("foo")`)) result = be.LessOrEqual("bar", "foo") g.Check(result.Ok) - g.Should(be.Equal(result.Message, `"bar" is less than "foo"`)) + g.Should(be.Equal(result.Message(), `"bar" is less than "foo"`)) }) t.Run("string greater", func(t *testing.T) { @@ -532,11 +532,11 @@ func TestLessOrEqual(t *testing.T) { result := be.LessOrEqual(a, b) g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `a ("foo") is not less than or equal to b ("bar")`)) + g.Should(be.Equal(result.Message(), `a ("foo") is not less than or equal to b ("bar")`)) result = be.LessOrEqual("foo", "bar") g.Check(!result.Ok) - g.Should(be.Equal(result.Message, `"foo" is not less than or equal to "bar"`)) + g.Should(be.Equal(result.Message(), `"foo" is not less than or equal to "bar"`)) }) t.Run("string equal", func(t *testing.T) { @@ -547,10 +547,10 @@ func TestLessOrEqual(t *testing.T) { result := be.LessOrEqual(a, b) g.Check(result.Ok) - g.Should(be.Equal(result.Message, `a ("foo") is equal to b ("foo")`)) + g.Should(be.Equal(result.Message(), `a ("foo") is equal to b ("foo")`)) result = be.LessOrEqual("foo", "foo") g.Check(result.Ok) - g.Should(be.Equal(result.Message, `"foo" is equal to "foo"`)) + g.Should(be.Equal(result.Message(), `"foo" is equal to "foo"`)) }) } diff --git a/ghost.go b/ghost.go index 5c3bfdb..f657c11 100644 --- a/ghost.go +++ b/ghost.go @@ -34,7 +34,7 @@ func (g Ghost) Should(result Result) bool { } if !result.Ok { - g.t.Log(result.Message) + g.t.Log(result.Message()) g.t.Fail() return false } @@ -51,7 +51,7 @@ func (g Ghost) ShouldNot(result Result) bool { } if result.Ok { - g.t.Log(result.Message) + g.t.Log(result.Message()) g.t.Fail() return false } @@ -93,7 +93,7 @@ func (g Ghost) Check(v bool) bool { args := ghostlib.ArgsFromAST(v) if !v { - g.t.Log(fmt.Sprintf("%s is %t", args[0], v)) + g.t.Log(fmt.Sprintf("%s is %t", args.Get(0), v)) g.t.Fail() return false } @@ -111,7 +111,7 @@ func (g Ghost) Assert(v bool) { args := ghostlib.ArgsFromAST(v) if !v { - g.t.Log(fmt.Sprintf("%s is %t", args[0], v)) + g.t.Log(fmt.Sprintf("%s is %t", args.Get(0), v)) g.t.FailNow() } } @@ -125,7 +125,7 @@ func (g Ghost) NoError(err error) { args := ghostlib.ArgsFromAST(err) if err != nil { - g.t.Log(fmt.Sprintf("%s has error value: %s", args[0], err)) + g.t.Log(fmt.Sprintf("%s has error value: %s", args.Get(0), err)) g.t.FailNow() } } @@ -139,5 +139,5 @@ type Result struct { // // A message should be present regardless of whether or not the assertion was // successful. - Message string + Message func() string } diff --git a/ghost_test.go b/ghost_test.go index 1205bbe..1df2ce7 100644 --- a/ghost_test.go +++ b/ghost_test.go @@ -19,7 +19,7 @@ func TestGhost_Should(t *testing.T) { ok := testG.Should(ghost.Result{ Ok: true, - Message: msg, + Message: func() string { return msg }, }) g.Should(be.True(ok)) @@ -37,7 +37,7 @@ func TestGhost_Should(t *testing.T) { ok := testG.Should(ghost.Result{ Ok: false, - Message: msg, + Message: func() string { return msg }, }) g.Should(be.False(ok)) @@ -58,7 +58,7 @@ func TestGhost_ShouldNot(t *testing.T) { ok := testG.ShouldNot(ghost.Result{ Ok: true, - Message: msg, + Message: func() string { return msg }, }) g.Should(be.False(ok)) @@ -77,7 +77,7 @@ func TestGhost_ShouldNot(t *testing.T) { ok := testG.ShouldNot(ghost.Result{ Ok: false, - Message: msg, + Message: func() string { return msg }, }) g.Should(be.True(ok)) @@ -97,7 +97,7 @@ func TestGhost_Must(t *testing.T) { testG.Must(ghost.Result{ Ok: true, - Message: msg, + Message: func() string { return msg }, }) g.Should(be.SliceLen(mockT.logCalls, 0)) @@ -114,7 +114,7 @@ func TestGhost_Must(t *testing.T) { testG.Must(ghost.Result{ Ok: false, - Message: msg, + Message: func() string { return msg }, }) g.Should(be.DeepEqual(mockT.logCalls, [][]any{{msg}})) @@ -132,7 +132,7 @@ func TestGhost_MustNot(t *testing.T) { testG.MustNot(ghost.Result{ Ok: true, - Message: msg, + Message: func() string { return msg }, }) g.Should(be.DeepEqual(mockT.logCalls, [][]any{{msg}})) @@ -148,7 +148,7 @@ func TestGhost_MustNot(t *testing.T) { testG.MustNot(ghost.Result{ Ok: false, - Message: msg, + Message: func() string { return msg }, }) g.Should(be.SliceLen(mockT.logCalls, 0)) diff --git a/ghostlib/ast.go b/ghostlib/ast.go index 6d81b70..50f04d7 100644 --- a/ghostlib/ast.go +++ b/ghostlib/ast.go @@ -2,7 +2,6 @@ package ghostlib import ( "bytes" - "errors" "fmt" "go/ast" "go/format" @@ -11,66 +10,95 @@ import ( "os" "runtime" "strings" + "sync" ) +// Args captures the call site information for deferred AST lookup. +type Args struct { + once func() []string +} + +// Get the string representation of the argument in the specified position. +// Panics on out-of-bounds lookups. Safe for concurrent use. +func (a Args) Get(index int) string { + return a.once()[index] +} + // ArgsFromAST gets the string representation of the caller's arguments from // the AST. To handle situations where this cannot be done reliably, the raw // arguments should be passed so their values can be used as a backup. -func ArgsFromAST(unformatted ...any) []string { - return argsFromASTSkip(1, unformatted...) +// +// This function must be called directly from an assertion function to ensure +// that the intended arguments are captured. +func ArgsFromAST(unformatted ...any) Args { + pc, _, _, _ := runtime.Caller(1) + _, file, line, _ := runtime.Caller(2) + return Args{ + once: sync.OnceValue(func() []string { + return argsFromAST(pc, findSystemFilepath(file), line, unformatted...) + }), + } } -// argsFromASTSkip gets the string representation of the caller's arguments -// from the AST, skipping the number specified. -func argsFromASTSkip(skip int, unformatted ...any) []string { - args, err := callExprArgs(2 + skip) +var fset = token.NewFileSet() + +func argsFromAST(pc uintptr, filename string, line int, unformatted ...any) []string { + wantFunc := runtime.FuncForPC(pc) + if wantFunc == nil { + return mapString(unformatted) + } + + astFile, err := parseFile(filename) if err != nil { return mapString(unformatted) } - out := make([]string, 0, len(args)) - for _, arg := range args { + node := callExprForFunc(wantFunc, fset, astFile, line) + if node == nil { + return mapString(unformatted) + } + + out := make([]string, 0, len(node.Args)) + for _, arg := range node.Args { out = append(out, nodeToString(arg)) } return out } -func mapString(s []any) []string { - out := make([]string, 0, len(s)) - for _, ss := range s { - out = append(out, fmt.Sprint(ss)) - } - return out -} +var ( + fileCache = map[string]*ast.File{} + fileMu sync.RWMutex +) -func callExprArgs(skip int) ([]ast.Expr, error) { - pc, _, _, ok := runtime.Caller(skip) - if !ok { - return nil, errors.New("failed to get file/line") +func parseFile(filename string) (*ast.File, error) { + fileMu.RLock() + f, ok := fileCache[filename] + fileMu.RUnlock() + if ok { + return f, nil } - _, filename, line, ok := runtime.Caller(skip + 1) - if !ok { - return nil, errors.New("failed to get file/line") + fileMu.Lock() + defer fileMu.Unlock() + if f, ok = fileCache[filename]; ok { + return f, nil } - filename = findSystemFilepath(filename) - - wantFunc := runtime.FuncForPC(pc) - - fset := token.NewFileSet() - astFile, err := parser.ParseFile(fset, filename, nil, parser.AllErrors) + f, err := parser.ParseFile(fset, filename, nil, parser.AllErrors) if err != nil { return nil, err } + fileCache[filename] = f + return f, nil +} - node := callExprForFunc(wantFunc, fset, astFile, line) - if node == nil { - return nil, errors.New("no node found at line") +func mapString(s []any) []string { + out := make([]string, 0, len(s)) + for _, ss := range s { + out = append(out, fmt.Sprint(ss)) } - - return node.Args, nil + return out } // Passing the -trimpath flag will prevent looking up filepaths directly. diff --git a/go.mod b/go.mod index 0c8c6ae..382f71f 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,7 @@ module github.com/rliebz/ghost -go 1.18 +go 1.25 + +toolchain go1.26.2 require github.com/google/go-cmp v0.5.9 diff --git a/tusk.yml b/tusk.yml index 119bb7d..caa34c6 100644 --- a/tusk.yml +++ b/tusk.yml @@ -3,7 +3,7 @@ tasks: usage: Clean up and format the code run: - go mod tidy - - go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.0.2 fmt + - go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.1 fmt lint: usage: Run static analysis @@ -13,7 +13,7 @@ tasks: short: f type: boolean rewrite: --fix - run: go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.0.2 run ${fix} + run: go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.1 run ${fix} test: usage: Run unit tests