1package assertion
2
3import (
4	"fmt"
5	"reflect"
6
7	"github.com/onsi/gomega/types"
8)
9
10type Assertion struct {
11	actualInput interface{}
12	failWrapper *types.GomegaFailWrapper
13	offset      int
14	extra       []interface{}
15}
16
17func New(actualInput interface{}, failWrapper *types.GomegaFailWrapper, offset int, extra ...interface{}) *Assertion {
18	return &Assertion{
19		actualInput: actualInput,
20		failWrapper: failWrapper,
21		offset:      offset,
22		extra:       extra,
23	}
24}
25
26func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
27	assertion.failWrapper.TWithHelper.Helper()
28	return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
29}
30
31func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
32	assertion.failWrapper.TWithHelper.Helper()
33	return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
34}
35
36func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
37	assertion.failWrapper.TWithHelper.Helper()
38	return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
39}
40
41func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
42	assertion.failWrapper.TWithHelper.Helper()
43	return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
44}
45
46func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
47	assertion.failWrapper.TWithHelper.Helper()
48	return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
49}
50
51func (assertion *Assertion) buildDescription(optionalDescription ...interface{}) string {
52	switch len(optionalDescription) {
53	case 0:
54		return ""
55	default:
56		return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
57	}
58}
59
60func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
61	matches, err := matcher.Match(assertion.actualInput)
62	description := assertion.buildDescription(optionalDescription...)
63	assertion.failWrapper.TWithHelper.Helper()
64	if err != nil {
65		assertion.failWrapper.Fail(description+err.Error(), 2+assertion.offset)
66		return false
67	}
68	if matches != desiredMatch {
69		var message string
70		if desiredMatch {
71			message = matcher.FailureMessage(assertion.actualInput)
72		} else {
73			message = matcher.NegatedFailureMessage(assertion.actualInput)
74		}
75		assertion.failWrapper.Fail(description+message, 2+assertion.offset)
76		return false
77	}
78
79	return true
80}
81
82func (assertion *Assertion) vetExtras(optionalDescription ...interface{}) bool {
83	success, message := vetExtras(assertion.extra)
84	if success {
85		return true
86	}
87
88	description := assertion.buildDescription(optionalDescription...)
89	assertion.failWrapper.TWithHelper.Helper()
90	assertion.failWrapper.Fail(description+message, 2+assertion.offset)
91	return false
92}
93
94func vetExtras(extras []interface{}) (bool, string) {
95	for i, extra := range extras {
96		if extra != nil {
97			zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface()
98			if !reflect.DeepEqual(zeroValue, extra) {
99				message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra)
100				return false, message
101			}
102		}
103	}
104	return true, ""
105}
106