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