package assertions import ( "encoding/json" "errors" "fmt" "math" "reflect" "strings" "github.com/smartystreets/assertions/internal/go-render/render" "github.com/smartystreets/assertions/internal/oglematchers" ) // ShouldEqual receives exactly two parameters and does an equality check // using the following semantics: // 1. If the expected and actual values implement an Equal method in the form // `func (this T) Equal(that T) bool` then call the method. If true, they are equal. // 2. The expected and actual values are judged equal or not by oglematchers.Equals. func ShouldEqual(actual interface{}, expected ...interface{}) string { if message := need(1, expected); message != success { return message } return shouldEqual(actual, expected[0]) } func shouldEqual(actual, expected interface{}) (message string) { defer func() { if r := recover(); r != nil { message = serializer.serialize(expected, actual, composeEqualityMismatchMessage(expected, actual)) } }() if spec := newEqualityMethodSpecification(expected, actual); spec.IsSatisfied() && spec.AreEqual() { return success } else if matchError := oglematchers.Equals(expected).Matches(actual); matchError == nil { return success } return serializer.serialize(expected, actual, composeEqualityMismatchMessage(expected, actual)) } func composeEqualityMismatchMessage(expected, actual interface{}) string { var ( renderedExpected = fmt.Sprintf("%v", expected) renderedActual = fmt.Sprintf("%v", actual) ) if renderedExpected != renderedActual { return fmt.Sprintf(shouldHaveBeenEqual+composePrettyDiff(renderedExpected, renderedActual), expected, actual) } else if reflect.TypeOf(expected) != reflect.TypeOf(actual) { return fmt.Sprintf(shouldHaveBeenEqualTypeMismatch, expected, expected, actual, actual) } else { return fmt.Sprintf(shouldHaveBeenEqualNoResemblance, renderedExpected) } } // ShouldNotEqual receives exactly two parameters and does an inequality check. // See ShouldEqual for details on how equality is determined. func ShouldNotEqual(actual interface{}, expected ...interface{}) string { if fail := need(1, expected); fail != success { return fail } else if ShouldEqual(actual, expected[0]) == success { return fmt.Sprintf(shouldNotHaveBeenEqual, actual, expected[0]) } return success } // ShouldAlmostEqual makes sure that two parameters are close enough to being equal. // The acceptable delta may be specified with a third argument, // or a very small default delta will be used. func ShouldAlmostEqual(actual interface{}, expected ...interface{}) string { actualFloat, expectedFloat, deltaFloat, err := cleanAlmostEqualInput(actual, expected...) if err != "" { return err } if math.Abs(actualFloat-expectedFloat) <= deltaFloat { return success } else { return fmt.Sprintf(shouldHaveBeenAlmostEqual, actualFloat, expectedFloat) } } // ShouldNotAlmostEqual is the inverse of ShouldAlmostEqual func ShouldNotAlmostEqual(actual interface{}, expected ...interface{}) string { actualFloat, expectedFloat, deltaFloat, err := cleanAlmostEqualInput(actual, expected...) if err != "" { return err } if math.Abs(actualFloat-expectedFloat) > deltaFloat { return success } else { return fmt.Sprintf(shouldHaveNotBeenAlmostEqual, actualFloat, expectedFloat) } } func cleanAlmostEqualInput(actual interface{}, expected ...interface{}) (float64, float64, float64, string) { deltaFloat := 0.0000000001 if len(expected) == 0 { return 0.0, 0.0, 0.0, "This assertion requires exactly one comparison value and an optional delta (you provided neither)" } else if len(expected) == 2 { delta, err := getFloat(expected[1]) if err != nil { return 0.0, 0.0, 0.0, "The delta value " + err.Error() } deltaFloat = delta } else if len(expected) > 2 { return 0.0, 0.0, 0.0, "This assertion requires exactly one comparison value and an optional delta (you provided more values)" } actualFloat, err := getFloat(actual) if err != nil { return 0.0, 0.0, 0.0, "The actual value " + err.Error() } expectedFloat, err := getFloat(expected[0]) if err != nil { return 0.0, 0.0, 0.0, "The comparison value " + err.Error() } return actualFloat, expectedFloat, deltaFloat, "" } // returns the float value of any real number, or error if it is not a numerical type func getFloat(num interface{}) (float64, error) { numValue := reflect.ValueOf(num) numKind := numValue.Kind() if numKind == reflect.Int || numKind == reflect.Int8 || numKind == reflect.Int16 || numKind == reflect.Int32 || numKind == reflect.Int64 { return float64(numValue.Int()), nil } else if numKind == reflect.Uint || numKind == reflect.Uint8 || numKind == reflect.Uint16 || numKind == reflect.Uint32 || numKind == reflect.Uint64 { return float64(numValue.Uint()), nil } else if numKind == reflect.Float32 || numKind == reflect.Float64 { return numValue.Float(), nil } else { return 0.0, errors.New("must be a numerical type, but was: " + numKind.String()) } } // ShouldEqualJSON receives exactly two parameters and does an equality check by marshalling to JSON func ShouldEqualJSON(actual interface{}, expected ...interface{}) string { if message := need(1, expected); message != success { return message } expectedString, expectedErr := remarshal(expected[0].(string)) if expectedErr != nil { return "Expected value not valid JSON: " + expectedErr.Error() } actualString, actualErr := remarshal(actual.(string)) if actualErr != nil { return "Actual value not valid JSON: " + actualErr.Error() } return ShouldEqual(actualString, expectedString) } func remarshal(value string) (string, error) { var structured interface{} err := json.Unmarshal([]byte(value), &structured) if err != nil { return "", err } canonical, _ := json.Marshal(structured) return string(canonical), nil } // ShouldResemble receives exactly two parameters and does a deep equal check (see reflect.DeepEqual) func ShouldResemble(actual interface{}, expected ...interface{}) string { if message := need(1, expected); message != success { return message } if matchError := oglematchers.DeepEquals(expected[0]).Matches(actual); matchError != nil { renderedExpected, renderedActual := render.Render(expected[0]), render.Render(actual) message := fmt.Sprintf(shouldHaveResembled, renderedExpected, renderedActual) + composePrettyDiff(renderedExpected, renderedActual) return serializer.serializeDetailed(expected[0], actual, message) } return success } // ShouldNotResemble receives exactly two parameters and does an inverse deep equal check (see reflect.DeepEqual) func ShouldNotResemble(actual interface{}, expected ...interface{}) string { if message := need(1, expected); message != success { return message } else if ShouldResemble(actual, expected[0]) == success { return fmt.Sprintf(shouldNotHaveResembled, render.Render(actual), render.Render(expected[0])) } return success } // ShouldPointTo receives exactly two parameters and checks to see that they point to the same address. func ShouldPointTo(actual interface{}, expected ...interface{}) string { if message := need(1, expected); message != success { return message } return shouldPointTo(actual, expected[0]) } func shouldPointTo(actual, expected interface{}) string { actualValue := reflect.ValueOf(actual) expectedValue := reflect.ValueOf(expected) if ShouldNotBeNil(actual) != success { return fmt.Sprintf(shouldHaveBeenNonNilPointer, "first", "nil") } else if ShouldNotBeNil(expected) != success { return fmt.Sprintf(shouldHaveBeenNonNilPointer, "second", "nil") } else if actualValue.Kind() != reflect.Ptr { return fmt.Sprintf(shouldHaveBeenNonNilPointer, "first", "not") } else if expectedValue.Kind() != reflect.Ptr { return fmt.Sprintf(shouldHaveBeenNonNilPointer, "second", "not") } else if ShouldEqual(actualValue.Pointer(), expectedValue.Pointer()) != success { actualAddress := reflect.ValueOf(actual).Pointer() expectedAddress := reflect.ValueOf(expected).Pointer() return serializer.serialize(expectedAddress, actualAddress, fmt.Sprintf(shouldHavePointedTo, actual, actualAddress, expected, expectedAddress)) } return success } // ShouldNotPointTo receives exactly two parameters and checks to see that they point to different addresess. func ShouldNotPointTo(actual interface{}, expected ...interface{}) string { if message := need(1, expected); message != success { return message } compare := ShouldPointTo(actual, expected[0]) if strings.HasPrefix(compare, shouldBePointers) { return compare } else if compare == success { return fmt.Sprintf(shouldNotHavePointedTo, actual, expected[0], reflect.ValueOf(actual).Pointer()) } return success } // ShouldBeNil receives a single parameter and ensures that it is nil. func ShouldBeNil(actual interface{}, expected ...interface{}) string { if fail := need(0, expected); fail != success { return fail } else if actual == nil { return success } else if interfaceHasNilValue(actual) { return success } return fmt.Sprintf(shouldHaveBeenNil, actual) } func interfaceHasNilValue(actual interface{}) bool { value := reflect.ValueOf(actual) kind := value.Kind() nilable := kind == reflect.Slice || kind == reflect.Chan || kind == reflect.Func || kind == reflect.Ptr || kind == reflect.Map // Careful: reflect.Value.IsNil() will panic unless it's an interface, chan, map, func, slice, or ptr // Reference: http://golang.org/pkg/reflect/#Value.IsNil return nilable && value.IsNil() } // ShouldNotBeNil receives a single parameter and ensures that it is not nil. func ShouldNotBeNil(actual interface{}, expected ...interface{}) string { if fail := need(0, expected); fail != success { return fail } else if ShouldBeNil(actual) == success { return fmt.Sprintf(shouldNotHaveBeenNil, actual) } return success } // ShouldBeTrue receives a single parameter and ensures that it is true. func ShouldBeTrue(actual interface{}, expected ...interface{}) string { if fail := need(0, expected); fail != success { return fail } else if actual != true { return fmt.Sprintf(shouldHaveBeenTrue, actual) } return success } // ShouldBeFalse receives a single parameter and ensures that it is false. func ShouldBeFalse(actual interface{}, expected ...interface{}) string { if fail := need(0, expected); fail != success { return fail } else if actual != false { return fmt.Sprintf(shouldHaveBeenFalse, actual) } return success } // ShouldBeZeroValue receives a single parameter and ensures that it is // the Go equivalent of the default value, or "zero" value. func ShouldBeZeroValue(actual interface{}, expected ...interface{}) string { if fail := need(0, expected); fail != success { return fail } zeroVal := reflect.Zero(reflect.TypeOf(actual)).Interface() if !reflect.DeepEqual(zeroVal, actual) { return serializer.serialize(zeroVal, actual, fmt.Sprintf(shouldHaveBeenZeroValue, actual)) } return success } // ShouldBeZeroValue receives a single parameter and ensures that it is NOT // the Go equivalent of the default value, or "zero" value. func ShouldNotBeZeroValue(actual interface{}, expected ...interface{}) string { if fail := need(0, expected); fail != success { return fail } zeroVal := reflect.Zero(reflect.TypeOf(actual)).Interface() if reflect.DeepEqual(zeroVal, actual) { return serializer.serialize(zeroVal, actual, fmt.Sprintf(shouldNotHaveBeenZeroValue, actual)) } return success }