123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985 |
- // Copyright 2018 Frank Schroeder. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package properties
- import (
- "bytes"
- "flag"
- "fmt"
- "math"
- "os"
- "reflect"
- "strings"
- "testing"
- "time"
- "github.com/magiconair/properties/assert"
- )
- var verbose = flag.Bool("verbose", false, "Verbose output")
- func init() {
- ErrorHandler = PanicHandler
- }
- // ----------------------------------------------------------------------------
- // define test cases in the form of
- // {"input", "key1", "value1", "key2", "value2", ...}
- var complexTests = [][]string{
- // whitespace prefix
- {" key=value", "key", "value"}, // SPACE prefix
- {"\fkey=value", "key", "value"}, // FF prefix
- {"\tkey=value", "key", "value"}, // TAB prefix
- {" \f\tkey=value", "key", "value"}, // mix prefix
- // multiple keys
- {"key1=value1\nkey2=value2\n", "key1", "value1", "key2", "value2"},
- {"key1=value1\rkey2=value2\r", "key1", "value1", "key2", "value2"},
- {"key1=value1\r\nkey2=value2\r\n", "key1", "value1", "key2", "value2"},
- // blank lines
- {"\nkey=value\n", "key", "value"},
- {"\rkey=value\r", "key", "value"},
- {"\r\nkey=value\r\n", "key", "value"},
- {"\nkey=value\n \nkey2=value2", "key", "value", "key2", "value2"},
- {"\nkey=value\n\t\nkey2=value2", "key", "value", "key2", "value2"},
- // escaped chars in key
- {"k\\ ey = value", "k ey", "value"},
- {"k\\:ey = value", "k:ey", "value"},
- {"k\\=ey = value", "k=ey", "value"},
- {"k\\fey = value", "k\fey", "value"},
- {"k\\ney = value", "k\ney", "value"},
- {"k\\rey = value", "k\rey", "value"},
- {"k\\tey = value", "k\tey", "value"},
- // escaped chars in value
- {"key = v\\ alue", "key", "v alue"},
- {"key = v\\:alue", "key", "v:alue"},
- {"key = v\\=alue", "key", "v=alue"},
- {"key = v\\falue", "key", "v\falue"},
- {"key = v\\nalue", "key", "v\nalue"},
- {"key = v\\ralue", "key", "v\ralue"},
- {"key = v\\talue", "key", "v\talue"},
- // silently dropped escape character
- {"k\\zey = value", "kzey", "value"},
- {"key = v\\zalue", "key", "vzalue"},
- // unicode literals
- {"key\\u2318 = value", "key⌘", "value"},
- {"k\\u2318ey = value", "k⌘ey", "value"},
- {"key = value\\u2318", "key", "value⌘"},
- {"key = valu\\u2318e", "key", "valu⌘e"},
- // multiline values
- {"key = valueA,\\\n valueB", "key", "valueA,valueB"}, // SPACE indent
- {"key = valueA,\\\n\f\f\fvalueB", "key", "valueA,valueB"}, // FF indent
- {"key = valueA,\\\n\t\t\tvalueB", "key", "valueA,valueB"}, // TAB indent
- {"key = valueA,\\\n \f\tvalueB", "key", "valueA,valueB"}, // mix indent
- // comments
- {"# 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"},
- // expansion tests
- {"key=value\nkey2=${key}", "key", "value", "key2", "value"},
- {"key=value\nkey2=aa${key}", "key", "value", "key2", "aavalue"},
- {"key=value\nkey2=${key}bb", "key", "value", "key2", "valuebb"},
- {"key=value\nkey2=aa${key}bb", "key", "value", "key2", "aavaluebb"},
- {"key=value\nkey2=${key}\nkey3=${key2}", "key", "value", "key2", "value", "key3", "value"},
- {"key=value\nkey2=${key}${key}", "key", "value", "key2", "valuevalue"},
- {"key=value\nkey2=${key}${key}${key}${key}", "key", "value", "key2", "valuevaluevaluevalue"},
- {"key=value\nkey2=${key}${key3}\nkey3=${key}", "key", "value", "key2", "valuevalue", "key3", "value"},
- {"key=value\nkey2=${key3}${key}${key4}\nkey3=${key}\nkey4=${key}", "key", "value", "key2", "valuevaluevalue", "key3", "value", "key4", "value"},
- {"key=${USER}", "key", os.Getenv("USER")},
- {"key=${USER}\nUSER=value", "key", "value", "USER", "value"},
- }
- // ----------------------------------------------------------------------------
- var commentTests = []struct {
- input, key, value string
- comments []string
- }{
- {"key=value", "key", "value", nil},
- {"#\nkey=value", "key", "value", []string{""}},
- {"#comment\nkey=value", "key", "value", []string{"comment"}},
- {"# comment\nkey=value", "key", "value", []string{"comment"}},
- {"# comment\nkey=value", "key", "value", []string{"comment"}},
- {"# comment\n\nkey=value", "key", "value", []string{"comment"}},
- {"# comment1\n# comment2\nkey=value", "key", "value", []string{"comment1", "comment2"}},
- {"# comment1\n\n# comment2\n\nkey=value", "key", "value", []string{"comment1", "comment2"}},
- {"!comment\nkey=value", "key", "value", []string{"comment"}},
- {"! comment\nkey=value", "key", "value", []string{"comment"}},
- {"! comment\nkey=value", "key", "value", []string{"comment"}},
- {"! comment\n\nkey=value", "key", "value", []string{"comment"}},
- {"! comment1\n! comment2\nkey=value", "key", "value", []string{"comment1", "comment2"}},
- {"! comment1\n\n! comment2\n\nkey=value", "key", "value", []string{"comment1", "comment2"}},
- }
- // ----------------------------------------------------------------------------
- var errorTests = []struct {
- input, msg string
- }{
- // unicode literals
- {"key\\u1 = value", "invalid unicode literal"},
- {"key\\u12 = value", "invalid unicode literal"},
- {"key\\u123 = value", "invalid unicode literal"},
- {"key\\u123g = value", "invalid unicode literal"},
- {"key\\u123", "invalid unicode literal"},
- // circular references
- {"key=${key}", "circular reference"},
- {"key1=${key2}\nkey2=${key1}", "circular reference"},
- // malformed expressions
- {"key=${ke", "malformed expression"},
- {"key=valu${ke", "malformed expression"},
- }
- // ----------------------------------------------------------------------------
- var writeTests = []struct {
- input, output, encoding string
- }{
- // ISO-8859-1 tests
- {"key = value", "key = value\n", "ISO-8859-1"},
- {"key = value \\\n continued", "key = value continued\n", "ISO-8859-1"},
- {"key⌘ = value", "key\\u2318 = value\n", "ISO-8859-1"},
- {"ke\\ \\:y = value", "ke\\ \\:y = value\n", "ISO-8859-1"},
- // UTF-8 tests
- {"key = value", "key = value\n", "UTF-8"},
- {"key = value \\\n continued", "key = value continued\n", "UTF-8"},
- {"key⌘ = value⌘", "key⌘ = value⌘\n", "UTF-8"},
- {"ke\\ \\:y = value", "ke\\ \\:y = value\n", "UTF-8"},
- }
- // ----------------------------------------------------------------------------
- var writeCommentTests = []struct {
- input, output, encoding string
- }{
- // ISO-8859-1 tests
- {"key = value", "key = value\n", "ISO-8859-1"},
- {"#\nkey = value", "key = value\n", "ISO-8859-1"},
- {"#\n#\n#\nkey = value", "key = value\n", "ISO-8859-1"},
- {"# comment\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
- {"\n# comment\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
- {"# comment\n\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
- {"# comment1\n# comment2\nkey = value", "# comment1\n# comment2\nkey = value\n", "ISO-8859-1"},
- {"#comment1\nkey1 = value1\n#comment2\nkey2 = value2", "# comment1\nkey1 = value1\n\n# comment2\nkey2 = value2\n", "ISO-8859-1"},
- // UTF-8 tests
- {"key = value", "key = value\n", "UTF-8"},
- {"# comment⌘\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
- {"\n# comment⌘\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
- {"# comment⌘\n\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
- {"# comment1⌘\n# comment2⌘\nkey = value⌘", "# comment1⌘\n# comment2⌘\nkey = value⌘\n", "UTF-8"},
- {"#comment1⌘\nkey1 = value1⌘\n#comment2⌘\nkey2 = value2⌘", "# comment1⌘\nkey1 = value1⌘\n\n# comment2⌘\nkey2 = value2⌘\n", "UTF-8"},
- }
- // ----------------------------------------------------------------------------
- var boolTests = []struct {
- input, key string
- def, value bool
- }{
- // valid values for TRUE
- {"key = 1", "key", false, true},
- {"key = on", "key", false, true},
- {"key = On", "key", false, true},
- {"key = ON", "key", false, true},
- {"key = true", "key", false, true},
- {"key = True", "key", false, true},
- {"key = TRUE", "key", false, true},
- {"key = yes", "key", false, true},
- {"key = Yes", "key", false, true},
- {"key = YES", "key", false, true},
- // valid values for FALSE (all other)
- {"key = 0", "key", true, false},
- {"key = off", "key", true, false},
- {"key = false", "key", true, false},
- {"key = no", "key", true, false},
- // non existent key
- {"key = true", "key2", false, false},
- }
- // ----------------------------------------------------------------------------
- var durationTests = []struct {
- input, key string
- def, value time.Duration
- }{
- // valid values
- {"key = 1", "key", 999, 1},
- {"key = 0", "key", 999, 0},
- {"key = -1", "key", 999, -1},
- {"key = 0123", "key", 999, 123},
- // invalid values
- {"key = 0xff", "key", 999, 999},
- {"key = 1.0", "key", 999, 999},
- {"key = a", "key", 999, 999},
- // non existent key
- {"key = 1", "key2", 999, 999},
- }
- // ----------------------------------------------------------------------------
- var parsedDurationTests = []struct {
- input, key string
- def, value time.Duration
- }{
- // valid values
- {"key = -1ns", "key", 999, -1 * time.Nanosecond},
- {"key = 300ms", "key", 999, 300 * time.Millisecond},
- {"key = 5s", "key", 999, 5 * time.Second},
- {"key = 3h", "key", 999, 3 * time.Hour},
- {"key = 2h45m", "key", 999, 2*time.Hour + 45*time.Minute},
- // invalid values
- {"key = 0xff", "key", 999, 999},
- {"key = 1.0", "key", 999, 999},
- {"key = a", "key", 999, 999},
- {"key = 1", "key", 999, 999},
- {"key = 0", "key", 999, 0},
- // non existent key
- {"key = 1", "key2", 999, 999},
- }
- // ----------------------------------------------------------------------------
- var floatTests = []struct {
- input, key string
- def, value float64
- }{
- // valid values
- {"key = 1.0", "key", 999, 1.0},
- {"key = 0.0", "key", 999, 0.0},
- {"key = -1.0", "key", 999, -1.0},
- {"key = 1", "key", 999, 1},
- {"key = 0", "key", 999, 0},
- {"key = -1", "key", 999, -1},
- {"key = 0123", "key", 999, 123},
- // invalid values
- {"key = 0xff", "key", 999, 999},
- {"key = a", "key", 999, 999},
- // non existent key
- {"key = 1", "key2", 999, 999},
- }
- // ----------------------------------------------------------------------------
- var int64Tests = []struct {
- input, key string
- def, value int64
- }{
- // valid values
- {"key = 1", "key", 999, 1},
- {"key = 0", "key", 999, 0},
- {"key = -1", "key", 999, -1},
- {"key = 0123", "key", 999, 123},
- // invalid values
- {"key = 0xff", "key", 999, 999},
- {"key = 1.0", "key", 999, 999},
- {"key = a", "key", 999, 999},
- // non existent key
- {"key = 1", "key2", 999, 999},
- }
- // ----------------------------------------------------------------------------
- var uint64Tests = []struct {
- input, key string
- def, value uint64
- }{
- // valid values
- {"key = 1", "key", 999, 1},
- {"key = 0", "key", 999, 0},
- {"key = 0123", "key", 999, 123},
- // invalid values
- {"key = -1", "key", 999, 999},
- {"key = 0xff", "key", 999, 999},
- {"key = 1.0", "key", 999, 999},
- {"key = a", "key", 999, 999},
- // non existent key
- {"key = 1", "key2", 999, 999},
- }
- // ----------------------------------------------------------------------------
- var stringTests = []struct {
- input, key string
- def, value string
- }{
- // valid values
- {"key = abc", "key", "def", "abc"},
- // non existent key
- {"key = abc", "key2", "def", "def"},
- }
- // ----------------------------------------------------------------------------
- var keysTests = []struct {
- input string
- keys []string
- }{
- {"", []string{}},
- {"key = abc", []string{"key"}},
- {"key = abc\nkey2=def", []string{"key", "key2"}},
- {"key2 = abc\nkey=def", []string{"key2", "key"}},
- {"key = abc\nkey=def", []string{"key"}},
- }
- // ----------------------------------------------------------------------------
- var filterTests = []struct {
- input string
- pattern string
- keys []string
- err string
- }{
- {"", "", []string{}, ""},
- {"", "abc", []string{}, ""},
- {"key=value", "", []string{"key"}, ""},
- {"key=value", "key=", []string{}, ""},
- {"key=value\nfoo=bar", "", []string{"foo", "key"}, ""},
- {"key=value\nfoo=bar", "f", []string{"foo"}, ""},
- {"key=value\nfoo=bar", "fo", []string{"foo"}, ""},
- {"key=value\nfoo=bar", "foo", []string{"foo"}, ""},
- {"key=value\nfoo=bar", "fooo", []string{}, ""},
- {"key=value\nkey2=value2\nfoo=bar", "ey", []string{"key", "key2"}, ""},
- {"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}, ""},
- {"key=value\nkey2=value2\nfoo=bar", "^key", []string{"key", "key2"}, ""},
- {"key=value\nkey2=value2\nfoo=bar", "^(key|foo)", []string{"foo", "key", "key2"}, ""},
- {"key=value\nkey2=value2\nfoo=bar", "[ abc", nil, "error parsing regexp.*"},
- }
- // ----------------------------------------------------------------------------
- var filterPrefixTests = []struct {
- input string
- prefix string
- keys []string
- }{
- {"", "", []string{}},
- {"", "abc", []string{}},
- {"key=value", "", []string{"key"}},
- {"key=value", "key=", []string{}},
- {"key=value\nfoo=bar", "", []string{"foo", "key"}},
- {"key=value\nfoo=bar", "f", []string{"foo"}},
- {"key=value\nfoo=bar", "fo", []string{"foo"}},
- {"key=value\nfoo=bar", "foo", []string{"foo"}},
- {"key=value\nfoo=bar", "fooo", []string{}},
- {"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}},
- }
- // ----------------------------------------------------------------------------
- var filterStripPrefixTests = []struct {
- input string
- prefix string
- keys []string
- }{
- {"", "", []string{}},
- {"", "abc", []string{}},
- {"key=value", "", []string{"key"}},
- {"key=value", "key=", []string{}},
- {"key=value\nfoo=bar", "", []string{"foo", "key"}},
- {"key=value\nfoo=bar", "f", []string{"foo"}},
- {"key=value\nfoo=bar", "fo", []string{"foo"}},
- {"key=value\nfoo=bar", "foo", []string{"foo"}},
- {"key=value\nfoo=bar", "fooo", []string{}},
- {"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}},
- }
- // ----------------------------------------------------------------------------
- var setTests = []struct {
- input string
- key, value string
- prev string
- ok bool
- err string
- keys []string
- }{
- {"", "", "", "", false, "", []string{}},
- {"", "key", "value", "", false, "", []string{"key"}},
- {"key=value", "key2", "value2", "", false, "", []string{"key", "key2"}},
- {"key=value", "abc", "value3", "", false, "", []string{"key", "abc"}},
- {"key=value", "key", "value3", "value", true, "", []string{"key"}},
- }
- // ----------------------------------------------------------------------------
- // TestBasic tests basic single key/value combinations with all possible
- // whitespace, delimiter and newline permutations.
- func TestBasic(t *testing.T) {
- testWhitespaceAndDelimiterCombinations(t, "key", "")
- testWhitespaceAndDelimiterCombinations(t, "key", "value")
- testWhitespaceAndDelimiterCombinations(t, "key", "value ")
- }
- func TestComplex(t *testing.T) {
- for _, test := range complexTests {
- testKeyValue(t, test[0], test[1:]...)
- }
- }
- func TestErrors(t *testing.T) {
- for _, test := range errorTests {
- _, err := Load([]byte(test.input), ISO_8859_1)
- assert.Equal(t, err != nil, true, "want error")
- assert.Equal(t, strings.Contains(err.Error(), test.msg), true)
- }
- }
- func TestVeryDeep(t *testing.T) {
- input := "key0=value\n"
- prefix := "${"
- postfix := "}"
- i := 0
- for i = 0; i < maxExpansionDepth-1; i++ {
- input += fmt.Sprintf("key%d=%skey%d%s\n", i+1, prefix, i, postfix)
- }
- p, err := Load([]byte(input), ISO_8859_1)
- assert.Equal(t, err, nil)
- p.Prefix = prefix
- p.Postfix = postfix
- assert.Equal(t, p.MustGet(fmt.Sprintf("key%d", i)), "value")
- // Nudge input over the edge
- input += fmt.Sprintf("key%d=%skey%d%s\n", i+1, prefix, i, postfix)
- _, err = Load([]byte(input), ISO_8859_1)
- assert.Equal(t, err != nil, true, "want error")
- assert.Equal(t, strings.Contains(err.Error(), "expansion too deep"), true)
- }
- func TestDisableExpansion(t *testing.T) {
- input := "key=value\nkey2=${key}"
- p := mustParse(t, input)
- p.DisableExpansion = true
- assert.Equal(t, p.MustGet("key"), "value")
- assert.Equal(t, p.MustGet("key2"), "${key}")
- // with expansion disabled we can introduce circular references
- p.MustSet("keyA", "${keyB}")
- p.MustSet("keyB", "${keyA}")
- assert.Equal(t, p.MustGet("keyA"), "${keyB}")
- assert.Equal(t, p.MustGet("keyB"), "${keyA}")
- }
- func TestDisableExpansionStillUpdatesKeys(t *testing.T) {
- p := NewProperties()
- p.MustSet("p1", "a")
- assert.Equal(t, p.Keys(), []string{"p1"})
- assert.Equal(t, p.String(), "p1 = a\n")
- p.DisableExpansion = true
- p.MustSet("p2", "b")
- assert.Equal(t, p.Keys(), []string{"p1", "p2"})
- assert.Equal(t, p.String(), "p1 = a\np2 = b\n")
- }
- func TestMustGet(t *testing.T) {
- input := "key = value\nkey2 = ghi"
- p := mustParse(t, input)
- assert.Equal(t, p.MustGet("key"), "value")
- assert.Panic(t, func() { p.MustGet("invalid") }, "unknown property: invalid")
- }
- func TestGetBool(t *testing.T) {
- for _, test := range boolTests {
- p := mustParse(t, test.input)
- assert.Equal(t, p.Len(), 1)
- assert.Equal(t, p.GetBool(test.key, test.def), test.value)
- }
- }
- func TestMustGetBool(t *testing.T) {
- input := "key = true\nkey2 = ghi"
- p := mustParse(t, input)
- assert.Equal(t, p.MustGetBool("key"), true)
- assert.Panic(t, func() { p.MustGetBool("invalid") }, "unknown property: invalid")
- }
- func TestGetDuration(t *testing.T) {
- for _, test := range durationTests {
- p := mustParse(t, test.input)
- assert.Equal(t, p.Len(), 1)
- assert.Equal(t, p.GetDuration(test.key, test.def), test.value)
- }
- }
- func TestMustGetDuration(t *testing.T) {
- input := "key = 123\nkey2 = ghi"
- p := mustParse(t, input)
- assert.Equal(t, p.MustGetDuration("key"), time.Duration(123))
- assert.Panic(t, func() { p.MustGetDuration("key2") }, "strconv.ParseInt: parsing.*")
- assert.Panic(t, func() { p.MustGetDuration("invalid") }, "unknown property: invalid")
- }
- func TestGetParsedDuration(t *testing.T) {
- for _, test := range parsedDurationTests {
- p := mustParse(t, test.input)
- assert.Equal(t, p.Len(), 1)
- assert.Equal(t, p.GetParsedDuration(test.key, test.def), test.value)
- }
- }
- func TestMustGetParsedDuration(t *testing.T) {
- input := "key = 123ms\nkey2 = ghi"
- p := mustParse(t, input)
- assert.Equal(t, p.MustGetParsedDuration("key"), 123*time.Millisecond)
- assert.Panic(t, func() { p.MustGetParsedDuration("key2") }, "time: invalid duration ghi")
- assert.Panic(t, func() { p.MustGetParsedDuration("invalid") }, "unknown property: invalid")
- }
- func TestGetFloat64(t *testing.T) {
- for _, test := range floatTests {
- p := mustParse(t, test.input)
- assert.Equal(t, p.Len(), 1)
- assert.Equal(t, p.GetFloat64(test.key, test.def), test.value)
- }
- }
- func TestMustGetFloat64(t *testing.T) {
- input := "key = 123\nkey2 = ghi"
- p := mustParse(t, input)
- assert.Equal(t, p.MustGetFloat64("key"), float64(123))
- assert.Panic(t, func() { p.MustGetFloat64("key2") }, "strconv.ParseFloat: parsing.*")
- assert.Panic(t, func() { p.MustGetFloat64("invalid") }, "unknown property: invalid")
- }
- func TestGetInt(t *testing.T) {
- for _, test := range int64Tests {
- p := mustParse(t, test.input)
- assert.Equal(t, p.Len(), 1)
- assert.Equal(t, p.GetInt(test.key, int(test.def)), int(test.value))
- }
- }
- func TestMustGetInt(t *testing.T) {
- input := "key = 123\nkey2 = ghi"
- p := mustParse(t, input)
- assert.Equal(t, p.MustGetInt("key"), int(123))
- assert.Panic(t, func() { p.MustGetInt("key2") }, "strconv.ParseInt: parsing.*")
- assert.Panic(t, func() { p.MustGetInt("invalid") }, "unknown property: invalid")
- }
- func TestGetInt64(t *testing.T) {
- for _, test := range int64Tests {
- p := mustParse(t, test.input)
- assert.Equal(t, p.Len(), 1)
- assert.Equal(t, p.GetInt64(test.key, test.def), test.value)
- }
- }
- func TestMustGetInt64(t *testing.T) {
- input := "key = 123\nkey2 = ghi"
- p := mustParse(t, input)
- assert.Equal(t, p.MustGetInt64("key"), int64(123))
- assert.Panic(t, func() { p.MustGetInt64("key2") }, "strconv.ParseInt: parsing.*")
- assert.Panic(t, func() { p.MustGetInt64("invalid") }, "unknown property: invalid")
- }
- func TestGetUint(t *testing.T) {
- for _, test := range uint64Tests {
- p := mustParse(t, test.input)
- assert.Equal(t, p.Len(), 1)
- assert.Equal(t, p.GetUint(test.key, uint(test.def)), uint(test.value))
- }
- }
- func TestMustGetUint(t *testing.T) {
- input := "key = 123\nkey2 = ghi"
- p := mustParse(t, input)
- assert.Equal(t, p.MustGetUint("key"), uint(123))
- assert.Panic(t, func() { p.MustGetUint64("key2") }, "strconv.ParseUint: parsing.*")
- assert.Panic(t, func() { p.MustGetUint64("invalid") }, "unknown property: invalid")
- }
- func TestGetUint64(t *testing.T) {
- for _, test := range uint64Tests {
- p := mustParse(t, test.input)
- assert.Equal(t, p.Len(), 1)
- assert.Equal(t, p.GetUint64(test.key, test.def), test.value)
- }
- }
- func TestMustGetUint64(t *testing.T) {
- input := "key = 123\nkey2 = ghi"
- p := mustParse(t, input)
- assert.Equal(t, p.MustGetUint64("key"), uint64(123))
- assert.Panic(t, func() { p.MustGetUint64("key2") }, "strconv.ParseUint: parsing.*")
- assert.Panic(t, func() { p.MustGetUint64("invalid") }, "unknown property: invalid")
- }
- func TestGetString(t *testing.T) {
- for _, test := range stringTests {
- p := mustParse(t, test.input)
- assert.Equal(t, p.Len(), 1)
- assert.Equal(t, p.GetString(test.key, test.def), test.value)
- }
- }
- func TestMustGetString(t *testing.T) {
- input := `key = value`
- p := mustParse(t, input)
- assert.Equal(t, p.MustGetString("key"), "value")
- assert.Panic(t, func() { p.MustGetString("invalid") }, "unknown property: invalid")
- }
- func TestComment(t *testing.T) {
- for _, test := range commentTests {
- p := mustParse(t, test.input)
- assert.Equal(t, p.MustGetString(test.key), test.value)
- assert.Equal(t, p.GetComments(test.key), test.comments)
- if test.comments != nil {
- assert.Equal(t, p.GetComment(test.key), test.comments[len(test.comments)-1])
- } else {
- assert.Equal(t, p.GetComment(test.key), "")
- }
- // test setting comments
- if len(test.comments) > 0 {
- // set single comment
- p.ClearComments()
- assert.Equal(t, len(p.c), 0)
- p.SetComment(test.key, test.comments[0])
- assert.Equal(t, p.GetComment(test.key), test.comments[0])
- // set multiple comments
- p.ClearComments()
- assert.Equal(t, len(p.c), 0)
- p.SetComments(test.key, test.comments)
- assert.Equal(t, p.GetComments(test.key), test.comments)
- // clear comments for a key
- p.SetComments(test.key, nil)
- assert.Equal(t, p.GetComment(test.key), "")
- assert.Equal(t, p.GetComments(test.key), ([]string)(nil))
- }
- }
- }
- func TestFilter(t *testing.T) {
- for _, test := range filterTests {
- p := mustParse(t, test.input)
- pp, err := p.Filter(test.pattern)
- if err != nil {
- assert.Matches(t, err.Error(), test.err)
- continue
- }
- assert.Equal(t, pp != nil, true, "want properties")
- assert.Equal(t, pp.Len(), len(test.keys))
- for _, key := range test.keys {
- v1, ok1 := p.Get(key)
- v2, ok2 := pp.Get(key)
- assert.Equal(t, ok1, true)
- assert.Equal(t, ok2, true)
- assert.Equal(t, v1, v2)
- }
- }
- }
- func TestFilterPrefix(t *testing.T) {
- for _, test := range filterPrefixTests {
- p := mustParse(t, test.input)
- pp := p.FilterPrefix(test.prefix)
- assert.Equal(t, pp != nil, true, "want properties")
- assert.Equal(t, pp.Len(), len(test.keys))
- for _, key := range test.keys {
- v1, ok1 := p.Get(key)
- v2, ok2 := pp.Get(key)
- assert.Equal(t, ok1, true)
- assert.Equal(t, ok2, true)
- assert.Equal(t, v1, v2)
- }
- }
- }
- func TestFilterStripPrefix(t *testing.T) {
- for _, test := range filterStripPrefixTests {
- p := mustParse(t, test.input)
- pp := p.FilterPrefix(test.prefix)
- assert.Equal(t, pp != nil, true, "want properties")
- assert.Equal(t, pp.Len(), len(test.keys))
- for _, key := range test.keys {
- v1, ok1 := p.Get(key)
- v2, ok2 := pp.Get(key)
- assert.Equal(t, ok1, true)
- assert.Equal(t, ok2, true)
- assert.Equal(t, v1, v2)
- }
- }
- }
- func TestKeys(t *testing.T) {
- for _, test := range keysTests {
- p := mustParse(t, test.input)
- assert.Equal(t, p.Len(), len(test.keys))
- assert.Equal(t, len(p.Keys()), len(test.keys))
- assert.Equal(t, p.Keys(), test.keys)
- }
- }
- func TestSet(t *testing.T) {
- for _, test := range setTests {
- p := mustParse(t, test.input)
- prev, ok, err := p.Set(test.key, test.value)
- if test.err != "" {
- assert.Matches(t, err.Error(), test.err)
- continue
- }
- assert.Equal(t, err, nil)
- assert.Equal(t, ok, test.ok)
- if ok {
- assert.Equal(t, prev, test.prev)
- }
- assert.Equal(t, p.Keys(), test.keys)
- }
- }
- func TestSetValue(t *testing.T) {
- tests := []interface{}{
- true, false,
- int8(123), int16(123), int32(123), int64(123), int(123),
- uint8(123), uint16(123), uint32(123), uint64(123), uint(123),
- float32(1.23), float64(1.23),
- "abc",
- }
- for _, v := range tests {
- p := NewProperties()
- err := p.SetValue("x", v)
- assert.Equal(t, err, nil)
- assert.Equal(t, p.GetString("x", ""), fmt.Sprintf("%v", v))
- }
- }
- func TestMustSet(t *testing.T) {
- input := "key=${key}"
- p := mustParse(t, input)
- assert.Panic(t, func() { p.MustSet("key", "${key}") }, "circular reference .*")
- }
- func TestWrite(t *testing.T) {
- for _, test := range writeTests {
- p, err := parse(test.input)
- buf := new(bytes.Buffer)
- var n int
- switch test.encoding {
- case "UTF-8":
- n, err = p.Write(buf, UTF8)
- case "ISO-8859-1":
- n, err = p.Write(buf, ISO_8859_1)
- }
- assert.Equal(t, err, nil)
- s := string(buf.Bytes())
- assert.Equal(t, n, len(test.output), fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
- assert.Equal(t, s, test.output, fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
- }
- }
- func TestWriteComment(t *testing.T) {
- for _, test := range writeCommentTests {
- p, err := parse(test.input)
- buf := new(bytes.Buffer)
- var n int
- switch test.encoding {
- case "UTF-8":
- n, err = p.WriteComment(buf, "# ", UTF8)
- case "ISO-8859-1":
- n, err = p.WriteComment(buf, "# ", ISO_8859_1)
- }
- assert.Equal(t, err, nil)
- s := string(buf.Bytes())
- assert.Equal(t, n, len(test.output), fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
- assert.Equal(t, s, test.output, fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
- }
- }
- func TestCustomExpansionExpression(t *testing.T) {
- testKeyValuePrePostfix(t, "*[", "]*", "key=value\nkey2=*[key]*", "key", "value", "key2", "value")
- }
- func TestPanicOn32BitIntOverflow(t *testing.T) {
- is32Bit = true
- var min, max int64 = math.MinInt32 - 1, math.MaxInt32 + 1
- input := fmt.Sprintf("min=%d\nmax=%d", min, max)
- p := mustParse(t, input)
- assert.Equal(t, p.MustGetInt64("min"), min)
- assert.Equal(t, p.MustGetInt64("max"), max)
- assert.Panic(t, func() { p.MustGetInt("min") }, ".* out of range")
- assert.Panic(t, func() { p.MustGetInt("max") }, ".* out of range")
- }
- func TestPanicOn32BitUintOverflow(t *testing.T) {
- is32Bit = true
- var max uint64 = math.MaxUint32 + 1
- input := fmt.Sprintf("max=%d", max)
- p := mustParse(t, input)
- assert.Equal(t, p.MustGetUint64("max"), max)
- assert.Panic(t, func() { p.MustGetUint("max") }, ".* out of range")
- }
- func TestDeleteKey(t *testing.T) {
- input := "#comments should also be gone\nkey=to-be-deleted\nsecond=key"
- p := mustParse(t, input)
- assert.Equal(t, len(p.m), 2)
- assert.Equal(t, len(p.c), 1)
- assert.Equal(t, len(p.k), 2)
- p.Delete("key")
- assert.Equal(t, len(p.m), 1)
- assert.Equal(t, len(p.c), 0)
- assert.Equal(t, len(p.k), 1)
- assert.Equal(t, p.k[0], "second")
- assert.Equal(t, p.m["second"], "key")
- }
- func TestDeleteUnknownKey(t *testing.T) {
- input := "#comments should also be gone\nkey=to-be-deleted"
- p := mustParse(t, input)
- assert.Equal(t, len(p.m), 1)
- assert.Equal(t, len(p.c), 1)
- assert.Equal(t, len(p.k), 1)
- p.Delete("wrong-key")
- assert.Equal(t, len(p.m), 1)
- assert.Equal(t, len(p.c), 1)
- assert.Equal(t, len(p.k), 1)
- }
- func TestMerge(t *testing.T) {
- input1 := "#comment\nkey=value\nkey2=value2"
- input2 := "#another comment\nkey=another value\nkey3=value3"
- p1 := mustParse(t, input1)
- p2 := mustParse(t, input2)
- p1.Merge(p2)
- assert.Equal(t, len(p1.m), 3)
- assert.Equal(t, len(p1.c), 1)
- assert.Equal(t, len(p1.k), 3)
- assert.Equal(t, p1.MustGet("key"), "another value")
- assert.Equal(t, p1.GetComment("key"), "another comment")
- }
- func TestMap(t *testing.T) {
- input := "key=value\nabc=def"
- p := mustParse(t, input)
- m := map[string]string{"key": "value", "abc": "def"}
- assert.Equal(t, p.Map(), m)
- }
- func TestFilterFunc(t *testing.T) {
- input := "key=value\nabc=def"
- p := mustParse(t, input)
- pp := p.FilterFunc(func(k, v string) bool {
- return k != "abc"
- })
- m := map[string]string{"key": "value"}
- assert.Equal(t, pp.Map(), m)
- }
- func TestLoad(t *testing.T) {
- x := "key=${value}\nvalue=${key}"
- p := NewProperties()
- p.DisableExpansion = true
- err := p.Load([]byte(x), UTF8)
- assert.Equal(t, err, nil)
- }
- // ----------------------------------------------------------------------------
- // tests all combinations of delimiters, leading and/or trailing whitespace and newlines.
- func testWhitespaceAndDelimiterCombinations(t *testing.T, key, value string) {
- whitespace := []string{"", " ", "\f", "\t"}
- delimiters := []string{"", " ", "=", ":"}
- newlines := []string{"", "\r", "\n", "\r\n"}
- for _, dl := range delimiters {
- for _, ws1 := range whitespace {
- for _, ws2 := range whitespace {
- for _, nl := range newlines {
- // skip the one case where there is nothing between a key and a value
- if ws1 == "" && dl == "" && ws2 == "" && value != "" {
- continue
- }
- input := fmt.Sprintf("%s%s%s%s%s%s", key, ws1, dl, ws2, value, nl)
- testKeyValue(t, input, key, value)
- }
- }
- }
- }
- }
- // tests whether key/value pairs exist for a given input.
- // keyvalues is expected to be an even number of strings of "key", "value", ...
- func testKeyValue(t *testing.T, input string, keyvalues ...string) {
- testKeyValuePrePostfix(t, "${", "}", input, keyvalues...)
- }
- // tests whether key/value pairs exist for a given input.
- // keyvalues is expected to be an even number of strings of "key", "value", ...
- func testKeyValuePrePostfix(t *testing.T, prefix, postfix, input string, keyvalues ...string) {
- p, err := Load([]byte(input), ISO_8859_1)
- assert.Equal(t, err, nil)
- p.Prefix = prefix
- p.Postfix = postfix
- assertKeyValues(t, input, p, keyvalues...)
- }
- // tests whether key/value pairs exist for a given input.
- // keyvalues is expected to be an even number of strings of "key", "value", ...
- func assertKeyValues(t *testing.T, input string, p *Properties, keyvalues ...string) {
- assert.Equal(t, p != nil, true, "want properties")
- assert.Equal(t, 2*p.Len(), len(keyvalues), "Odd number of key/value pairs.")
- for i := 0; i < len(keyvalues); i += 2 {
- key, value := keyvalues[i], keyvalues[i+1]
- v, ok := p.Get(key)
- if !ok {
- t.Errorf("No key %q found (input=%q)", key, input)
- }
- if got, want := v, value; !reflect.DeepEqual(got, want) {
- t.Errorf("Value %q does not match %q (input=%q)", v, value, input)
- }
- }
- }
- func mustParse(t *testing.T, s string) *Properties {
- p, err := parse(s)
- if err != nil {
- t.Fatalf("parse failed with %s", err)
- }
- return p
- }
- // prints to stderr if the -verbose flag was given.
- func printf(format string, args ...interface{}) {
- if *verbose {
- fmt.Fprintf(os.Stderr, format, args...)
- }
- }
|