1// Copyright 2017 Frank Schroeder. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package properties 6 7import ( 8 "bytes" 9 "flag" 10 "fmt" 11 "math" 12 "os" 13 "reflect" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/magiconair/properties/assert" 19) 20 21var verbose = flag.Bool("verbose", false, "Verbose output") 22 23func init() { 24 ErrorHandler = PanicHandler 25} 26 27// ---------------------------------------------------------------------------- 28 29// define test cases in the form of 30// {"input", "key1", "value1", "key2", "value2", ...} 31var complexTests = [][]string{ 32 // whitespace prefix 33 {" key=value", "key", "value"}, // SPACE prefix 34 {"\fkey=value", "key", "value"}, // FF prefix 35 {"\tkey=value", "key", "value"}, // TAB prefix 36 {" \f\tkey=value", "key", "value"}, // mix prefix 37 38 // multiple keys 39 {"key1=value1\nkey2=value2\n", "key1", "value1", "key2", "value2"}, 40 {"key1=value1\rkey2=value2\r", "key1", "value1", "key2", "value2"}, 41 {"key1=value1\r\nkey2=value2\r\n", "key1", "value1", "key2", "value2"}, 42 43 // blank lines 44 {"\nkey=value\n", "key", "value"}, 45 {"\rkey=value\r", "key", "value"}, 46 {"\r\nkey=value\r\n", "key", "value"}, 47 {"\nkey=value\n \nkey2=value2", "key", "value", "key2", "value2"}, 48 {"\nkey=value\n\t\nkey2=value2", "key", "value", "key2", "value2"}, 49 50 // escaped chars in key 51 {"k\\ ey = value", "k ey", "value"}, 52 {"k\\:ey = value", "k:ey", "value"}, 53 {"k\\=ey = value", "k=ey", "value"}, 54 {"k\\fey = value", "k\fey", "value"}, 55 {"k\\ney = value", "k\ney", "value"}, 56 {"k\\rey = value", "k\rey", "value"}, 57 {"k\\tey = value", "k\tey", "value"}, 58 59 // escaped chars in value 60 {"key = v\\ alue", "key", "v alue"}, 61 {"key = v\\:alue", "key", "v:alue"}, 62 {"key = v\\=alue", "key", "v=alue"}, 63 {"key = v\\falue", "key", "v\falue"}, 64 {"key = v\\nalue", "key", "v\nalue"}, 65 {"key = v\\ralue", "key", "v\ralue"}, 66 {"key = v\\talue", "key", "v\talue"}, 67 68 // silently dropped escape character 69 {"k\\zey = value", "kzey", "value"}, 70 {"key = v\\zalue", "key", "vzalue"}, 71 72 // unicode literals 73 {"key\\u2318 = value", "key⌘", "value"}, 74 {"k\\u2318ey = value", "k⌘ey", "value"}, 75 {"key = value\\u2318", "key", "value⌘"}, 76 {"key = valu\\u2318e", "key", "valu⌘e"}, 77 78 // multiline values 79 {"key = valueA,\\\n valueB", "key", "valueA,valueB"}, // SPACE indent 80 {"key = valueA,\\\n\f\f\fvalueB", "key", "valueA,valueB"}, // FF indent 81 {"key = valueA,\\\n\t\t\tvalueB", "key", "valueA,valueB"}, // TAB indent 82 {"key = valueA,\\\n \f\tvalueB", "key", "valueA,valueB"}, // mix indent 83 84 // comments 85 {"# this is a comment\n! and so is this\nkey1=value1\nkey#2=value#2\n\nkey!3=value!3\n# and another one\n! and the final one", "key1", "value1", "key#2", "value#2", "key!3", "value!3"}, 86 87 // expansion tests 88 {"key=value\nkey2=${key}", "key", "value", "key2", "value"}, 89 {"key=value\nkey2=aa${key}", "key", "value", "key2", "aavalue"}, 90 {"key=value\nkey2=${key}bb", "key", "value", "key2", "valuebb"}, 91 {"key=value\nkey2=aa${key}bb", "key", "value", "key2", "aavaluebb"}, 92 {"key=value\nkey2=${key}\nkey3=${key2}", "key", "value", "key2", "value", "key3", "value"}, 93 {"key=${USER}", "key", os.Getenv("USER")}, 94 {"key=${USER}\nUSER=value", "key", "value", "USER", "value"}, 95} 96 97// ---------------------------------------------------------------------------- 98 99var commentTests = []struct { 100 input, key, value string 101 comments []string 102}{ 103 {"key=value", "key", "value", nil}, 104 {"#\nkey=value", "key", "value", []string{""}}, 105 {"#comment\nkey=value", "key", "value", []string{"comment"}}, 106 {"# comment\nkey=value", "key", "value", []string{"comment"}}, 107 {"# comment\nkey=value", "key", "value", []string{"comment"}}, 108 {"# comment\n\nkey=value", "key", "value", []string{"comment"}}, 109 {"# comment1\n# comment2\nkey=value", "key", "value", []string{"comment1", "comment2"}}, 110 {"# comment1\n\n# comment2\n\nkey=value", "key", "value", []string{"comment1", "comment2"}}, 111 {"!comment\nkey=value", "key", "value", []string{"comment"}}, 112 {"! comment\nkey=value", "key", "value", []string{"comment"}}, 113 {"! comment\nkey=value", "key", "value", []string{"comment"}}, 114 {"! comment\n\nkey=value", "key", "value", []string{"comment"}}, 115 {"! comment1\n! comment2\nkey=value", "key", "value", []string{"comment1", "comment2"}}, 116 {"! comment1\n\n! comment2\n\nkey=value", "key", "value", []string{"comment1", "comment2"}}, 117} 118 119// ---------------------------------------------------------------------------- 120 121var errorTests = []struct { 122 input, msg string 123}{ 124 // unicode literals 125 {"key\\u1 = value", "invalid unicode literal"}, 126 {"key\\u12 = value", "invalid unicode literal"}, 127 {"key\\u123 = value", "invalid unicode literal"}, 128 {"key\\u123g = value", "invalid unicode literal"}, 129 {"key\\u123", "invalid unicode literal"}, 130 131 // circular references 132 {"key=${key}", "circular reference"}, 133 {"key1=${key2}\nkey2=${key1}", "circular reference"}, 134 135 // malformed expressions 136 {"key=${ke", "malformed expression"}, 137 {"key=valu${ke", "malformed expression"}, 138} 139 140// ---------------------------------------------------------------------------- 141 142var writeTests = []struct { 143 input, output, encoding string 144}{ 145 // ISO-8859-1 tests 146 {"key = value", "key = value\n", "ISO-8859-1"}, 147 {"key = value \\\n continued", "key = value continued\n", "ISO-8859-1"}, 148 {"key⌘ = value", "key\\u2318 = value\n", "ISO-8859-1"}, 149 {"ke\\ \\:y = value", "ke\\ \\:y = value\n", "ISO-8859-1"}, 150 151 // UTF-8 tests 152 {"key = value", "key = value\n", "UTF-8"}, 153 {"key = value \\\n continued", "key = value continued\n", "UTF-8"}, 154 {"key⌘ = value⌘", "key⌘ = value⌘\n", "UTF-8"}, 155 {"ke\\ \\:y = value", "ke\\ \\:y = value\n", "UTF-8"}, 156} 157 158// ---------------------------------------------------------------------------- 159 160var writeCommentTests = []struct { 161 input, output, encoding string 162}{ 163 // ISO-8859-1 tests 164 {"key = value", "key = value\n", "ISO-8859-1"}, 165 {"#\nkey = value", "key = value\n", "ISO-8859-1"}, 166 {"#\n#\n#\nkey = value", "key = value\n", "ISO-8859-1"}, 167 {"# comment\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"}, 168 {"\n# comment\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"}, 169 {"# comment\n\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"}, 170 {"# comment1\n# comment2\nkey = value", "# comment1\n# comment2\nkey = value\n", "ISO-8859-1"}, 171 {"#comment1\nkey1 = value1\n#comment2\nkey2 = value2", "# comment1\nkey1 = value1\n\n# comment2\nkey2 = value2\n", "ISO-8859-1"}, 172 173 // UTF-8 tests 174 {"key = value", "key = value\n", "UTF-8"}, 175 {"# comment⌘\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"}, 176 {"\n# comment⌘\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"}, 177 {"# comment⌘\n\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"}, 178 {"# comment1⌘\n# comment2⌘\nkey = value⌘", "# comment1⌘\n# comment2⌘\nkey = value⌘\n", "UTF-8"}, 179 {"#comment1⌘\nkey1 = value1⌘\n#comment2⌘\nkey2 = value2⌘", "# comment1⌘\nkey1 = value1⌘\n\n# comment2⌘\nkey2 = value2⌘\n", "UTF-8"}, 180} 181 182// ---------------------------------------------------------------------------- 183 184var boolTests = []struct { 185 input, key string 186 def, value bool 187}{ 188 // valid values for TRUE 189 {"key = 1", "key", false, true}, 190 {"key = on", "key", false, true}, 191 {"key = On", "key", false, true}, 192 {"key = ON", "key", false, true}, 193 {"key = true", "key", false, true}, 194 {"key = True", "key", false, true}, 195 {"key = TRUE", "key", false, true}, 196 {"key = yes", "key", false, true}, 197 {"key = Yes", "key", false, true}, 198 {"key = YES", "key", false, true}, 199 200 // valid values for FALSE (all other) 201 {"key = 0", "key", true, false}, 202 {"key = off", "key", true, false}, 203 {"key = false", "key", true, false}, 204 {"key = no", "key", true, false}, 205 206 // non existent key 207 {"key = true", "key2", false, false}, 208} 209 210// ---------------------------------------------------------------------------- 211 212var durationTests = []struct { 213 input, key string 214 def, value time.Duration 215}{ 216 // valid values 217 {"key = 1", "key", 999, 1}, 218 {"key = 0", "key", 999, 0}, 219 {"key = -1", "key", 999, -1}, 220 {"key = 0123", "key", 999, 123}, 221 222 // invalid values 223 {"key = 0xff", "key", 999, 999}, 224 {"key = 1.0", "key", 999, 999}, 225 {"key = a", "key", 999, 999}, 226 227 // non existent key 228 {"key = 1", "key2", 999, 999}, 229} 230 231// ---------------------------------------------------------------------------- 232 233var parsedDurationTests = []struct { 234 input, key string 235 def, value time.Duration 236}{ 237 // valid values 238 {"key = -1ns", "key", 999, -1 * time.Nanosecond}, 239 {"key = 300ms", "key", 999, 300 * time.Millisecond}, 240 {"key = 5s", "key", 999, 5 * time.Second}, 241 {"key = 3h", "key", 999, 3 * time.Hour}, 242 {"key = 2h45m", "key", 999, 2*time.Hour + 45*time.Minute}, 243 244 // invalid values 245 {"key = 0xff", "key", 999, 999}, 246 {"key = 1.0", "key", 999, 999}, 247 {"key = a", "key", 999, 999}, 248 {"key = 1", "key", 999, 999}, 249 {"key = 0", "key", 999, 0}, 250 251 // non existent key 252 {"key = 1", "key2", 999, 999}, 253} 254 255// ---------------------------------------------------------------------------- 256 257var floatTests = []struct { 258 input, key string 259 def, value float64 260}{ 261 // valid values 262 {"key = 1.0", "key", 999, 1.0}, 263 {"key = 0.0", "key", 999, 0.0}, 264 {"key = -1.0", "key", 999, -1.0}, 265 {"key = 1", "key", 999, 1}, 266 {"key = 0", "key", 999, 0}, 267 {"key = -1", "key", 999, -1}, 268 {"key = 0123", "key", 999, 123}, 269 270 // invalid values 271 {"key = 0xff", "key", 999, 999}, 272 {"key = a", "key", 999, 999}, 273 274 // non existent key 275 {"key = 1", "key2", 999, 999}, 276} 277 278// ---------------------------------------------------------------------------- 279 280var int64Tests = []struct { 281 input, key string 282 def, value int64 283}{ 284 // valid values 285 {"key = 1", "key", 999, 1}, 286 {"key = 0", "key", 999, 0}, 287 {"key = -1", "key", 999, -1}, 288 {"key = 0123", "key", 999, 123}, 289 290 // invalid values 291 {"key = 0xff", "key", 999, 999}, 292 {"key = 1.0", "key", 999, 999}, 293 {"key = a", "key", 999, 999}, 294 295 // non existent key 296 {"key = 1", "key2", 999, 999}, 297} 298 299// ---------------------------------------------------------------------------- 300 301var uint64Tests = []struct { 302 input, key string 303 def, value uint64 304}{ 305 // valid values 306 {"key = 1", "key", 999, 1}, 307 {"key = 0", "key", 999, 0}, 308 {"key = 0123", "key", 999, 123}, 309 310 // invalid values 311 {"key = -1", "key", 999, 999}, 312 {"key = 0xff", "key", 999, 999}, 313 {"key = 1.0", "key", 999, 999}, 314 {"key = a", "key", 999, 999}, 315 316 // non existent key 317 {"key = 1", "key2", 999, 999}, 318} 319 320// ---------------------------------------------------------------------------- 321 322var stringTests = []struct { 323 input, key string 324 def, value string 325}{ 326 // valid values 327 {"key = abc", "key", "def", "abc"}, 328 329 // non existent key 330 {"key = abc", "key2", "def", "def"}, 331} 332 333// ---------------------------------------------------------------------------- 334 335var keysTests = []struct { 336 input string 337 keys []string 338}{ 339 {"", []string{}}, 340 {"key = abc", []string{"key"}}, 341 {"key = abc\nkey2=def", []string{"key", "key2"}}, 342 {"key2 = abc\nkey=def", []string{"key2", "key"}}, 343 {"key = abc\nkey=def", []string{"key"}}, 344} 345 346// ---------------------------------------------------------------------------- 347 348var filterTests = []struct { 349 input string 350 pattern string 351 keys []string 352 err string 353}{ 354 {"", "", []string{}, ""}, 355 {"", "abc", []string{}, ""}, 356 {"key=value", "", []string{"key"}, ""}, 357 {"key=value", "key=", []string{}, ""}, 358 {"key=value\nfoo=bar", "", []string{"foo", "key"}, ""}, 359 {"key=value\nfoo=bar", "f", []string{"foo"}, ""}, 360 {"key=value\nfoo=bar", "fo", []string{"foo"}, ""}, 361 {"key=value\nfoo=bar", "foo", []string{"foo"}, ""}, 362 {"key=value\nfoo=bar", "fooo", []string{}, ""}, 363 {"key=value\nkey2=value2\nfoo=bar", "ey", []string{"key", "key2"}, ""}, 364 {"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}, ""}, 365 {"key=value\nkey2=value2\nfoo=bar", "^key", []string{"key", "key2"}, ""}, 366 {"key=value\nkey2=value2\nfoo=bar", "^(key|foo)", []string{"foo", "key", "key2"}, ""}, 367 {"key=value\nkey2=value2\nfoo=bar", "[ abc", nil, "error parsing regexp.*"}, 368} 369 370// ---------------------------------------------------------------------------- 371 372var filterPrefixTests = []struct { 373 input string 374 prefix string 375 keys []string 376}{ 377 {"", "", []string{}}, 378 {"", "abc", []string{}}, 379 {"key=value", "", []string{"key"}}, 380 {"key=value", "key=", []string{}}, 381 {"key=value\nfoo=bar", "", []string{"foo", "key"}}, 382 {"key=value\nfoo=bar", "f", []string{"foo"}}, 383 {"key=value\nfoo=bar", "fo", []string{"foo"}}, 384 {"key=value\nfoo=bar", "foo", []string{"foo"}}, 385 {"key=value\nfoo=bar", "fooo", []string{}}, 386 {"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}}, 387} 388 389// ---------------------------------------------------------------------------- 390 391var filterStripPrefixTests = []struct { 392 input string 393 prefix string 394 keys []string 395}{ 396 {"", "", []string{}}, 397 {"", "abc", []string{}}, 398 {"key=value", "", []string{"key"}}, 399 {"key=value", "key=", []string{}}, 400 {"key=value\nfoo=bar", "", []string{"foo", "key"}}, 401 {"key=value\nfoo=bar", "f", []string{"foo"}}, 402 {"key=value\nfoo=bar", "fo", []string{"foo"}}, 403 {"key=value\nfoo=bar", "foo", []string{"foo"}}, 404 {"key=value\nfoo=bar", "fooo", []string{}}, 405 {"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}}, 406} 407 408// ---------------------------------------------------------------------------- 409 410var setTests = []struct { 411 input string 412 key, value string 413 prev string 414 ok bool 415 err string 416 keys []string 417}{ 418 {"", "", "", "", false, "", []string{}}, 419 {"", "key", "value", "", false, "", []string{"key"}}, 420 {"key=value", "key2", "value2", "", false, "", []string{"key", "key2"}}, 421 {"key=value", "abc", "value3", "", false, "", []string{"key", "abc"}}, 422 {"key=value", "key", "value3", "value", true, "", []string{"key"}}, 423} 424 425// ---------------------------------------------------------------------------- 426 427// TestBasic tests basic single key/value combinations with all possible 428// whitespace, delimiter and newline permutations. 429func TestBasic(t *testing.T) { 430 testWhitespaceAndDelimiterCombinations(t, "key", "") 431 testWhitespaceAndDelimiterCombinations(t, "key", "value") 432 testWhitespaceAndDelimiterCombinations(t, "key", "value ") 433} 434 435func TestComplex(t *testing.T) { 436 for _, test := range complexTests { 437 testKeyValue(t, test[0], test[1:]...) 438 } 439} 440 441func TestErrors(t *testing.T) { 442 for _, test := range errorTests { 443 _, err := Load([]byte(test.input), ISO_8859_1) 444 assert.Equal(t, err != nil, true, "want error") 445 assert.Equal(t, strings.Contains(err.Error(), test.msg), true) 446 } 447} 448 449func TestDisableExpansion(t *testing.T) { 450 input := "key=value\nkey2=${key}" 451 p := mustParse(t, input) 452 p.DisableExpansion = true 453 assert.Equal(t, p.MustGet("key"), "value") 454 assert.Equal(t, p.MustGet("key2"), "${key}") 455 456 // with expansion disabled we can introduce circular references 457 p.MustSet("keyA", "${keyB}") 458 p.MustSet("keyB", "${keyA}") 459 assert.Equal(t, p.MustGet("keyA"), "${keyB}") 460 assert.Equal(t, p.MustGet("keyB"), "${keyA}") 461} 462 463func TestDisableExpansionStillUpdatesKeys(t *testing.T) { 464 p := NewProperties() 465 p.MustSet("p1", "a") 466 assert.Equal(t, p.Keys(), []string{"p1"}) 467 assert.Equal(t, p.String(), "p1 = a\n") 468 469 p.DisableExpansion = true 470 p.MustSet("p2", "b") 471 472 assert.Equal(t, p.Keys(), []string{"p1", "p2"}) 473 assert.Equal(t, p.String(), "p1 = a\np2 = b\n") 474} 475 476func TestMustGet(t *testing.T) { 477 input := "key = value\nkey2 = ghi" 478 p := mustParse(t, input) 479 assert.Equal(t, p.MustGet("key"), "value") 480 assert.Panic(t, func() { p.MustGet("invalid") }, "unknown property: invalid") 481} 482 483func TestGetBool(t *testing.T) { 484 for _, test := range boolTests { 485 p := mustParse(t, test.input) 486 assert.Equal(t, p.Len(), 1) 487 assert.Equal(t, p.GetBool(test.key, test.def), test.value) 488 } 489} 490 491func TestMustGetBool(t *testing.T) { 492 input := "key = true\nkey2 = ghi" 493 p := mustParse(t, input) 494 assert.Equal(t, p.MustGetBool("key"), true) 495 assert.Panic(t, func() { p.MustGetBool("invalid") }, "unknown property: invalid") 496} 497 498func TestGetDuration(t *testing.T) { 499 for _, test := range durationTests { 500 p := mustParse(t, test.input) 501 assert.Equal(t, p.Len(), 1) 502 assert.Equal(t, p.GetDuration(test.key, test.def), test.value) 503 } 504} 505 506func TestMustGetDuration(t *testing.T) { 507 input := "key = 123\nkey2 = ghi" 508 p := mustParse(t, input) 509 assert.Equal(t, p.MustGetDuration("key"), time.Duration(123)) 510 assert.Panic(t, func() { p.MustGetDuration("key2") }, "strconv.ParseInt: parsing.*") 511 assert.Panic(t, func() { p.MustGetDuration("invalid") }, "unknown property: invalid") 512} 513 514func TestGetParsedDuration(t *testing.T) { 515 for _, test := range parsedDurationTests { 516 p := mustParse(t, test.input) 517 assert.Equal(t, p.Len(), 1) 518 assert.Equal(t, p.GetParsedDuration(test.key, test.def), test.value) 519 } 520} 521 522func TestMustGetParsedDuration(t *testing.T) { 523 input := "key = 123ms\nkey2 = ghi" 524 p := mustParse(t, input) 525 assert.Equal(t, p.MustGetParsedDuration("key"), 123*time.Millisecond) 526 assert.Panic(t, func() { p.MustGetParsedDuration("key2") }, "time: invalid duration ghi") 527 assert.Panic(t, func() { p.MustGetParsedDuration("invalid") }, "unknown property: invalid") 528} 529 530func TestGetFloat64(t *testing.T) { 531 for _, test := range floatTests { 532 p := mustParse(t, test.input) 533 assert.Equal(t, p.Len(), 1) 534 assert.Equal(t, p.GetFloat64(test.key, test.def), test.value) 535 } 536} 537 538func TestMustGetFloat64(t *testing.T) { 539 input := "key = 123\nkey2 = ghi" 540 p := mustParse(t, input) 541 assert.Equal(t, p.MustGetFloat64("key"), float64(123)) 542 assert.Panic(t, func() { p.MustGetFloat64("key2") }, "strconv.ParseFloat: parsing.*") 543 assert.Panic(t, func() { p.MustGetFloat64("invalid") }, "unknown property: invalid") 544} 545 546func TestGetInt(t *testing.T) { 547 for _, test := range int64Tests { 548 p := mustParse(t, test.input) 549 assert.Equal(t, p.Len(), 1) 550 assert.Equal(t, p.GetInt(test.key, int(test.def)), int(test.value)) 551 } 552} 553 554func TestMustGetInt(t *testing.T) { 555 input := "key = 123\nkey2 = ghi" 556 p := mustParse(t, input) 557 assert.Equal(t, p.MustGetInt("key"), int(123)) 558 assert.Panic(t, func() { p.MustGetInt("key2") }, "strconv.ParseInt: parsing.*") 559 assert.Panic(t, func() { p.MustGetInt("invalid") }, "unknown property: invalid") 560} 561 562func TestGetInt64(t *testing.T) { 563 for _, test := range int64Tests { 564 p := mustParse(t, test.input) 565 assert.Equal(t, p.Len(), 1) 566 assert.Equal(t, p.GetInt64(test.key, test.def), test.value) 567 } 568} 569 570func TestMustGetInt64(t *testing.T) { 571 input := "key = 123\nkey2 = ghi" 572 p := mustParse(t, input) 573 assert.Equal(t, p.MustGetInt64("key"), int64(123)) 574 assert.Panic(t, func() { p.MustGetInt64("key2") }, "strconv.ParseInt: parsing.*") 575 assert.Panic(t, func() { p.MustGetInt64("invalid") }, "unknown property: invalid") 576} 577 578func TestGetUint(t *testing.T) { 579 for _, test := range uint64Tests { 580 p := mustParse(t, test.input) 581 assert.Equal(t, p.Len(), 1) 582 assert.Equal(t, p.GetUint(test.key, uint(test.def)), uint(test.value)) 583 } 584} 585 586func TestMustGetUint(t *testing.T) { 587 input := "key = 123\nkey2 = ghi" 588 p := mustParse(t, input) 589 assert.Equal(t, p.MustGetUint("key"), uint(123)) 590 assert.Panic(t, func() { p.MustGetUint64("key2") }, "strconv.ParseUint: parsing.*") 591 assert.Panic(t, func() { p.MustGetUint64("invalid") }, "unknown property: invalid") 592} 593 594func TestGetUint64(t *testing.T) { 595 for _, test := range uint64Tests { 596 p := mustParse(t, test.input) 597 assert.Equal(t, p.Len(), 1) 598 assert.Equal(t, p.GetUint64(test.key, test.def), test.value) 599 } 600} 601 602func TestMustGetUint64(t *testing.T) { 603 input := "key = 123\nkey2 = ghi" 604 p := mustParse(t, input) 605 assert.Equal(t, p.MustGetUint64("key"), uint64(123)) 606 assert.Panic(t, func() { p.MustGetUint64("key2") }, "strconv.ParseUint: parsing.*") 607 assert.Panic(t, func() { p.MustGetUint64("invalid") }, "unknown property: invalid") 608} 609 610func TestGetString(t *testing.T) { 611 for _, test := range stringTests { 612 p := mustParse(t, test.input) 613 assert.Equal(t, p.Len(), 1) 614 assert.Equal(t, p.GetString(test.key, test.def), test.value) 615 } 616} 617 618func TestMustGetString(t *testing.T) { 619 input := `key = value` 620 p := mustParse(t, input) 621 assert.Equal(t, p.MustGetString("key"), "value") 622 assert.Panic(t, func() { p.MustGetString("invalid") }, "unknown property: invalid") 623} 624 625func TestComment(t *testing.T) { 626 for _, test := range commentTests { 627 p := mustParse(t, test.input) 628 assert.Equal(t, p.MustGetString(test.key), test.value) 629 assert.Equal(t, p.GetComments(test.key), test.comments) 630 if test.comments != nil { 631 assert.Equal(t, p.GetComment(test.key), test.comments[len(test.comments)-1]) 632 } else { 633 assert.Equal(t, p.GetComment(test.key), "") 634 } 635 636 // test setting comments 637 if len(test.comments) > 0 { 638 // set single comment 639 p.ClearComments() 640 assert.Equal(t, len(p.c), 0) 641 p.SetComment(test.key, test.comments[0]) 642 assert.Equal(t, p.GetComment(test.key), test.comments[0]) 643 644 // set multiple comments 645 p.ClearComments() 646 assert.Equal(t, len(p.c), 0) 647 p.SetComments(test.key, test.comments) 648 assert.Equal(t, p.GetComments(test.key), test.comments) 649 650 // clear comments for a key 651 p.SetComments(test.key, nil) 652 assert.Equal(t, p.GetComment(test.key), "") 653 assert.Equal(t, p.GetComments(test.key), ([]string)(nil)) 654 } 655 } 656} 657 658func TestFilter(t *testing.T) { 659 for _, test := range filterTests { 660 p := mustParse(t, test.input) 661 pp, err := p.Filter(test.pattern) 662 if err != nil { 663 assert.Matches(t, err.Error(), test.err) 664 continue 665 } 666 assert.Equal(t, pp != nil, true, "want properties") 667 assert.Equal(t, pp.Len(), len(test.keys)) 668 for _, key := range test.keys { 669 v1, ok1 := p.Get(key) 670 v2, ok2 := pp.Get(key) 671 assert.Equal(t, ok1, true) 672 assert.Equal(t, ok2, true) 673 assert.Equal(t, v1, v2) 674 } 675 } 676} 677 678func TestFilterPrefix(t *testing.T) { 679 for _, test := range filterPrefixTests { 680 p := mustParse(t, test.input) 681 pp := p.FilterPrefix(test.prefix) 682 assert.Equal(t, pp != nil, true, "want properties") 683 assert.Equal(t, pp.Len(), len(test.keys)) 684 for _, key := range test.keys { 685 v1, ok1 := p.Get(key) 686 v2, ok2 := pp.Get(key) 687 assert.Equal(t, ok1, true) 688 assert.Equal(t, ok2, true) 689 assert.Equal(t, v1, v2) 690 } 691 } 692} 693 694func TestFilterStripPrefix(t *testing.T) { 695 for _, test := range filterStripPrefixTests { 696 p := mustParse(t, test.input) 697 pp := p.FilterPrefix(test.prefix) 698 assert.Equal(t, pp != nil, true, "want properties") 699 assert.Equal(t, pp.Len(), len(test.keys)) 700 for _, key := range test.keys { 701 v1, ok1 := p.Get(key) 702 v2, ok2 := pp.Get(key) 703 assert.Equal(t, ok1, true) 704 assert.Equal(t, ok2, true) 705 assert.Equal(t, v1, v2) 706 } 707 } 708} 709 710func TestKeys(t *testing.T) { 711 for _, test := range keysTests { 712 p := mustParse(t, test.input) 713 assert.Equal(t, p.Len(), len(test.keys)) 714 assert.Equal(t, len(p.Keys()), len(test.keys)) 715 assert.Equal(t, p.Keys(), test.keys) 716 } 717} 718 719func TestSet(t *testing.T) { 720 for _, test := range setTests { 721 p := mustParse(t, test.input) 722 prev, ok, err := p.Set(test.key, test.value) 723 if test.err != "" { 724 assert.Matches(t, err.Error(), test.err) 725 continue 726 } 727 728 assert.Equal(t, err, nil) 729 assert.Equal(t, ok, test.ok) 730 if ok { 731 assert.Equal(t, prev, test.prev) 732 } 733 assert.Equal(t, p.Keys(), test.keys) 734 } 735} 736 737func TestSetValue(t *testing.T) { 738 tests := []interface{}{ 739 true, false, 740 int8(123), int16(123), int32(123), int64(123), int(123), 741 uint8(123), uint16(123), uint32(123), uint64(123), uint(123), 742 float32(1.23), float64(1.23), 743 "abc", 744 } 745 746 for _, v := range tests { 747 p := NewProperties() 748 err := p.SetValue("x", v) 749 assert.Equal(t, err, nil) 750 assert.Equal(t, p.GetString("x", ""), fmt.Sprintf("%v", v)) 751 } 752} 753 754func TestMustSet(t *testing.T) { 755 input := "key=${key}" 756 p := mustParse(t, input) 757 assert.Panic(t, func() { p.MustSet("key", "${key}") }, "circular reference .*") 758} 759 760func TestWrite(t *testing.T) { 761 for _, test := range writeTests { 762 p, err := parse(test.input) 763 764 buf := new(bytes.Buffer) 765 var n int 766 switch test.encoding { 767 case "UTF-8": 768 n, err = p.Write(buf, UTF8) 769 case "ISO-8859-1": 770 n, err = p.Write(buf, ISO_8859_1) 771 } 772 assert.Equal(t, err, nil) 773 s := string(buf.Bytes()) 774 assert.Equal(t, n, len(test.output), fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s)) 775 assert.Equal(t, s, test.output, fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s)) 776 } 777} 778 779func TestWriteComment(t *testing.T) { 780 for _, test := range writeCommentTests { 781 p, err := parse(test.input) 782 783 buf := new(bytes.Buffer) 784 var n int 785 switch test.encoding { 786 case "UTF-8": 787 n, err = p.WriteComment(buf, "# ", UTF8) 788 case "ISO-8859-1": 789 n, err = p.WriteComment(buf, "# ", ISO_8859_1) 790 } 791 assert.Equal(t, err, nil) 792 s := string(buf.Bytes()) 793 assert.Equal(t, n, len(test.output), fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s)) 794 assert.Equal(t, s, test.output, fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s)) 795 } 796} 797 798func TestCustomExpansionExpression(t *testing.T) { 799 testKeyValuePrePostfix(t, "*[", "]*", "key=value\nkey2=*[key]*", "key", "value", "key2", "value") 800} 801 802func TestPanicOn32BitIntOverflow(t *testing.T) { 803 is32Bit = true 804 var min, max int64 = math.MinInt32 - 1, math.MaxInt32 + 1 805 input := fmt.Sprintf("min=%d\nmax=%d", min, max) 806 p := mustParse(t, input) 807 assert.Equal(t, p.MustGetInt64("min"), min) 808 assert.Equal(t, p.MustGetInt64("max"), max) 809 assert.Panic(t, func() { p.MustGetInt("min") }, ".* out of range") 810 assert.Panic(t, func() { p.MustGetInt("max") }, ".* out of range") 811} 812 813func TestPanicOn32BitUintOverflow(t *testing.T) { 814 is32Bit = true 815 var max uint64 = math.MaxUint32 + 1 816 input := fmt.Sprintf("max=%d", max) 817 p := mustParse(t, input) 818 assert.Equal(t, p.MustGetUint64("max"), max) 819 assert.Panic(t, func() { p.MustGetUint("max") }, ".* out of range") 820} 821 822func TestDeleteKey(t *testing.T) { 823 input := "#comments should also be gone\nkey=to-be-deleted\nsecond=key" 824 p := mustParse(t, input) 825 assert.Equal(t, len(p.m), 2) 826 assert.Equal(t, len(p.c), 1) 827 assert.Equal(t, len(p.k), 2) 828 p.Delete("key") 829 assert.Equal(t, len(p.m), 1) 830 assert.Equal(t, len(p.c), 0) 831 assert.Equal(t, len(p.k), 1) 832 assert.Equal(t, p.k[0], "second") 833 assert.Equal(t, p.m["second"], "key") 834} 835 836func TestDeleteUnknownKey(t *testing.T) { 837 input := "#comments should also be gone\nkey=to-be-deleted" 838 p := mustParse(t, input) 839 assert.Equal(t, len(p.m), 1) 840 assert.Equal(t, len(p.c), 1) 841 assert.Equal(t, len(p.k), 1) 842 p.Delete("wrong-key") 843 assert.Equal(t, len(p.m), 1) 844 assert.Equal(t, len(p.c), 1) 845 assert.Equal(t, len(p.k), 1) 846} 847 848func TestMerge(t *testing.T) { 849 input1 := "#comment\nkey=value\nkey2=value2" 850 input2 := "#another comment\nkey=another value\nkey3=value3" 851 p1 := mustParse(t, input1) 852 p2 := mustParse(t, input2) 853 p1.Merge(p2) 854 assert.Equal(t, len(p1.m), 3) 855 assert.Equal(t, len(p1.c), 1) 856 assert.Equal(t, len(p1.k), 3) 857 assert.Equal(t, p1.MustGet("key"), "another value") 858 assert.Equal(t, p1.GetComment("key"), "another comment") 859} 860 861func TestMap(t *testing.T) { 862 input := "key=value\nabc=def" 863 p := mustParse(t, input) 864 m := map[string]string{"key": "value", "abc": "def"} 865 assert.Equal(t, p.Map(), m) 866} 867 868func TestFilterFunc(t *testing.T) { 869 input := "key=value\nabc=def" 870 p := mustParse(t, input) 871 pp := p.FilterFunc(func(k, v string) bool { 872 return k != "abc" 873 }) 874 m := map[string]string{"key": "value"} 875 assert.Equal(t, pp.Map(), m) 876} 877 878// ---------------------------------------------------------------------------- 879 880// tests all combinations of delimiters, leading and/or trailing whitespace and newlines. 881func testWhitespaceAndDelimiterCombinations(t *testing.T, key, value string) { 882 whitespace := []string{"", " ", "\f", "\t"} 883 delimiters := []string{"", " ", "=", ":"} 884 newlines := []string{"", "\r", "\n", "\r\n"} 885 for _, dl := range delimiters { 886 for _, ws1 := range whitespace { 887 for _, ws2 := range whitespace { 888 for _, nl := range newlines { 889 // skip the one case where there is nothing between a key and a value 890 if ws1 == "" && dl == "" && ws2 == "" && value != "" { 891 continue 892 } 893 894 input := fmt.Sprintf("%s%s%s%s%s%s", key, ws1, dl, ws2, value, nl) 895 testKeyValue(t, input, key, value) 896 } 897 } 898 } 899 } 900} 901 902// tests whether key/value pairs exist for a given input. 903// keyvalues is expected to be an even number of strings of "key", "value", ... 904func testKeyValue(t *testing.T, input string, keyvalues ...string) { 905 testKeyValuePrePostfix(t, "${", "}", input, keyvalues...) 906} 907 908// tests whether key/value pairs exist for a given input. 909// keyvalues is expected to be an even number of strings of "key", "value", ... 910func testKeyValuePrePostfix(t *testing.T, prefix, postfix, input string, keyvalues ...string) { 911 p, err := Load([]byte(input), ISO_8859_1) 912 assert.Equal(t, err, nil) 913 p.Prefix = prefix 914 p.Postfix = postfix 915 assertKeyValues(t, input, p, keyvalues...) 916} 917 918// tests whether key/value pairs exist for a given input. 919// keyvalues is expected to be an even number of strings of "key", "value", ... 920func assertKeyValues(t *testing.T, input string, p *Properties, keyvalues ...string) { 921 assert.Equal(t, p != nil, true, "want properties") 922 assert.Equal(t, 2*p.Len(), len(keyvalues), "Odd number of key/value pairs.") 923 924 for i := 0; i < len(keyvalues); i += 2 { 925 key, value := keyvalues[i], keyvalues[i+1] 926 v, ok := p.Get(key) 927 if !ok { 928 t.Errorf("No key %q found (input=%q)", key, input) 929 } 930 if got, want := v, value; !reflect.DeepEqual(got, want) { 931 t.Errorf("Value %q does not match %q (input=%q)", v, value, input) 932 } 933 } 934} 935 936func mustParse(t *testing.T, s string) *Properties { 937 p, err := parse(s) 938 if err != nil { 939 t.Fatalf("parse failed with %s", err) 940 } 941 return p 942} 943 944// prints to stderr if the -verbose flag was given. 945func printf(format string, args ...interface{}) { 946 if *verbose { 947 fmt.Fprintf(os.Stderr, format, args...) 948 } 949} 950