ini_test.go 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282
  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. package ini_test
  15. import (
  16. "bytes"
  17. "flag"
  18. "io/ioutil"
  19. "testing"
  20. . "github.com/smartystreets/goconvey/convey"
  21. "gopkg.in/ini.v1"
  22. )
  23. const (
  24. _CONF_DATA = `
  25. ; Package name
  26. NAME = ini
  27. ; Package version
  28. VERSION = v1
  29. ; Package import path
  30. IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
  31. # Information about package author
  32. # Bio can be written in multiple lines.
  33. [author]
  34. NAME = Unknwon ; Succeeding comment
  35. E-MAIL = fake@localhost
  36. GITHUB = https://github.com/%(NAME)s
  37. BIO = """Gopher.
  38. Coding addict.
  39. Good man.
  40. """ # Succeeding comment`
  41. _MINIMAL_CONF = "testdata/minimal.ini"
  42. _FULL_CONF = "testdata/full.ini"
  43. _NOT_FOUND_CONF = "testdata/404.ini"
  44. )
  45. var update = flag.Bool("update", false, "Update .golden files")
  46. func TestLoad(t *testing.T) {
  47. Convey("Load from good data sources", t, func() {
  48. f, err := ini.Load([]byte(`
  49. NAME = ini
  50. VERSION = v1
  51. IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s`),
  52. "testdata/minimal.ini",
  53. ioutil.NopCloser(bytes.NewReader([]byte(`
  54. [author]
  55. NAME = Unknwon
  56. `))),
  57. )
  58. So(err, ShouldBeNil)
  59. So(f, ShouldNotBeNil)
  60. // Validate values make sure all sources are loaded correctly
  61. sec := f.Section("")
  62. So(sec.Key("NAME").String(), ShouldEqual, "ini")
  63. So(sec.Key("VERSION").String(), ShouldEqual, "v1")
  64. So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1")
  65. sec = f.Section("author")
  66. So(sec.Key("NAME").String(), ShouldEqual, "Unknwon")
  67. So(sec.Key("E-MAIL").String(), ShouldEqual, "u@gogs.io")
  68. })
  69. Convey("Load from bad data sources", t, func() {
  70. Convey("Invalid input", func() {
  71. _, err := ini.Load(_NOT_FOUND_CONF)
  72. So(err, ShouldNotBeNil)
  73. })
  74. Convey("Unsupported type", func() {
  75. _, err := ini.Load(123)
  76. So(err, ShouldNotBeNil)
  77. })
  78. })
  79. Convey("Can't properly parse INI files containing `#` or `;` in value", t, func() {
  80. f, err := ini.Load([]byte(`
  81. [author]
  82. NAME = U#n#k#n#w#o#n
  83. GITHUB = U;n;k;n;w;o;n
  84. `))
  85. So(err, ShouldBeNil)
  86. So(f, ShouldNotBeNil)
  87. sec := f.Section("author")
  88. nameValue := sec.Key("NAME").String()
  89. githubValue := sec.Key("GITHUB").String()
  90. So(nameValue, ShouldEqual, "U")
  91. So(githubValue, ShouldEqual, "U")
  92. })
  93. Convey("Can't parse small python-compatible INI files", t, func() {
  94. f, err := ini.Load([]byte(`
  95. [long]
  96. long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
  97. foo
  98. bar
  99. foobar
  100. barfoo
  101. -----END RSA PRIVATE KEY-----
  102. `))
  103. So(err, ShouldNotBeNil)
  104. So(f, ShouldBeNil)
  105. So(err.Error(), ShouldEqual, "key-value delimiter not found: foo\n")
  106. })
  107. Convey("Can't parse big python-compatible INI files", t, func() {
  108. f, err := ini.Load([]byte(`
  109. [long]
  110. long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
  111. 1foo
  112. 2bar
  113. 3foobar
  114. 4barfoo
  115. 5foo
  116. 6bar
  117. 7foobar
  118. 8barfoo
  119. 9foo
  120. 10bar
  121. 11foobar
  122. 12barfoo
  123. 13foo
  124. 14bar
  125. 15foobar
  126. 16barfoo
  127. 17foo
  128. 18bar
  129. 19foobar
  130. 20barfoo
  131. 21foo
  132. 22bar
  133. 23foobar
  134. 24barfoo
  135. 25foo
  136. 26bar
  137. 27foobar
  138. 28barfoo
  139. 29foo
  140. 30bar
  141. 31foobar
  142. 32barfoo
  143. 33foo
  144. 34bar
  145. 35foobar
  146. 36barfoo
  147. 37foo
  148. 38bar
  149. 39foobar
  150. 40barfoo
  151. 41foo
  152. 42bar
  153. 43foobar
  154. 44barfoo
  155. 45foo
  156. 46bar
  157. 47foobar
  158. 48barfoo
  159. 49foo
  160. 50bar
  161. 51foobar
  162. 52barfoo
  163. 53foo
  164. 54bar
  165. 55foobar
  166. 56barfoo
  167. 57foo
  168. 58bar
  169. 59foobar
  170. 60barfoo
  171. 61foo
  172. 62bar
  173. 63foobar
  174. 64barfoo
  175. 65foo
  176. 66bar
  177. 67foobar
  178. 68barfoo
  179. 69foo
  180. 70bar
  181. 71foobar
  182. 72barfoo
  183. 73foo
  184. 74bar
  185. 75foobar
  186. 76barfoo
  187. 77foo
  188. 78bar
  189. 79foobar
  190. 80barfoo
  191. 81foo
  192. 82bar
  193. 83foobar
  194. 84barfoo
  195. 85foo
  196. 86bar
  197. 87foobar
  198. 88barfoo
  199. 89foo
  200. 90bar
  201. 91foobar
  202. 92barfoo
  203. 93foo
  204. 94bar
  205. 95foobar
  206. 96barfoo
  207. -----END RSA PRIVATE KEY-----
  208. `))
  209. So(err, ShouldNotBeNil)
  210. So(f, ShouldBeNil)
  211. So(err.Error(), ShouldEqual, "key-value delimiter not found: 1foo\n")
  212. })
  213. }
  214. func TestLooseLoad(t *testing.T) {
  215. Convey("Load from data sources with option `Loose` true", t, func() {
  216. f, err := ini.LoadSources(ini.LoadOptions{Loose: true}, _NOT_FOUND_CONF, _MINIMAL_CONF)
  217. So(err, ShouldBeNil)
  218. So(f, ShouldNotBeNil)
  219. Convey("Inverse case", func() {
  220. _, err = ini.Load(_NOT_FOUND_CONF)
  221. So(err, ShouldNotBeNil)
  222. })
  223. })
  224. }
  225. func TestInsensitiveLoad(t *testing.T) {
  226. Convey("Insensitive to section and key names", t, func() {
  227. f, err := ini.InsensitiveLoad(_MINIMAL_CONF)
  228. So(err, ShouldBeNil)
  229. So(f, ShouldNotBeNil)
  230. So(f.Section("Author").Key("e-mail").String(), ShouldEqual, "u@gogs.io")
  231. Convey("Write out", func() {
  232. var buf bytes.Buffer
  233. _, err := f.WriteTo(&buf)
  234. So(err, ShouldBeNil)
  235. So(buf.String(), ShouldEqual, `[author]
  236. e-mail = u@gogs.io
  237. `)
  238. })
  239. Convey("Inverse case", func() {
  240. f, err := ini.Load(_MINIMAL_CONF)
  241. So(err, ShouldBeNil)
  242. So(f, ShouldNotBeNil)
  243. So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty)
  244. })
  245. })
  246. }
  247. func TestLoadSources(t *testing.T) {
  248. Convey("Load from data sources with options", t, func() {
  249. Convey("with true `AllowPythonMultilineValues`", func() {
  250. Convey("Ignore nonexistent files", func() {
  251. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true, Loose: true}, _NOT_FOUND_CONF, _MINIMAL_CONF)
  252. So(err, ShouldBeNil)
  253. So(f, ShouldNotBeNil)
  254. Convey("Inverse case", func() {
  255. _, err = ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, _NOT_FOUND_CONF)
  256. So(err, ShouldNotBeNil)
  257. })
  258. })
  259. Convey("Insensitive to section and key names", func() {
  260. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true, Insensitive: true}, _MINIMAL_CONF)
  261. So(err, ShouldBeNil)
  262. So(f, ShouldNotBeNil)
  263. So(f.Section("Author").Key("e-mail").String(), ShouldEqual, "u@gogs.io")
  264. Convey("Write out", func() {
  265. var buf bytes.Buffer
  266. _, err := f.WriteTo(&buf)
  267. So(err, ShouldBeNil)
  268. So(buf.String(), ShouldEqual, `[author]
  269. e-mail = u@gogs.io
  270. `)
  271. })
  272. Convey("Inverse case", func() {
  273. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, _MINIMAL_CONF)
  274. So(err, ShouldBeNil)
  275. So(f, ShouldNotBeNil)
  276. So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty)
  277. })
  278. })
  279. Convey("Ignore continuation lines", func() {
  280. f, err := ini.LoadSources(ini.LoadOptions{
  281. AllowPythonMultilineValues: true,
  282. IgnoreContinuation: true,
  283. }, []byte(`
  284. key1=a\b\
  285. key2=c\d\
  286. key3=value`))
  287. So(err, ShouldBeNil)
  288. So(f, ShouldNotBeNil)
  289. So(f.Section("").Key("key1").String(), ShouldEqual, `a\b\`)
  290. So(f.Section("").Key("key2").String(), ShouldEqual, `c\d\`)
  291. So(f.Section("").Key("key3").String(), ShouldEqual, "value")
  292. Convey("Inverse case", func() {
  293. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
  294. key1=a\b\
  295. key2=c\d\`))
  296. So(err, ShouldBeNil)
  297. So(f, ShouldNotBeNil)
  298. So(f.Section("").Key("key1").String(), ShouldEqual, `a\bkey2=c\d`)
  299. })
  300. })
  301. Convey("Ignore inline comments", func() {
  302. f, err := ini.LoadSources(ini.LoadOptions{
  303. AllowPythonMultilineValues: true,
  304. IgnoreInlineComment: true,
  305. }, []byte(`
  306. key1=value ;comment
  307. key2=value2 #comment2
  308. key3=val#ue #comment3`))
  309. So(err, ShouldBeNil)
  310. So(f, ShouldNotBeNil)
  311. So(f.Section("").Key("key1").String(), ShouldEqual, `value ;comment`)
  312. So(f.Section("").Key("key2").String(), ShouldEqual, `value2 #comment2`)
  313. So(f.Section("").Key("key3").String(), ShouldEqual, `val#ue #comment3`)
  314. Convey("Inverse case", func() {
  315. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
  316. key1=value ;comment
  317. key2=value2 #comment2`))
  318. So(err, ShouldBeNil)
  319. So(f, ShouldNotBeNil)
  320. So(f.Section("").Key("key1").String(), ShouldEqual, `value`)
  321. So(f.Section("").Key("key1").Comment, ShouldEqual, `;comment`)
  322. So(f.Section("").Key("key2").String(), ShouldEqual, `value2`)
  323. So(f.Section("").Key("key2").Comment, ShouldEqual, `#comment2`)
  324. })
  325. })
  326. Convey("Skip unrecognizable lines", func() {
  327. f, err := ini.LoadSources(ini.LoadOptions{
  328. SkipUnrecognizableLines: true,
  329. }, []byte(`
  330. GenerationDepth: 13
  331. BiomeRarityScale: 100
  332. ################
  333. # Biome Groups #
  334. ################
  335. BiomeGroup(NormalBiomes, 3, 99, RoofedForestEnchanted, ForestSakura, FloatingJungle
  336. BiomeGroup(IceBiomes, 4, 85, Ice Plains)
  337. `))
  338. So(err, ShouldBeNil)
  339. So(f, ShouldNotBeNil)
  340. So(f.Section("").Key("GenerationDepth").String(), ShouldEqual, "13")
  341. So(f.Section("").Key("BiomeRarityScale").String(), ShouldEqual, "100")
  342. So(f.Section("").HasKey("BiomeGroup"), ShouldBeFalse)
  343. })
  344. Convey("Allow boolean type keys", func() {
  345. f, err := ini.LoadSources(ini.LoadOptions{
  346. AllowPythonMultilineValues: true,
  347. AllowBooleanKeys: true,
  348. }, []byte(`
  349. key1=hello
  350. #key2
  351. key3`))
  352. So(err, ShouldBeNil)
  353. So(f, ShouldNotBeNil)
  354. So(f.Section("").KeyStrings(), ShouldResemble, []string{"key1", "key3"})
  355. So(f.Section("").Key("key3").MustBool(false), ShouldBeTrue)
  356. Convey("Write out", func() {
  357. var buf bytes.Buffer
  358. _, err := f.WriteTo(&buf)
  359. So(err, ShouldBeNil)
  360. So(buf.String(), ShouldEqual, `key1 = hello
  361. # key2
  362. key3
  363. `)
  364. })
  365. Convey("Inverse case", func() {
  366. _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
  367. key1=hello
  368. #key2
  369. key3`))
  370. So(err, ShouldNotBeNil)
  371. })
  372. })
  373. Convey("Allow shadow keys", func() {
  374. f, err := ini.LoadSources(ini.LoadOptions{AllowShadows: true, AllowPythonMultilineValues: true}, []byte(`
  375. [remote "origin"]
  376. url = https://github.com/Antergone/test1.git
  377. url = https://github.com/Antergone/test2.git
  378. fetch = +refs/heads/*:refs/remotes/origin/*`))
  379. So(err, ShouldBeNil)
  380. So(f, ShouldNotBeNil)
  381. So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test1.git")
  382. So(f.Section(`remote "origin"`).Key("url").ValueWithShadows(), ShouldResemble, []string{
  383. "https://github.com/Antergone/test1.git",
  384. "https://github.com/Antergone/test2.git",
  385. })
  386. So(f.Section(`remote "origin"`).Key("fetch").String(), ShouldEqual, "+refs/heads/*:refs/remotes/origin/*")
  387. Convey("Write out", func() {
  388. var buf bytes.Buffer
  389. _, err := f.WriteTo(&buf)
  390. So(err, ShouldBeNil)
  391. So(buf.String(), ShouldEqual, `[remote "origin"]
  392. url = https://github.com/Antergone/test1.git
  393. url = https://github.com/Antergone/test2.git
  394. fetch = +refs/heads/*:refs/remotes/origin/*
  395. `)
  396. })
  397. Convey("Inverse case", func() {
  398. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
  399. [remote "origin"]
  400. url = https://github.com/Antergone/test1.git
  401. url = https://github.com/Antergone/test2.git`))
  402. So(err, ShouldBeNil)
  403. So(f, ShouldNotBeNil)
  404. So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test2.git")
  405. })
  406. })
  407. Convey("Unescape double quotes inside value", func() {
  408. f, err := ini.LoadSources(ini.LoadOptions{
  409. AllowPythonMultilineValues: true,
  410. UnescapeValueDoubleQuotes: true,
  411. }, []byte(`
  412. create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
  413. So(err, ShouldBeNil)
  414. So(f, ShouldNotBeNil)
  415. So(f.Section("").Key("create_repo").String(), ShouldEqual, `创建了仓库 <a href="%s">%s</a>`)
  416. Convey("Inverse case", func() {
  417. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
  418. create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
  419. So(err, ShouldBeNil)
  420. So(f, ShouldNotBeNil)
  421. So(f.Section("").Key("create_repo").String(), ShouldEqual, `"创建了仓库 <a href=\"%s\">%s</a>"`)
  422. })
  423. })
  424. Convey("Unescape comment symbols inside value", func() {
  425. f, err := ini.LoadSources(ini.LoadOptions{
  426. AllowPythonMultilineValues: true,
  427. IgnoreInlineComment: true,
  428. UnescapeValueCommentSymbols: true,
  429. }, []byte(`
  430. key = test value <span style="color: %s\; background: %s">more text</span>
  431. `))
  432. So(err, ShouldBeNil)
  433. So(f, ShouldNotBeNil)
  434. So(f.Section("").Key("key").String(), ShouldEqual, `test value <span style="color: %s; background: %s">more text</span>`)
  435. })
  436. Convey("Can parse small python-compatible INI files", func() {
  437. f, err := ini.LoadSources(ini.LoadOptions{
  438. AllowPythonMultilineValues: true,
  439. Insensitive: true,
  440. UnparseableSections: []string{"core_lesson", "comments"},
  441. }, []byte(`
  442. [long]
  443. long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
  444. foo
  445. bar
  446. foobar
  447. barfoo
  448. -----END RSA PRIVATE KEY-----
  449. `))
  450. So(err, ShouldBeNil)
  451. So(f, ShouldNotBeNil)
  452. So(f.Section("long").Key("long_rsa_private_key").String(), ShouldEqual, "-----BEGIN RSA PRIVATE KEY-----\nfoo\nbar\nfoobar\nbarfoo\n-----END RSA PRIVATE KEY-----")
  453. })
  454. Convey("Can parse big python-compatible INI files", func() {
  455. f, err := ini.LoadSources(ini.LoadOptions{
  456. AllowPythonMultilineValues: true,
  457. Insensitive: true,
  458. UnparseableSections: []string{"core_lesson", "comments"},
  459. }, []byte(`
  460. [long]
  461. long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
  462. 1foo
  463. 2bar
  464. 3foobar
  465. 4barfoo
  466. 5foo
  467. 6bar
  468. 7foobar
  469. 8barfoo
  470. 9foo
  471. 10bar
  472. 11foobar
  473. 12barfoo
  474. 13foo
  475. 14bar
  476. 15foobar
  477. 16barfoo
  478. 17foo
  479. 18bar
  480. 19foobar
  481. 20barfoo
  482. 21foo
  483. 22bar
  484. 23foobar
  485. 24barfoo
  486. 25foo
  487. 26bar
  488. 27foobar
  489. 28barfoo
  490. 29foo
  491. 30bar
  492. 31foobar
  493. 32barfoo
  494. 33foo
  495. 34bar
  496. 35foobar
  497. 36barfoo
  498. 37foo
  499. 38bar
  500. 39foobar
  501. 40barfoo
  502. 41foo
  503. 42bar
  504. 43foobar
  505. 44barfoo
  506. 45foo
  507. 46bar
  508. 47foobar
  509. 48barfoo
  510. 49foo
  511. 50bar
  512. 51foobar
  513. 52barfoo
  514. 53foo
  515. 54bar
  516. 55foobar
  517. 56barfoo
  518. 57foo
  519. 58bar
  520. 59foobar
  521. 60barfoo
  522. 61foo
  523. 62bar
  524. 63foobar
  525. 64barfoo
  526. 65foo
  527. 66bar
  528. 67foobar
  529. 68barfoo
  530. 69foo
  531. 70bar
  532. 71foobar
  533. 72barfoo
  534. 73foo
  535. 74bar
  536. 75foobar
  537. 76barfoo
  538. 77foo
  539. 78bar
  540. 79foobar
  541. 80barfoo
  542. 81foo
  543. 82bar
  544. 83foobar
  545. 84barfoo
  546. 85foo
  547. 86bar
  548. 87foobar
  549. 88barfoo
  550. 89foo
  551. 90bar
  552. 91foobar
  553. 92barfoo
  554. 93foo
  555. 94bar
  556. 95foobar
  557. 96barfoo
  558. -----END RSA PRIVATE KEY-----
  559. `))
  560. So(err, ShouldBeNil)
  561. So(f, ShouldNotBeNil)
  562. So(f.Section("long").Key("long_rsa_private_key").String(), ShouldEqual, `-----BEGIN RSA PRIVATE KEY-----
  563. 1foo
  564. 2bar
  565. 3foobar
  566. 4barfoo
  567. 5foo
  568. 6bar
  569. 7foobar
  570. 8barfoo
  571. 9foo
  572. 10bar
  573. 11foobar
  574. 12barfoo
  575. 13foo
  576. 14bar
  577. 15foobar
  578. 16barfoo
  579. 17foo
  580. 18bar
  581. 19foobar
  582. 20barfoo
  583. 21foo
  584. 22bar
  585. 23foobar
  586. 24barfoo
  587. 25foo
  588. 26bar
  589. 27foobar
  590. 28barfoo
  591. 29foo
  592. 30bar
  593. 31foobar
  594. 32barfoo
  595. 33foo
  596. 34bar
  597. 35foobar
  598. 36barfoo
  599. 37foo
  600. 38bar
  601. 39foobar
  602. 40barfoo
  603. 41foo
  604. 42bar
  605. 43foobar
  606. 44barfoo
  607. 45foo
  608. 46bar
  609. 47foobar
  610. 48barfoo
  611. 49foo
  612. 50bar
  613. 51foobar
  614. 52barfoo
  615. 53foo
  616. 54bar
  617. 55foobar
  618. 56barfoo
  619. 57foo
  620. 58bar
  621. 59foobar
  622. 60barfoo
  623. 61foo
  624. 62bar
  625. 63foobar
  626. 64barfoo
  627. 65foo
  628. 66bar
  629. 67foobar
  630. 68barfoo
  631. 69foo
  632. 70bar
  633. 71foobar
  634. 72barfoo
  635. 73foo
  636. 74bar
  637. 75foobar
  638. 76barfoo
  639. 77foo
  640. 78bar
  641. 79foobar
  642. 80barfoo
  643. 81foo
  644. 82bar
  645. 83foobar
  646. 84barfoo
  647. 85foo
  648. 86bar
  649. 87foobar
  650. 88barfoo
  651. 89foo
  652. 90bar
  653. 91foobar
  654. 92barfoo
  655. 93foo
  656. 94bar
  657. 95foobar
  658. 96barfoo
  659. -----END RSA PRIVATE KEY-----`)
  660. })
  661. Convey("Allow unparsable sections", func() {
  662. f, err := ini.LoadSources(ini.LoadOptions{
  663. AllowPythonMultilineValues: true,
  664. Insensitive: true,
  665. UnparseableSections: []string{"core_lesson", "comments"},
  666. }, []byte(`
  667. Lesson_Location = 87
  668. Lesson_Status = C
  669. Score = 3
  670. Time = 00:02:30
  671. [CORE_LESSON]
  672. my lesson state data – 1111111111111111111000000000000000001110000
  673. 111111111111111111100000000000111000000000 – end my lesson state data
  674. [COMMENTS]
  675. <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`))
  676. So(err, ShouldBeNil)
  677. So(f, ShouldNotBeNil)
  678. So(f.Section("").Key("score").String(), ShouldEqual, "3")
  679. So(f.Section("").Body(), ShouldBeEmpty)
  680. So(f.Section("core_lesson").Body(), ShouldEqual, `my lesson state data – 1111111111111111111000000000000000001110000
  681. 111111111111111111100000000000111000000000 – end my lesson state data`)
  682. So(f.Section("comments").Body(), ShouldEqual, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)
  683. Convey("Write out", func() {
  684. var buf bytes.Buffer
  685. _, err := f.WriteTo(&buf)
  686. So(err, ShouldBeNil)
  687. So(buf.String(), ShouldEqual, `lesson_location = 87
  688. lesson_status = C
  689. score = 3
  690. time = 00:02:30
  691. [core_lesson]
  692. my lesson state data – 1111111111111111111000000000000000001110000
  693. 111111111111111111100000000000111000000000 – end my lesson state data
  694. [comments]
  695. <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
  696. `)
  697. })
  698. Convey("Inverse case", func() {
  699. _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: true}, []byte(`
  700. [CORE_LESSON]
  701. my lesson state data – 1111111111111111111000000000000000001110000
  702. 111111111111111111100000000000111000000000 – end my lesson state data`))
  703. So(err, ShouldNotBeNil)
  704. })
  705. })
  706. Convey("And false `SpaceBeforeInlineComment`", func() {
  707. Convey("Can't parse INI files containing `#` or `;` in value", func() {
  708. f, err := ini.LoadSources(
  709. ini.LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: false},
  710. []byte(`
  711. [author]
  712. NAME = U#n#k#n#w#o#n
  713. GITHUB = U;n;k;n;w;o;n
  714. `))
  715. So(err, ShouldBeNil)
  716. So(f, ShouldNotBeNil)
  717. sec := f.Section("author")
  718. nameValue := sec.Key("NAME").String()
  719. githubValue := sec.Key("GITHUB").String()
  720. So(nameValue, ShouldEqual, "U")
  721. So(githubValue, ShouldEqual, "U")
  722. })
  723. })
  724. Convey("And true `SpaceBeforeInlineComment`", func() {
  725. Convey("Can parse INI files containing `#` or `;` in value", func() {
  726. f, err := ini.LoadSources(
  727. ini.LoadOptions{AllowPythonMultilineValues: false, SpaceBeforeInlineComment: true},
  728. []byte(`
  729. [author]
  730. NAME = U#n#k#n#w#o#n
  731. GITHUB = U;n;k;n;w;o;n
  732. `))
  733. So(err, ShouldBeNil)
  734. So(f, ShouldNotBeNil)
  735. sec := f.Section("author")
  736. nameValue := sec.Key("NAME").String()
  737. githubValue := sec.Key("GITHUB").String()
  738. So(nameValue, ShouldEqual, "U#n#k#n#w#o#n")
  739. So(githubValue, ShouldEqual, "U;n;k;n;w;o;n")
  740. })
  741. })
  742. })
  743. Convey("with false `AllowPythonMultilineValues`", func() {
  744. Convey("Ignore nonexistent files", func() {
  745. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false, Loose: true}, _NOT_FOUND_CONF, _MINIMAL_CONF)
  746. So(err, ShouldBeNil)
  747. So(f, ShouldNotBeNil)
  748. Convey("Inverse case", func() {
  749. _, err = ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, _NOT_FOUND_CONF)
  750. So(err, ShouldNotBeNil)
  751. })
  752. })
  753. Convey("Insensitive to section and key names", func() {
  754. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false, Insensitive: true}, _MINIMAL_CONF)
  755. So(err, ShouldBeNil)
  756. So(f, ShouldNotBeNil)
  757. So(f.Section("Author").Key("e-mail").String(), ShouldEqual, "u@gogs.io")
  758. Convey("Write out", func() {
  759. var buf bytes.Buffer
  760. _, err := f.WriteTo(&buf)
  761. So(err, ShouldBeNil)
  762. So(buf.String(), ShouldEqual, `[author]
  763. e-mail = u@gogs.io
  764. `)
  765. })
  766. Convey("Inverse case", func() {
  767. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, _MINIMAL_CONF)
  768. So(err, ShouldBeNil)
  769. So(f, ShouldNotBeNil)
  770. So(f.Section("Author").Key("e-mail").String(), ShouldBeEmpty)
  771. })
  772. })
  773. Convey("Ignore continuation lines", func() {
  774. f, err := ini.LoadSources(ini.LoadOptions{
  775. AllowPythonMultilineValues: false,
  776. IgnoreContinuation: true,
  777. }, []byte(`
  778. key1=a\b\
  779. key2=c\d\
  780. key3=value`))
  781. So(err, ShouldBeNil)
  782. So(f, ShouldNotBeNil)
  783. So(f.Section("").Key("key1").String(), ShouldEqual, `a\b\`)
  784. So(f.Section("").Key("key2").String(), ShouldEqual, `c\d\`)
  785. So(f.Section("").Key("key3").String(), ShouldEqual, "value")
  786. Convey("Inverse case", func() {
  787. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
  788. key1=a\b\
  789. key2=c\d\`))
  790. So(err, ShouldBeNil)
  791. So(f, ShouldNotBeNil)
  792. So(f.Section("").Key("key1").String(), ShouldEqual, `a\bkey2=c\d`)
  793. })
  794. })
  795. Convey("Ignore inline comments", func() {
  796. f, err := ini.LoadSources(ini.LoadOptions{
  797. AllowPythonMultilineValues: false,
  798. IgnoreInlineComment: true,
  799. }, []byte(`
  800. key1=value ;comment
  801. key2=value2 #comment2
  802. key3=val#ue #comment3`))
  803. So(err, ShouldBeNil)
  804. So(f, ShouldNotBeNil)
  805. So(f.Section("").Key("key1").String(), ShouldEqual, `value ;comment`)
  806. So(f.Section("").Key("key2").String(), ShouldEqual, `value2 #comment2`)
  807. So(f.Section("").Key("key3").String(), ShouldEqual, `val#ue #comment3`)
  808. Convey("Inverse case", func() {
  809. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
  810. key1=value ;comment
  811. key2=value2 #comment2`))
  812. So(err, ShouldBeNil)
  813. So(f, ShouldNotBeNil)
  814. So(f.Section("").Key("key1").String(), ShouldEqual, `value`)
  815. So(f.Section("").Key("key1").Comment, ShouldEqual, `;comment`)
  816. So(f.Section("").Key("key2").String(), ShouldEqual, `value2`)
  817. So(f.Section("").Key("key2").Comment, ShouldEqual, `#comment2`)
  818. })
  819. })
  820. Convey("Allow boolean type keys", func() {
  821. f, err := ini.LoadSources(ini.LoadOptions{
  822. AllowPythonMultilineValues: false,
  823. AllowBooleanKeys: true,
  824. }, []byte(`
  825. key1=hello
  826. #key2
  827. key3`))
  828. So(err, ShouldBeNil)
  829. So(f, ShouldNotBeNil)
  830. So(f.Section("").KeyStrings(), ShouldResemble, []string{"key1", "key3"})
  831. So(f.Section("").Key("key3").MustBool(false), ShouldBeTrue)
  832. Convey("Write out", func() {
  833. var buf bytes.Buffer
  834. _, err := f.WriteTo(&buf)
  835. So(err, ShouldBeNil)
  836. So(buf.String(), ShouldEqual, `key1 = hello
  837. # key2
  838. key3
  839. `)
  840. })
  841. Convey("Inverse case", func() {
  842. _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
  843. key1=hello
  844. #key2
  845. key3`))
  846. So(err, ShouldNotBeNil)
  847. })
  848. })
  849. Convey("Allow shadow keys", func() {
  850. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false, AllowShadows: true}, []byte(`
  851. [remote "origin"]
  852. url = https://github.com/Antergone/test1.git
  853. url = https://github.com/Antergone/test2.git
  854. fetch = +refs/heads/*:refs/remotes/origin/*`))
  855. So(err, ShouldBeNil)
  856. So(f, ShouldNotBeNil)
  857. So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test1.git")
  858. So(f.Section(`remote "origin"`).Key("url").ValueWithShadows(), ShouldResemble, []string{
  859. "https://github.com/Antergone/test1.git",
  860. "https://github.com/Antergone/test2.git",
  861. })
  862. So(f.Section(`remote "origin"`).Key("fetch").String(), ShouldEqual, "+refs/heads/*:refs/remotes/origin/*")
  863. Convey("Write out", func() {
  864. var buf bytes.Buffer
  865. _, err := f.WriteTo(&buf)
  866. So(err, ShouldBeNil)
  867. So(buf.String(), ShouldEqual, `[remote "origin"]
  868. url = https://github.com/Antergone/test1.git
  869. url = https://github.com/Antergone/test2.git
  870. fetch = +refs/heads/*:refs/remotes/origin/*
  871. `)
  872. })
  873. Convey("Inverse case", func() {
  874. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
  875. [remote "origin"]
  876. url = https://github.com/Antergone/test1.git
  877. url = https://github.com/Antergone/test2.git`))
  878. So(err, ShouldBeNil)
  879. So(f, ShouldNotBeNil)
  880. So(f.Section(`remote "origin"`).Key("url").String(), ShouldEqual, "https://github.com/Antergone/test2.git")
  881. })
  882. })
  883. Convey("Unescape double quotes inside value", func() {
  884. f, err := ini.LoadSources(ini.LoadOptions{
  885. AllowPythonMultilineValues: false,
  886. UnescapeValueDoubleQuotes: true,
  887. }, []byte(`
  888. create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
  889. So(err, ShouldBeNil)
  890. So(f, ShouldNotBeNil)
  891. So(f.Section("").Key("create_repo").String(), ShouldEqual, `创建了仓库 <a href="%s">%s</a>`)
  892. Convey("Inverse case", func() {
  893. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
  894. create_repo="创建了仓库 <a href=\"%s\">%s</a>"`))
  895. So(err, ShouldBeNil)
  896. So(f, ShouldNotBeNil)
  897. So(f.Section("").Key("create_repo").String(), ShouldEqual, `"创建了仓库 <a href=\"%s\">%s</a>"`)
  898. })
  899. })
  900. Convey("Unescape comment symbols inside value", func() {
  901. f, err := ini.LoadSources(ini.LoadOptions{
  902. AllowPythonMultilineValues: false,
  903. IgnoreInlineComment: true,
  904. UnescapeValueCommentSymbols: true,
  905. }, []byte(`
  906. key = test value <span style="color: %s\; background: %s">more text</span>
  907. `))
  908. So(err, ShouldBeNil)
  909. So(f, ShouldNotBeNil)
  910. So(f.Section("").Key("key").String(), ShouldEqual, `test value <span style="color: %s; background: %s">more text</span>`)
  911. })
  912. Convey("Can't parse small python-compatible INI files", func() {
  913. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
  914. [long]
  915. long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
  916. foo
  917. bar
  918. foobar
  919. barfoo
  920. -----END RSA PRIVATE KEY-----
  921. `))
  922. So(err, ShouldNotBeNil)
  923. So(f, ShouldBeNil)
  924. So(err.Error(), ShouldEqual, "key-value delimiter not found: foo\n")
  925. })
  926. Convey("Can't parse big python-compatible INI files", func() {
  927. f, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
  928. [long]
  929. long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
  930. 1foo
  931. 2bar
  932. 3foobar
  933. 4barfoo
  934. 5foo
  935. 6bar
  936. 7foobar
  937. 8barfoo
  938. 9foo
  939. 10bar
  940. 11foobar
  941. 12barfoo
  942. 13foo
  943. 14bar
  944. 15foobar
  945. 16barfoo
  946. 17foo
  947. 18bar
  948. 19foobar
  949. 20barfoo
  950. 21foo
  951. 22bar
  952. 23foobar
  953. 24barfoo
  954. 25foo
  955. 26bar
  956. 27foobar
  957. 28barfoo
  958. 29foo
  959. 30bar
  960. 31foobar
  961. 32barfoo
  962. 33foo
  963. 34bar
  964. 35foobar
  965. 36barfoo
  966. 37foo
  967. 38bar
  968. 39foobar
  969. 40barfoo
  970. 41foo
  971. 42bar
  972. 43foobar
  973. 44barfoo
  974. 45foo
  975. 46bar
  976. 47foobar
  977. 48barfoo
  978. 49foo
  979. 50bar
  980. 51foobar
  981. 52barfoo
  982. 53foo
  983. 54bar
  984. 55foobar
  985. 56barfoo
  986. 57foo
  987. 58bar
  988. 59foobar
  989. 60barfoo
  990. 61foo
  991. 62bar
  992. 63foobar
  993. 64barfoo
  994. 65foo
  995. 66bar
  996. 67foobar
  997. 68barfoo
  998. 69foo
  999. 70bar
  1000. 71foobar
  1001. 72barfoo
  1002. 73foo
  1003. 74bar
  1004. 75foobar
  1005. 76barfoo
  1006. 77foo
  1007. 78bar
  1008. 79foobar
  1009. 80barfoo
  1010. 81foo
  1011. 82bar
  1012. 83foobar
  1013. 84barfoo
  1014. 85foo
  1015. 86bar
  1016. 87foobar
  1017. 88barfoo
  1018. 89foo
  1019. 90bar
  1020. 91foobar
  1021. 92barfoo
  1022. 93foo
  1023. 94bar
  1024. 95foobar
  1025. 96barfoo
  1026. -----END RSA PRIVATE KEY-----
  1027. `))
  1028. So(err, ShouldNotBeNil)
  1029. So(f, ShouldBeNil)
  1030. So(err.Error(), ShouldEqual, "key-value delimiter not found: 1foo\n")
  1031. })
  1032. Convey("Allow unparsable sections", func() {
  1033. f, err := ini.LoadSources(ini.LoadOptions{
  1034. AllowPythonMultilineValues: false,
  1035. Insensitive: true,
  1036. UnparseableSections: []string{"core_lesson", "comments"},
  1037. }, []byte(`
  1038. Lesson_Location = 87
  1039. Lesson_Status = C
  1040. Score = 3
  1041. Time = 00:02:30
  1042. [CORE_LESSON]
  1043. my lesson state data – 1111111111111111111000000000000000001110000
  1044. 111111111111111111100000000000111000000000 – end my lesson state data
  1045. [COMMENTS]
  1046. <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`))
  1047. So(err, ShouldBeNil)
  1048. So(f, ShouldNotBeNil)
  1049. So(f.Section("").Key("score").String(), ShouldEqual, "3")
  1050. So(f.Section("").Body(), ShouldBeEmpty)
  1051. So(f.Section("core_lesson").Body(), ShouldEqual, `my lesson state data – 1111111111111111111000000000000000001110000
  1052. 111111111111111111100000000000111000000000 – end my lesson state data`)
  1053. So(f.Section("comments").Body(), ShouldEqual, `<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)
  1054. Convey("Write out", func() {
  1055. var buf bytes.Buffer
  1056. _, err := f.WriteTo(&buf)
  1057. So(err, ShouldBeNil)
  1058. So(buf.String(), ShouldEqual, `lesson_location = 87
  1059. lesson_status = C
  1060. score = 3
  1061. time = 00:02:30
  1062. [core_lesson]
  1063. my lesson state data – 1111111111111111111000000000000000001110000
  1064. 111111111111111111100000000000111000000000 – end my lesson state data
  1065. [comments]
  1066. <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
  1067. `)
  1068. })
  1069. Convey("Inverse case", func() {
  1070. _, err := ini.LoadSources(ini.LoadOptions{AllowPythonMultilineValues: false}, []byte(`
  1071. [CORE_LESSON]
  1072. my lesson state data – 1111111111111111111000000000000000001110000
  1073. 111111111111111111100000000000111000000000 – end my lesson state data`))
  1074. So(err, ShouldNotBeNil)
  1075. })
  1076. })
  1077. Convey("And false `SpaceBeforeInlineComment`", func() {
  1078. Convey("Can't parse INI files containing `#` or `;` in value", func() {
  1079. f, err := ini.LoadSources(
  1080. ini.LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: false},
  1081. []byte(`
  1082. [author]
  1083. NAME = U#n#k#n#w#o#n
  1084. GITHUB = U;n;k;n;w;o;n
  1085. `))
  1086. So(err, ShouldBeNil)
  1087. So(f, ShouldNotBeNil)
  1088. sec := f.Section("author")
  1089. nameValue := sec.Key("NAME").String()
  1090. githubValue := sec.Key("GITHUB").String()
  1091. So(nameValue, ShouldEqual, "U")
  1092. So(githubValue, ShouldEqual, "U")
  1093. })
  1094. })
  1095. Convey("And true `SpaceBeforeInlineComment`", func() {
  1096. Convey("Can parse INI files containing `#` or `;` in value", func() {
  1097. f, err := ini.LoadSources(
  1098. ini.LoadOptions{AllowPythonMultilineValues: true, SpaceBeforeInlineComment: true},
  1099. []byte(`
  1100. [author]
  1101. NAME = U#n#k#n#w#o#n
  1102. GITHUB = U;n;k;n;w;o;n
  1103. `))
  1104. So(err, ShouldBeNil)
  1105. So(f, ShouldNotBeNil)
  1106. sec := f.Section("author")
  1107. nameValue := sec.Key("NAME").String()
  1108. githubValue := sec.Key("GITHUB").String()
  1109. So(nameValue, ShouldEqual, "U#n#k#n#w#o#n")
  1110. So(githubValue, ShouldEqual, "U;n;k;n;w;o;n")
  1111. })
  1112. })
  1113. })
  1114. })
  1115. }
  1116. func Test_KeyValueDelimiters(t *testing.T) {
  1117. Convey("Custom key-value delimiters", t, func() {
  1118. f, err := ini.LoadSources(ini.LoadOptions{
  1119. KeyValueDelimiters: "?!",
  1120. }, []byte(`
  1121. [section]
  1122. key1?value1
  1123. key2!value2
  1124. `))
  1125. So(err, ShouldBeNil)
  1126. So(f, ShouldNotBeNil)
  1127. So(f.Section("section").Key("key1").String(), ShouldEqual, "value1")
  1128. So(f.Section("section").Key("key2").String(), ShouldEqual, "value2")
  1129. })
  1130. }
  1131. func Test_PreserveSurroundedQuote(t *testing.T) {
  1132. Convey("Preserve surrounded quote test", t, func() {
  1133. f, err := ini.LoadSources(ini.LoadOptions{
  1134. PreserveSurroundedQuote: true,
  1135. }, []byte(`
  1136. [section]
  1137. key1 = "value1"
  1138. key2 = value2
  1139. `))
  1140. So(err, ShouldBeNil)
  1141. So(f, ShouldNotBeNil)
  1142. So(f.Section("section").Key("key1").String(), ShouldEqual, "\"value1\"")
  1143. So(f.Section("section").Key("key2").String(), ShouldEqual, "value2")
  1144. })
  1145. Convey("Preserve surrounded quote test inverse test", t, func() {
  1146. f, err := ini.LoadSources(ini.LoadOptions{
  1147. PreserveSurroundedQuote: false,
  1148. }, []byte(`
  1149. [section]
  1150. key1 = "value1"
  1151. key2 = value2
  1152. `))
  1153. So(err, ShouldBeNil)
  1154. So(f, ShouldNotBeNil)
  1155. So(f.Section("section").Key("key1").String(), ShouldEqual, "value1")
  1156. So(f.Section("section").Key("key2").String(), ShouldEqual, "value2")
  1157. })
  1158. }