1// Copyright 2011 Aaron Jacobs. All Rights Reserved.
2// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16package ogletest
17
18import (
19	"fmt"
20	"path"
21	"reflect"
22	"runtime"
23
24	"github.com/smartystreets/assertions/internal/oglematchers"
25)
26
27// ExpectThat confirms that the supplied matcher matches the value x, adding a
28// failure record to the currently running test if it does not. If additional
29// parameters are supplied, the first will be used as a format string for the
30// later ones, and the user-supplied error message will be added to the test
31// output in the event of a failure.
32//
33// For example:
34//
35//     ExpectThat(userName, Equals("jacobsa"))
36//     ExpectThat(users[i], Equals("jacobsa"), "while processing user %d", i)
37//
38func ExpectThat(
39	x interface{},
40	m oglematchers.Matcher,
41	errorParts ...interface{}) {
42	expectThat(x, m, 1, errorParts)
43}
44
45// The generalized form of ExpectThat. depth is the distance on the stack
46// between the caller's frame and the user's frame. Returns passed iff the
47// match succeeded.
48func expectThat(
49	x interface{},
50	m oglematchers.Matcher,
51	depth int,
52	errorParts []interface{}) (passed bool) {
53	// Check whether the value matches. If it does, we are finished.
54	matcherErr := m.Matches(x)
55	if matcherErr == nil {
56		passed = true
57		return
58	}
59
60	var r FailureRecord
61
62	// Get information about the call site.
63	var ok bool
64	if _, r.FileName, r.LineNumber, ok = runtime.Caller(depth + 1); !ok {
65		panic("expectThat: runtime.Caller")
66	}
67
68	r.FileName = path.Base(r.FileName)
69
70	// Create an appropriate failure message. Make sure that the expected and
71	// actual values align properly.
72	relativeClause := ""
73	if matcherErr.Error() != "" {
74		relativeClause = fmt.Sprintf(", %s", matcherErr.Error())
75	}
76
77	r.Error = fmt.Sprintf(
78		"Expected: %s\nActual:   %v%s",
79		m.Description(),
80		x,
81		relativeClause)
82
83	// Add the user error, if any.
84	if len(errorParts) != 0 {
85		v := reflect.ValueOf(errorParts[0])
86		if v.Kind() != reflect.String {
87			panic(fmt.Sprintf("ExpectThat: invalid format string type %v", v.Kind()))
88		}
89
90		r.Error = fmt.Sprintf(
91			"%s\n%s",
92			r.Error,
93			fmt.Sprintf(v.String(), errorParts[1:]...))
94	}
95
96	// Report the failure.
97	AddFailureRecord(r)
98
99	return
100}
101