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