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