1/*
2Gomega's format package pretty-prints objects.  It explores input objects recursively and generates formatted, indented output with type information.
3*/
4package format
5
6import (
7	"fmt"
8	"reflect"
9	"strconv"
10	"strings"
11	"time"
12)
13
14// Use MaxDepth to set the maximum recursion depth when printing deeply nested objects
15var MaxDepth = uint(10)
16
17/*
18By default, all objects (even those that implement fmt.Stringer and fmt.GoStringer) are recursively inspected to generate output.
19
20Set UseStringerRepresentation = true to use GoString (for fmt.GoStringers) or String (for fmt.Stringer) instead.
21
22Note that GoString and String don't always have all the information you need to understand why a test failed!
23*/
24var UseStringerRepresentation = false
25
26/*
27Print the content of context objects. By default it will be suppressed.
28
29Set PrintContextObjects = true to enable printing of the context internals.
30*/
31var PrintContextObjects = false
32
33// TruncatedDiff choose if we should display a truncated pretty diff or not
34var TruncatedDiff = true
35
36// Ctx interface defined here to keep backwards compatability with go < 1.7
37// It matches the context.Context interface
38type Ctx interface {
39	Deadline() (deadline time.Time, ok bool)
40	Done() <-chan struct{}
41	Err() error
42	Value(key interface{}) interface{}
43}
44
45var contextType = reflect.TypeOf((*Ctx)(nil)).Elem()
46var timeType = reflect.TypeOf(time.Time{})
47
48//The default indentation string emitted by the format package
49var Indent = "    "
50
51var longFormThreshold = 20
52
53/*
54Generates a formatted matcher success/failure message of the form:
55
56	Expected
57		<pretty printed actual>
58	<message>
59		<pretty printed expected>
60
61If expected is omited, then the message looks like:
62
63	Expected
64		<pretty printed actual>
65	<message>
66*/
67func Message(actual interface{}, message string, expected ...interface{}) string {
68	if len(expected) == 0 {
69		return fmt.Sprintf("Expected\n%s\n%s", Object(actual, 1), message)
70	}
71	return fmt.Sprintf("Expected\n%s\n%s\n%s", Object(actual, 1), message, Object(expected[0], 1))
72}
73
74/*
75
76Generates a nicely formatted matcher success / failure message
77
78Much like Message(...), but it attempts to pretty print diffs in strings
79
80Expected
81    <string>: "...aaaaabaaaaa..."
82to equal               |
83    <string>: "...aaaaazaaaaa..."
84
85*/
86
87func MessageWithDiff(actual, message, expected string) string {
88	if TruncatedDiff && len(actual) >= truncateThreshold && len(expected) >= truncateThreshold {
89		diffPoint := findFirstMismatch(actual, expected)
90		formattedActual := truncateAndFormat(actual, diffPoint)
91		formattedExpected := truncateAndFormat(expected, diffPoint)
92
93		spacesBeforeFormattedMismatch := findFirstMismatch(formattedActual, formattedExpected)
94
95		tabLength := 4
96		spaceFromMessageToActual := tabLength + len("<string>: ") - len(message)
97		padding := strings.Repeat(" ", spaceFromMessageToActual+spacesBeforeFormattedMismatch) + "|"
98		return Message(formattedActual, message+padding, formattedExpected)
99	}
100	return Message(actual, message, expected)
101}
102
103func truncateAndFormat(str string, index int) string {
104	leftPadding := `...`
105	rightPadding := `...`
106
107	start := index - charactersAroundMismatchToInclude
108	if start < 0 {
109		start = 0
110		leftPadding = ""
111	}
112
113	// slice index must include the mis-matched character
114	lengthOfMismatchedCharacter := 1
115	end := index + charactersAroundMismatchToInclude + lengthOfMismatchedCharacter
116	if end > len(str) {
117		end = len(str)
118		rightPadding = ""
119
120	}
121	return fmt.Sprintf("\"%s\"", leftPadding+str[start:end]+rightPadding)
122}
123
124func findFirstMismatch(a, b string) int {
125	aSlice := strings.Split(a, "")
126	bSlice := strings.Split(b, "")
127
128	for index, str := range aSlice {
129		if index > len(bSlice)-1 {
130			return index
131		}
132		if str != bSlice[index] {
133			return index
134		}
135	}
136
137	if len(b) > len(a) {
138		return len(a) + 1
139	}
140
141	return 0
142}
143
144const (
145	truncateThreshold                 = 50
146	charactersAroundMismatchToInclude = 5
147)
148
149/*
150Pretty prints the passed in object at the passed in indentation level.
151
152Object recurses into deeply nested objects emitting pretty-printed representations of their components.
153
154Modify format.MaxDepth to control how deep the recursion is allowed to go
155Set format.UseStringerRepresentation to true to return object.GoString() or object.String() when available instead of
156recursing into the object.
157
158Set PrintContextObjects to true to print the content of objects implementing context.Context
159*/
160func Object(object interface{}, indentation uint) string {
161	indent := strings.Repeat(Indent, int(indentation))
162	value := reflect.ValueOf(object)
163	return fmt.Sprintf("%s<%s>: %s", indent, formatType(object), formatValue(value, indentation))
164}
165
166/*
167IndentString takes a string and indents each line by the specified amount.
168*/
169func IndentString(s string, indentation uint) string {
170	components := strings.Split(s, "\n")
171	result := ""
172	indent := strings.Repeat(Indent, int(indentation))
173	for i, component := range components {
174		result += indent + component
175		if i < len(components)-1 {
176			result += "\n"
177		}
178	}
179
180	return result
181}
182
183func formatType(object interface{}) string {
184	t := reflect.TypeOf(object)
185	if t == nil {
186		return "nil"
187	}
188	switch t.Kind() {
189	case reflect.Chan:
190		v := reflect.ValueOf(object)
191		return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap())
192	case reflect.Ptr:
193		return fmt.Sprintf("%T | %p", object, object)
194	case reflect.Slice:
195		v := reflect.ValueOf(object)
196		return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap())
197	case reflect.Map:
198		v := reflect.ValueOf(object)
199		return fmt.Sprintf("%T | len:%d", object, v.Len())
200	default:
201		return fmt.Sprintf("%T", object)
202	}
203}
204
205func formatValue(value reflect.Value, indentation uint) string {
206	if indentation > MaxDepth {
207		return "..."
208	}
209
210	if isNilValue(value) {
211		return "nil"
212	}
213
214	if UseStringerRepresentation {
215		if value.CanInterface() {
216			obj := value.Interface()
217			switch x := obj.(type) {
218			case fmt.GoStringer:
219				return x.GoString()
220			case fmt.Stringer:
221				return x.String()
222			}
223		}
224	}
225
226	if !PrintContextObjects {
227		if value.Type().Implements(contextType) && indentation > 1 {
228			return "<suppressed context>"
229		}
230	}
231
232	switch value.Kind() {
233	case reflect.Bool:
234		return fmt.Sprintf("%v", value.Bool())
235	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
236		return fmt.Sprintf("%v", value.Int())
237	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
238		return fmt.Sprintf("%v", value.Uint())
239	case reflect.Uintptr:
240		return fmt.Sprintf("0x%x", value.Uint())
241	case reflect.Float32, reflect.Float64:
242		return fmt.Sprintf("%v", value.Float())
243	case reflect.Complex64, reflect.Complex128:
244		return fmt.Sprintf("%v", value.Complex())
245	case reflect.Chan:
246		return fmt.Sprintf("0x%x", value.Pointer())
247	case reflect.Func:
248		return fmt.Sprintf("0x%x", value.Pointer())
249	case reflect.Ptr:
250		return formatValue(value.Elem(), indentation)
251	case reflect.Slice:
252		return formatSlice(value, indentation)
253	case reflect.String:
254		return formatString(value.String(), indentation)
255	case reflect.Array:
256		return formatSlice(value, indentation)
257	case reflect.Map:
258		return formatMap(value, indentation)
259	case reflect.Struct:
260		if value.Type() == timeType && value.CanInterface() {
261			t, _ := value.Interface().(time.Time)
262			return t.Format(time.RFC3339Nano)
263		}
264		return formatStruct(value, indentation)
265	case reflect.Interface:
266		return formatValue(value.Elem(), indentation)
267	default:
268		if value.CanInterface() {
269			return fmt.Sprintf("%#v", value.Interface())
270		}
271		return fmt.Sprintf("%#v", value)
272	}
273}
274
275func formatString(object interface{}, indentation uint) string {
276	if indentation == 1 {
277		s := fmt.Sprintf("%s", object)
278		components := strings.Split(s, "\n")
279		result := ""
280		for i, component := range components {
281			if i == 0 {
282				result += component
283			} else {
284				result += Indent + component
285			}
286			if i < len(components)-1 {
287				result += "\n"
288			}
289		}
290
291		return fmt.Sprintf("%s", result)
292	} else {
293		return fmt.Sprintf("%q", object)
294	}
295}
296
297func formatSlice(v reflect.Value, indentation uint) string {
298	if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 && isPrintableString(string(v.Bytes())) {
299		return formatString(v.Bytes(), indentation)
300	}
301
302	l := v.Len()
303	result := make([]string, l)
304	longest := 0
305	for i := 0; i < l; i++ {
306		result[i] = formatValue(v.Index(i), indentation+1)
307		if len(result[i]) > longest {
308			longest = len(result[i])
309		}
310	}
311
312	if longest > longFormThreshold {
313		indenter := strings.Repeat(Indent, int(indentation))
314		return fmt.Sprintf("[\n%s%s,\n%s]", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
315	}
316	return fmt.Sprintf("[%s]", strings.Join(result, ", "))
317}
318
319func formatMap(v reflect.Value, indentation uint) string {
320	l := v.Len()
321	result := make([]string, l)
322
323	longest := 0
324	for i, key := range v.MapKeys() {
325		value := v.MapIndex(key)
326		result[i] = fmt.Sprintf("%s: %s", formatValue(key, indentation+1), formatValue(value, indentation+1))
327		if len(result[i]) > longest {
328			longest = len(result[i])
329		}
330	}
331
332	if longest > longFormThreshold {
333		indenter := strings.Repeat(Indent, int(indentation))
334		return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
335	}
336	return fmt.Sprintf("{%s}", strings.Join(result, ", "))
337}
338
339func formatStruct(v reflect.Value, indentation uint) string {
340	t := v.Type()
341
342	l := v.NumField()
343	result := []string{}
344	longest := 0
345	for i := 0; i < l; i++ {
346		structField := t.Field(i)
347		fieldEntry := v.Field(i)
348		representation := fmt.Sprintf("%s: %s", structField.Name, formatValue(fieldEntry, indentation+1))
349		result = append(result, representation)
350		if len(representation) > longest {
351			longest = len(representation)
352		}
353	}
354	if longest > longFormThreshold {
355		indenter := strings.Repeat(Indent, int(indentation))
356		return fmt.Sprintf("{\n%s%s,\n%s}", indenter+Indent, strings.Join(result, ",\n"+indenter+Indent), indenter)
357	}
358	return fmt.Sprintf("{%s}", strings.Join(result, ", "))
359}
360
361func isNilValue(a reflect.Value) bool {
362	switch a.Kind() {
363	case reflect.Invalid:
364		return true
365	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
366		return a.IsNil()
367	}
368
369	return false
370}
371
372/*
373Returns true when the string is entirely made of printable runes, false otherwise.
374*/
375func isPrintableString(str string) bool {
376	for _, runeValue := range str {
377		if !strconv.IsPrint(runeValue) {
378			return false
379		}
380	}
381	return true
382}
383