1// Copyright 2014 Unknwon 2// 3// Licensed under the Apache License, Version 2.0 (the "License"): you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12// License for the specific language governing permissions and limitations 13// under the License. 14 15package ini 16 17import ( 18 "bytes" 19 "flag" 20 "io/ioutil" 21 "path/filepath" 22 "runtime" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27) 28 29const ( 30 confData = ` 31 ; Package name 32 NAME = ini 33 ; Package version 34 VERSION = v1 35 ; Package import path 36 IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s 37 38 # Information about package author 39 # Bio can be written in multiple lines. 40 [author] 41 NAME = Unknwon ; Succeeding comment 42 E-MAIL = fake@localhost 43 GITHUB = https://github.com/%(NAME)s 44 BIO = """Gopher. 45 Coding addict. 46 Good man. 47 """ # Succeeding comment` 48 minimalConf = "testdata/minimal.ini" 49 fullConf = "testdata/full.ini" 50 notFoundConf = "testdata/404.ini" 51) 52 53var update = flag.Bool("update", false, "Update .golden files") 54 55func TestLoad(t *testing.T) { 56 t.Run("load from good data sources", func(t *testing.T) { 57 f, err := Load( 58 "testdata/minimal.ini", 59 []byte("NAME = ini\nIMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s"), 60 bytes.NewReader([]byte(`VERSION = v1`)), 61 ioutil.NopCloser(bytes.NewReader([]byte("[author]\nNAME = Unknwon"))), 62 ) 63 require.NoError(t, err) 64 require.NotNil(t, f) 65 66 // Validate values make sure all sources are loaded correctly 67 sec := f.Section("") 68 assert.Equal(t, "ini", sec.Key("NAME").String()) 69 assert.Equal(t, "v1", sec.Key("VERSION").String()) 70 assert.Equal(t, "gopkg.in/ini.v1", sec.Key("IMPORT_PATH").String()) 71 72 sec = f.Section("author") 73 assert.Equal(t, "Unknwon", sec.Key("NAME").String()) 74 assert.Equal(t, "u@gogs.io", sec.Key("E-MAIL").String()) 75 }) 76 77 t.Run("load from bad data sources", func(t *testing.T) { 78 t.Run("invalid input", func(t *testing.T) { 79 _, err := Load(notFoundConf) 80 require.Error(t, err) 81 }) 82 83 t.Run("unsupported type", func(t *testing.T) { 84 _, err := Load(123) 85 require.Error(t, err) 86 }) 87 }) 88 89 t.Run("cannot properly parse INI files containing `#` or `;` in value", func(t *testing.T) { 90 f, err := Load([]byte(` 91 [author] 92 NAME = U#n#k#n#w#o#n 93 GITHUB = U;n;k;n;w;o;n 94 `)) 95 require.NoError(t, err) 96 require.NotNil(t, f) 97 98 sec := f.Section("author") 99 nameValue := sec.Key("NAME").String() 100 githubValue := sec.Key("GITHUB").String() 101 assert.Equal(t, "U", nameValue) 102 assert.Equal(t, "U", githubValue) 103 }) 104 105 t.Run("cannot parse small python-compatible INI files", func(t *testing.T) { 106 f, err := Load([]byte(` 107[long] 108long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 109 foo 110 bar 111 foobar 112 barfoo 113 -----END RSA PRIVATE KEY----- 114`)) 115 require.Error(t, err) 116 assert.Nil(t, f) 117 assert.Equal(t, "key-value delimiter not found: foo\n", err.Error()) 118 }) 119 120 t.Run("cannot parse big python-compatible INI files", func(t *testing.T) { 121 f, err := Load([]byte(` 122[long] 123long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 124 1foo 125 2bar 126 3foobar 127 4barfoo 128 5foo 129 6bar 130 7foobar 131 8barfoo 132 9foo 133 10bar 134 11foobar 135 12barfoo 136 13foo 137 14bar 138 15foobar 139 16barfoo 140 17foo 141 18bar 142 19foobar 143 20barfoo 144 21foo 145 22bar 146 23foobar 147 24barfoo 148 25foo 149 26bar 150 27foobar 151 28barfoo 152 29foo 153 30bar 154 31foobar 155 32barfoo 156 33foo 157 34bar 158 35foobar 159 36barfoo 160 37foo 161 38bar 162 39foobar 163 40barfoo 164 41foo 165 42bar 166 43foobar 167 44barfoo 168 45foo 169 46bar 170 47foobar 171 48barfoo 172 49foo 173 50bar 174 51foobar 175 52barfoo 176 53foo 177 54bar 178 55foobar 179 56barfoo 180 57foo 181 58bar 182 59foobar 183 60barfoo 184 61foo 185 62bar 186 63foobar 187 64barfoo 188 65foo 189 66bar 190 67foobar 191 68barfoo 192 69foo 193 70bar 194 71foobar 195 72barfoo 196 73foo 197 74bar 198 75foobar 199 76barfoo 200 77foo 201 78bar 202 79foobar 203 80barfoo 204 81foo 205 82bar 206 83foobar 207 84barfoo 208 85foo 209 86bar 210 87foobar 211 88barfoo 212 89foo 213 90bar 214 91foobar 215 92barfoo 216 93foo 217 94bar 218 95foobar 219 96barfoo 220 -----END RSA PRIVATE KEY----- 221`)) 222 require.Error(t, err) 223 assert.Nil(t, f) 224 assert.Equal(t, "key-value delimiter not found: 1foo\n", err.Error()) 225 }) 226} 227 228func TestLooseLoad(t *testing.T) { 229 f, err := LoadSources(LoadOptions{Loose: true}, notFoundConf, minimalConf) 230 require.NoError(t, err) 231 require.NotNil(t, f) 232 233 t.Run("inverse case", func(t *testing.T) { 234 _, err = Load(notFoundConf) 235 require.Error(t, err) 236 }) 237} 238 239func TestInsensitiveLoad(t *testing.T) { 240 t.Run("insensitive to section and key names", func(t *testing.T) { 241 f, err := InsensitiveLoad(minimalConf) 242 require.NoError(t, err) 243 require.NotNil(t, f) 244 245 assert.Equal(t, "u@gogs.io", f.Section("Author").Key("e-mail").String()) 246 247 t.Run("write out", func(t *testing.T) { 248 var buf bytes.Buffer 249 _, err := f.WriteTo(&buf) 250 require.NoError(t, err) 251 assert.Equal(t, `[author] 252e-mail = u@gogs.io 253 254`, 255 buf.String(), 256 ) 257 }) 258 259 t.Run("inverse case", func(t *testing.T) { 260 f, err := Load(minimalConf) 261 require.NoError(t, err) 262 require.NotNil(t, f) 263 264 assert.Empty(t, f.Section("Author").Key("e-mail").String()) 265 }) 266 }) 267 268 // Ref: https://github.com/go-ini/ini/issues/198 269 t.Run("insensitive load with default section", func(t *testing.T) { 270 f, err := InsensitiveLoad([]byte(` 271user = unknwon 272[profile] 273email = unknwon@local 274`)) 275 require.NoError(t, err) 276 require.NotNil(t, f) 277 278 assert.Equal(t, "unknwon", f.Section(DefaultSection).Key("user").String()) 279 }) 280} 281 282func TestLoadSources(t *testing.T) { 283 t.Run("with true `AllowPythonMultilineValues`", func(t *testing.T) { 284 t.Run("ignore nonexistent files", func(t *testing.T) { 285 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true, Loose: true}, notFoundConf, minimalConf) 286 require.NoError(t, err) 287 require.NotNil(t, f) 288 289 t.Run("inverse case", func(t *testing.T) { 290 _, err = LoadSources(LoadOptions{AllowPythonMultilineValues: true}, notFoundConf) 291 require.Error(t, err) 292 }) 293 }) 294 295 t.Run("insensitive to section and key names", func(t *testing.T) { 296 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true, Insensitive: true}, minimalConf) 297 require.NoError(t, err) 298 require.NotNil(t, f) 299 300 assert.Equal(t, "u@gogs.io", f.Section("Author").Key("e-mail").String()) 301 302 t.Run("write out", func(t *testing.T) { 303 var buf bytes.Buffer 304 _, err := f.WriteTo(&buf) 305 require.NoError(t, err) 306 assert.Equal(t, `[author] 307e-mail = u@gogs.io 308 309`, 310 buf.String(), 311 ) 312 }) 313 314 t.Run("inverse case", func(t *testing.T) { 315 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, minimalConf) 316 require.NoError(t, err) 317 require.NotNil(t, f) 318 319 assert.Empty(t, f.Section("Author").Key("e-mail").String()) 320 }) 321 }) 322 323 t.Run("insensitive to sections and sensitive to key names", func(t *testing.T) { 324 f, err := LoadSources(LoadOptions{InsensitiveSections: true}, minimalConf) 325 require.NoError(t, err) 326 require.NotNil(t, f) 327 328 assert.Equal(t, "u@gogs.io", f.Section("Author").Key("E-MAIL").String()) 329 330 t.Run("write out", func(t *testing.T) { 331 var buf bytes.Buffer 332 _, err := f.WriteTo(&buf) 333 require.NoError(t, err) 334 assert.Equal(t, `[author] 335E-MAIL = u@gogs.io 336 337`, 338 buf.String(), 339 ) 340 }) 341 342 t.Run("inverse case", func(t *testing.T) { 343 f, err := LoadSources(LoadOptions{}, minimalConf) 344 require.NoError(t, err) 345 require.NotNil(t, f) 346 347 assert.Empty(t, f.Section("Author").Key("e-mail").String()) 348 }) 349 }) 350 351 t.Run("sensitive to sections and insensitive to key names", func(t *testing.T) { 352 f, err := LoadSources(LoadOptions{InsensitiveKeys: true}, minimalConf) 353 require.NoError(t, err) 354 require.NotNil(t, f) 355 356 assert.Equal(t, "u@gogs.io", f.Section("author").Key("e-mail").String()) 357 358 t.Run("write out", func(t *testing.T) { 359 var buf bytes.Buffer 360 _, err := f.WriteTo(&buf) 361 require.NoError(t, err) 362 assert.Equal(t, `[author] 363e-mail = u@gogs.io 364 365`, 366 buf.String(), 367 ) 368 }) 369 370 t.Run("inverse case", func(t *testing.T) { 371 f, err := LoadSources(LoadOptions{}, minimalConf) 372 require.NoError(t, err) 373 require.NotNil(t, f) 374 375 assert.Empty(t, f.Section("Author").Key("e-mail").String()) 376 }) 377 }) 378 379 t.Run("ignore continuation lines", func(t *testing.T) { 380 f, err := LoadSources(LoadOptions{ 381 AllowPythonMultilineValues: true, 382 IgnoreContinuation: true, 383 }, []byte(` 384key1=a\b\ 385key2=c\d\ 386key3=value`)) 387 require.NoError(t, err) 388 require.NotNil(t, f) 389 390 assert.Equal(t, `a\b\`, f.Section("").Key("key1").String()) 391 assert.Equal(t, `c\d\`, f.Section("").Key("key2").String()) 392 assert.Equal(t, "value", f.Section("").Key("key3").String()) 393 394 t.Run("inverse case", func(t *testing.T) { 395 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(` 396key1=a\b\ 397key2=c\d\`)) 398 require.NoError(t, err) 399 require.NotNil(t, f) 400 401 assert.Equal(t, `a\bkey2=c\d`, f.Section("").Key("key1").String()) 402 }) 403 }) 404 405 t.Run("ignore inline comments", func(t *testing.T) { 406 f, err := LoadSources(LoadOptions{ 407 AllowPythonMultilineValues: true, 408 IgnoreInlineComment: true, 409 }, []byte(` 410key1=value ;comment 411key2=value2 #comment2 412key3=val#ue #comment3`)) 413 require.NoError(t, err) 414 require.NotNil(t, f) 415 416 assert.Equal(t, `value ;comment`, f.Section("").Key("key1").String()) 417 assert.Equal(t, `value2 #comment2`, f.Section("").Key("key2").String()) 418 assert.Equal(t, `val#ue #comment3`, f.Section("").Key("key3").String()) 419 420 t.Run("inverse case", func(t *testing.T) { 421 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(` 422key1=value ;comment 423key2=value2 #comment2`)) 424 require.NoError(t, err) 425 require.NotNil(t, f) 426 427 assert.Equal(t, `value`, f.Section("").Key("key1").String()) 428 assert.Equal(t, `;comment`, f.Section("").Key("key1").Comment) 429 assert.Equal(t, `value2`, f.Section("").Key("key2").String()) 430 assert.Equal(t, `#comment2`, f.Section("").Key("key2").Comment) 431 }) 432 }) 433 434 t.Run("skip unrecognizable lines", func(t *testing.T) { 435 f, err := LoadSources(LoadOptions{ 436 SkipUnrecognizableLines: true, 437 }, []byte(` 438GenerationDepth: 13 439 440BiomeRarityScale: 100 441 442################ 443# Biome Groups # 444################ 445 446BiomeGroup(NormalBiomes, 3, 99, RoofedForestEnchanted, ForestSakura, FloatingJungle 447BiomeGroup(IceBiomes, 4, 85, Ice Plains) 448`)) 449 require.NoError(t, err) 450 require.NotNil(t, f) 451 452 assert.Equal(t, "13", f.Section("").Key("GenerationDepth").String()) 453 assert.Equal(t, "100", f.Section("").Key("BiomeRarityScale").String()) 454 assert.False(t, f.Section("").HasKey("BiomeGroup")) 455 }) 456 457 t.Run("allow boolean type keys", func(t *testing.T) { 458 f, err := LoadSources(LoadOptions{ 459 AllowPythonMultilineValues: true, 460 AllowBooleanKeys: true, 461 }, []byte(` 462key1=hello 463#key2 464key3`)) 465 require.NoError(t, err) 466 require.NotNil(t, f) 467 468 assert.Equal(t, []string{"key1", "key3"}, f.Section("").KeyStrings()) 469 assert.True(t, f.Section("").Key("key3").MustBool(false)) 470 471 t.Run("write out", func(t *testing.T) { 472 var buf bytes.Buffer 473 _, err := f.WriteTo(&buf) 474 require.NoError(t, err) 475 assert.Equal(t, `key1 = hello 476# key2 477key3 478`, 479 buf.String(), 480 ) 481 }) 482 483 t.Run("inverse case", func(t *testing.T) { 484 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(` 485key1=hello 486#key2 487key3`)) 488 require.Error(t, err) 489 }) 490 }) 491 492 t.Run("allow shadow keys", func(t *testing.T) { 493 f, err := LoadSources(LoadOptions{AllowShadows: true, AllowPythonMultilineValues: true}, []byte(` 494[remote "origin"] 495url = https://github.com/Antergone/test1.git 496url = https://github.com/Antergone/test2.git 497fetch = +refs/heads/*:refs/remotes/origin/*`)) 498 require.NoError(t, err) 499 require.NotNil(t, f) 500 501 assert.Equal(t, "https://github.com/Antergone/test1.git", f.Section(`remote "origin"`).Key("url").String()) 502 assert.Equal( 503 t, 504 []string{ 505 "https://github.com/Antergone/test1.git", 506 "https://github.com/Antergone/test2.git", 507 }, 508 f.Section(`remote "origin"`).Key("url").ValueWithShadows(), 509 ) 510 assert.Equal(t, "+refs/heads/*:refs/remotes/origin/*", f.Section(`remote "origin"`).Key("fetch").String()) 511 512 t.Run("write out", func(t *testing.T) { 513 var buf bytes.Buffer 514 _, err := f.WriteTo(&buf) 515 require.NoError(t, err) 516 assert.Equal(t, `[remote "origin"] 517url = https://github.com/Antergone/test1.git 518url = https://github.com/Antergone/test2.git 519fetch = +refs/heads/*:refs/remotes/origin/* 520 521`, 522 buf.String(), 523 ) 524 }) 525 526 t.Run("inverse case", func(t *testing.T) { 527 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(` 528[remote "origin"] 529url = https://github.com/Antergone/test1.git 530url = https://github.com/Antergone/test2.git`)) 531 require.NoError(t, err) 532 require.NotNil(t, f) 533 534 assert.Equal(t, "https://github.com/Antergone/test2.git", f.Section(`remote "origin"`).Key("url").String()) 535 }) 536 }) 537 538 t.Run("unescape double quotes inside value", func(t *testing.T) { 539 f, err := LoadSources(LoadOptions{ 540 AllowPythonMultilineValues: true, 541 UnescapeValueDoubleQuotes: true, 542 }, []byte(` 543create_repo="创建了仓库 <a href=\"%s\">%s</a>"`)) 544 require.NoError(t, err) 545 require.NotNil(t, f) 546 547 assert.Equal(t, `创建了仓库 <a href="%s">%s</a>`, f.Section("").Key("create_repo").String()) 548 549 t.Run("inverse case", func(t *testing.T) { 550 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(` 551create_repo="创建了仓库 <a href=\"%s\">%s</a>"`)) 552 require.NoError(t, err) 553 require.NotNil(t, f) 554 555 assert.Equal(t, `"创建了仓库 <a href=\"%s\">%s</a>"`, f.Section("").Key("create_repo").String()) 556 }) 557 }) 558 559 t.Run("unescape comment symbols inside value", func(t *testing.T) { 560 f, err := LoadSources(LoadOptions{ 561 AllowPythonMultilineValues: true, 562 IgnoreInlineComment: true, 563 UnescapeValueCommentSymbols: true, 564 }, []byte(` 565key = test value <span style="color: %s\; background: %s">more text</span> 566`)) 567 require.NoError(t, err) 568 require.NotNil(t, f) 569 570 assert.Equal(t, `test value <span style="color: %s; background: %s">more text</span>`, f.Section("").Key("key").String()) 571 }) 572 573 t.Run("can parse small python-compatible INI files", func(t *testing.T) { 574 f, err := LoadSources(LoadOptions{ 575 AllowPythonMultilineValues: true, 576 Insensitive: true, 577 UnparseableSections: []string{"core_lesson", "comments"}, 578 }, []byte(` 579[long] 580long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 581 foo 582 bar 583 foobar 584 barfoo 585 -----END RSA PRIVATE KEY----- 586multiline_list = 587 first 588 second 589 third 590`)) 591 require.NoError(t, err) 592 require.NotNil(t, f) 593 594 assert.Equal(t, "-----BEGIN RSA PRIVATE KEY-----\nfoo\nbar\nfoobar\nbarfoo\n-----END RSA PRIVATE KEY-----", f.Section("long").Key("long_rsa_private_key").String()) 595 assert.Equal(t, "\nfirst\nsecond\nthird", f.Section("long").Key("multiline_list").String()) 596 }) 597 598 t.Run("can parse big python-compatible INI files", func(t *testing.T) { 599 f, err := LoadSources(LoadOptions{ 600 AllowPythonMultilineValues: true, 601 Insensitive: true, 602 UnparseableSections: []string{"core_lesson", "comments"}, 603 }, []byte(` 604[long] 605long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 606 1foo 607 2bar 608 3foobar 609 4barfoo 610 5foo 611 6bar 612 7foobar 613 8barfoo 614 9foo 615 10bar 616 11foobar 617 12barfoo 618 13foo 619 14bar 620 15foobar 621 16barfoo 622 17foo 623 18bar 624 19foobar 625 20barfoo 626 21foo 627 22bar 628 23foobar 629 24barfoo 630 25foo 631 26bar 632 27foobar 633 28barfoo 634 29foo 635 30bar 636 31foobar 637 32barfoo 638 33foo 639 34bar 640 35foobar 641 36barfoo 642 37foo 643 38bar 644 39foobar 645 40barfoo 646 41foo 647 42bar 648 43foobar 649 44barfoo 650 45foo 651 46bar 652 47foobar 653 48barfoo 654 49foo 655 50bar 656 51foobar 657 52barfoo 658 53foo 659 54bar 660 55foobar 661 56barfoo 662 57foo 663 58bar 664 59foobar 665 60barfoo 666 61foo 667 62bar 668 63foobar 669 64barfoo 670 65foo 671 66bar 672 67foobar 673 68barfoo 674 69foo 675 70bar 676 71foobar 677 72barfoo 678 73foo 679 74bar 680 75foobar 681 76barfoo 682 77foo 683 78bar 684 79foobar 685 80barfoo 686 81foo 687 82bar 688 83foobar 689 84barfoo 690 85foo 691 86bar 692 87foobar 693 88barfoo 694 89foo 695 90bar 696 91foobar 697 92barfoo 698 93foo 699 94bar 700 95foobar 701 96barfoo 702 -----END RSA PRIVATE KEY----- 703`)) 704 require.NoError(t, err) 705 require.NotNil(t, f) 706 707 assert.Equal(t, `-----BEGIN RSA PRIVATE KEY----- 7081foo 7092bar 7103foobar 7114barfoo 7125foo 7136bar 7147foobar 7158barfoo 7169foo 71710bar 71811foobar 71912barfoo 72013foo 72114bar 72215foobar 72316barfoo 72417foo 72518bar 72619foobar 72720barfoo 72821foo 72922bar 73023foobar 73124barfoo 73225foo 73326bar 73427foobar 73528barfoo 73629foo 73730bar 73831foobar 73932barfoo 74033foo 74134bar 74235foobar 74336barfoo 74437foo 74538bar 74639foobar 74740barfoo 74841foo 74942bar 75043foobar 75144barfoo 75245foo 75346bar 75447foobar 75548barfoo 75649foo 75750bar 75851foobar 75952barfoo 76053foo 76154bar 76255foobar 76356barfoo 76457foo 76558bar 76659foobar 76760barfoo 76861foo 76962bar 77063foobar 77164barfoo 77265foo 77366bar 77467foobar 77568barfoo 77669foo 77770bar 77871foobar 77972barfoo 78073foo 78174bar 78275foobar 78376barfoo 78477foo 78578bar 78679foobar 78780barfoo 78881foo 78982bar 79083foobar 79184barfoo 79285foo 79386bar 79487foobar 79588barfoo 79689foo 79790bar 79891foobar 79992barfoo 80093foo 80194bar 80295foobar 80396barfoo 804-----END RSA PRIVATE KEY-----`, 805 f.Section("long").Key("long_rsa_private_key").String(), 806 ) 807 }) 808 809 t.Run("allow unparsable sections", func(t *testing.T) { 810 f, err := LoadSources(LoadOptions{ 811 AllowPythonMultilineValues: true, 812 Insensitive: true, 813 UnparseableSections: []string{"core_lesson", "comments"}, 814 }, []byte(` 815Lesson_Location = 87 816Lesson_Status = C 817Score = 3 818Time = 00:02:30 819 820[CORE_LESSON] 821my lesson state data – 1111111111111111111000000000000000001110000 822111111111111111111100000000000111000000000 – end my lesson state data 823 824[COMMENTS] 825<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)) 826 require.NoError(t, err) 827 require.NotNil(t, f) 828 829 assert.Equal(t, "3", f.Section("").Key("score").String()) 830 assert.Empty(t, f.Section("").Body()) 831 assert.Equal(t, `my lesson state data – 1111111111111111111000000000000000001110000 832111111111111111111100000000000111000000000 – end my lesson state data`, 833 f.Section("core_lesson").Body(), 834 ) 835 assert.Equal(t, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`, f.Section("comments").Body()) 836 837 t.Run("write out", func(t *testing.T) { 838 var buf bytes.Buffer 839 _, err := f.WriteTo(&buf) 840 require.NoError(t, err) 841 assert.Equal(t, `lesson_location = 87 842lesson_status = C 843score = 3 844time = 00:02:30 845 846[core_lesson] 847my lesson state data – 1111111111111111111000000000000000001110000 848111111111111111111100000000000111000000000 – end my lesson state data 849 850[comments] 851<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1> 852`, 853 buf.String(), 854 ) 855 }) 856 857 t.Run("inverse case", func(t *testing.T) { 858 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: true}, []byte(` 859[CORE_LESSON] 860my lesson state data – 1111111111111111111000000000000000001110000 861111111111111111111100000000000111000000000 – end my lesson state data`)) 862 require.Error(t, err) 863 }) 864 }) 865 866 t.Run("and false `SpaceBeforeInlineComment`", func(t *testing.T) { 867 t.Run("cannot parse INI files containing `#` or `;` in value", func(t *testing.T) { 868 f, err := LoadSources( 869 LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: false}, 870 []byte(` 871[author] 872NAME = U#n#k#n#w#o#n 873GITHUB = U;n;k;n;w;o;n 874`)) 875 require.NoError(t, err) 876 require.NotNil(t, f) 877 sec := f.Section("author") 878 nameValue := sec.Key("NAME").String() 879 githubValue := sec.Key("GITHUB").String() 880 assert.Equal(t, "U", nameValue) 881 assert.Equal(t, "U", githubValue) 882 }) 883 }) 884 885 t.Run("and true `SpaceBeforeInlineComment`", func(t *testing.T) { 886 t.Run("can parse INI files containing `#` or `;` in value", func(t *testing.T) { 887 f, err := LoadSources( 888 LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: true}, 889 []byte(` 890[author] 891NAME = U#n#k#n#w#o#n 892GITHUB = U;n;k;n;w;o;n 893`)) 894 require.NoError(t, err) 895 require.NotNil(t, f) 896 sec := f.Section("author") 897 nameValue := sec.Key("NAME").String() 898 githubValue := sec.Key("GITHUB").String() 899 assert.Equal(t, "U#n#k#n#w#o#n", nameValue) 900 assert.Equal(t, "U;n;k;n;w;o;n", githubValue) 901 }) 902 }) 903 }) 904 905 t.Run("with false `AllowPythonMultilineValues`", func(t *testing.T) { 906 t.Run("ignore nonexistent files", func(t *testing.T) { 907 f, err := LoadSources(LoadOptions{ 908 AllowPythonMultilineValues: false, 909 Loose: true, 910 }, notFoundConf, minimalConf) 911 require.NoError(t, err) 912 require.NotNil(t, f) 913 914 t.Run("inverse case", func(t *testing.T) { 915 _, err = LoadSources(LoadOptions{ 916 AllowPythonMultilineValues: false, 917 }, notFoundConf) 918 require.Error(t, err) 919 }) 920 }) 921 922 t.Run("insensitive to section and key names", func(t *testing.T) { 923 f, err := LoadSources(LoadOptions{ 924 AllowPythonMultilineValues: false, 925 Insensitive: true, 926 }, minimalConf) 927 require.NoError(t, err) 928 require.NotNil(t, f) 929 930 assert.Equal(t, "u@gogs.io", f.Section("Author").Key("e-mail").String()) 931 932 t.Run("write out", func(t *testing.T) { 933 var buf bytes.Buffer 934 _, err := f.WriteTo(&buf) 935 require.NoError(t, err) 936 assert.Equal(t, `[author] 937e-mail = u@gogs.io 938 939`, 940 buf.String(), 941 ) 942 }) 943 944 t.Run("inverse case", func(t *testing.T) { 945 f, err := LoadSources(LoadOptions{ 946 AllowPythonMultilineValues: false, 947 }, minimalConf) 948 require.NoError(t, err) 949 require.NotNil(t, f) 950 951 assert.Empty(t, f.Section("Author").Key("e-mail").String()) 952 }) 953 }) 954 955 t.Run("ignore continuation lines", func(t *testing.T) { 956 f, err := LoadSources(LoadOptions{ 957 AllowPythonMultilineValues: false, 958 IgnoreContinuation: true, 959 }, []byte(` 960key1=a\b\ 961key2=c\d\ 962key3=value`)) 963 require.NoError(t, err) 964 require.NotNil(t, f) 965 966 assert.Equal(t, `a\b\`, f.Section("").Key("key1").String()) 967 assert.Equal(t, `c\d\`, f.Section("").Key("key2").String()) 968 assert.Equal(t, "value", f.Section("").Key("key3").String()) 969 970 t.Run("inverse case", func(t *testing.T) { 971 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(` 972key1=a\b\ 973key2=c\d\`)) 974 require.NoError(t, err) 975 require.NotNil(t, f) 976 977 assert.Equal(t, `a\bkey2=c\d`, f.Section("").Key("key1").String()) 978 }) 979 }) 980 981 t.Run("ignore inline comments", func(t *testing.T) { 982 f, err := LoadSources(LoadOptions{ 983 AllowPythonMultilineValues: false, 984 IgnoreInlineComment: true, 985 }, []byte(` 986key1=value ;comment 987key2=value2 #comment2 988key3=val#ue #comment3`)) 989 require.NoError(t, err) 990 require.NotNil(t, f) 991 992 assert.Equal(t, `value ;comment`, f.Section("").Key("key1").String()) 993 assert.Equal(t, `value2 #comment2`, f.Section("").Key("key2").String()) 994 assert.Equal(t, `val#ue #comment3`, f.Section("").Key("key3").String()) 995 996 t.Run("inverse case", func(t *testing.T) { 997 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(` 998key1=value ;comment 999key2=value2 #comment2`)) 1000 require.NoError(t, err) 1001 require.NotNil(t, f) 1002 1003 assert.Equal(t, `value`, f.Section("").Key("key1").String()) 1004 assert.Equal(t, `;comment`, f.Section("").Key("key1").Comment) 1005 assert.Equal(t, `value2`, f.Section("").Key("key2").String()) 1006 assert.Equal(t, `#comment2`, f.Section("").Key("key2").Comment) 1007 }) 1008 }) 1009 1010 t.Run("allow boolean type keys", func(t *testing.T) { 1011 f, err := LoadSources(LoadOptions{ 1012 AllowPythonMultilineValues: false, 1013 AllowBooleanKeys: true, 1014 }, []byte(` 1015key1=hello 1016#key2 1017key3`)) 1018 require.NoError(t, err) 1019 require.NotNil(t, f) 1020 1021 assert.Equal(t, []string{"key1", "key3"}, f.Section("").KeyStrings()) 1022 assert.True(t, f.Section("").Key("key3").MustBool(false)) 1023 1024 t.Run("write out", func(t *testing.T) { 1025 var buf bytes.Buffer 1026 _, err := f.WriteTo(&buf) 1027 require.NoError(t, err) 1028 assert.Equal(t, `key1 = hello 1029# key2 1030key3 1031`, 1032 buf.String(), 1033 ) 1034 }) 1035 1036 t.Run("inverse case", func(t *testing.T) { 1037 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(` 1038key1=hello 1039#key2 1040key3`)) 1041 require.Error(t, err) 1042 }) 1043 }) 1044 1045 t.Run("allow shadow keys", func(t *testing.T) { 1046 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false, AllowShadows: true}, []byte(` 1047[remote "origin"] 1048url = https://github.com/Antergone/test1.git 1049url = https://github.com/Antergone/test2.git 1050fetch = +refs/heads/*:refs/remotes/origin/*`)) 1051 require.NoError(t, err) 1052 require.NotNil(t, f) 1053 1054 assert.Equal(t, "https://github.com/Antergone/test1.git", f.Section(`remote "origin"`).Key("url").String()) 1055 assert.Equal( 1056 t, 1057 []string{ 1058 "https://github.com/Antergone/test1.git", 1059 "https://github.com/Antergone/test2.git", 1060 }, 1061 f.Section(`remote "origin"`).Key("url").ValueWithShadows(), 1062 ) 1063 assert.Equal(t, "+refs/heads/*:refs/remotes/origin/*", f.Section(`remote "origin"`).Key("fetch").String()) 1064 1065 t.Run("write out", func(t *testing.T) { 1066 var buf bytes.Buffer 1067 _, err := f.WriteTo(&buf) 1068 require.NoError(t, err) 1069 assert.Equal(t, `[remote "origin"] 1070url = https://github.com/Antergone/test1.git 1071url = https://github.com/Antergone/test2.git 1072fetch = +refs/heads/*:refs/remotes/origin/* 1073 1074`, 1075 buf.String(), 1076 ) 1077 }) 1078 1079 t.Run("inverse case", func(t *testing.T) { 1080 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(` 1081[remote "origin"] 1082url = https://github.com/Antergone/test1.git 1083url = https://github.com/Antergone/test2.git`)) 1084 require.NoError(t, err) 1085 require.NotNil(t, f) 1086 1087 assert.Equal(t, "https://github.com/Antergone/test2.git", f.Section(`remote "origin"`).Key("url").String()) 1088 }) 1089 }) 1090 1091 t.Run("unescape double quotes inside value", func(t *testing.T) { 1092 f, err := LoadSources(LoadOptions{ 1093 AllowPythonMultilineValues: false, 1094 UnescapeValueDoubleQuotes: true, 1095 }, []byte(` 1096create_repo="创建了仓库 <a href=\"%s\">%s</a>"`)) 1097 require.NoError(t, err) 1098 require.NotNil(t, f) 1099 1100 assert.Equal(t, `创建了仓库 <a href="%s">%s</a>`, f.Section("").Key("create_repo").String()) 1101 1102 t.Run("inverse case", func(t *testing.T) { 1103 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(` 1104create_repo="创建了仓库 <a href=\"%s\">%s</a>"`)) 1105 require.NoError(t, err) 1106 require.NotNil(t, f) 1107 1108 assert.Equal(t, `"创建了仓库 <a href=\"%s\">%s</a>"`, f.Section("").Key("create_repo").String()) 1109 }) 1110 }) 1111 1112 t.Run("unescape comment symbols inside value", func(t *testing.T) { 1113 f, err := LoadSources(LoadOptions{ 1114 AllowPythonMultilineValues: false, 1115 IgnoreInlineComment: true, 1116 UnescapeValueCommentSymbols: true, 1117 }, []byte(` 1118key = test value <span style="color: %s\; background: %s">more text</span> 1119`)) 1120 require.NoError(t, err) 1121 require.NotNil(t, f) 1122 1123 assert.Equal(t, `test value <span style="color: %s; background: %s">more text</span>`, f.Section("").Key("key").String()) 1124 }) 1125 1126 t.Run("cannot parse small python-compatible INI files", func(t *testing.T) { 1127 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(` 1128[long] 1129long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 1130 foo 1131 bar 1132 foobar 1133 barfoo 1134 -----END RSA PRIVATE KEY----- 1135`)) 1136 require.Error(t, err) 1137 assert.Nil(t, f) 1138 assert.Equal(t, "key-value delimiter not found: foo\n", err.Error()) 1139 }) 1140 1141 t.Run("cannot parse big python-compatible INI files", func(t *testing.T) { 1142 f, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(` 1143[long] 1144long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 1145 1foo 1146 2bar 1147 3foobar 1148 4barfoo 1149 5foo 1150 6bar 1151 7foobar 1152 8barfoo 1153 9foo 1154 10bar 1155 11foobar 1156 12barfoo 1157 13foo 1158 14bar 1159 15foobar 1160 16barfoo 1161 17foo 1162 18bar 1163 19foobar 1164 20barfoo 1165 21foo 1166 22bar 1167 23foobar 1168 24barfoo 1169 25foo 1170 26bar 1171 27foobar 1172 28barfoo 1173 29foo 1174 30bar 1175 31foobar 1176 32barfoo 1177 33foo 1178 34bar 1179 35foobar 1180 36barfoo 1181 37foo 1182 38bar 1183 39foobar 1184 40barfoo 1185 41foo 1186 42bar 1187 43foobar 1188 44barfoo 1189 45foo 1190 46bar 1191 47foobar 1192 48barfoo 1193 49foo 1194 50bar 1195 51foobar 1196 52barfoo 1197 53foo 1198 54bar 1199 55foobar 1200 56barfoo 1201 57foo 1202 58bar 1203 59foobar 1204 60barfoo 1205 61foo 1206 62bar 1207 63foobar 1208 64barfoo 1209 65foo 1210 66bar 1211 67foobar 1212 68barfoo 1213 69foo 1214 70bar 1215 71foobar 1216 72barfoo 1217 73foo 1218 74bar 1219 75foobar 1220 76barfoo 1221 77foo 1222 78bar 1223 79foobar 1224 80barfoo 1225 81foo 1226 82bar 1227 83foobar 1228 84barfoo 1229 85foo 1230 86bar 1231 87foobar 1232 88barfoo 1233 89foo 1234 90bar 1235 91foobar 1236 92barfoo 1237 93foo 1238 94bar 1239 95foobar 1240 96barfoo 1241 -----END RSA PRIVATE KEY----- 1242`)) 1243 require.Error(t, err) 1244 assert.Nil(t, f) 1245 assert.Equal(t, "key-value delimiter not found: 1foo\n", err.Error()) 1246 }) 1247 1248 t.Run("allow unparsable sections", func(t *testing.T) { 1249 f, err := LoadSources(LoadOptions{ 1250 AllowPythonMultilineValues: false, 1251 Insensitive: true, 1252 UnparseableSections: []string{"core_lesson", "comments"}, 1253 }, []byte(` 1254Lesson_Location = 87 1255Lesson_Status = C 1256Score = 3 1257Time = 00:02:30 1258 1259[CORE_LESSON] 1260my lesson state data – 1111111111111111111000000000000000001110000 1261111111111111111111100000000000111000000000 – end my lesson state data 1262 1263[COMMENTS] 1264<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)) 1265 require.NoError(t, err) 1266 require.NotNil(t, f) 1267 1268 assert.Equal(t, "3", f.Section("").Key("score").String()) 1269 assert.Empty(t, f.Section("").Body()) 1270 assert.Equal(t, `my lesson state data – 1111111111111111111000000000000000001110000 1271111111111111111111100000000000111000000000 – end my lesson state data`, 1272 f.Section("core_lesson").Body(), 1273 ) 1274 assert.Equal(t, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`, f.Section("comments").Body()) 1275 1276 t.Run("write out", func(t *testing.T) { 1277 var buf bytes.Buffer 1278 _, err := f.WriteTo(&buf) 1279 require.NoError(t, err) 1280 assert.Equal(t, `lesson_location = 87 1281lesson_status = C 1282score = 3 1283time = 00:02:30 1284 1285[core_lesson] 1286my lesson state data – 1111111111111111111000000000000000001110000 1287111111111111111111100000000000111000000000 – end my lesson state data 1288 1289[comments] 1290<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1> 1291`, 1292 buf.String(), 1293 ) 1294 }) 1295 1296 t.Run("inverse case", func(t *testing.T) { 1297 _, err := LoadSources(LoadOptions{AllowPythonMultilineValues: false}, []byte(` 1298[CORE_LESSON] 1299my lesson state data – 1111111111111111111000000000000000001110000 1300111111111111111111100000000000111000000000 – end my lesson state data`)) 1301 require.Error(t, err) 1302 }) 1303 }) 1304 1305 t.Run("and false `SpaceBeforeInlineComment`", func(t *testing.T) { 1306 t.Run("cannot parse INI files containing `#` or `;` in value", func(t *testing.T) { 1307 f, err := LoadSources( 1308 LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: false}, 1309 []byte(` 1310[author] 1311NAME = U#n#k#n#w#o#n 1312GITHUB = U;n;k;n;w;o;n 1313`)) 1314 require.NoError(t, err) 1315 require.NotNil(t, f) 1316 sec := f.Section("author") 1317 nameValue := sec.Key("NAME").String() 1318 githubValue := sec.Key("GITHUB").String() 1319 assert.Equal(t, "U", nameValue) 1320 assert.Equal(t, "U", githubValue) 1321 }) 1322 }) 1323 1324 t.Run("and true `SpaceBeforeInlineComment`", func(t *testing.T) { 1325 t.Run("can parse INI files containing `#` or `;` in value", func(t *testing.T) { 1326 f, err := LoadSources( 1327 LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: true}, 1328 []byte(` 1329[author] 1330NAME = U#n#k#n#w#o#n 1331GITHUB = U;n;k;n;w;o;n 1332`)) 1333 require.NoError(t, err) 1334 require.NotNil(t, f) 1335 sec := f.Section("author") 1336 nameValue := sec.Key("NAME").String() 1337 githubValue := sec.Key("GITHUB").String() 1338 assert.Equal(t, "U#n#k#n#w#o#n", nameValue) 1339 assert.Equal(t, "U;n;k;n;w;o;n", githubValue) 1340 }) 1341 }) 1342 }) 1343 1344 t.Run("with `ChildSectionDelimiter` ':'", func(t *testing.T) { 1345 t.Run("get all keys of parent sections", func(t *testing.T) { 1346 f := Empty(LoadOptions{ChildSectionDelimiter: ":"}) 1347 require.NotNil(t, f) 1348 1349 k, err := f.Section("package").NewKey("NAME", "ini") 1350 require.NoError(t, err) 1351 assert.NotNil(t, k) 1352 k, err = f.Section("package").NewKey("VERSION", "v1") 1353 require.NoError(t, err) 1354 assert.NotNil(t, k) 1355 k, err = f.Section("package").NewKey("IMPORT_PATH", "gopkg.in/ini.v1") 1356 require.NoError(t, err) 1357 assert.NotNil(t, k) 1358 1359 keys := f.Section("package:sub:sub2").ParentKeys() 1360 names := []string{"NAME", "VERSION", "IMPORT_PATH"} 1361 assert.Equal(t, len(names), len(keys)) 1362 for i, name := range names { 1363 assert.Equal(t, name, keys[i].Name()) 1364 } 1365 }) 1366 1367 t.Run("getting and setting values", func(t *testing.T) { 1368 f, err := LoadSources(LoadOptions{ChildSectionDelimiter: ":"}, fullConf) 1369 require.NoError(t, err) 1370 require.NotNil(t, f) 1371 1372 t.Run("get parent-keys that are available to the child section", func(t *testing.T) { 1373 parentKeys := f.Section("package:sub").ParentKeys() 1374 assert.NotNil(t, parentKeys) 1375 for _, k := range parentKeys { 1376 assert.Equal(t, "CLONE_URL", k.Name()) 1377 } 1378 }) 1379 1380 t.Run("get parent section value", func(t *testing.T) { 1381 assert.Equal(t, "https://gopkg.in/ini.v1", f.Section("package:sub").Key("CLONE_URL").String()) 1382 assert.Equal(t, "https://gopkg.in/ini.v1", f.Section("package:fake:sub").Key("CLONE_URL").String()) 1383 }) 1384 }) 1385 1386 t.Run("get child sections by parent name", func(t *testing.T) { 1387 f, err := LoadSources(LoadOptions{ChildSectionDelimiter: ":"}, []byte(` 1388[node] 1389[node:biz1] 1390[node:biz2] 1391[node.biz3] 1392[node.bizN] 1393`)) 1394 require.NoError(t, err) 1395 require.NotNil(t, f) 1396 1397 children := f.ChildSections("node") 1398 names := []string{"node:biz1", "node:biz2"} 1399 assert.Equal(t, len(names), len(children)) 1400 for i, name := range names { 1401 assert.Equal(t, name, children[i].Name()) 1402 } 1403 }) 1404 }) 1405 1406 t.Run("ShortCircuit", func(t *testing.T) { 1407 t.Run("load the first available configuration, ignore other configuration", func(t *testing.T) { 1408 f, err := LoadSources(LoadOptions{ShortCircuit: true}, minimalConf, []byte(`key1 = value1`)) 1409 require.NotNil(t, f) 1410 require.NoError(t, err) 1411 var buf bytes.Buffer 1412 _, err = f.WriteTo(&buf) 1413 require.NoError(t, err) 1414 assert.Equal(t, `[author] 1415E-MAIL = u@gogs.io 1416 1417`, 1418 buf.String(), 1419 ) 1420 }) 1421 1422 t.Run("return an error when fail to load", func(t *testing.T) { 1423 f, err := LoadSources(LoadOptions{ShortCircuit: true}, notFoundConf, minimalConf) 1424 assert.Nil(t, f) 1425 require.Error(t, err) 1426 }) 1427 1428 t.Run("used with Loose to ignore errors that the file does not exist", func(t *testing.T) { 1429 f, err := LoadSources(LoadOptions{ShortCircuit: true, Loose: true}, notFoundConf, minimalConf) 1430 require.NotNil(t, f) 1431 require.NoError(t, err) 1432 var buf bytes.Buffer 1433 _, err = f.WriteTo(&buf) 1434 require.NoError(t, err) 1435 assert.Equal(t, `[author] 1436E-MAIL = u@gogs.io 1437 1438`, 1439 buf.String(), 1440 ) 1441 }) 1442 1443 t.Run("ensure all sources are loaded without ShortCircuit", func(t *testing.T) { 1444 f, err := LoadSources(LoadOptions{ShortCircuit: false}, minimalConf, []byte(`key1 = value1`)) 1445 require.NotNil(t, f) 1446 require.NoError(t, err) 1447 var buf bytes.Buffer 1448 _, err = f.WriteTo(&buf) 1449 require.NoError(t, err) 1450 assert.Equal(t, `key1 = value1 1451 1452[author] 1453E-MAIL = u@gogs.io 1454 1455`, 1456 buf.String(), 1457 ) 1458 }) 1459 }) 1460} 1461 1462func Test_KeyValueDelimiters(t *testing.T) { 1463 t.Run("custom key-value delimiters", func(t *testing.T) { 1464 f, err := LoadSources(LoadOptions{ 1465 KeyValueDelimiters: "?!", 1466 }, []byte(` 1467[section] 1468key1?value1 1469key2!value2 1470`)) 1471 require.NoError(t, err) 1472 require.NotNil(t, f) 1473 1474 assert.Equal(t, "value1", f.Section("section").Key("key1").String()) 1475 assert.Equal(t, "value2", f.Section("section").Key("key2").String()) 1476 }) 1477} 1478 1479func Test_PreserveSurroundedQuote(t *testing.T) { 1480 t.Run("preserve surrounded quote test", func(t *testing.T) { 1481 f, err := LoadSources(LoadOptions{ 1482 PreserveSurroundedQuote: true, 1483 }, []byte(` 1484[section] 1485key1 = "value1" 1486key2 = value2 1487`)) 1488 require.NoError(t, err) 1489 require.NotNil(t, f) 1490 1491 assert.Equal(t, "\"value1\"", f.Section("section").Key("key1").String()) 1492 assert.Equal(t, "value2", f.Section("section").Key("key2").String()) 1493 }) 1494 1495 t.Run("preserve surrounded quote test inverse test", func(t *testing.T) { 1496 f, err := LoadSources(LoadOptions{ 1497 PreserveSurroundedQuote: false, 1498 }, []byte(` 1499[section] 1500key1 = "value1" 1501key2 = value2 1502`)) 1503 require.NoError(t, err) 1504 require.NotNil(t, f) 1505 1506 assert.Equal(t, "value1", f.Section("section").Key("key1").String()) 1507 assert.Equal(t, "value2", f.Section("section").Key("key2").String()) 1508 }) 1509} 1510 1511type testData struct { 1512 Value1 string `ini:"value1"` 1513 Value2 string `ini:"value2"` 1514 Value3 string `ini:"value3"` 1515} 1516 1517func TestPythonMultiline(t *testing.T) { 1518 if runtime.GOOS == "windows" { 1519 t.Skip("Skipping testing on Windows") 1520 } 1521 1522 path := filepath.Join("testdata", "multiline.ini") 1523 f, err := LoadSources(LoadOptions{ 1524 AllowPythonMultilineValues: true, 1525 ReaderBufferSize: 64 * 1024, 1526 }, path) 1527 require.NoError(t, err) 1528 require.NotNil(t, f) 1529 assert.Len(t, f.Sections(), 1) 1530 1531 defaultSection := f.Section("") 1532 assert.NotNil(t, f.Section("")) 1533 1534 var testData testData 1535 err = defaultSection.MapTo(&testData) 1536 require.NoError(t, err) 1537 assert.Equal(t, "some text here\nsome more text here\n\nthere is an empty line above and below\n", testData.Value1) 1538 assert.Equal(t, "there is an empty line above\nthat is not indented so it should not be part\nof the value", testData.Value2) 1539 assert.Equal(t, `. 1540 1541Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Eu consequat ac felis donec et odio pellentesque diam volutpat. Mauris commodo quis imperdiet massa tincidunt nunc. Interdum velit euismod in pellentesque. Nisl condimentum id venenatis a condimentum vitae sapien pellentesque. Nascetur ridiculus mus mauris vitae. Posuere urna nec tincidunt praesent semper feugiat. Lorem donec massa sapien faucibus et molestie ac feugiat sed. Ipsum dolor sit amet consectetur adipiscing elit. Enim sed faucibus turpis in eu mi. A diam sollicitudin tempor id. Quam nulla porttitor massa id neque aliquam vestibulum morbi blandit. 1542 1543Lectus sit amet est placerat in egestas. At risus viverra adipiscing at in tellus integer. Tristique senectus et netus et malesuada fames ac. In hac habitasse platea dictumst. Purus in mollis nunc sed. Pellentesque sit amet porttitor eget dolor morbi. Elit at imperdiet dui accumsan sit amet nulla. Cursus in hac habitasse platea dictumst. Bibendum arcu vitae elementum curabitur. Faucibus ornare suspendisse sed nisi lacus. In vitae turpis massa sed. Libero nunc consequat interdum varius sit amet. Molestie a iaculis at erat pellentesque. 1544 1545Dui faucibus in ornare quam viverra orci sagittis eu. Purus in mollis nunc sed id semper. Sed arcu non odio euismod lacinia at. Quis commodo odio aenean sed adipiscing diam donec. Quisque id diam vel quam elementum pulvinar. Lorem ipsum dolor sit amet. Purus ut faucibus pulvinar elementum integer enim neque volutpat ac. Fermentum posuere urna nec tincidunt praesent semper feugiat nibh sed. Gravida rutrum quisque non tellus orci. Ipsum dolor sit amet consectetur adipiscing elit pellentesque habitant. Et sollicitudin ac orci phasellus egestas tellus rutrum tellus pellentesque. Eget gravida cum sociis natoque penatibus et magnis. Elementum eu facilisis sed odio morbi quis commodo. Mollis nunc sed id semper risus in hendrerit gravida rutrum. Lorem dolor sed viverra ipsum. 1546 1547Pellentesque adipiscing commodo elit at imperdiet dui accumsan sit amet. Justo eget magna fermentum iaculis eu non diam. Condimentum mattis pellentesque id nibh tortor id aliquet lectus. Tellus molestie nunc non blandit massa enim. Mauris ultrices eros in cursus turpis. Purus viverra accumsan in nisl nisi scelerisque. Quis lectus nulla at volutpat. Purus ut faucibus pulvinar elementum integer enim. In pellentesque massa placerat duis ultricies lacus sed turpis. Elit sed vulputate mi sit amet mauris commodo. Tellus elementum sagittis vitae et. Duis tristique sollicitudin nibh sit amet commodo nulla facilisi nullam. Lectus vestibulum mattis ullamcorper velit sed ullamcorper morbi tincidunt ornare. Libero id faucibus nisl tincidunt eget nullam. Mattis aliquam faucibus purus in massa tempor. Fames ac turpis egestas sed tempus urna. Gravida in fermentum et sollicitudin ac orci phasellus egestas. 1548 1549Blandit turpis cursus in hac habitasse. Sed id semper risus in. Amet porttitor eget dolor morbi non arcu. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt. Ut morbi tincidunt augue interdum velit. Lorem mollis aliquam ut porttitor leo a. Nunc eget lorem dolor sed viverra. Scelerisque mauris pellentesque pulvinar pellentesque. Elit at imperdiet dui accumsan sit amet. Eget magna fermentum iaculis eu non diam phasellus vestibulum lorem. Laoreet non curabitur gravida arcu ac tortor dignissim. Tortor pretium viverra suspendisse potenti nullam ac tortor vitae purus. Lacus sed viverra tellus in hac habitasse platea dictumst vestibulum. Viverra adipiscing at in tellus. Duis at tellus at urna condimentum. Eget gravida cum sociis natoque penatibus et magnis dis parturient. Pharetra massa massa ultricies mi quis hendrerit. 1550 1551Mauris pellentesque pulvinar pellentesque habitant morbi tristique. Maecenas volutpat blandit aliquam etiam. Sed turpis tincidunt id aliquet. Eget duis at tellus at urna condimentum. Pellentesque habitant morbi tristique senectus et. Amet aliquam id diam maecenas. Volutpat est velit egestas dui id. Vulputate eu scelerisque felis imperdiet proin fermentum leo vel orci. Massa sed elementum tempus egestas sed sed risus pretium. Quam quisque id diam vel quam elementum pulvinar etiam non. Sapien faucibus et molestie ac. Ipsum dolor sit amet consectetur adipiscing. Viverra orci sagittis eu volutpat. Leo urna molestie at elementum. Commodo viverra maecenas accumsan lacus. Non sodales neque sodales ut etiam sit amet. Habitant morbi tristique senectus et netus et malesuada fames. Habitant morbi tristique senectus et netus et malesuada. Blandit aliquam etiam erat velit scelerisque in. Varius duis at consectetur lorem donec massa sapien faucibus et. 1552 1553Augue mauris augue neque gravida in. Odio ut sem nulla pharetra diam sit amet nisl suscipit. Nulla aliquet enim tortor at auctor urna nunc id. Morbi tristique senectus et netus et malesuada fames ac. Quam id leo in vitae turpis massa sed elementum tempus. Ipsum faucibus vitae aliquet nec ullamcorper sit amet risus nullam. Maecenas volutpat blandit aliquam etiam erat velit scelerisque in. Sagittis nisl rhoncus mattis rhoncus urna neque viverra justo. Massa tempor nec feugiat nisl pretium. Vulputate sapien nec sagittis aliquam malesuada bibendum arcu vitae elementum. Enim lobortis scelerisque fermentum dui faucibus in ornare. Faucibus ornare suspendisse sed nisi lacus. Morbi tristique senectus et netus et malesuada fames. Malesuada pellentesque elit eget gravida cum sociis natoque penatibus et. Dictum non consectetur a erat nam at. Leo urna molestie at elementum eu facilisis sed odio morbi. Quam id leo in vitae turpis massa. Neque egestas congue quisque egestas diam in arcu. Varius morbi enim nunc faucibus a pellentesque sit. Aliquet enim tortor at auctor urna. 1554 1555Elit scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique. Luctus accumsan tortor posuere ac. Eu ultrices vitae auctor eu augue ut lectus arcu bibendum. Pretium nibh ipsum consequat nisl vel pretium lectus. Aliquam etiam erat velit scelerisque in dictum. Sem et tortor consequat id porta nibh venenatis cras sed. A scelerisque purus semper eget duis at tellus at urna. At auctor urna nunc id. Ornare quam viverra orci sagittis eu volutpat odio. Nisl purus in mollis nunc sed id semper. Ornare suspendisse sed nisi lacus sed. Consectetur lorem donec massa sapien faucibus et. Ipsum dolor sit amet consectetur adipiscing elit ut. Porta nibh venenatis cras sed. Dignissim diam quis enim lobortis scelerisque. Quam nulla porttitor massa id. Tellus molestie nunc non blandit massa. 1556 1557Malesuada fames ac turpis egestas. Suscipit tellus mauris a diam maecenas. Turpis in eu mi bibendum neque egestas. Venenatis tellus in metus vulputate eu scelerisque felis imperdiet. Quis imperdiet massa tincidunt nunc pulvinar sapien et. Urna duis convallis convallis tellus id. Velit egestas dui id ornare arcu odio. Consectetur purus ut faucibus pulvinar elementum integer enim neque. Aenean sed adipiscing diam donec adipiscing tristique. Tortor aliquam nulla facilisi cras fermentum odio eu. Diam in arcu cursus euismod quis viverra nibh cras. 1558 1559Id ornare arcu odio ut sem. Arcu dictum varius duis at consectetur lorem donec massa sapien. Proin libero nunc consequat interdum varius sit. Ut eu sem integer vitae justo. Vitae elementum curabitur vitae nunc. Diam quam nulla porttitor massa. Lectus mauris ultrices eros in cursus turpis massa tincidunt dui. Natoque penatibus et magnis dis parturient montes. Pellentesque habitant morbi tristique senectus et netus et malesuada fames. Libero nunc consequat interdum varius sit. Rhoncus dolor purus non enim praesent. Pellentesque sit amet porttitor eget. Nibh tortor id aliquet lectus proin nibh. Fermentum iaculis eu non diam phasellus vestibulum lorem sed. 1560 1561Eu feugiat pretium nibh ipsum consequat nisl vel pretium lectus. Habitant morbi tristique senectus et netus et malesuada fames ac. Urna condimentum mattis pellentesque id. Lorem sed risus ultricies tristique nulla aliquet enim tortor at. Ipsum dolor sit amet consectetur adipiscing elit. Convallis a cras semper auctor neque vitae tempus quam. A diam sollicitudin tempor id eu nisl nunc mi ipsum. Maecenas sed enim ut sem viverra aliquet eget. Massa enim nec dui nunc mattis enim. Nam aliquam sem et tortor consequat. Adipiscing commodo elit at imperdiet dui accumsan sit amet nulla. Nullam eget felis eget nunc lobortis. Mauris a diam maecenas sed enim ut sem viverra. Ornare massa eget egestas purus. In hac habitasse platea dictumst. Ut tortor pretium viverra suspendisse potenti nullam ac tortor. Nisl nunc mi ipsum faucibus. At varius vel pharetra vel. Mauris ultrices eros in cursus turpis massa tincidunt.`, 1562 testData.Value3, 1563 ) 1564} 1565