github.com/ecadlabs/pretty@v0.0.0-20230412123216-0f3d25fb750b/formatter_test.go (about) 1 package pretty 2 3 import ( 4 "fmt" 5 "io" 6 "strings" 7 "testing" 8 "time" 9 "unsafe" 10 ) 11 12 type test struct { 13 v interface{} 14 s string 15 opt []Option 16 } 17 18 type passtest struct { 19 v interface{} 20 f, s string 21 } 22 23 type LongStructTypeName struct { 24 longFieldName interface{} 25 otherLongFieldName interface{} 26 } 27 28 type SA struct { 29 t *T 30 v T 31 } 32 33 type T struct { 34 x, y int 35 } 36 37 type F int 38 39 func (f F) Format(s fmt.State, c rune) { 40 fmt.Fprintf(s, "F(%d)", int(f)) 41 } 42 43 type Stringer struct { 44 //lint:ignore U1000 test only 45 i int 46 } 47 48 func (s *Stringer) String() string { return "foo" } 49 50 var long = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 51 52 var passthrough = []passtest{ 53 {1, "%d", "1"}, 54 {"a", "%s", "a"}, 55 {&Stringer{}, "%s", "foo"}, 56 } 57 58 func TestPassthrough(t *testing.T) { 59 for _, tt := range passthrough { 60 s := fmt.Sprintf(tt.f, Formatter(tt.v)) 61 if tt.s != s { 62 t.Errorf("expected %q", tt.s) 63 t.Errorf("got %q", s) 64 t.Errorf("expraw\n%s", tt.s) 65 t.Errorf("gotraw\n%s", s) 66 } 67 } 68 } 69 70 type StructWithPrivateFields struct { 71 A string 72 b string 73 } 74 75 func NewStructWithPrivateFields(a string) StructWithPrivateFields { 76 return StructWithPrivateFields{a, "fixedb"} 77 } 78 79 func (s StructWithPrivateFields) GoString() string { 80 return fmt.Sprintf("NewStructWithPrivateFields(%q)", s.A) 81 } 82 83 var gosyntax = []test{ 84 {nil, `nil`, nil}, 85 {"", `""`, nil}, 86 {"a", `"a"`, nil}, 87 {1, "int(1)", nil}, 88 {1.0, "float64(1)", nil}, 89 {[]int(nil), "[]int(nil)", nil}, 90 {[0]int{}, "[0]int{}", nil}, 91 {complex(1, 0), "(1+0i)", nil}, 92 //{make(chan int), "(chan int)(0x1234)"}, 93 {unsafe.Pointer(uintptr(unsafe.Pointer(&long))), fmt.Sprintf("unsafe.Pointer(0x%02x)", uintptr(unsafe.Pointer(&long))), nil}, 94 {func(int) {}, "func(int) {...}", nil}, 95 {map[string]string{"a": "a", "b": "b"}, "map[string]string{\"a\":\"a\", \"b\":\"b\"}", nil}, 96 {map[int]int{1: 1}, "map[int]int{1:1}", nil}, 97 {int32(1), "int32(1)", nil}, 98 {io.EOF, `&errors.errorString{s:"EOF"}`, nil}, 99 {[]string{"a"}, `[]string{"a"}`, nil}, 100 { 101 []string{long}, 102 `[]string{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}`, 103 nil, 104 }, 105 {F(5), "pretty.F(5)", nil}, 106 {NewStructWithPrivateFields("foo"), `NewStructWithPrivateFields("foo")`, nil}, 107 { 108 SA{&T{1, 2}, T{3, 4}}, 109 `pretty.SA{ 110 t: &pretty.T{x:1, y:2}, 111 v: pretty.T{x:3, y:4}, 112 }`, 113 nil, 114 }, 115 { 116 map[int][]byte{1: {}}, 117 `map[int][]uint8{ 118 1: {}, 119 }`, 120 nil, 121 }, 122 { 123 map[int]T{1: {}}, 124 `map[int]pretty.T{ 125 1: {}, 126 }`, 127 nil, 128 }, 129 { 130 long, 131 `"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"`, 132 nil, 133 }, 134 { 135 LongStructTypeName{ 136 longFieldName: LongStructTypeName{}, 137 otherLongFieldName: long, 138 }, 139 `pretty.LongStructTypeName{ 140 longFieldName: pretty.LongStructTypeName{}, 141 otherLongFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 142 }`, 143 nil, 144 }, 145 { 146 &LongStructTypeName{ 147 longFieldName: &LongStructTypeName{}, 148 otherLongFieldName: (*LongStructTypeName)(nil), 149 }, 150 `&pretty.LongStructTypeName{ 151 longFieldName: &pretty.LongStructTypeName{}, 152 otherLongFieldName: (*pretty.LongStructTypeName)(nil), 153 }`, 154 nil, 155 }, 156 { 157 []LongStructTypeName{ 158 {nil, nil}, 159 {3, 3}, 160 {long, nil}, 161 }, 162 `[]pretty.LongStructTypeName{ 163 {}, 164 { 165 longFieldName: int(3), 166 otherLongFieldName: int(3), 167 }, 168 { 169 longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 170 otherLongFieldName: nil, 171 }, 172 }`, 173 nil, 174 }, 175 { 176 []interface{}{ 177 LongStructTypeName{nil, nil}, 178 []byte{1, 2, 3}, 179 T{3, 4}, 180 LongStructTypeName{long, nil}, 181 }, 182 `[]interface {}{ 183 pretty.LongStructTypeName{}, 184 []uint8{0x1, 0x2, 0x3}, 185 pretty.T{x:3, y:4}, 186 pretty.LongStructTypeName{ 187 longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 188 otherLongFieldName: nil, 189 }, 190 }`, 191 nil, 192 }, 193 {(*time.Time)(nil), "(*time.Time)(nil)", nil}, 194 {&ValueGoString{"vgs"}, `VGS vgs`, nil}, 195 {&ValueGoString{"vgs"}, `&pretty.ValueGoString{s:"vgs"}`, []Option{OptGoStringer(false)}}, 196 {&ValueGoString{"vgs"}, `"VS vgs"`, []Option{OptGoStringer(false), OptStringer(true)}}, 197 {&ValueGoString{"vgs"}, `"VT vgs"`, []Option{OptGoStringer(false), OptTextMarshaler(true)}}, 198 {(*ValueGoString)(nil), `(*pretty.ValueGoString)(nil)`, nil}, 199 {(*VGSWrapper)(nil), `(*pretty.VGSWrapper)(nil)`, nil}, 200 {&PointerGoString{"pgs"}, `PGS pgs`, nil}, 201 {(*PointerGoString)(nil), "(*pretty.PointerGoString)(nil)", nil}, 202 {&PanicGoString{"oops!"}, `(*pretty.PanicGoString)(PANIC=calling method "GoString": oops!)`, nil}, 203 } 204 205 type ValueGoString struct { 206 s string 207 } 208 209 func (g ValueGoString) GoString() string { 210 return "VGS " + g.s 211 } 212 213 func (g ValueGoString) String() string { 214 return "VS " + g.s 215 } 216 217 func (g ValueGoString) MarshalText() (text []byte, err error) { 218 return []byte("VT " + g.s), nil 219 } 220 221 type VGSWrapper struct { 222 ValueGoString 223 } 224 225 type PointerGoString struct { 226 s string 227 } 228 229 func (g *PointerGoString) GoString() string { 230 return "PGS " + g.s 231 } 232 233 type PanicGoString struct { 234 s string 235 } 236 237 func (g *PanicGoString) GoString() string { 238 panic(g.s) 239 } 240 241 func TestGoSyntax(t *testing.T) { 242 for _, tt := range gosyntax { 243 s := fmt.Sprintf("%# v", Formatter(tt.v, tt.opt...)) 244 if tt.s != s { 245 t.Errorf("expected %q", tt.s) 246 t.Errorf("got %q", s) 247 t.Errorf("expraw\n%s", tt.s) 248 t.Errorf("gotraw\n%s", s) 249 } 250 } 251 } 252 253 type I struct { 254 i int 255 R interface{} 256 } 257 258 func (i *I) I() *I { return i.R.(*I) } 259 260 func TestCycle(t *testing.T) { 261 type A struct{ *A } 262 v := &A{} 263 v.A = v 264 265 // panics from stack overflow without cycle detection 266 t.Logf("Example cycle:\n%# v", Formatter(v)) 267 268 p := &A{} 269 s := fmt.Sprintf("%# v", Formatter([]*A{p, p})) 270 if strings.Contains(s, "CYCLIC") { 271 t.Errorf("Repeated address detected as cyclic reference:\n%s", s) 272 } 273 274 type R struct { 275 i int 276 *R 277 } 278 r := &R{ 279 i: 1, 280 R: &R{ 281 i: 2, 282 R: &R{ 283 i: 3, 284 }, 285 }, 286 } 287 r.R.R.R = r 288 t.Logf("Example longer cycle:\n%# v", Formatter(r)) 289 290 r = &R{ 291 i: 1, 292 R: &R{ 293 i: 2, 294 R: &R{ 295 i: 3, 296 R: &R{ 297 i: 4, 298 R: &R{ 299 i: 5, 300 R: &R{ 301 i: 6, 302 R: &R{ 303 i: 7, 304 R: &R{ 305 i: 8, 306 R: &R{ 307 i: 9, 308 R: &R{ 309 i: 10, 310 R: &R{ 311 i: 11, 312 }, 313 }, 314 }, 315 }, 316 }, 317 }, 318 }, 319 }, 320 }, 321 }, 322 } 323 // here be pirates 324 r.R.R.R.R.R.R.R.R.R.R.R = r 325 t.Logf("Example very long cycle:\n%# v", Formatter(r)) 326 327 i := &I{ 328 i: 1, 329 R: &I{ 330 i: 2, 331 R: &I{ 332 i: 3, 333 R: &I{ 334 i: 4, 335 R: &I{ 336 i: 5, 337 R: &I{ 338 i: 6, 339 R: &I{ 340 i: 7, 341 R: &I{ 342 i: 8, 343 R: &I{ 344 i: 9, 345 R: &I{ 346 i: 10, 347 R: &I{ 348 i: 11, 349 }, 350 }, 351 }, 352 }, 353 }, 354 }, 355 }, 356 }, 357 }, 358 }, 359 } 360 iv := i.I().I().I().I().I().I().I().I().I().I() 361 *iv = *i 362 t.Logf("Example long interface cycle:\n%# v", Formatter(i)) 363 }