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