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_test 16 17import ( 18 "bytes" 19 "flag" 20 "io/ioutil" 21 "testing" 22 23 . "github.com/smartystreets/goconvey/convey" 24 "gopkg.in/ini.v1" 25) 26 27const ( 28 confData = ` 29 ; Package name 30 NAME = ini 31 ; Package version 32 VERSION = v1 33 ; Package import path 34 IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s 35 36 # Information about package author 37 # Bio can be written in multiple lines. 38 [author] 39 NAME = Unknwon ; Succeeding comment 40 E-MAIL = fake@localhost 41 GITHUB = https://github.com/%(NAME)s 42 BIO = """Gopher. 43 Coding addict. 44 Good man. 45 """ # Succeeding comment` 46 minimalConf = "testdata/minimal.ini" 47 fullConf = "testdata/full.ini" 48 notFoundConf = "testdata/404.ini" 49) 50 51var update = flag.Bool("update", false, "Update .golden files") 52 53func TestLoad(t *testing.T) { 54 Convey("Load from good data sources", t, func() { 55 f, err := ini.Load([]byte(` 56NAME = ini 57VERSION = v1 58IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s`), 59 "testdata/minimal.ini", 60 ioutil.NopCloser(bytes.NewReader([]byte(` 61[author] 62NAME = Unknwon 63`))), 64 ) 65 So(err, ShouldBeNil) 66 So(f, ShouldNotBeNil) 67 68 // Validate values make sure all sources are loaded correctly 69 sec := f.Section("") 70 So(sec.Key("NAME").String(), ShouldEqual, "ini") 71 So(sec.Key("VERSION").String(), ShouldEqual, "v1") 72 So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1") 73 74 sec = f.Section("author") 75 So(sec.Key("NAME").String(), ShouldEqual, "Unknwon") 76 So(sec.Key("E-MAIL").String(), ShouldEqual, "u@gogs.io") 77 }) 78 79 Convey("Load from bad data sources", t, func() { 80 Convey("Invalid input", func() { 81 _, err := ini.Load(notFoundConf) 82 So(err, ShouldNotBeNil) 83 }) 84 85 Convey("Unsupported type", func() { 86 _, err := ini.Load(123) 87 So(err, ShouldNotBeNil) 88 }) 89 }) 90 91 Convey("Can't properly parse INI files containing `#` or `;` in value", t, func() { 92 f, err := ini.Load([]byte(` 93 [author] 94 NAME = U#n#k#n#w#o#n 95 GITHUB = U;n;k;n;w;o;n 96 `)) 97 So(err, ShouldBeNil) 98 So(f, ShouldNotBeNil) 99 100 sec := f.Section("author") 101 nameValue := sec.Key("NAME").String() 102 githubValue := sec.Key("GITHUB").String() 103 So(nameValue, ShouldEqual, "U") 104 So(githubValue, ShouldEqual, "U") 105 }) 106 107 Convey("Can't parse small python-compatible INI files", t, func() { 108 f, err := ini.Load([]byte(` 109[long] 110long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 111 foo 112 bar 113 foobar 114 barfoo 115 -----END RSA PRIVATE KEY----- 116`)) 117 So(err, ShouldNotBeNil) 118 So(f, ShouldBeNil) 119 So(err.Error(), ShouldEqual, "key-value delimiter not found: foo\n") 120 }) 121 122 Convey("Can't parse big python-compatible INI files", t, func() { 123 f, err := ini.Load([]byte(` 124[long] 125long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 126 1foo 127 2bar 128 3foobar 129 4barfoo 130 5foo 131 6bar 132 7foobar 133 8barfoo 134 9foo 135 10bar 136 11foobar 137 12barfoo 138 13foo 139 14bar 140 15foobar 141 16barfoo 142 17foo 143 18bar 144 19foobar 145 20barfoo 146 21foo 147 22bar 148 23foobar 149 24barfoo 150 25foo 151 26bar 152 27foobar 153 28barfoo 154 29foo 155 30bar 156 31foobar 157 32barfoo 158 33foo 159 34bar 160 35foobar 161 36barfoo 162 37foo 163 38bar 164 39foobar 165 40barfoo 166 41foo 167 42bar 168 43foobar 169 44barfoo 170 45foo 171 46bar 172 47foobar 173 48barfoo 174 49foo 175 50bar 176 51foobar 177 52barfoo 178 53foo 179 54bar 180 55foobar 181 56barfoo 182 57foo 183 58bar 184 59foobar 185 60barfoo 186 61foo 187 62bar 188 63foobar 189 64barfoo 190 65foo 191 66bar 192 67foobar 193 68barfoo 194 69foo 195 70bar 196 71foobar 197 72barfoo 198 73foo 199 74bar 200 75foobar 201 76barfoo 202 77foo 203 78bar 204 79foobar 205 80barfoo 206 81foo 207 82bar 208 83foobar 209 84barfoo 210 85foo 211 86bar 212 87foobar 213 88barfoo 214 89foo 215 90bar 216 91foobar 217 92barfoo 218 93foo 219 94bar 220 95foobar 221 96barfoo 222 -----END RSA PRIVATE KEY----- 223`)) 224 So(err, ShouldNotBeNil) 225 So(f, ShouldBeNil) 226 So(err.Error(), ShouldEqual, "key-value delimiter not found: 1foo\n") 227 }) 228} 229 230func TestLooseLoad(t *testing.T) { 231 Convey("Load from data sources with option `Loose` true", t, func() { 232 f, err := ini.LoadSources(ini.LoadOptions{Loose: true}, notFoundConf, minimalConf) 233 So(err, ShouldBeNil) 234 So(f, ShouldNotBeNil) 235 236 Convey("Inverse case", func() { 237 _, err = ini.Load(notFoundConf) 238 So(err, ShouldNotBeNil) 239 }) 240 }) 241} 242 243func TestInsensitiveLoad(t *testing.T) { 244 Convey("Insensitive to section and key names", t, func() { 245 f, err := ini.InsensitiveLoad(minimalConf) 246 So(err, ShouldBeNil) 247 So(f, ShouldNotBeNil) 248 249 So(f.Section("Author").Key("e-mail").String(), ShouldEqual, "u@gogs.io") 250 251 Convey("Write out", func() { 252 var buf bytes.Buffer 253 _, err := f.WriteTo(&buf) 254 So(err, ShouldBeNil) 255 So(buf.String(), ShouldEqual, `[author] 256e-mail = u@gogs.io 257 258`) 259 }) 260 261 Convey("Inverse case", func() { 262 f, err := ini.Load(minimalConf) 263 So(err, ShouldBeNil) 264 So(f, ShouldNotBeNil) 265 266 So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty) 267 }) 268 }) 269 270 // Ref: https://github.com/go-ini/ini/issues/198 271 Convey("Insensitive load with default section", t, func() { 272 f, err := ini.InsensitiveLoad([]byte(` 273user = unknwon 274[profile] 275email = unknwon@local 276`)) 277 So(err, ShouldBeNil) 278 So(f, ShouldNotBeNil) 279 280 So(f.Section(ini.DefaultSection).Key("user").String(), ShouldEqual, "unknwon") 281 }) 282} 283 284func TestLoadSources(t *testing.T) { 285 Convey("Load from data sources with options", t, func() { 286 Convey("with true `AllowPythonMultilineValues`", func() { 287 Convey("Ignore nonexistent files", func() { 288 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true, Loose: true}, notFoundConf, minimalConf) 289 So(err, ShouldBeNil) 290 So(f, ShouldNotBeNil) 291 292 Convey("Inverse case", func() { 293 _, err = ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, notFoundConf) 294 So(err, ShouldNotBeNil) 295 }) 296 }) 297 298 Convey("Insensitive to section and key names", func() { 299 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true, Insensitive: true}, minimalConf) 300 So(err, ShouldBeNil) 301 So(f, ShouldNotBeNil) 302 303 So(f.Section("Author").Key("e-mail").String(), ShouldEqual, "u@gogs.io") 304 305 Convey("Write out", func() { 306 var buf bytes.Buffer 307 _, err := f.WriteTo(&buf) 308 So(err, ShouldBeNil) 309 So(buf.String(), ShouldEqual, `[author] 310e-mail = u@gogs.io 311 312`) 313 }) 314 315 Convey("Inverse case", func() { 316 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, minimalConf) 317 So(err, ShouldBeNil) 318 So(f, ShouldNotBeNil) 319 320 So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty) 321 }) 322 }) 323 324 Convey("Ignore continuation lines", func() { 325 f, err := ini.LoadSources(ini.LoadOptions{ 326 AllowPythonMultilineValues: true, 327 IgnoreContinuation: true, 328 }, []byte(` 329key1=a\b\ 330key2=c\d\ 331key3=value`)) 332 So(err, ShouldBeNil) 333 So(f, ShouldNotBeNil) 334 335 So(f.Section("").Key("key1").String(), ShouldEqual, `a\b\`) 336 So(f.Section("").Key("key2").String(), ShouldEqual, `c\d\`) 337 So(f.Section("").Key("key3").String(), ShouldEqual, "value") 338 339 Convey("Inverse case", func() { 340 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(` 341key1=a\b\ 342key2=c\d\`)) 343 So(err, ShouldBeNil) 344 So(f, ShouldNotBeNil) 345 346 So(f.Section("").Key("key1").String(), ShouldEqual, `a\bkey2=c\d`) 347 }) 348 }) 349 350 Convey("Ignore inline comments", func() { 351 f, err := ini.LoadSources(ini.LoadOptions{ 352 AllowPythonMultilineValues: true, 353 IgnoreInlineComment: true, 354 }, []byte(` 355key1=value ;comment 356key2=value2 #comment2 357key3=val#ue #comment3`)) 358 So(err, ShouldBeNil) 359 So(f, ShouldNotBeNil) 360 361 So(f.Section("").Key("key1").String(), ShouldEqual, `value ;comment`) 362 So(f.Section("").Key("key2").String(), ShouldEqual, `value2 #comment2`) 363 So(f.Section("").Key("key3").String(), ShouldEqual, `val#ue #comment3`) 364 365 Convey("Inverse case", func() { 366 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(` 367key1=value ;comment 368key2=value2 #comment2`)) 369 So(err, ShouldBeNil) 370 So(f, ShouldNotBeNil) 371 372 So(f.Section("").Key("key1").String(), ShouldEqual, `value`) 373 So(f.Section("").Key("key1").Comment, ShouldEqual, `;comment`) 374 So(f.Section("").Key("key2").String(), ShouldEqual, `value2`) 375 So(f.Section("").Key("key2").Comment, ShouldEqual, `#comment2`) 376 }) 377 }) 378 379 Convey("Skip unrecognizable lines", func() { 380 f, err := ini.LoadSources(ini.LoadOptions{ 381 SkipUnrecognizableLines: true, 382 }, []byte(` 383GenerationDepth: 13 384 385BiomeRarityScale: 100 386 387################ 388# Biome Groups # 389################ 390 391BiomeGroup(NormalBiomes, 3, 99, RoofedForestEnchanted, ForestSakura, FloatingJungle 392BiomeGroup(IceBiomes, 4, 85, Ice Plains) 393`)) 394 So(err, ShouldBeNil) 395 So(f, ShouldNotBeNil) 396 397 So(f.Section("").Key("GenerationDepth").String(), ShouldEqual, "13") 398 So(f.Section("").Key("BiomeRarityScale").String(), ShouldEqual, "100") 399 So(f.Section("").HasKey("BiomeGroup"), ShouldBeFalse) 400 }) 401 402 Convey("Allow boolean type keys", func() { 403 f, err := ini.LoadSources(ini.LoadOptions{ 404 AllowPythonMultilineValues: true, 405 AllowBooleanKeys: true, 406 }, []byte(` 407key1=hello 408#key2 409key3`)) 410 So(err, ShouldBeNil) 411 So(f, ShouldNotBeNil) 412 413 So(f.Section("").KeyStrings(), ShouldResemble, []string{"key1", "key3"}) 414 So(f.Section("").Key("key3").MustBool(false), ShouldBeTrue) 415 416 Convey("Write out", func() { 417 var buf bytes.Buffer 418 _, err := f.WriteTo(&buf) 419 So(err, ShouldBeNil) 420 So(buf.String(), ShouldEqual, `key1 = hello 421# key2 422key3 423`) 424 }) 425 426 Convey("Inverse case", func() { 427 _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(` 428key1=hello 429#key2 430key3`)) 431 So(err, ShouldNotBeNil) 432 }) 433 }) 434 435 Convey("Allow shadow keys", func() { 436 f, err := ini.LoadSources(ini.LoadOptions{AllowShadows: true, AllowPythonMultilineValues: true}, []byte(` 437[remote "origin"] 438url = https://github.com/Antergone/test1.git 439url = https://github.com/Antergone/test2.git 440fetch = +refs/heads/*:refs/remotes/origin/*`)) 441 So(err, ShouldBeNil) 442 So(f, ShouldNotBeNil) 443 444 So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test1.git") 445 So(f.Section(`remote "origin"`).Key("url").ValueWithShadows(), ShouldResemble, []string{ 446 "https://github.com/Antergone/test1.git", 447 "https://github.com/Antergone/test2.git", 448 }) 449 So(f.Section(`remote "origin"`).Key("fetch").String(), ShouldEqual, "+refs/heads/*:refs/remotes/origin/*") 450 451 Convey("Write out", func() { 452 var buf bytes.Buffer 453 _, err := f.WriteTo(&buf) 454 So(err, ShouldBeNil) 455 So(buf.String(), ShouldEqual, `[remote "origin"] 456url = https://github.com/Antergone/test1.git 457url = https://github.com/Antergone/test2.git 458fetch = +refs/heads/*:refs/remotes/origin/* 459 460`) 461 }) 462 463 Convey("Inverse case", func() { 464 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(` 465[remote "origin"] 466url = https://github.com/Antergone/test1.git 467url = https://github.com/Antergone/test2.git`)) 468 So(err, ShouldBeNil) 469 So(f, ShouldNotBeNil) 470 471 So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test2.git") 472 }) 473 }) 474 475 Convey("Unescape double quotes inside value", func() { 476 f, err := ini.LoadSources(ini.LoadOptions{ 477 AllowPythonMultilineValues: true, 478 UnescapeValueDoubleQuotes: true, 479 }, []byte(` 480create_repo="创建了仓库 <a href=\"%s\">%s</a>"`)) 481 So(err, ShouldBeNil) 482 So(f, ShouldNotBeNil) 483 484 So(f.Section("").Key("create_repo").String(), ShouldEqual, `创建了仓库 <a href="%s">%s</a>`) 485 486 Convey("Inverse case", func() { 487 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(` 488create_repo="创建了仓库 <a href=\"%s\">%s</a>"`)) 489 So(err, ShouldBeNil) 490 So(f, ShouldNotBeNil) 491 492 So(f.Section("").Key("create_repo").String(), ShouldEqual, `"创建了仓库 <a href=\"%s\">%s</a>"`) 493 }) 494 }) 495 496 Convey("Unescape comment symbols inside value", func() { 497 f, err := ini.LoadSources(ini.LoadOptions{ 498 AllowPythonMultilineValues: true, 499 IgnoreInlineComment: true, 500 UnescapeValueCommentSymbols: true, 501 }, []byte(` 502key = test value <span style="color: %s\; background: %s">more text</span> 503`)) 504 So(err, ShouldBeNil) 505 So(f, ShouldNotBeNil) 506 507 So(f.Section("").Key("key").String(), ShouldEqual, `test value <span style="color: %s; background: %s">more text</span>`) 508 }) 509 510 Convey("Can parse small python-compatible INI files", func() { 511 f, err := ini.LoadSources(ini.LoadOptions{ 512 AllowPythonMultilineValues: true, 513 Insensitive: true, 514 UnparseableSections: []string{"core_lesson", "comments"}, 515 }, []byte(` 516[long] 517long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 518 foo 519 bar 520 foobar 521 barfoo 522 -----END RSA PRIVATE KEY----- 523multiline_list = 524 first 525 second 526 third 527`)) 528 So(err, ShouldBeNil) 529 So(f, ShouldNotBeNil) 530 531 So(f.Section("long").Key("long_rsa_private_key").String(), ShouldEqual, "-----BEGIN RSA PRIVATE KEY-----\nfoo\nbar\nfoobar\nbarfoo\n-----END RSA PRIVATE KEY-----") 532 So(f.Section("long").Key("multiline_list").String(), ShouldEqual, "\nfirst\nsecond\nthird") 533 }) 534 535 Convey("Can parse big python-compatible INI files", func() { 536 f, err := ini.LoadSources(ini.LoadOptions{ 537 AllowPythonMultilineValues: true, 538 Insensitive: true, 539 UnparseableSections: []string{"core_lesson", "comments"}, 540 }, []byte(` 541[long] 542long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 543 1foo 544 2bar 545 3foobar 546 4barfoo 547 5foo 548 6bar 549 7foobar 550 8barfoo 551 9foo 552 10bar 553 11foobar 554 12barfoo 555 13foo 556 14bar 557 15foobar 558 16barfoo 559 17foo 560 18bar 561 19foobar 562 20barfoo 563 21foo 564 22bar 565 23foobar 566 24barfoo 567 25foo 568 26bar 569 27foobar 570 28barfoo 571 29foo 572 30bar 573 31foobar 574 32barfoo 575 33foo 576 34bar 577 35foobar 578 36barfoo 579 37foo 580 38bar 581 39foobar 582 40barfoo 583 41foo 584 42bar 585 43foobar 586 44barfoo 587 45foo 588 46bar 589 47foobar 590 48barfoo 591 49foo 592 50bar 593 51foobar 594 52barfoo 595 53foo 596 54bar 597 55foobar 598 56barfoo 599 57foo 600 58bar 601 59foobar 602 60barfoo 603 61foo 604 62bar 605 63foobar 606 64barfoo 607 65foo 608 66bar 609 67foobar 610 68barfoo 611 69foo 612 70bar 613 71foobar 614 72barfoo 615 73foo 616 74bar 617 75foobar 618 76barfoo 619 77foo 620 78bar 621 79foobar 622 80barfoo 623 81foo 624 82bar 625 83foobar 626 84barfoo 627 85foo 628 86bar 629 87foobar 630 88barfoo 631 89foo 632 90bar 633 91foobar 634 92barfoo 635 93foo 636 94bar 637 95foobar 638 96barfoo 639 -----END RSA PRIVATE KEY----- 640`)) 641 So(err, ShouldBeNil) 642 So(f, ShouldNotBeNil) 643 644 So(f.Section("long").Key("long_rsa_private_key").String(), ShouldEqual, `-----BEGIN RSA PRIVATE KEY----- 6451foo 6462bar 6473foobar 6484barfoo 6495foo 6506bar 6517foobar 6528barfoo 6539foo 65410bar 65511foobar 65612barfoo 65713foo 65814bar 65915foobar 66016barfoo 66117foo 66218bar 66319foobar 66420barfoo 66521foo 66622bar 66723foobar 66824barfoo 66925foo 67026bar 67127foobar 67228barfoo 67329foo 67430bar 67531foobar 67632barfoo 67733foo 67834bar 67935foobar 68036barfoo 68137foo 68238bar 68339foobar 68440barfoo 68541foo 68642bar 68743foobar 68844barfoo 68945foo 69046bar 69147foobar 69248barfoo 69349foo 69450bar 69551foobar 69652barfoo 69753foo 69854bar 69955foobar 70056barfoo 70157foo 70258bar 70359foobar 70460barfoo 70561foo 70662bar 70763foobar 70864barfoo 70965foo 71066bar 71167foobar 71268barfoo 71369foo 71470bar 71571foobar 71672barfoo 71773foo 71874bar 71975foobar 72076barfoo 72177foo 72278bar 72379foobar 72480barfoo 72581foo 72682bar 72783foobar 72884barfoo 72985foo 73086bar 73187foobar 73288barfoo 73389foo 73490bar 73591foobar 73692barfoo 73793foo 73894bar 73995foobar 74096barfoo 741-----END RSA PRIVATE KEY-----`) 742 }) 743 744 Convey("Allow unparsable sections", func() { 745 f, err := ini.LoadSources(ini.LoadOptions{ 746 AllowPythonMultilineValues: true, 747 Insensitive: true, 748 UnparseableSections: []string{"core_lesson", "comments"}, 749 }, []byte(` 750Lesson_Location = 87 751Lesson_Status = C 752Score = 3 753Time = 00:02:30 754 755[CORE_LESSON] 756my lesson state data – 1111111111111111111000000000000000001110000 757111111111111111111100000000000111000000000 – end my lesson state data 758 759[COMMENTS] 760<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)) 761 So(err, ShouldBeNil) 762 So(f, ShouldNotBeNil) 763 764 So(f.Section("").Key("score").String(), ShouldEqual, "3") 765 So(f.Section("").Body(), ShouldBeEmpty) 766 So(f.Section("core_lesson").Body(), ShouldEqual, `my lesson state data – 1111111111111111111000000000000000001110000 767111111111111111111100000000000111000000000 – end my lesson state data`) 768 So(f.Section("comments").Body(), ShouldEqual, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`) 769 770 Convey("Write out", func() { 771 var buf bytes.Buffer 772 _, err := f.WriteTo(&buf) 773 So(err, ShouldBeNil) 774 So(buf.String(), ShouldEqual, `lesson_location = 87 775lesson_status = C 776score = 3 777time = 00:02:30 778 779[core_lesson] 780my lesson state data – 1111111111111111111000000000000000001110000 781111111111111111111100000000000111000000000 – end my lesson state data 782 783[comments] 784<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1> 785`) 786 }) 787 788 Convey("Inverse case", func() { 789 _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(` 790[CORE_LESSON] 791my lesson state data – 1111111111111111111000000000000000001110000 792111111111111111111100000000000111000000000 – end my lesson state data`)) 793 So(err, ShouldNotBeNil) 794 }) 795 }) 796 797 Convey("And false `SpaceBeforeInlineComment`", func() { 798 Convey("Can't parse INI files containing `#` or `;` in value", func() { 799 f, err := ini.LoadSources( 800 ini.LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: false}, 801 []byte(` 802[author] 803NAME = U#n#k#n#w#o#n 804GITHUB = U;n;k;n;w;o;n 805`)) 806 So(err, ShouldBeNil) 807 So(f, ShouldNotBeNil) 808 sec := f.Section("author") 809 nameValue := sec.Key("NAME").String() 810 githubValue := sec.Key("GITHUB").String() 811 So(nameValue, ShouldEqual, "U") 812 So(githubValue, ShouldEqual, "U") 813 }) 814 }) 815 816 Convey("And true `SpaceBeforeInlineComment`", func() { 817 Convey("Can parse INI files containing `#` or `;` in value", func() { 818 f, err := ini.LoadSources( 819 ini.LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: true}, 820 []byte(` 821[author] 822NAME = U#n#k#n#w#o#n 823GITHUB = U;n;k;n;w;o;n 824`)) 825 So(err, ShouldBeNil) 826 So(f, ShouldNotBeNil) 827 sec := f.Section("author") 828 nameValue := sec.Key("NAME").String() 829 githubValue := sec.Key("GITHUB").String() 830 So(nameValue, ShouldEqual, "U#n#k#n#w#o#n") 831 So(githubValue, ShouldEqual, "U;n;k;n;w;o;n") 832 }) 833 }) 834 }) 835 836 Convey("with false `AllowPythonMultilineValues`", func() { 837 Convey("Ignore nonexistent files", func() { 838 f, err := ini.LoadSources(ini.LoadOptions{ 839 AllowPythonMultilineValues: false, 840 Loose: true, 841 }, notFoundConf, minimalConf) 842 So(err, ShouldBeNil) 843 So(f, ShouldNotBeNil) 844 845 Convey("Inverse case", func() { 846 _, err = ini.LoadSources(ini.LoadOptions{ 847 AllowPythonMultilineValues: false, 848 }, notFoundConf) 849 So(err, ShouldNotBeNil) 850 }) 851 }) 852 853 Convey("Insensitive to section and key names", func() { 854 f, err := ini.LoadSources(ini.LoadOptions{ 855 AllowPythonMultilineValues: false, 856 Insensitive: true, 857 }, minimalConf) 858 So(err, ShouldBeNil) 859 So(f, ShouldNotBeNil) 860 861 So(f.Section("Author").Key("e-mail").String(), ShouldEqual, "u@gogs.io") 862 863 Convey("Write out", func() { 864 var buf bytes.Buffer 865 _, err := f.WriteTo(&buf) 866 So(err, ShouldBeNil) 867 So(buf.String(), ShouldEqual, `[author] 868e-mail = u@gogs.io 869 870`) 871 }) 872 873 Convey("Inverse case", func() { 874 f, err := ini.LoadSources(ini.LoadOptions{ 875 AllowPythonMultilineValues: false, 876 }, minimalConf) 877 So(err, ShouldBeNil) 878 So(f, ShouldNotBeNil) 879 880 So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty) 881 }) 882 }) 883 884 Convey("Ignore continuation lines", func() { 885 f, err := ini.LoadSources(ini.LoadOptions{ 886 AllowPythonMultilineValues: false, 887 IgnoreContinuation: true, 888 }, []byte(` 889key1=a\b\ 890key2=c\d\ 891key3=value`)) 892 So(err, ShouldBeNil) 893 So(f, ShouldNotBeNil) 894 895 So(f.Section("").Key("key1").String(), ShouldEqual, `a\b\`) 896 So(f.Section("").Key("key2").String(), ShouldEqual, `c\d\`) 897 So(f.Section("").Key("key3").String(), ShouldEqual, "value") 898 899 Convey("Inverse case", func() { 900 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(` 901key1=a\b\ 902key2=c\d\`)) 903 So(err, ShouldBeNil) 904 So(f, ShouldNotBeNil) 905 906 So(f.Section("").Key("key1").String(), ShouldEqual, `a\bkey2=c\d`) 907 }) 908 }) 909 910 Convey("Ignore inline comments", func() { 911 f, err := ini.LoadSources(ini.LoadOptions{ 912 AllowPythonMultilineValues: false, 913 IgnoreInlineComment: true, 914 }, []byte(` 915key1=value ;comment 916key2=value2 #comment2 917key3=val#ue #comment3`)) 918 So(err, ShouldBeNil) 919 So(f, ShouldNotBeNil) 920 921 So(f.Section("").Key("key1").String(), ShouldEqual, `value ;comment`) 922 So(f.Section("").Key("key2").String(), ShouldEqual, `value2 #comment2`) 923 So(f.Section("").Key("key3").String(), ShouldEqual, `val#ue #comment3`) 924 925 Convey("Inverse case", func() { 926 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(` 927key1=value ;comment 928key2=value2 #comment2`)) 929 So(err, ShouldBeNil) 930 So(f, ShouldNotBeNil) 931 932 So(f.Section("").Key("key1").String(), ShouldEqual, `value`) 933 So(f.Section("").Key("key1").Comment, ShouldEqual, `;comment`) 934 So(f.Section("").Key("key2").String(), ShouldEqual, `value2`) 935 So(f.Section("").Key("key2").Comment, ShouldEqual, `#comment2`) 936 }) 937 }) 938 939 Convey("Allow boolean type keys", func() { 940 f, err := ini.LoadSources(ini.LoadOptions{ 941 AllowPythonMultilineValues: false, 942 AllowBooleanKeys: true, 943 }, []byte(` 944key1=hello 945#key2 946key3`)) 947 So(err, ShouldBeNil) 948 So(f, ShouldNotBeNil) 949 950 So(f.Section("").KeyStrings(), ShouldResemble, []string{"key1", "key3"}) 951 So(f.Section("").Key("key3").MustBool(false), ShouldBeTrue) 952 953 Convey("Write out", func() { 954 var buf bytes.Buffer 955 _, err := f.WriteTo(&buf) 956 So(err, ShouldBeNil) 957 So(buf.String(), ShouldEqual, `key1 = hello 958# key2 959key3 960`) 961 }) 962 963 Convey("Inverse case", func() { 964 _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(` 965key1=hello 966#key2 967key3`)) 968 So(err, ShouldNotBeNil) 969 }) 970 }) 971 972 Convey("Allow shadow keys", func() { 973 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false, AllowShadows: true}, []byte(` 974[remote "origin"] 975url = https://github.com/Antergone/test1.git 976url = https://github.com/Antergone/test2.git 977fetch = +refs/heads/*:refs/remotes/origin/*`)) 978 So(err, ShouldBeNil) 979 So(f, ShouldNotBeNil) 980 981 So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test1.git") 982 So(f.Section(`remote "origin"`).Key("url").ValueWithShadows(), ShouldResemble, []string{ 983 "https://github.com/Antergone/test1.git", 984 "https://github.com/Antergone/test2.git", 985 }) 986 So(f.Section(`remote "origin"`).Key("fetch").String(), ShouldEqual, "+refs/heads/*:refs/remotes/origin/*") 987 988 Convey("Write out", func() { 989 var buf bytes.Buffer 990 _, err := f.WriteTo(&buf) 991 So(err, ShouldBeNil) 992 So(buf.String(), ShouldEqual, `[remote "origin"] 993url = https://github.com/Antergone/test1.git 994url = https://github.com/Antergone/test2.git 995fetch = +refs/heads/*:refs/remotes/origin/* 996 997`) 998 }) 999 1000 Convey("Inverse case", func() { 1001 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(` 1002[remote "origin"] 1003url = https://github.com/Antergone/test1.git 1004url = https://github.com/Antergone/test2.git`)) 1005 So(err, ShouldBeNil) 1006 So(f, ShouldNotBeNil) 1007 1008 So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test2.git") 1009 }) 1010 }) 1011 1012 Convey("Unescape double quotes inside value", func() { 1013 f, err := ini.LoadSources(ini.LoadOptions{ 1014 AllowPythonMultilineValues: false, 1015 UnescapeValueDoubleQuotes: true, 1016 }, []byte(` 1017create_repo="创建了仓库 <a href=\"%s\">%s</a>"`)) 1018 So(err, ShouldBeNil) 1019 So(f, ShouldNotBeNil) 1020 1021 So(f.Section("").Key("create_repo").String(), ShouldEqual, `创建了仓库 <a href="%s">%s</a>`) 1022 1023 Convey("Inverse case", func() { 1024 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(` 1025create_repo="创建了仓库 <a href=\"%s\">%s</a>"`)) 1026 So(err, ShouldBeNil) 1027 So(f, ShouldNotBeNil) 1028 1029 So(f.Section("").Key("create_repo").String(), ShouldEqual, `"创建了仓库 <a href=\"%s\">%s</a>"`) 1030 }) 1031 }) 1032 1033 Convey("Unescape comment symbols inside value", func() { 1034 f, err := ini.LoadSources(ini.LoadOptions{ 1035 AllowPythonMultilineValues: false, 1036 IgnoreInlineComment: true, 1037 UnescapeValueCommentSymbols: true, 1038 }, []byte(` 1039key = test value <span style="color: %s\; background: %s">more text</span> 1040`)) 1041 So(err, ShouldBeNil) 1042 So(f, ShouldNotBeNil) 1043 1044 So(f.Section("").Key("key").String(), ShouldEqual, `test value <span style="color: %s; background: %s">more text</span>`) 1045 }) 1046 1047 Convey("Can't parse small python-compatible INI files", func() { 1048 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(` 1049[long] 1050long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 1051 foo 1052 bar 1053 foobar 1054 barfoo 1055 -----END RSA PRIVATE KEY----- 1056`)) 1057 So(err, ShouldNotBeNil) 1058 So(f, ShouldBeNil) 1059 So(err.Error(), ShouldEqual, "key-value delimiter not found: foo\n") 1060 }) 1061 1062 Convey("Can't parse big python-compatible INI files", func() { 1063 f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(` 1064[long] 1065long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- 1066 1foo 1067 2bar 1068 3foobar 1069 4barfoo 1070 5foo 1071 6bar 1072 7foobar 1073 8barfoo 1074 9foo 1075 10bar 1076 11foobar 1077 12barfoo 1078 13foo 1079 14bar 1080 15foobar 1081 16barfoo 1082 17foo 1083 18bar 1084 19foobar 1085 20barfoo 1086 21foo 1087 22bar 1088 23foobar 1089 24barfoo 1090 25foo 1091 26bar 1092 27foobar 1093 28barfoo 1094 29foo 1095 30bar 1096 31foobar 1097 32barfoo 1098 33foo 1099 34bar 1100 35foobar 1101 36barfoo 1102 37foo 1103 38bar 1104 39foobar 1105 40barfoo 1106 41foo 1107 42bar 1108 43foobar 1109 44barfoo 1110 45foo 1111 46bar 1112 47foobar 1113 48barfoo 1114 49foo 1115 50bar 1116 51foobar 1117 52barfoo 1118 53foo 1119 54bar 1120 55foobar 1121 56barfoo 1122 57foo 1123 58bar 1124 59foobar 1125 60barfoo 1126 61foo 1127 62bar 1128 63foobar 1129 64barfoo 1130 65foo 1131 66bar 1132 67foobar 1133 68barfoo 1134 69foo 1135 70bar 1136 71foobar 1137 72barfoo 1138 73foo 1139 74bar 1140 75foobar 1141 76barfoo 1142 77foo 1143 78bar 1144 79foobar 1145 80barfoo 1146 81foo 1147 82bar 1148 83foobar 1149 84barfoo 1150 85foo 1151 86bar 1152 87foobar 1153 88barfoo 1154 89foo 1155 90bar 1156 91foobar 1157 92barfoo 1158 93foo 1159 94bar 1160 95foobar 1161 96barfoo 1162 -----END RSA PRIVATE KEY----- 1163`)) 1164 So(err, ShouldNotBeNil) 1165 So(f, ShouldBeNil) 1166 So(err.Error(), ShouldEqual, "key-value delimiter not found: 1foo\n") 1167 }) 1168 1169 Convey("Allow unparsable sections", func() { 1170 f, err := ini.LoadSources(ini.LoadOptions{ 1171 AllowPythonMultilineValues: false, 1172 Insensitive: true, 1173 UnparseableSections: []string{"core_lesson", "comments"}, 1174 }, []byte(` 1175Lesson_Location = 87 1176Lesson_Status = C 1177Score = 3 1178Time = 00:02:30 1179 1180[CORE_LESSON] 1181my lesson state data – 1111111111111111111000000000000000001110000 1182111111111111111111100000000000111000000000 – end my lesson state data 1183 1184[COMMENTS] 1185<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)) 1186 So(err, ShouldBeNil) 1187 So(f, ShouldNotBeNil) 1188 1189 So(f.Section("").Key("score").String(), ShouldEqual, "3") 1190 So(f.Section("").Body(), ShouldBeEmpty) 1191 So(f.Section("core_lesson").Body(), ShouldEqual, `my lesson state data – 1111111111111111111000000000000000001110000 1192111111111111111111100000000000111000000000 – end my lesson state data`) 1193 So(f.Section("comments").Body(), ShouldEqual, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`) 1194 1195 Convey("Write out", func() { 1196 var buf bytes.Buffer 1197 _, err := f.WriteTo(&buf) 1198 So(err, ShouldBeNil) 1199 So(buf.String(), ShouldEqual, `lesson_location = 87 1200lesson_status = C 1201score = 3 1202time = 00:02:30 1203 1204[core_lesson] 1205my lesson state data – 1111111111111111111000000000000000001110000 1206111111111111111111100000000000111000000000 – end my lesson state data 1207 1208[comments] 1209<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1> 1210`) 1211 }) 1212 1213 Convey("Inverse case", func() { 1214 _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(` 1215[CORE_LESSON] 1216my lesson state data – 1111111111111111111000000000000000001110000 1217111111111111111111100000000000111000000000 – end my lesson state data`)) 1218 So(err, ShouldNotBeNil) 1219 }) 1220 }) 1221 1222 Convey("And false `SpaceBeforeInlineComment`", func() { 1223 Convey("Can't parse INI files containing `#` or `;` in value", func() { 1224 f, err := ini.LoadSources( 1225 ini.LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: false}, 1226 []byte(` 1227[author] 1228NAME = U#n#k#n#w#o#n 1229GITHUB = U;n;k;n;w;o;n 1230`)) 1231 So(err, ShouldBeNil) 1232 So(f, ShouldNotBeNil) 1233 sec := f.Section("author") 1234 nameValue := sec.Key("NAME").String() 1235 githubValue := sec.Key("GITHUB").String() 1236 So(nameValue, ShouldEqual, "U") 1237 So(githubValue, ShouldEqual, "U") 1238 }) 1239 }) 1240 1241 Convey("And true `SpaceBeforeInlineComment`", func() { 1242 Convey("Can parse INI files containing `#` or `;` in value", func() { 1243 f, err := ini.LoadSources( 1244 ini.LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: true}, 1245 []byte(` 1246[author] 1247NAME = U#n#k#n#w#o#n 1248GITHUB = U;n;k;n;w;o;n 1249`)) 1250 So(err, ShouldBeNil) 1251 So(f, ShouldNotBeNil) 1252 sec := f.Section("author") 1253 nameValue := sec.Key("NAME").String() 1254 githubValue := sec.Key("GITHUB").String() 1255 So(nameValue, ShouldEqual, "U#n#k#n#w#o#n") 1256 So(githubValue, ShouldEqual, "U;n;k;n;w;o;n") 1257 }) 1258 }) 1259 }) 1260 }) 1261} 1262 1263func Test_KeyValueDelimiters(t *testing.T) { 1264 Convey("Custom key-value delimiters", t, func() { 1265 f, err := ini.LoadSources(ini.LoadOptions{ 1266 KeyValueDelimiters: "?!", 1267 }, []byte(` 1268[section] 1269key1?value1 1270key2!value2 1271`)) 1272 So(err, ShouldBeNil) 1273 So(f, ShouldNotBeNil) 1274 1275 So(f.Section("section").Key("key1").String(), ShouldEqual, "value1") 1276 So(f.Section("section").Key("key2").String(), ShouldEqual, "value2") 1277 }) 1278} 1279 1280func Test_PreserveSurroundedQuote(t *testing.T) { 1281 Convey("Preserve surrounded quote test", t, func() { 1282 f, err := ini.LoadSources(ini.LoadOptions{ 1283 PreserveSurroundedQuote: true, 1284 }, []byte(` 1285[section] 1286key1 = "value1" 1287key2 = value2 1288`)) 1289 So(err, ShouldBeNil) 1290 So(f, ShouldNotBeNil) 1291 1292 So(f.Section("section").Key("key1").String(), ShouldEqual, "\"value1\"") 1293 So(f.Section("section").Key("key2").String(), ShouldEqual, "value2") 1294 }) 1295 1296 Convey("Preserve surrounded quote test inverse test", t, func() { 1297 f, err := ini.LoadSources(ini.LoadOptions{ 1298 PreserveSurroundedQuote: false, 1299 }, []byte(` 1300[section] 1301key1 = "value1" 1302key2 = value2 1303`)) 1304 So(err, ShouldBeNil) 1305 So(f, ShouldNotBeNil) 1306 1307 So(f.Section("section").Key("key1").String(), ShouldEqual, "value1") 1308 So(f.Section("section").Key("key2").String(), ShouldEqual, "value2") 1309 }) 1310} 1311