1package imported_tests 2 3// Those tests were imported directly from go-toml v1 4// https://raw.githubusercontent.com/pelletier/go-toml/a2e52561804c6cd9392ebf0048ca64fe4af67a43/marshal_test.go 5// They have been cleaned up to only include Unmarshal tests, and only depend 6// on the public API. Tests related to strict mode have been commented out and 7// marked as skipped until we figure out if that's something we want in v2. 8 9import ( 10 "bytes" 11 "errors" 12 "fmt" 13 "reflect" 14 "strconv" 15 "testing" 16 "time" 17 18 "github.com/pelletier/go-toml/v2" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21) 22 23type basicMarshalTestStruct struct { 24 String string `toml:"Zstring"` 25 StringList []string `toml:"Ystrlist"` 26 BasicMarshalTestSubAnonymousStruct 27 Sub basicMarshalTestSubStruct `toml:"Xsubdoc"` 28 SubList []basicMarshalTestSubStruct `toml:"Wsublist"` 29} 30 31type basicMarshalTestSubStruct struct { 32 String2 string 33} 34 35type BasicMarshalTestSubAnonymousStruct struct { 36 String3 string 37} 38 39var basicTestData = basicMarshalTestStruct{ 40 String: "Hello", 41 StringList: []string{"Howdy", "Hey There"}, 42 BasicMarshalTestSubAnonymousStruct: BasicMarshalTestSubAnonymousStruct{"One"}, 43 Sub: basicMarshalTestSubStruct{"Two"}, 44 SubList: []basicMarshalTestSubStruct{{"Three"}, {"Four"}}, 45} 46 47var basicTestToml = []byte(`String3 = "One" 48Ystrlist = ["Howdy", "Hey There"] 49Zstring = "Hello" 50 51[[Wsublist]] 52 String2 = "Three" 53 54[[Wsublist]] 55 String2 = "Four" 56 57[Xsubdoc] 58 String2 = "Two" 59`) 60 61var marshalTestToml = []byte(`title = "TOML Marshal Testing" 62 63[basic] 64 bool = true 65 date = 1979-05-27T07:32:00Z 66 float = 123.4 67 float64 = 123.456782132399 68 int = 5000 69 string = "Bite me" 70 uint = 5001 71 72[basic_lists] 73 bools = [true, false, true] 74 dates = [1979-05-27T07:32:00Z, 1980-05-27T07:32:00Z] 75 floats = [12.3, 45.6, 78.9] 76 ints = [8001, 8001, 8002] 77 strings = ["One", "Two", "Three"] 78 uints = [5002, 5003] 79 80[basic_map] 81 one = "one" 82 two = "two" 83 84[subdoc] 85 86 [subdoc.first] 87 name = "First" 88 89 [subdoc.second] 90 name = "Second" 91 92[[subdoclist]] 93 name = "List.First" 94 95[[subdoclist]] 96 name = "List.Second" 97 98[[subdocptrs]] 99 name = "Second" 100`) 101 102type Conf struct { 103 Name string 104 Age int 105 Inter interface{} 106} 107 108type NestedStruct struct { 109 FirstName string 110 LastName string 111 Age int 112} 113 114var doc = []byte(`Name = "rui" 115Age = 18 116 117[Inter] 118 FirstName = "wang" 119 LastName = "jl" 120 Age = 100`) 121 122func TestInterface(t *testing.T) { 123 var config Conf 124 config.Inter = &NestedStruct{} 125 err := toml.Unmarshal(doc, &config) 126 require.NoError(t, err) 127 expected := Conf{ 128 Name: "rui", 129 Age: 18, 130 Inter: map[string]interface{}{ 131 "FirstName": "wang", 132 "LastName": "jl", 133 "Age": int64(100), 134 }, 135 } 136 assert.Equal(t, expected, config) 137} 138 139func TestBasicUnmarshal(t *testing.T) { 140 result := basicMarshalTestStruct{} 141 err := toml.Unmarshal(basicTestToml, &result) 142 require.NoError(t, err) 143 require.Equal(t, basicTestData, result) 144} 145 146type quotedKeyMarshalTestStruct struct { 147 String string `toml:"Z.string-àéù"` 148 Float float64 `toml:"Yfloat-"` 149 Sub basicMarshalTestSubStruct `toml:"Xsubdoc-àéù"` 150 SubList []basicMarshalTestSubStruct `toml:"W.sublist-"` 151} 152 153// TODO: Remove nolint once var is used by a test 154//nolint:deadcode,unused,varcheck 155var quotedKeyMarshalTestData = quotedKeyMarshalTestStruct{ 156 String: "Hello", 157 Float: 3.5, 158 Sub: basicMarshalTestSubStruct{"One"}, 159 SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}}, 160} 161 162// TODO: Remove nolint once var is used by a test 163//nolint:deadcode,unused,varcheck 164var quotedKeyMarshalTestToml = []byte(`"Yfloat-" = 3.5 165"Z.string-àéù" = "Hello" 166 167[["W.sublist-"]] 168 String2 = "Two" 169 170[["W.sublist-"]] 171 String2 = "Three" 172 173["Xsubdoc-àéù"] 174 String2 = "One" 175`) 176 177type testDoc struct { 178 Title string `toml:"title"` 179 BasicLists testDocBasicLists `toml:"basic_lists"` 180 SubDocPtrs []*testSubDoc `toml:"subdocptrs"` 181 BasicMap map[string]string `toml:"basic_map"` 182 Subdocs testDocSubs `toml:"subdoc"` 183 Basics testDocBasics `toml:"basic"` 184 SubDocList []testSubDoc `toml:"subdoclist"` 185 err int `toml:"shouldntBeHere"` // nolint:structcheck,unused 186 unexported int `toml:"shouldntBeHere"` 187 Unexported2 int `toml:"-"` 188} 189 190type testMapDoc struct { 191 Title string `toml:"title"` 192 BasicMap map[string]string `toml:"basic_map"` 193 LongMap map[string]string `toml:"long_map"` 194} 195 196type testDocBasics struct { 197 Uint uint `toml:"uint"` 198 Bool bool `toml:"bool"` 199 Float32 float32 `toml:"float"` 200 Float64 float64 `toml:"float64"` 201 Int int `toml:"int"` 202 String *string `toml:"string"` 203 Date time.Time `toml:"date"` 204 unexported int `toml:"shouldntBeHere"` 205} 206 207type testDocBasicLists struct { 208 Floats []*float32 `toml:"floats"` 209 Bools []bool `toml:"bools"` 210 Dates []time.Time `toml:"dates"` 211 Ints []int `toml:"ints"` 212 UInts []uint `toml:"uints"` 213 Strings []string `toml:"strings"` 214} 215 216type testDocSubs struct { 217 Second *testSubDoc `toml:"second"` 218 First testSubDoc `toml:"first"` 219} 220 221type testSubDoc struct { 222 Name string `toml:"name"` 223 unexported int `toml:"shouldntBeHere"` 224} 225 226var ( 227 biteMe = "Bite me" 228 float1 float32 = 12.3 229 float2 float32 = 45.6 230 float3 float32 = 78.9 231 subdoc = testSubDoc{"Second", 0} 232) 233 234var docData = testDoc{ 235 Title: "TOML Marshal Testing", 236 unexported: 0, 237 Unexported2: 0, 238 Basics: testDocBasics{ 239 Bool: true, 240 Date: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC), 241 Float32: 123.4, 242 Float64: 123.456782132399, 243 Int: 5000, 244 Uint: 5001, 245 String: &biteMe, 246 unexported: 0, 247 }, 248 BasicLists: testDocBasicLists{ 249 Bools: []bool{true, false, true}, 250 Dates: []time.Time{ 251 time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC), 252 time.Date(1980, 5, 27, 7, 32, 0, 0, time.UTC), 253 }, 254 Floats: []*float32{&float1, &float2, &float3}, 255 Ints: []int{8001, 8001, 8002}, 256 Strings: []string{"One", "Two", "Three"}, 257 UInts: []uint{5002, 5003}, 258 }, 259 BasicMap: map[string]string{ 260 "one": "one", 261 "two": "two", 262 }, 263 Subdocs: testDocSubs{ 264 First: testSubDoc{"First", 0}, 265 Second: &subdoc, 266 }, 267 SubDocList: []testSubDoc{ 268 {"List.First", 0}, 269 {"List.Second", 0}, 270 }, 271 SubDocPtrs: []*testSubDoc{&subdoc}, 272} 273 274// TODO: Remove nolint once var is used by a test 275//nolint:deadcode,unused,varcheck 276var mapTestDoc = testMapDoc{ 277 Title: "TOML Marshal Testing", 278 BasicMap: map[string]string{ 279 "one": "one", 280 "two": "two", 281 }, 282 LongMap: map[string]string{ 283 "h1": "8", 284 "i2": "9", 285 "b3": "2", 286 "d4": "4", 287 "f5": "6", 288 "e6": "5", 289 "a7": "1", 290 "c8": "3", 291 "j9": "10", 292 "g10": "7", 293 }, 294} 295 296func TestDocUnmarshal(t *testing.T) { 297 result := testDoc{} 298 err := toml.Unmarshal(marshalTestToml, &result) 299 expected := docData 300 require.NoError(t, err) 301 assert.Equal(t, expected, result) 302} 303 304type unexportedMarshalTestStruct struct { 305 String string `toml:"string"` 306 StringList []string `toml:"strlist"` 307 Sub basicMarshalTestSubStruct `toml:"subdoc"` 308 SubList []basicMarshalTestSubStruct `toml:"sublist"` 309 unexported int `toml:"shouldntBeHere"` 310 Unexported2 int `toml:"-"` 311} 312 313var unexportedTestData = unexportedMarshalTestStruct{ 314 String: "Hello", 315 StringList: []string{"Howdy", "Hey There"}, 316 Sub: basicMarshalTestSubStruct{"One"}, 317 SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}}, 318 unexported: 0, 319 Unexported2: 0, 320} 321 322var unexportedTestToml = []byte(`string = "Hello" 323strlist = ["Howdy","Hey There"] 324unexported = 1 325shouldntBeHere = 2 326 327[subdoc] 328 String2 = "One" 329 330[[sublist]] 331 String2 = "Two" 332 333[[sublist]] 334 String2 = "Three" 335`) 336 337func TestUnexportedUnmarshal(t *testing.T) { 338 result := unexportedMarshalTestStruct{} 339 err := toml.Unmarshal(unexportedTestToml, &result) 340 require.NoError(t, err) 341 assert.Equal(t, unexportedTestData, result) 342} 343 344type errStruct struct { 345 Bool bool `toml:"bool"` 346 Date time.Time `toml:"date"` 347 Float float64 `toml:"float"` 348 Int int16 `toml:"int"` 349 String *string `toml:"string"` 350} 351 352type mapErr struct { 353 Vals map[string]float64 354} 355 356type intErr struct { 357 Int1 int 358 Int2 int8 359 Int3 int16 360 Int4 int32 361 Int5 int64 362 UInt1 uint 363 UInt2 uint8 364 UInt3 uint16 365 UInt4 uint32 366 UInt5 uint64 367 Flt1 float32 368 Flt2 float64 369} 370 371var intErrTomls = []string{ 372 "Int1 = []\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", 373 "Int1 = 1\nInt2 = []\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", 374 "Int1 = 1\nInt2 = 2\nInt3 = []\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", 375 "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = []\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", 376 "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = []\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", 377 "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = []\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", 378 "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = []\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", 379 "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = []\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", 380 "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = []\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", 381 "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = []\nFlt1 = 1.0\nFlt2 = 2.0", 382 "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = []\nFlt2 = 2.0", 383 "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = []", 384} 385 386func TestErrUnmarshal(t *testing.T) { 387 errTomls := []string{ 388 "bool = truly\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", 389 "bool = true\ndate = 1979-05-27T07:3200Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", 390 "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123a4\nint = 5000\nstring = \"Bite me\"", 391 "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = j000\nstring = \"Bite me\"", 392 "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me", 393 "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me", 394 "bool = 1\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", 395 "bool = true\ndate = 1\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", 396 "bool = true\ndate = 1979-05-27T07:32:00Z\n\"sorry\"\nint = 5000\nstring = \"Bite me\"", 397 "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = \"sorry\"\nstring = \"Bite me\"", 398 "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = 1", 399 } 400 401 for ind, x := range errTomls { 402 t.Run(fmt.Sprintf("Base Case %d", ind), func(t *testing.T) { 403 result := errStruct{} 404 err := toml.Unmarshal([]byte(x), &result) 405 if err == nil { 406 t.Errorf("Expected err from case %d\n", ind) 407 } 408 }) 409 } 410 result2 := mapErr{} 411 err := toml.Unmarshal([]byte("[Vals]\nfred=\"1.2\""), &result2) 412 if err == nil { 413 t.Errorf("Expected err from map") 414 } 415 for ind, x := range intErrTomls { 416 result3 := intErr{} 417 err := toml.Unmarshal([]byte(x), &result3) 418 if err == nil { 419 t.Errorf("Expected int err from case %d\n", ind) 420 } 421 } 422} 423 424var emptyTestToml = []byte(`bool = false 425int = 0 426string = "" 427stringlist = [] 428title = "Placeholder" 429 430[map] 431`) 432 433func TestEmptytomlUnmarshal(t *testing.T) { 434 type emptyMarshalTestStruct struct { 435 Title string `toml:"title"` 436 Bool bool `toml:"bool"` 437 Int int `toml:"int"` 438 String string `toml:"string"` 439 StringList []string `toml:"stringlist"` 440 Ptr *basicMarshalTestStruct `toml:"ptr"` 441 Map map[string]string `toml:"map"` 442 } 443 444 emptyTestData := emptyMarshalTestStruct{ 445 Title: "Placeholder", 446 Bool: false, 447 Int: 0, 448 String: "", 449 StringList: []string{}, 450 Ptr: nil, 451 Map: nil, 452 } 453 454 result := emptyMarshalTestStruct{} 455 err := toml.Unmarshal(emptyTestToml, &result) 456 require.NoError(t, err) 457 assert.Equal(t, emptyTestData, result) 458} 459 460func TestEmptyUnmarshalOmit(t *testing.T) { 461 t.Skipf("Have not figured yet if omitempty is a good idea") 462 463 type emptyMarshalTestStruct2 struct { 464 Title string `toml:"title"` 465 Bool bool `toml:"bool,omitempty"` 466 Int int `toml:"int, omitempty"` 467 String string `toml:"string,omitempty "` 468 StringList []string `toml:"stringlist,omitempty"` 469 Ptr *basicMarshalTestStruct `toml:"ptr,omitempty"` 470 Map map[string]string `toml:"map,omitempty"` 471 } 472 473 emptyTestData2 := emptyMarshalTestStruct2{ 474 Title: "Placeholder", 475 Bool: false, 476 Int: 0, 477 String: "", 478 StringList: []string{}, 479 Ptr: nil, 480 Map: map[string]string{}, 481 } 482 483 result := emptyMarshalTestStruct2{} 484 err := toml.Unmarshal(emptyTestToml, &result) 485 require.NoError(t, err) 486 assert.Equal(t, emptyTestData2, result) 487} 488 489type pointerMarshalTestStruct struct { 490 Str *string 491 List *[]string 492 ListPtr *[]*string 493 Map *map[string]string 494 MapPtr *map[string]*string 495 EmptyStr *string 496 EmptyList *[]string 497 EmptyMap *map[string]string 498 DblPtr *[]*[]*string 499} 500 501var ( 502 pointerStr = "Hello" 503 pointerList = []string{"Hello back"} 504 pointerListPtr = []*string{&pointerStr} 505 pointerMap = map[string]string{"response": "Goodbye"} 506 pointerMapPtr = map[string]*string{"alternate": &pointerStr} 507 pointerTestData = pointerMarshalTestStruct{ 508 Str: &pointerStr, 509 List: &pointerList, 510 ListPtr: &pointerListPtr, 511 Map: &pointerMap, 512 MapPtr: &pointerMapPtr, 513 EmptyStr: nil, 514 EmptyList: nil, 515 EmptyMap: nil, 516 } 517) 518 519var pointerTestToml = []byte(`List = ["Hello back"] 520ListPtr = ["Hello"] 521Str = "Hello" 522 523[Map] 524 response = "Goodbye" 525 526[MapPtr] 527 alternate = "Hello" 528`) 529 530func TestPointerUnmarshal(t *testing.T) { 531 result := pointerMarshalTestStruct{} 532 err := toml.Unmarshal(pointerTestToml, &result) 533 require.NoError(t, err) 534 assert.Equal(t, pointerTestData, result) 535} 536 537func TestUnmarshalTypeMismatch(t *testing.T) { 538 result := pointerMarshalTestStruct{} 539 err := toml.Unmarshal([]byte("List = 123"), &result) 540 assert.Error(t, err) 541} 542 543type nestedMarshalTestStruct struct { 544 String [][]string 545 // Struct [][]basicMarshalTestSubStruct 546 StringPtr *[]*[]*string 547 // StructPtr *[]*[]*basicMarshalTestSubStruct 548} 549 550var ( 551 str1 = "Three" 552 str2 = "Four" 553 strPtr = []*string{&str1, &str2} 554 strPtr2 = []*[]*string{&strPtr} 555) 556 557var nestedTestData = nestedMarshalTestStruct{ 558 String: [][]string{{"Five", "Six"}, {"One", "Two"}}, 559 StringPtr: &strPtr2, 560} 561 562var nestedTestToml = []byte(`String = [["Five", "Six"], ["One", "Two"]] 563StringPtr = [["Three", "Four"]] 564`) 565 566func TestNestedUnmarshal(t *testing.T) { 567 result := nestedMarshalTestStruct{} 568 err := toml.Unmarshal(nestedTestToml, &result) 569 require.NoError(t, err) 570 assert.Equal(t, nestedTestData, result) 571} 572 573type customMarshalerParent struct { 574 Self customMarshaler `toml:"me"` 575 Friends []customMarshaler `toml:"friends"` 576} 577 578type customMarshaler struct { 579 FirstName string 580 LastName string 581} 582 583func (c customMarshaler) MarshalTOML() ([]byte, error) { 584 fullName := fmt.Sprintf("%s %s", c.FirstName, c.LastName) 585 return []byte(fullName), nil 586} 587 588var customMarshalerData = customMarshaler{FirstName: "Sally", LastName: "Fields"} 589 590// TODO: Remove nolint once var is used by a test 591//nolint:deadcode,unused,varcheck 592var customMarshalerToml = []byte(`Sally Fields`) 593 594// TODO: Remove nolint once var is used by a test 595//nolint:deadcode,unused,varcheck 596var nestedCustomMarshalerData = customMarshalerParent{ 597 Self: customMarshaler{FirstName: "Maiku", LastName: "Suteda"}, 598 Friends: []customMarshaler{customMarshalerData}, 599} 600 601// TODO: Remove nolint once var is used by a test 602//nolint:deadcode,unused,varcheck 603var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"] 604me = "Maiku Suteda" 605`) 606 607var nestedCustomMarshalerTomlForUnmarshal = []byte(`[friends] 608FirstName = "Sally" 609LastName = "Fields"`) 610 611type IntOrString string 612 613func (x *IntOrString) MarshalTOML() ([]byte, error) { 614 s := *(*string)(x) 615 _, err := strconv.Atoi(s) 616 if err != nil { 617 return []byte(fmt.Sprintf(`"%s"`, s)), nil 618 } 619 return []byte(s), nil 620} 621 622func TestUnmarshalTextMarshaler(t *testing.T) { 623 nested := struct { 624 Friends textMarshaler `toml:"friends"` 625 }{} 626 627 expected := struct { 628 Friends textMarshaler `toml:"friends"` 629 }{ 630 Friends: textMarshaler{FirstName: "Sally", LastName: "Fields"}, 631 } 632 633 err := toml.Unmarshal(nestedCustomMarshalerTomlForUnmarshal, &nested) 634 if err != nil { 635 t.Fatal(err) 636 } 637 if !reflect.DeepEqual(nested, expected) { 638 t.Errorf("Bad unmarshal: expected %v, got %v", expected, nested) 639 } 640} 641 642// TODO: Remove nolint once type and methods are used by a test 643//nolint:unused 644type precedentMarshaler struct { 645 FirstName string 646 LastName string 647} 648 649//nolint:unused 650func (m precedentMarshaler) MarshalText() ([]byte, error) { 651 return []byte("shadowed"), nil 652} 653 654//nolint:unused 655func (m precedentMarshaler) MarshalTOML() ([]byte, error) { 656 fullName := fmt.Sprintf("%s %s", m.FirstName, m.LastName) 657 return []byte(fullName), nil 658} 659 660// TODO: Remove nolint once type and method are used by a test 661//nolint:unused 662type customPointerMarshaler struct { 663 FirstName string 664 LastName string 665} 666 667//nolint:unused 668func (m *customPointerMarshaler) MarshalTOML() ([]byte, error) { 669 return []byte(`"hidden"`), nil 670} 671 672// TODO: Remove nolint once type and method are used by a test 673//nolint:unused 674type textPointerMarshaler struct { 675 FirstName string 676 LastName string 677} 678 679//nolint:unused 680func (m *textPointerMarshaler) MarshalText() ([]byte, error) { 681 return []byte("hidden"), nil 682} 683 684// TODO: Remove nolint once var is used by a test 685//nolint:deadcode,unused,varcheck 686var commentTestToml = []byte(` 687# it's a comment on type 688[postgres] 689 # isCommented = "dvalue" 690 noComment = "cvalue" 691 692 # A comment on AttrB with a 693 # break line 694 password = "bvalue" 695 696 # A comment on AttrA 697 user = "avalue" 698 699 [[postgres.My]] 700 701 # a comment on my on typeC 702 My = "Foo" 703 704 [[postgres.My]] 705 706 # a comment on my on typeC 707 My = "Baar" 708`) 709 710type mapsTestStruct struct { 711 Simple map[string]string 712 Paths map[string]string 713 Other map[string]float64 714 X struct { 715 Y struct { 716 Z map[string]bool 717 } 718 } 719} 720 721// TODO: Remove nolint once var is used by a test 722//nolint:deadcode,unused,varcheck 723var mapsTestData = mapsTestStruct{ 724 Simple: map[string]string{ 725 "one plus one": "two", 726 "next": "three", 727 }, 728 Paths: map[string]string{ 729 "/this/is/a/path": "/this/is/also/a/path", 730 "/heloo.txt": "/tmp/lololo.txt", 731 }, 732 Other: map[string]float64{ 733 "testing": 3.9999, 734 }, 735 X: struct{ Y struct{ Z map[string]bool } }{ 736 Y: struct{ Z map[string]bool }{ 737 Z: map[string]bool{ 738 "is.Nested": true, 739 }, 740 }, 741 }, 742} 743 744// TODO: Remove nolint once var is used by a test 745//nolint:deadcode,unused,varcheck 746var mapsTestToml = []byte(` 747[Other] 748 "testing" = 3.9999 749 750[Paths] 751 "/heloo.txt" = "/tmp/lololo.txt" 752 "/this/is/a/path" = "/this/is/also/a/path" 753 754[Simple] 755 "next" = "three" 756 "one plus one" = "two" 757 758[X] 759 760 [X.Y] 761 762 [X.Y.Z] 763 "is.Nested" = true 764`) 765 766// TODO: Remove nolint once type is used by a test 767//nolint:deadcode,unused 768type structArrayNoTag struct { 769 A struct { 770 B []int64 771 C []int64 772 } 773} 774 775// TODO: Remove nolint once var is used by a test 776//nolint:deadcode,unused,varcheck 777var customTagTestToml = []byte(` 778[postgres] 779 password = "bvalue" 780 user = "avalue" 781 782 [[postgres.My]] 783 My = "Foo" 784 785 [[postgres.My]] 786 My = "Baar" 787`) 788 789// TODO: Remove nolint once var is used by a test 790//nolint:deadcode,unused,varcheck 791var customCommentTagTestToml = []byte(` 792# db connection 793[postgres] 794 795 # db pass 796 password = "bvalue" 797 798 # db user 799 user = "avalue" 800`) 801 802// TODO: Remove nolint once var is used by a test 803//nolint:deadcode,unused,varcheck 804var customCommentedTagTestToml = []byte(` 805[postgres] 806 # password = "bvalue" 807 # user = "avalue" 808`) 809 810func TestUnmarshalTabInStringAndQuotedKey(t *testing.T) { 811 type Test struct { 812 Field1 string `toml:"Fie ld1"` 813 Field2 string 814 } 815 816 type TestCase struct { 817 desc string 818 input []byte 819 expected Test 820 } 821 822 testCases := []TestCase{ 823 { 824 desc: "multiline string with tab", 825 input: []byte("Field2 = \"\"\"\nhello\tworld\"\"\""), 826 expected: Test{ 827 Field2: "hello\tworld", 828 }, 829 }, 830 { 831 desc: "quoted key with tab", 832 input: []byte("\"Fie\tld1\" = \"key with tab\""), 833 expected: Test{ 834 Field1: "key with tab", 835 }, 836 }, 837 { 838 desc: "basic string tab", 839 input: []byte("Field2 = \"hello\tworld\""), 840 expected: Test{ 841 Field2: "hello\tworld", 842 }, 843 }, 844 } 845 846 for _, test := range testCases { 847 t.Run(test.desc, func(t *testing.T) { 848 result := Test{} 849 err := toml.Unmarshal(test.input, &result) 850 require.NoError(t, err) 851 assert.Equal(t, test.expected, result) 852 }) 853 } 854} 855 856// TODO: Remove nolint once var is used by a test 857//nolint:deadcode,unused,varcheck 858var customMultilineTagTestToml = []byte(`int_slice = [ 859 1, 860 2, 861 3, 862] 863`) 864 865// TODO: Remove nolint once var is used by a test 866//nolint:deadcode,unused,varcheck 867var testDocBasicToml = []byte(` 868[document] 869 bool_val = true 870 date_val = 1979-05-27T07:32:00Z 871 float_val = 123.4 872 int_val = 5000 873 string_val = "Bite me" 874 uint_val = 5001 875`) 876 877// TODO: Remove nolint once type is used by a test 878//nolint:deadcode 879type testDocCustomTag struct { 880 Doc testDocBasicsCustomTag `file:"document"` 881} 882 883// TODO: Remove nolint once type is used by a test 884//nolint:deadcode 885type testDocBasicsCustomTag struct { 886 Bool bool `file:"bool_val"` 887 Date time.Time `file:"date_val"` 888 Float float32 `file:"float_val"` 889 Int int `file:"int_val"` 890 Uint uint `file:"uint_val"` 891 String *string `file:"string_val"` 892 unexported int `file:"shouldntBeHere"` 893} 894 895// TODO: Remove nolint once var is used by a test 896//nolint:deadcode,varcheck 897var testDocCustomTagData = testDocCustomTag{ 898 Doc: testDocBasicsCustomTag{ 899 Bool: true, 900 Date: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC), 901 Float: 123.4, 902 Int: 5000, 903 Uint: 5001, 904 String: &biteMe, 905 unexported: 0, 906 }, 907} 908 909func TestUnmarshalMap(t *testing.T) { 910 testToml := []byte(` 911 a = 1 912 b = 2 913 c = 3 914 `) 915 var result map[string]int 916 err := toml.Unmarshal(testToml, &result) 917 if err != nil { 918 t.Errorf("Received unexpected error: %s", err) 919 return 920 } 921 922 expected := map[string]int{ 923 "a": 1, 924 "b": 2, 925 "c": 3, 926 } 927 928 if !reflect.DeepEqual(result, expected) { 929 t.Errorf("Bad unmarshal: expected %v, got %v", expected, result) 930 } 931} 932 933func TestUnmarshalMapWithTypedKey(t *testing.T) { 934 testToml := []byte(` 935 a = 1 936 b = 2 937 c = 3 938 `) 939 940 type letter string 941 var result map[letter]int 942 err := toml.Unmarshal(testToml, &result) 943 if err != nil { 944 t.Errorf("Received unexpected error: %s", err) 945 return 946 } 947 948 expected := map[letter]int{ 949 "a": 1, 950 "b": 2, 951 "c": 3, 952 } 953 954 if !reflect.DeepEqual(result, expected) { 955 t.Errorf("Bad unmarshal: expected %v, got %v", expected, result) 956 } 957} 958 959func TestUnmarshalNonPointer(t *testing.T) { 960 a := 1 961 err := toml.Unmarshal([]byte{}, a) 962 if err == nil { 963 t.Fatal("unmarshal should err when given a non pointer") 964 } 965} 966 967func TestUnmarshalInvalidPointerKind(t *testing.T) { 968 t.Skipf("should this really be an error?") 969 a := 1 970 err := toml.Unmarshal([]byte{}, &a) 971 assert.Error(t, err) 972} 973 974// TODO: Remove nolint once var is used by a test 975//nolint:deadcode,unused 976type testDuration struct { 977 Nanosec time.Duration `toml:"nanosec"` 978 Microsec1 time.Duration `toml:"microsec1"` 979 Microsec2 *time.Duration `toml:"microsec2"` 980 Millisec time.Duration `toml:"millisec"` 981 Sec time.Duration `toml:"sec"` 982 Min time.Duration `toml:"min"` 983 Hour time.Duration `toml:"hour"` 984 Mixed time.Duration `toml:"mixed"` 985 AString string `toml:"a_string"` 986} 987 988// TODO: Remove nolint once var is used by a test 989//nolint:deadcode,unused,varcheck 990var testDurationToml = []byte(` 991nanosec = "1ns" 992microsec1 = "1us" 993microsec2 = "1µs" 994millisec = "1ms" 995sec = "1s" 996min = "1m" 997hour = "1h" 998mixed = "1h1m1s1ms1µs1ns" 999a_string = "15s" 1000`) 1001 1002// TODO: Remove nolint once var is used by a test 1003//nolint:deadcode,unused,varcheck 1004var testDurationToml2 = []byte(`a_string = "15s" 1005hour = "1h0m0s" 1006microsec1 = "1µs" 1007microsec2 = "1µs" 1008millisec = "1ms" 1009min = "1m0s" 1010mixed = "1h1m1.001001001s" 1011nanosec = "1ns" 1012sec = "1s" 1013`) 1014 1015// TODO: Remove nolint once type is used by a test 1016//nolint:deadcode,unused 1017type testBadDuration struct { 1018 Val time.Duration `toml:"val"` 1019} 1020 1021// TODO: add back camelCase test 1022var testCamelCaseKeyToml = []byte(`fooBar = 10`) //nolint:unused 1023 1024//nolint:unused 1025func TestUnmarshalCamelCaseKey(t *testing.T) { 1026 t.Skipf("don't know if it is a good idea to automatically convert like that yet") 1027 var x struct { 1028 FooBar int 1029 B int 1030 } 1031 1032 if err := toml.Unmarshal(testCamelCaseKeyToml, &x); err != nil { 1033 t.Fatal(err) 1034 } 1035 1036 if x.FooBar != 10 { 1037 t.Fatal("Did not set camelCase'd key") 1038 } 1039} 1040 1041func TestUnmarshalNegativeUint(t *testing.T) { 1042 t.Skipf("not sure if we this should always error") 1043 type check struct{ U uint } // nolint:unused 1044 err := toml.Unmarshal([]byte("U = -1"), &check{}) 1045 assert.Error(t, err) 1046} 1047 1048func TestUnmarshalCheckConversionFloatInt(t *testing.T) { 1049 type conversionCheck struct { 1050 U uint 1051 I int 1052 F float64 1053 } 1054 1055 type TestCase struct { 1056 desc string 1057 input string 1058 } 1059 1060 testCases := []TestCase{ 1061 { 1062 desc: "unsigned int", 1063 input: `U = 1e300`, 1064 }, 1065 { 1066 desc: "int", 1067 input: `I = 1e300`, 1068 }, 1069 { 1070 desc: "float", 1071 input: `F = 9223372036854775806`, 1072 }, 1073 } 1074 1075 for _, test := range testCases { 1076 t.Run(test.desc, func(t *testing.T) { 1077 err := toml.Unmarshal([]byte(test.input), &conversionCheck{}) 1078 require.Error(t, err) 1079 }) 1080 } 1081} 1082 1083func TestUnmarshalOverflow(t *testing.T) { 1084 type overflow struct { 1085 U8 uint8 1086 I8 int8 1087 F32 float32 1088 } 1089 1090 type TestCase struct { 1091 desc string 1092 input string 1093 } 1094 1095 testCases := []TestCase{ 1096 { 1097 desc: "byte", 1098 input: `U8 = 300`, 1099 }, 1100 { 1101 desc: "int8", 1102 input: `I8 = 300`, 1103 }, 1104 { 1105 desc: "float32", 1106 input: `F32 = 1e300`, 1107 }, 1108 } 1109 1110 for _, test := range testCases { 1111 t.Run(test.desc, func(t *testing.T) { 1112 err := toml.Unmarshal([]byte(test.input), &overflow{}) 1113 require.Error(t, err) 1114 }) 1115 } 1116} 1117 1118func TestUnmarshalDefault(t *testing.T) { 1119 t.Skipf("don't know if it is a good idea to have `default`") 1120 t.Run("main", func(t *testing.T) { 1121 type EmbeddedStruct struct { 1122 StringField string `default:"c"` 1123 } 1124 1125 type aliasUint uint 1126 1127 var doc struct { 1128 StringField string `default:"a"` 1129 BoolField bool `default:"true"` 1130 UintField uint `default:"1"` 1131 Uint8Field uint8 `default:"8"` 1132 Uint16Field uint16 `default:"16"` 1133 Uint32Field uint32 `default:"32"` 1134 Uint64Field uint64 `default:"64"` 1135 IntField int `default:"-1"` 1136 Int8Field int8 `default:"-8"` 1137 Int16Field int16 `default:"-16"` 1138 Int32Field int32 `default:"-32"` 1139 Int64Field int64 `default:"-64"` 1140 Float32Field float32 `default:"32.1"` 1141 Float64Field float64 `default:"64.1"` 1142 DurationField time.Duration `default:"120ms"` 1143 DurationField2 time.Duration `default:"120000000"` 1144 NonEmbeddedStruct struct { 1145 StringField string `default:"b"` 1146 } 1147 EmbeddedStruct 1148 AliasUintField aliasUint `default:"1000"` 1149 } 1150 1151 err := toml.Unmarshal([]byte(``), &doc) 1152 if err != nil { 1153 t.Fatal(err) 1154 } 1155 if doc.BoolField != true { 1156 t.Errorf("BoolField should be true, not %t", doc.BoolField) 1157 } 1158 if doc.StringField != "a" { 1159 t.Errorf("StringField should be \"a\", not %s", doc.StringField) 1160 } 1161 if doc.UintField != 1 { 1162 t.Errorf("UintField should be 1, not %d", doc.UintField) 1163 } 1164 if doc.Uint8Field != 8 { 1165 t.Errorf("Uint8Field should be 8, not %d", doc.Uint8Field) 1166 } 1167 if doc.Uint16Field != 16 { 1168 t.Errorf("Uint16Field should be 16, not %d", doc.Uint16Field) 1169 } 1170 if doc.Uint32Field != 32 { 1171 t.Errorf("Uint32Field should be 32, not %d", doc.Uint32Field) 1172 } 1173 if doc.Uint64Field != 64 { 1174 t.Errorf("Uint64Field should be 64, not %d", doc.Uint64Field) 1175 } 1176 if doc.IntField != -1 { 1177 t.Errorf("IntField should be -1, not %d", doc.IntField) 1178 } 1179 if doc.Int8Field != -8 { 1180 t.Errorf("Int8Field should be -8, not %d", doc.Int8Field) 1181 } 1182 if doc.Int16Field != -16 { 1183 t.Errorf("Int16Field should be -16, not %d", doc.Int16Field) 1184 } 1185 if doc.Int32Field != -32 { 1186 t.Errorf("Int32Field should be -32, not %d", doc.Int32Field) 1187 } 1188 if doc.Int64Field != -64 { 1189 t.Errorf("Int64Field should be -64, not %d", doc.Int64Field) 1190 } 1191 if doc.Float32Field != 32.1 { 1192 t.Errorf("Float32Field should be 32.1, not %f", doc.Float32Field) 1193 } 1194 if doc.Float64Field != 64.1 { 1195 t.Errorf("Float64Field should be 64.1, not %f", doc.Float64Field) 1196 } 1197 if doc.DurationField != 120*time.Millisecond { 1198 t.Errorf("DurationField should be 120ms, not %s", doc.DurationField.String()) 1199 } 1200 if doc.DurationField2 != 120*time.Millisecond { 1201 t.Errorf("DurationField2 should be 120000000, not %d", doc.DurationField2) 1202 } 1203 if doc.NonEmbeddedStruct.StringField != "b" { 1204 t.Errorf("StringField should be \"b\", not %s", doc.NonEmbeddedStruct.StringField) 1205 } 1206 if doc.EmbeddedStruct.StringField != "c" { 1207 t.Errorf("StringField should be \"c\", not %s", doc.EmbeddedStruct.StringField) 1208 } 1209 if doc.AliasUintField != 1000 { 1210 t.Errorf("AliasUintField should be 1000, not %d", doc.AliasUintField) 1211 } 1212 }) 1213 1214 t.Run("failure bool", func(t *testing.T) { 1215 var doc struct { 1216 Field bool `default:"blah"` 1217 } 1218 1219 err := toml.Unmarshal([]byte(``), &doc) 1220 if err == nil { 1221 t.Fatal("should error") 1222 } 1223 }) 1224 1225 t.Run("failure int", func(t *testing.T) { 1226 var doc struct { 1227 Field int `default:"blah"` 1228 } 1229 1230 err := toml.Unmarshal([]byte(``), &doc) 1231 if err == nil { 1232 t.Fatal("should error") 1233 } 1234 }) 1235 1236 t.Run("failure int64", func(t *testing.T) { 1237 var doc struct { 1238 Field int64 `default:"blah"` 1239 } 1240 1241 err := toml.Unmarshal([]byte(``), &doc) 1242 if err == nil { 1243 t.Fatal("should error") 1244 } 1245 }) 1246 1247 t.Run("failure float64", func(t *testing.T) { 1248 var doc struct { 1249 Field float64 `default:"blah"` 1250 } 1251 1252 err := toml.Unmarshal([]byte(``), &doc) 1253 if err == nil { 1254 t.Fatal("should error") 1255 } 1256 }) 1257 1258 t.Run("failure duration", func(t *testing.T) { 1259 var doc struct { 1260 Field time.Duration `default:"blah"` 1261 } 1262 1263 err := toml.Unmarshal([]byte(``), &doc) 1264 if err == nil { 1265 t.Fatal("should error") 1266 } 1267 }) 1268 1269 t.Run("failure unsupported", func(t *testing.T) { 1270 var doc struct { 1271 Field struct{} `default:"blah"` 1272 } 1273 1274 err := toml.Unmarshal([]byte(``), &doc) 1275 if err == nil { 1276 t.Fatal("should error") 1277 } 1278 }) 1279} 1280 1281func TestUnmarshalNestedAnonymousStructs(t *testing.T) { 1282 type Nested struct { 1283 Value string `toml:"nested_field"` 1284 } 1285 type Deep struct { 1286 Nested 1287 } 1288 type Document struct { 1289 Deep 1290 Value string `toml:"own_field"` 1291 } 1292 1293 var doc Document 1294 1295 err := toml.Unmarshal([]byte(`nested_field = "nested value"`+"\n"+`own_field = "own value"`), &doc) 1296 if err != nil { 1297 t.Fatal("should not error") 1298 } 1299 if doc.Value != "own value" || doc.Nested.Value != "nested value" { 1300 t.Fatal("unexpected values") 1301 } 1302} 1303 1304func TestUnmarshalNestedAnonymousStructs_Controversial(t *testing.T) { 1305 t.Skipf("TODO: what does encoding/json do?") 1306 type Nested struct { 1307 Value string `toml:"nested"` 1308 } 1309 type Deep struct { 1310 Nested 1311 } 1312 type Document struct { 1313 Deep 1314 Value string `toml:"own"` 1315 } 1316 1317 var doc Document 1318 1319 err := toml.Unmarshal([]byte(`nested = "nested value"`+"\n"+`own = "own value"`), &doc) 1320 if err == nil { 1321 t.Fatal("should error") 1322 } 1323} 1324 1325type unexportedFieldPreservationTest struct { 1326 Exported string `toml:"exported"` 1327 unexported string 1328 Nested1 unexportedFieldPreservationTestNested `toml:"nested1"` 1329 Nested2 *unexportedFieldPreservationTestNested `toml:"nested2"` 1330 Nested3 *unexportedFieldPreservationTestNested `toml:"nested3"` 1331 Slice1 []unexportedFieldPreservationTestNested `toml:"slice1"` 1332 Slice2 []*unexportedFieldPreservationTestNested `toml:"slice2"` 1333} 1334 1335type unexportedFieldPreservationTestNested struct { 1336 Exported1 string `toml:"exported1"` 1337 unexported1 string 1338} 1339 1340func TestUnmarshalPreservesUnexportedFields(t *testing.T) { 1341 doc := ` 1342 exported = "visible" 1343 unexported = "ignored" 1344 1345 [nested1] 1346 exported1 = "visible1" 1347 unexported1 = "ignored1" 1348 1349 [nested2] 1350 exported1 = "visible2" 1351 unexported1 = "ignored2" 1352 1353 [nested3] 1354 exported1 = "visible3" 1355 unexported1 = "ignored3" 1356 1357 [[slice1]] 1358 exported1 = "visible3" 1359 1360 [[slice1]] 1361 exported1 = "visible4" 1362 1363 [[slice2]] 1364 exported1 = "visible5" 1365 ` 1366 1367 t.Run("unexported field should not be set from toml", func(t *testing.T) { 1368 var actual unexportedFieldPreservationTest 1369 err := toml.Unmarshal([]byte(doc), &actual) 1370 if err != nil { 1371 t.Fatal("did not expect an error") 1372 } 1373 1374 expect := unexportedFieldPreservationTest{ 1375 Exported: "visible", 1376 unexported: "", 1377 Nested1: unexportedFieldPreservationTestNested{"visible1", ""}, 1378 Nested2: &unexportedFieldPreservationTestNested{"visible2", ""}, 1379 Nested3: &unexportedFieldPreservationTestNested{"visible3", ""}, 1380 Slice1: []unexportedFieldPreservationTestNested{ 1381 {Exported1: "visible3"}, 1382 {Exported1: "visible4"}, 1383 }, 1384 Slice2: []*unexportedFieldPreservationTestNested{ 1385 {Exported1: "visible5"}, 1386 }, 1387 } 1388 1389 if !reflect.DeepEqual(actual, expect) { 1390 t.Fatalf("%+v did not equal %+v", actual, expect) 1391 } 1392 }) 1393 1394 t.Run("unexported field should be preserved", func(t *testing.T) { 1395 actual := unexportedFieldPreservationTest{ 1396 Exported: "foo", 1397 unexported: "bar", 1398 Nested1: unexportedFieldPreservationTestNested{"baz", "bax"}, 1399 Nested2: nil, 1400 Nested3: &unexportedFieldPreservationTestNested{"baz", "bax"}, 1401 } 1402 err := toml.Unmarshal([]byte(doc), &actual) 1403 if err != nil { 1404 t.Fatal("did not expect an error") 1405 } 1406 1407 expect := unexportedFieldPreservationTest{ 1408 Exported: "visible", 1409 unexported: "bar", 1410 Nested1: unexportedFieldPreservationTestNested{"visible1", "bax"}, 1411 Nested2: &unexportedFieldPreservationTestNested{"visible2", ""}, 1412 Nested3: &unexportedFieldPreservationTestNested{"visible3", "bax"}, 1413 Slice1: []unexportedFieldPreservationTestNested{ 1414 {Exported1: "visible3"}, 1415 {Exported1: "visible4"}, 1416 }, 1417 Slice2: []*unexportedFieldPreservationTestNested{ 1418 {Exported1: "visible5"}, 1419 }, 1420 } 1421 1422 if !reflect.DeepEqual(actual, expect) { 1423 t.Fatalf("%+v did not equal %+v", actual, expect) 1424 } 1425 }) 1426} 1427 1428func TestUnmarshalLocalDate(t *testing.T) { 1429 t.Run("ToLocalDate", func(t *testing.T) { 1430 type dateStruct struct { 1431 Date toml.LocalDate 1432 } 1433 1434 doc := `date = 1979-05-27` 1435 1436 var obj dateStruct 1437 1438 err := toml.Unmarshal([]byte(doc), &obj) 1439 if err != nil { 1440 t.Fatal(err) 1441 } 1442 1443 if obj.Date.Year != 1979 { 1444 t.Errorf("expected year 1979, got %d", obj.Date.Year) 1445 } 1446 if obj.Date.Month != 5 { 1447 t.Errorf("expected month 5, got %d", obj.Date.Month) 1448 } 1449 if obj.Date.Day != 27 { 1450 t.Errorf("expected day 27, got %d", obj.Date.Day) 1451 } 1452 }) 1453 1454 t.Run("ToLocalDate", func(t *testing.T) { 1455 type dateStruct struct { 1456 Date time.Time 1457 } 1458 1459 doc := `date = 1979-05-27` 1460 1461 var obj dateStruct 1462 1463 err := toml.Unmarshal([]byte(doc), &obj) 1464 if err != nil { 1465 t.Fatal(err) 1466 } 1467 1468 if obj.Date.Year() != 1979 { 1469 t.Errorf("expected year 1979, got %d", obj.Date.Year()) 1470 } 1471 if obj.Date.Month() != 5 { 1472 t.Errorf("expected month 5, got %d", obj.Date.Month()) 1473 } 1474 if obj.Date.Day() != 27 { 1475 t.Errorf("expected day 27, got %d", obj.Date.Day()) 1476 } 1477 }) 1478} 1479 1480func TestUnmarshalLocalDateTime(t *testing.T) { 1481 examples := []struct { 1482 name string 1483 in string 1484 out toml.LocalDateTime 1485 }{ 1486 { 1487 name: "normal", 1488 in: "1979-05-27T07:32:00", 1489 out: toml.LocalDateTime{ 1490 LocalDate: toml.LocalDate{ 1491 Year: 1979, 1492 Month: 5, 1493 Day: 27, 1494 }, 1495 LocalTime: toml.LocalTime{ 1496 Hour: 7, 1497 Minute: 32, 1498 Second: 0, 1499 Nanosecond: 0, 1500 }, 1501 }, 1502 }, 1503 { 1504 name: "with nanoseconds", 1505 in: "1979-05-27T00:32:00.999999", 1506 out: toml.LocalDateTime{ 1507 LocalDate: toml.LocalDate{ 1508 Year: 1979, 1509 Month: 5, 1510 Day: 27, 1511 }, 1512 LocalTime: toml.LocalTime{ 1513 Hour: 0, 1514 Minute: 32, 1515 Second: 0, 1516 Nanosecond: 999999000, 1517 }, 1518 }, 1519 }, 1520 } 1521 1522 for i, example := range examples { 1523 doc := fmt.Sprintf(`date = %s`, example.in) 1524 1525 t.Run(fmt.Sprintf("ToLocalDateTime_%d_%s", i, example.name), func(t *testing.T) { 1526 type dateStruct struct { 1527 Date toml.LocalDateTime 1528 } 1529 1530 var obj dateStruct 1531 1532 err := toml.Unmarshal([]byte(doc), &obj) 1533 if err != nil { 1534 t.Fatal(err) 1535 } 1536 1537 if obj.Date != example.out { 1538 t.Errorf("expected '%s', got '%s'", example.out, obj.Date) 1539 } 1540 }) 1541 1542 t.Run(fmt.Sprintf("ToTime_%d_%s", i, example.name), func(t *testing.T) { 1543 type dateStruct struct { 1544 Date time.Time 1545 } 1546 1547 var obj dateStruct 1548 1549 err := toml.Unmarshal([]byte(doc), &obj) 1550 if err != nil { 1551 t.Fatal(err) 1552 } 1553 1554 if obj.Date.Year() != example.out.Year { 1555 t.Errorf("expected year %d, got %d", example.out.Year, obj.Date.Year()) 1556 } 1557 if obj.Date.Month() != time.Month(example.out.Month) { 1558 t.Errorf("expected month %d, got %d", example.out.Month, obj.Date.Month()) 1559 } 1560 if obj.Date.Day() != example.out.Day { 1561 t.Errorf("expected day %d, got %d", example.out.Day, obj.Date.Day()) 1562 } 1563 if obj.Date.Hour() != example.out.Hour { 1564 t.Errorf("expected hour %d, got %d", example.out.Hour, obj.Date.Hour()) 1565 } 1566 if obj.Date.Minute() != example.out.Minute { 1567 t.Errorf("expected minute %d, got %d", example.out.Minute, obj.Date.Minute()) 1568 } 1569 if obj.Date.Second() != example.out.Second { 1570 t.Errorf("expected second %d, got %d", example.out.Second, obj.Date.Second()) 1571 } 1572 if obj.Date.Nanosecond() != example.out.Nanosecond { 1573 t.Errorf("expected nanoseconds %d, got %d", example.out.Nanosecond, obj.Date.Nanosecond()) 1574 } 1575 }) 1576 } 1577} 1578 1579func TestUnmarshalLocalTime(t *testing.T) { 1580 examples := []struct { 1581 name string 1582 in string 1583 out toml.LocalTime 1584 }{ 1585 { 1586 name: "normal", 1587 in: "07:32:00", 1588 out: toml.LocalTime{ 1589 Hour: 7, 1590 Minute: 32, 1591 Second: 0, 1592 Nanosecond: 0, 1593 }, 1594 }, 1595 { 1596 name: "with nanoseconds", 1597 in: "00:32:00.999999", 1598 out: toml.LocalTime{ 1599 Hour: 0, 1600 Minute: 32, 1601 Second: 0, 1602 Nanosecond: 999999000, 1603 }, 1604 }, 1605 } 1606 1607 for i, example := range examples { 1608 doc := fmt.Sprintf(`Time = %s`, example.in) 1609 1610 t.Run(fmt.Sprintf("ToLocalTime_%d_%s", i, example.name), func(t *testing.T) { 1611 type dateStruct struct { 1612 Time toml.LocalTime 1613 } 1614 1615 var obj dateStruct 1616 1617 err := toml.Unmarshal([]byte(doc), &obj) 1618 if err != nil { 1619 t.Fatal(err) 1620 } 1621 1622 if obj.Time != example.out { 1623 t.Errorf("expected '%s', got '%s'", example.out, obj.Time) 1624 } 1625 }) 1626 } 1627} 1628 1629// test case for issue #339 1630func TestUnmarshalSameInnerField(t *testing.T) { 1631 type InterStruct2 struct { 1632 Test string 1633 Name string 1634 Age int 1635 } 1636 type Inter2 struct { 1637 Name string 1638 Age int 1639 InterStruct2 InterStruct2 1640 } 1641 type Server struct { 1642 Name string `toml:"name"` 1643 Inter2 Inter2 `toml:"inter2"` 1644 } 1645 1646 var server Server 1647 1648 if err := toml.Unmarshal([]byte(`name = "123" 1649[inter2] 1650name = "inter2" 1651age = 222`), &server); err == nil { 1652 expected := Server{ 1653 Name: "123", 1654 Inter2: Inter2{ 1655 Name: "inter2", 1656 Age: 222, 1657 }, 1658 } 1659 if !reflect.DeepEqual(server, expected) { 1660 t.Errorf("Bad unmarshal: expected %v, got %v", expected, server) 1661 } 1662 } else { 1663 t.Fatalf("unexpected error: %v", err) 1664 } 1665} 1666 1667func TestUnmarshalToNilInterface(t *testing.T) { 1668 doc := []byte(` 1669PrimitiveField = "Hello" 1670ArrayField = [1,2,3] 1671InterfacePointerField = "World" 1672 1673[StructField] 1674Field1 = 123 1675Field2 = "Field2" 1676 1677[MapField] 1678MapField1 = [4,5,6] 1679MapField2 = {A = "A"} 1680MapField3 = false 1681 1682[[StructArrayField]] 1683Name = "Allen" 1684Age = 20 1685 1686[[StructArrayField]] 1687Name = "Jack" 1688Age = 23 1689`) 1690 1691 type OuterStruct struct { 1692 PrimitiveField interface{} 1693 ArrayField interface{} 1694 StructArrayField interface{} 1695 MapField map[string]interface{} 1696 StructField interface{} 1697 NilField interface{} 1698 InterfacePointerField *interface{} 1699 } 1700 1701 var s interface{} = "World" 1702 expected := OuterStruct{ 1703 PrimitiveField: "Hello", 1704 ArrayField: []interface{}{int64(1), int64(2), int64(3)}, 1705 StructField: map[string]interface{}{ 1706 "Field1": int64(123), 1707 "Field2": "Field2", 1708 }, 1709 MapField: map[string]interface{}{ 1710 "MapField1": []interface{}{int64(4), int64(5), int64(6)}, 1711 "MapField2": map[string]interface{}{ 1712 "A": "A", 1713 }, 1714 "MapField3": false, 1715 }, 1716 NilField: nil, 1717 InterfacePointerField: &s, 1718 StructArrayField: []interface{}{ 1719 map[string]interface{}{ 1720 "Name": "Allen", 1721 "Age": int64(20), 1722 }, 1723 map[string]interface{}{ 1724 "Name": "Jack", 1725 "Age": int64(23), 1726 }, 1727 }, 1728 } 1729 actual := OuterStruct{} 1730 err := toml.Unmarshal(doc, &actual) 1731 require.NoError(t, err) 1732 assert.Equal(t, expected, actual) 1733} 1734 1735func TestUnmarshalToNonNilInterface(t *testing.T) { 1736 doc := []byte(` 1737PrimitiveField = "Allen" 1738ArrayField = [1,2,3] 1739 1740[StructField] 1741InnerField = "After1" 1742 1743[PointerField] 1744InnerField = "After2" 1745 1746[InterfacePointerField] 1747InnerField = "After" 1748 1749[MapField] 1750MapField1 = [4,5,6] 1751MapField2 = {A = "A"} 1752MapField3 = false 1753 1754[[StructArrayField]] 1755InnerField = "After3" 1756 1757[[StructArrayField]] 1758InnerField = "After4" 1759`) 1760 type InnerStruct struct { 1761 InnerField interface{} 1762 } 1763 1764 type OuterStruct struct { 1765 PrimitiveField interface{} 1766 ArrayField interface{} 1767 StructArrayField interface{} 1768 MapField map[string]interface{} 1769 StructField interface{} 1770 PointerField interface{} 1771 NilField interface{} 1772 InterfacePointerField *interface{} 1773 } 1774 1775 var s interface{} = InnerStruct{"After"} 1776 expected := OuterStruct{ 1777 PrimitiveField: "Allen", 1778 ArrayField: []interface{}{int64(1), int64(2), int64(3)}, 1779 StructField: map[string]interface{}{"InnerField": "After1"}, 1780 MapField: map[string]interface{}{ 1781 "MapField1": []interface{}{int64(4), int64(5), int64(6)}, 1782 "MapField2": map[string]interface{}{ 1783 "A": "A", 1784 }, 1785 "MapField3": false, 1786 }, 1787 PointerField: map[string]interface{}{"InnerField": "After2"}, 1788 NilField: nil, 1789 InterfacePointerField: &s, 1790 StructArrayField: []interface{}{ 1791 map[string]interface{}{"InnerField": "After3"}, 1792 map[string]interface{}{"InnerField": "After4"}, 1793 }, 1794 } 1795 actual := OuterStruct{ 1796 PrimitiveField: "aaa", 1797 ArrayField: []int{100, 200, 300, 400}, 1798 StructField: InnerStruct{InnerField: "Before1"}, 1799 MapField: map[string]interface{}{ 1800 "MapField1": []int{4, 5, 6}, 1801 "MapField2": map[string]string{ 1802 "B": "BBB", 1803 }, 1804 "MapField3": true, 1805 }, 1806 PointerField: &InnerStruct{InnerField: "Before2"}, 1807 NilField: nil, 1808 InterfacePointerField: &s, 1809 StructArrayField: []InnerStruct{ 1810 {InnerField: "Before3"}, 1811 {InnerField: "Before4"}, 1812 }, 1813 } 1814 1815 err := toml.Unmarshal(doc, &actual) 1816 require.NoError(t, err) 1817 assert.Equal(t, expected, actual) 1818} 1819 1820func TestUnmarshalNil(t *testing.T) { 1821 assert.Error(t, toml.Unmarshal([]byte(`whatever = "whatever"`), nil)) 1822 assert.Error(t, toml.Unmarshal([]byte(`whatever = "whatever"`), (*struct{})(nil))) 1823} 1824 1825var sliceTomlDemo = []byte(`str_slice = ["Howdy","Hey There"] 1826str_slice_ptr= ["Howdy","Hey There"] 1827int_slice=[1,2] 1828int_slice_ptr=[1,2] 1829[[struct_slice]] 1830String2="1" 1831[[struct_slice]] 1832String2="2" 1833[[struct_slice_ptr]] 1834String2="1" 1835[[struct_slice_ptr]] 1836String2="2" 1837`) 1838 1839type sliceStruct struct { 1840 Slice []string ` toml:"str_slice" ` 1841 SlicePtr *[]string ` toml:"str_slice_ptr" ` 1842 IntSlice []int ` toml:"int_slice" ` 1843 IntSlicePtr *[]int ` toml:"int_slice_ptr" ` 1844 StructSlice []basicMarshalTestSubStruct ` toml:"struct_slice" ` 1845 StructSlicePtr *[]basicMarshalTestSubStruct ` toml:"struct_slice_ptr" ` 1846} 1847 1848type arrayStruct struct { 1849 Slice [4]string ` toml:"str_slice" ` 1850 SlicePtr *[4]string ` toml:"str_slice_ptr" ` 1851 IntSlice [4]int ` toml:"int_slice" ` 1852 IntSlicePtr *[4]int ` toml:"int_slice_ptr" ` 1853 StructSlice [4]basicMarshalTestSubStruct ` toml:"struct_slice" ` 1854 StructSlicePtr *[4]basicMarshalTestSubStruct ` toml:"struct_slice_ptr" ` 1855} 1856 1857type arrayTooSmallStruct struct { 1858 Slice [1]string ` toml:"str_slice" ` 1859 StructSlice [1]basicMarshalTestSubStruct ` toml:"struct_slice" ` 1860} 1861 1862func TestUnmarshalSlice(t *testing.T) { 1863 var actual sliceStruct 1864 err := toml.Unmarshal(sliceTomlDemo, &actual) 1865 require.NoError(t, err) 1866 expected := sliceStruct{ 1867 Slice: []string{"Howdy", "Hey There"}, 1868 SlicePtr: &[]string{"Howdy", "Hey There"}, 1869 IntSlice: []int{1, 2}, 1870 IntSlicePtr: &[]int{1, 2}, 1871 StructSlice: []basicMarshalTestSubStruct{{"1"}, {"2"}}, 1872 StructSlicePtr: &[]basicMarshalTestSubStruct{{"1"}, {"2"}}, 1873 } 1874 assert.Equal(t, expected, actual) 1875} 1876 1877func TestUnmarshalSliceFail(t *testing.T) { 1878 var actual sliceStruct 1879 assert.Error(t, toml.Unmarshal([]byte(`str_slice = [1, 2]`), &actual)) 1880} 1881 1882func TestUnmarshalSliceFail2(t *testing.T) { 1883 doc := `str_slice=[1,2]` 1884 var actual sliceStruct 1885 assert.Error(t, toml.Unmarshal([]byte(doc), &actual)) 1886} 1887 1888func TestUnmarshalMixedTypeSlice(t *testing.T) { 1889 type TestStruct struct { 1890 ArrayField []interface{} 1891 } 1892 1893 //doc := []byte(`ArrayField = [3.14,100,true,"hello world",{Field = "inner1"},[{Field = "inner2"},{Field = "inner3"}]] 1894 //`) 1895 1896 doc := []byte(`ArrayField = [{Field = "inner1"},[{Field = "inner2"},{Field = "inner3"}]] 1897`) 1898 1899 actual := TestStruct{} 1900 expected := TestStruct{ 1901 ArrayField: []interface{}{ 1902 //3.14, 1903 //int64(100), 1904 //true, 1905 //"hello world", 1906 map[string]interface{}{ 1907 "Field": "inner1", 1908 }, 1909 []interface{}{ 1910 map[string]interface{}{"Field": "inner2"}, 1911 map[string]interface{}{"Field": "inner3"}, 1912 }, 1913 }, 1914 } 1915 err := toml.Unmarshal(doc, &actual) 1916 require.NoError(t, err) 1917 assert.Equal(t, expected, actual) 1918} 1919 1920func TestUnmarshalArray(t *testing.T) { 1921 var err error 1922 1923 var actual arrayStruct 1924 err = toml.Unmarshal(sliceTomlDemo, &actual) 1925 require.NoError(t, err) 1926 1927 expected := arrayStruct{ 1928 Slice: [4]string{"Howdy", "Hey There"}, 1929 SlicePtr: &[4]string{"Howdy", "Hey There"}, 1930 IntSlice: [4]int{1, 2}, 1931 IntSlicePtr: &[4]int{1, 2}, 1932 StructSlice: [4]basicMarshalTestSubStruct{{"1"}, {"2"}}, 1933 StructSlicePtr: &[4]basicMarshalTestSubStruct{{"1"}, {"2"}}, 1934 } 1935 assert.Equal(t, expected, actual) 1936} 1937 1938func TestUnmarshalArrayFail3(t *testing.T) { 1939 doc := `[[struct_slice]] 1940String2="1" 1941[[struct_slice]] 1942String2="2"` 1943 1944 var actual arrayTooSmallStruct 1945 err := toml.Unmarshal([]byte(doc), &actual) 1946 assert.Error(t, err) 1947} 1948 1949func decoder(doc string) *toml.Decoder { 1950 return toml.NewDecoder(bytes.NewReader([]byte(doc))) 1951} 1952 1953func strictDecoder(doc string) *toml.Decoder { 1954 d := decoder(doc) 1955 d.SetStrict(true) 1956 return d 1957} 1958 1959func TestDecoderStrict(t *testing.T) { 1960 input := ` 1961 [decoded] 1962 key = "" 1963 1964 [undecoded] 1965 key = "" 1966 1967 [undecoded.inner] 1968 key = "" 1969 1970 [[undecoded.array]] 1971 key = "" 1972 1973 [[undecoded.array]] 1974 key = "" 1975 1976 ` 1977 var doc struct { 1978 Decoded struct { 1979 Key string 1980 } 1981 } 1982 1983 err := strictDecoder(input).Decode(&doc) 1984 require.Error(t, err) 1985 require.IsType(t, &toml.StrictMissingError{}, err) 1986 se := err.(*toml.StrictMissingError) 1987 1988 keys := []toml.Key{} 1989 1990 for _, e := range se.Errors { 1991 keys = append(keys, e.Key()) 1992 } 1993 1994 expectedKeys := []toml.Key{ 1995 {"undecoded"}, 1996 {"undecoded", "inner"}, 1997 {"undecoded", "array"}, 1998 {"undecoded", "array"}, 1999 } 2000 2001 require.Equal(t, expectedKeys, keys) 2002 2003 err = decoder(input).Decode(&doc) 2004 require.NoError(t, err) 2005 2006 var m map[string]interface{} 2007 err = decoder(input).Decode(&m) 2008} 2009 2010func TestDecoderStrictValid(t *testing.T) { 2011 input := ` 2012 [decoded] 2013 key = "" 2014 ` 2015 var doc struct { 2016 Decoded struct { 2017 Key string 2018 } 2019 } 2020 2021 err := strictDecoder(input).Decode(&doc) 2022 require.NoError(t, err) 2023} 2024 2025type docUnmarshalTOML struct { 2026 Decoded struct { 2027 Key string 2028 } 2029} 2030 2031func (d *docUnmarshalTOML) UnmarshalTOML(i interface{}) error { 2032 if iMap, ok := i.(map[string]interface{}); !ok { 2033 return fmt.Errorf("type assertion error: wants %T, have %T", map[string]interface{}{}, i) 2034 } else if key, ok := iMap["key"]; !ok { 2035 return fmt.Errorf("key '%s' not in map", "key") 2036 } else if keyString, ok := key.(string); !ok { 2037 return fmt.Errorf("type assertion error: wants %T, have %T", "", key) 2038 } else { 2039 d.Decoded.Key = keyString 2040 } 2041 return nil 2042} 2043 2044func TestDecoderStrictCustomUnmarshal(t *testing.T) { 2045 t.Skip() 2046 //input := `key = "ok"` 2047 //var doc docUnmarshalTOML 2048 //err := NewDecoder(bytes.NewReader([]byte(input))).Strict(true).Decode(&doc) 2049 //if err != nil { 2050 // t.Fatal("unexpected error:", err) 2051 //} 2052 //if doc.Decoded.Key != "ok" { 2053 // t.Errorf("Bad unmarshal: expected ok, got %v", doc.Decoded.Key) 2054 //} 2055} 2056 2057type parent struct { 2058 Doc docUnmarshalTOML 2059 DocPointer *docUnmarshalTOML 2060} 2061 2062func TestCustomUnmarshal(t *testing.T) { 2063 t.Skip("not sure if UnmarshalTOML is a good idea") 2064 input := ` 2065[Doc] 2066 key = "ok1" 2067[DocPointer] 2068 key = "ok2" 2069` 2070 2071 var d parent 2072 err := toml.Unmarshal([]byte(input), &d) 2073 require.NoError(t, err) 2074 assert.Equal(t, "ok1", d.Doc.Decoded.Key) 2075 assert.Equal(t, "ok2", d.DocPointer.Decoded.Key) 2076} 2077 2078func TestCustomUnmarshalError(t *testing.T) { 2079 t.Skip("not sure if UnmarshalTOML is a good idea") 2080 2081 input := ` 2082[Doc] 2083 key = 1 2084[DocPointer] 2085 key = "ok2" 2086` 2087 2088 expected := "(2, 1): unmarshal toml: type assertion error: wants string, have int64" 2089 2090 var d parent 2091 err := toml.Unmarshal([]byte(input), &d) 2092 if err == nil { 2093 t.Error("expected error, got none") 2094 } else if err.Error() != expected { 2095 t.Errorf("expect err: %s, got: %s", expected, err.Error()) 2096 } 2097} 2098 2099type intWrapper struct { 2100 Value int 2101} 2102 2103func (w *intWrapper) UnmarshalText(text []byte) error { 2104 var err error 2105 if w.Value, err = strconv.Atoi(string(text)); err == nil { 2106 return nil 2107 } 2108 if b, err := strconv.ParseBool(string(text)); err == nil { 2109 if b { 2110 w.Value = 1 2111 } 2112 return nil 2113 } 2114 if f, err := strconv.ParseFloat(string(text), 32); err == nil { 2115 w.Value = int(f) 2116 return nil 2117 } 2118 return fmt.Errorf("unsupported: %s", text) 2119} 2120 2121func TestTextUnmarshal(t *testing.T) { 2122 var doc struct { 2123 UnixTime intWrapper 2124 Version *intWrapper 2125 2126 Bool intWrapper 2127 Int intWrapper 2128 Float intWrapper 2129 } 2130 2131 input := ` 2132UnixTime = "12" 2133Version = "42" 2134Bool = true 2135Int = 21 2136Float = 2.0 2137` 2138 err := toml.Unmarshal([]byte(input), &doc) 2139 require.NoError(t, err) 2140 assert.Equal(t, 12, doc.UnixTime.Value) 2141 assert.Equal(t, 42, doc.Version.Value) 2142 assert.Equal(t, 1, doc.Bool.Value) 2143 assert.Equal(t, 21, doc.Int.Value) 2144 assert.Equal(t, 2, doc.Float.Value) 2145} 2146 2147func TestTextUnmarshalError(t *testing.T) { 2148 var doc struct { 2149 Failer intWrapper 2150 } 2151 2152 input := `Failer = "hello"` 2153 if err := toml.Unmarshal([]byte(input), &doc); err == nil { 2154 t.Fatalf("expected err, got none") 2155 } 2156} 2157 2158// issue406 2159func TestPreserveNotEmptyField(t *testing.T) { 2160 doc := []byte(`Field1 = "ccc"`) 2161 type Inner struct { 2162 InnerField1 string 2163 InnerField2 int 2164 } 2165 type TestStruct struct { 2166 Field1 string 2167 Field2 int 2168 Field3 Inner 2169 } 2170 2171 actual := TestStruct{ 2172 "aaa", 2173 100, 2174 Inner{ 2175 "bbb", 2176 200, 2177 }, 2178 } 2179 2180 expected := TestStruct{ 2181 "ccc", 2182 100, 2183 Inner{ 2184 "bbb", 2185 200, 2186 }, 2187 } 2188 2189 err := toml.Unmarshal(doc, &actual) 2190 if err != nil { 2191 t.Fatal(err) 2192 } 2193 2194 if !reflect.DeepEqual(actual, expected) { 2195 t.Errorf("Bad unmarshal: expected %+v, got %+v", expected, actual) 2196 } 2197} 2198 2199// github issue 432 2200func TestUnmarshalEmptyInterface(t *testing.T) { 2201 doc := []byte(`User = "pelletier"`) 2202 2203 var v interface{} 2204 2205 err := toml.Unmarshal(doc, &v) 2206 if err != nil { 2207 t.Fatal(err) 2208 } 2209 require.IsType(t, map[string]interface{}{}, v) 2210 2211 x := v.(map[string]interface{}) 2212 assert.Equal(t, "pelletier", x["User"]) 2213} 2214 2215func TestUnmarshalEmptyInterfaceDeep(t *testing.T) { 2216 t.Skipf("TODO") 2217 doc := []byte(` 2218User = "pelletier" 2219Age = 99 2220 2221[foo] 2222bar = 42 2223`) 2224 2225 var v interface{} 2226 2227 err := toml.Unmarshal(doc, &v) 2228 if err != nil { 2229 t.Fatal(err) 2230 } 2231 2232 x, ok := v.(map[string]interface{}) 2233 if !ok { 2234 t.Fatal(err) 2235 } 2236 2237 expected := map[string]interface{}{ 2238 "User": "pelletier", 2239 "Age": 99, 2240 "foo": map[string]interface{}{ 2241 "bar": 42, 2242 }, 2243 } 2244 2245 reflect.DeepEqual(x, expected) 2246} 2247 2248type Config struct { 2249 Key string `toml:"key"` 2250 Obj Custom `toml:"obj"` 2251} 2252 2253type Custom struct { 2254 v string 2255} 2256 2257func (c *Custom) UnmarshalTOML(v interface{}) error { 2258 c.v = "called" 2259 return nil 2260} 2261 2262func TestGithubIssue431(t *testing.T) { 2263 doc := `key = "value"` 2264 var c Config 2265 if err := toml.Unmarshal([]byte(doc), &c); err != nil { 2266 t.Fatalf("unexpected error: %s", err) 2267 } 2268 2269 if c.Key != "value" { 2270 t.Errorf("expected c.Key='value', not '%s'", c.Key) 2271 } 2272 2273 if c.Obj.v == "called" { 2274 t.Errorf("UnmarshalTOML should not have been called") 2275 } 2276} 2277 2278type durationString struct { 2279 time.Duration 2280} 2281 2282func (d *durationString) UnmarshalTOML(v interface{}) error { 2283 d.Duration = 10 * time.Second 2284 return nil 2285} 2286 2287type config437Error struct{} 2288 2289func (e *config437Error) UnmarshalTOML(v interface{}) error { 2290 return errors.New("expected") 2291} 2292 2293type config437 struct { 2294 HTTP struct { 2295 PingTimeout durationString `toml:"PingTimeout"` 2296 ErrorField config437Error 2297 } `toml:"HTTP"` 2298} 2299 2300func TestGithubIssue437(t *testing.T) { 2301 t.Skipf("unmarshalTOML not implemented") 2302 src := ` 2303[HTTP] 2304PingTimeout = "32m" 2305` 2306 cfg := &config437{} 2307 cfg.HTTP.PingTimeout = durationString{time.Second} 2308 2309 err := toml.Unmarshal([]byte(src), cfg) 2310 if err != nil { 2311 t.Fatalf("unexpected errors %s", err) 2312 } 2313 expected := durationString{10 * time.Second} 2314 if cfg.HTTP.PingTimeout != expected { 2315 t.Fatalf("expected '%s', got '%s'", expected, cfg.HTTP.PingTimeout) 2316 } 2317} 2318 2319func TestLeafUnmarshalerError(t *testing.T) { 2320 src := ` 2321[HTTP] 2322ErrorField = "foo" 2323` 2324 cfg := &config437{} 2325 2326 err := toml.Unmarshal([]byte(src), cfg) 2327 if err == nil { 2328 t.Fatalf("error was expected") 2329 } 2330} 2331