github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/dump/dumper_test.go (about) 1 package dump 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "reflect" 8 "testing" 9 "unsafe" 10 11 "github.com/gookit/color" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 func newBufDumper(buf *bytes.Buffer) *Dumper { 16 return NewDumper(buf, 2) 17 } 18 19 var ( 20 ints1 = []int{1, 2, 3, 4} 21 ints2 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} 22 user = &struct { 23 id string 24 Name string 25 Age int 26 }{"ab12345", "inhere", 22} 27 ) 28 29 func newStd() *Dumper { 30 return NewDumper(os.Stdout, 2) 31 } 32 33 func TestDumper_Fprint(t *testing.T) { 34 buffer := new(bytes.Buffer) 35 dumper := newStd() 36 dumper.WithoutColor() 37 38 dumper.Fprint(buffer, user) 39 str := buffer.String() 40 assert.Contains(t, str, "{ id string; Name string; Age int }") 41 assert.Contains(t, str, `id: string("ab12345"),`) 42 assert.Contains(t, str, `Name: string("inhere"),`) 43 44 dumper.ResetOptions() 45 dumper.Print(user) 46 } 47 48 func TestDump_Basic(t *testing.T) { 49 buffer := new(bytes.Buffer) 50 dumper := NewDumper(buffer, 2) 51 52 dumper.Dump( 53 nil, 54 // bool 55 false, 56 true, 57 // int(X) 58 12, 59 int8(12), 60 int16(12), 61 int32(12), 62 int64(12), 63 // uint(X) 64 uint(12), 65 uint8(12), 66 uint16(12), 67 uint32(12), 68 uint64(12), 69 // float 70 float32(3.1415926), 71 3.1415926, // float64 72 // string 73 "abc1234", 74 'a', // rune 75 byte('a'), 76 ) 77 78 str := buffer.String() 79 str = color.ClearCode(str) // clear color codes. 80 assert.Contains(t, str, "github.com/bingoohuang/gg/pkg/dump.TestDump_Basic(dumper_test.go") 81 assert.Contains(t, str, "float64(3.1415926)") 82 assert.Contains(t, str, `string("abc1234")`) 83 84 // fmt.Println(str) 85 fmt.Println(buffer.String()) 86 } 87 88 func TestDump_Ints(t *testing.T) { 89 buffer := new(bytes.Buffer) 90 dumper := NewDumper(buffer, 2) 91 dumper.WithoutColor() 92 93 // assert.Equal(t, 8, dumper.MoreLenNL) 94 95 dumper.Println(ints1) 96 str := buffer.String() 97 buffer.Reset() 98 assert.Contains(t, str, "[]int [ #len=4") 99 assert.Contains(t, str, "int(1),\n") 100 101 dumper.Print(ints2) 102 str = buffer.String() 103 buffer.Reset() 104 assert.Contains(t, str, "[]int [ #len=11") 105 assert.Contains(t, str, "int(1),\n") 106 assert.NotContains(t, str, "1, 2, 3, 4") 107 assert.NotContains(t, str, "[]int{1, 2, 3, 4}") 108 109 dumper.ResetOptions() 110 dumper.Dump(ints1) 111 dumper.Println(ints2) 112 } 113 114 func TestDump_Ptr(t *testing.T) { 115 buffer := new(bytes.Buffer) 116 dumper := NewDumper(buffer, 2) 117 // dumper.WithoutColor() 118 119 var s string 120 121 // refer string 122 dumper.Print(&s) 123 dumper.Print(s) 124 125 s = "abc23dddd" 126 dumper.Print(&s) 127 dumper.Print(s) 128 129 // refer struct 130 dumper.Println(user) 131 str := buffer.String() 132 str = color.ClearCode(str) 133 assert.Contains(t, str, "&struct") 134 assert.Contains(t, str, "Age: int(22),") 135 assert.Contains(t, str, `id: string("ab12345"),`) 136 assert.Contains(t, str, `Name: string("inhere"),`) 137 138 fmt.Println(buffer.String()) 139 // Output: 140 // *struct { id string; Name string; Age int } { 141 // id: string("ab12345"), 142 // Name: string("inhere"), 143 // Age: int(22), 144 // } 145 } 146 147 // code from https://stackoverflow.com/questions/42664837/how-to-access-unexported-struct-fields-in-golang 148 func TestDumper_AccessCantExportedField(t *testing.T) { 149 type MyStruct struct { 150 // id string 151 id interface{} 152 } 153 154 myStruct := MyStruct{ 155 id: "abc111222", 156 } 157 158 // - 下面的方式适用于: 结构体指针 159 rs := reflect.ValueOf(&myStruct).Elem() 160 rf := rs.Field(0) 161 162 fmt.Println(rf.CanInterface(), rf.String()) 163 P(myStruct) 164 165 // rf can't be read or set. 166 rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem() 167 // Now rf can be read and set. 168 169 fmt.Println(rf.CanInterface(), rf.Interface()) 170 171 // - 下面的方式适用于: 结构体值 172 rs = reflect.ValueOf(myStruct) 173 rs2 := reflect.New(rs.Type()).Elem() 174 rs2.Set(rs) 175 rf = rs2.Field(0) 176 177 fmt.Println(rf.CanInterface(), rf.String()) 178 179 rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem() 180 // Now rf can be read. Setting will succeed but only affects the temporary copy 181 fmt.Println(rf.CanInterface(), rf.String()) 182 } 183 184 // code from https://stackoverflow.com/questions/42664837/how-to-access-unexported-struct-fields-in-golang 185 func TestDumper_AccessCantExportedField1(t *testing.T) { 186 // init an nested struct 187 s1 := st1{st0{2}, 23, "inhere"} 188 myS1 := struct { 189 // cannotExport interface{} // ok 190 cannotExport st1 // ok 191 // CanExport interface{} ok 192 CanExport st1 // ok 193 }{ 194 cannotExport: s1, 195 CanExport: s1, 196 } 197 198 Println(myS1) 199 } 200 201 // ------------------------- map ------------------------- 202 203 func TestDump_Map(t *testing.T) { 204 m4 := map[string]interface{}{ 205 "key1": 12, 206 "key2": "val1", 207 "key3": [][]int{ 208 {23, 34}, 209 {230, 340}, 210 }, 211 "key4": 3.14, 212 "key5": -34, 213 "key6": nil, 214 "key7": []int{23, 34}, 215 "key8": map[string]interface{}{ 216 "key8sub1": []int{23, 34}, 217 "key8sub2": []string{"a", "b"}, 218 }, 219 } 220 Print(m4) 221 } 222 223 func TestMap_Simpled(t *testing.T) { 224 m1 := map[int]int{ 225 23: 12, 226 24: 13, 227 } 228 229 m2 := map[string]int{ 230 "key1": 12, 231 "key2": 13, 232 } 233 234 m3 := map[string]string{ 235 "key1": "val1", 236 "key2": "val2", 237 } 238 P(m1, m2, m3) 239 /* 240 Output: 241 PRINT AT github.com/bingoohuang/gg/pkg/dump.TestMap_Simpled(dump_test.go:309) 242 map[int]int { 243 24: int(13), 244 23: int(12), 245 } 246 map[string]int { 247 key1: int(12), 248 key2: int(13), 249 } 250 map[string]string { 251 key1: string("val1"), 252 key2: string("val2"), 253 } 254 255 */ 256 257 m4 := map[string]interface{}{ 258 "key1": 12, 259 "key2": "val1", 260 "key3": 34, 261 "key4": 3.14, 262 "key5": -34, 263 "key6": nil, 264 } 265 Print(m4) 266 /* 267 PRINT AT github.com/bingoohuang/gg/pkg/dump.TestMap_Simpled(dump_test.go:335) 268 map[string]interface {} { 269 key4: float64(3.14), 270 key5: int(-34), 271 key6: <nil>, 272 key1: int(12), 273 key2: string("val1"), 274 key3: int(34), 275 } 276 */ 277 } 278 279 func TestMap_InterfaceNested(t *testing.T) { 280 s1 := st1{st0{2}, 23, "inhere"} 281 m1 := map[string]interface{}{ 282 "key1": 112, 283 "key2": uint(112), 284 "key3": int64(112), 285 "key4": 112.23, 286 "key5": nil, 287 "key6": 'b', // rune 288 "key7": byte('a'), 289 "st1": s1, 290 "user": user, 291 "submap1": map[string]int{ 292 "key1": 12, 293 "key2": 13, 294 }, 295 "submap2": map[string]interface{}{ 296 "key1": 12, 297 "key2": "abc", 298 "submap21": map[string]string{ 299 "key1": "val1", 300 "key2": "val2", 301 }, 302 }, 303 "submap3": map[string]interface{}{ 304 "key1": 12, 305 "key2": "abc", 306 "submap31": map[string]interface{}{ 307 "key31": 12, 308 "key32": 13, 309 "user": user, 310 "submap311": map[string]int{ 311 "key1": 12, 312 "key2": 13, 313 }, 314 }, 315 }, 316 } 317 318 newStd().WithOptions(func(opts *Options) { 319 opts.IndentChar = '-' 320 }).Dump(m1) 321 } 322 323 var myOpts = struct { 324 opt0 *int 325 opt1 bool 326 opt2 int 327 opt3 float64 328 opt4 string 329 }{nil, true, 22, 34.45, "abc"} 330 331 // ------------------------- map ------------------------- 332 333 type st0 struct { 334 Sex int 335 } 336 337 type st1 struct { 338 st0 339 Age int 340 Name string 341 } 342 343 var s1 = st1{st0{2}, 23, "inhere"} 344 345 func TestDump_Struct(t *testing.T) { 346 } 347 348 func TestStruct_WithNested(t *testing.T) { 349 // buffer := new(bytes.Buffer) 350 dumper := newStd() 351 dumper.IndentChar = '.' 352 dumper.Println(s1) 353 // OUT: 354 // PRINT AT github.com/bingoohuang/gg/pkg/dump.TestStruct_WithNested(dump_test.go:223) 355 // struct { dump.st0; Age int; Name string } { 356 // st0: dump.st0 { 357 // Sex: 2, 358 // }, 359 // Age: 23, 360 // Name: "inhere", 361 // } 362 363 type st2 struct { 364 st1 365 Github string 366 } 367 368 s2 := st2{st1: s1, Github: "https://github.com/inhere"} 369 dumper.IndentChar = ' ' 370 dumper.Print(s2) 371 372 // Output: 373 // PRINT AT github.com/bingoohuang/gg/pkg/dump.TestStruct_WithNested(dump_test.go:257) 374 // dump.st2 { 375 // st1: dump.st1 { 376 // st0: dump.st0 { 377 // Sex: int(2), 378 // }, 379 // Age: int(23), 380 // Name: string("inhere"), 381 // }, 382 // Github: string("https://github.com/inhere"), 383 // } 384 385 s3 := struct { 386 st1 387 Github string 388 }{st1: s1, Github: "https://github.com/inhere"} 389 390 dumper.IndentChar = '.' 391 dumper.Print(s3) 392 393 // Output: 394 // PRINT AT github.com/bingoohuang/gg/pkg/dump.TestStruct_WithNested(dump_test.go:278) 395 // struct { dump.st1; Github string } { 396 // st1: dump.st1 { 397 // st0: dump.st0 { 398 // Sex: int(2), 399 // }, 400 // Age: int(23), 401 // Name: string("inhere"), 402 // }, 403 // Github: string("https://github.com/inhere"), 404 // } 405 }