package toml import ( "fmt" "math" "reflect" "testing" "time" ) func assertSubTree(t *testing.T, path []string, tree *Tree, err error, ref map[string]interface{}) { if err != nil { t.Error("Non-nil error:", err.Error()) return } for k, v := range ref { nextPath := append(path, k) t.Log("asserting path", nextPath) // NOTE: directly access key instead of resolve by path // NOTE: see TestSpecialKV switch node := tree.GetPath([]string{k}).(type) { case []*Tree: t.Log("\tcomparing key", nextPath, "by array iteration") for idx, item := range node { assertSubTree(t, nextPath, item, err, v.([]map[string]interface{})[idx]) } case *Tree: t.Log("\tcomparing key", nextPath, "by subtree assestion") assertSubTree(t, nextPath, node, err, v.(map[string]interface{})) default: t.Log("\tcomparing key", nextPath, "by string representation because it's of type", reflect.TypeOf(node)) if fmt.Sprintf("%v", node) != fmt.Sprintf("%v", v) { t.Errorf("was expecting %v at %v but got %v", v, k, node) } } } } func assertTree(t *testing.T, tree *Tree, err error, ref map[string]interface{}) { t.Logf("Asserting tree:\n (%T)(%p)(%+v)", tree, tree, tree) assertSubTree(t, []string{}, tree, err, ref) t.Log("Finished tree assertion.") } func TestCreateSubTree(t *testing.T) { tree := newTree() tree.createSubTree([]string{"a", "b", "c"}, Position{}) tree.Set("a.b.c", 42) if tree.Get("a.b.c") != 42 { t.Fail() } } func TestSimpleKV(t *testing.T) { tree, err := Load("a = 42") assertTree(t, tree, err, map[string]interface{}{ "a": int64(42), }) tree, _ = Load("a = 42\nb = 21") assertTree(t, tree, err, map[string]interface{}{ "a": int64(42), "b": int64(21), }) } func TestNumberInKey(t *testing.T) { tree, err := Load("hello2 = 42") assertTree(t, tree, err, map[string]interface{}{ "hello2": int64(42), }) } func TestIncorrectKeyExtraSquareBracket(t *testing.T) { _, err := Load(`[a]b] zyx = 42`) if err == nil { t.Error("Error should have been returned.") } if err.Error() != "(1, 4): parsing error: keys cannot contain ] character" { t.Error("Bad error message:", err.Error()) } } func TestSimpleNumbers(t *testing.T) { tree, err := Load("a = +42\nb = -21\nc = +4.2\nd = -2.1") assertTree(t, tree, err, map[string]interface{}{ "a": int64(42), "b": int64(-21), "c": float64(4.2), "d": float64(-2.1), }) } func TestSpecialFloats(t *testing.T) { tree, err := Load(` normalinf = inf plusinf = +inf minusinf = -inf normalnan = nan plusnan = +nan minusnan = -nan `) assertTree(t, tree, err, map[string]interface{}{ "normalinf": math.Inf(1), "plusinf": math.Inf(1), "minusinf": math.Inf(-1), "normalnan": math.NaN(), "plusnan": math.NaN(), "minusnan": math.NaN(), }) } func TestHexIntegers(t *testing.T) { tree, err := Load(`a = 0xDEADBEEF`) assertTree(t, tree, err, map[string]interface{}{"a": int64(3735928559)}) tree, err = Load(`a = 0xdeadbeef`) assertTree(t, tree, err, map[string]interface{}{"a": int64(3735928559)}) tree, err = Load(`a = 0xdead_beef`) assertTree(t, tree, err, map[string]interface{}{"a": int64(3735928559)}) _, err = Load(`a = 0x_1`) if err.Error() != "(1, 5): invalid use of _ in hex number" { t.Error("Bad error message:", err.Error()) } } func TestOctIntegers(t *testing.T) { tree, err := Load(`a = 0o01234567`) assertTree(t, tree, err, map[string]interface{}{"a": int64(342391)}) tree, err = Load(`a = 0o755`) assertTree(t, tree, err, map[string]interface{}{"a": int64(493)}) _, err = Load(`a = 0o_1`) if err.Error() != "(1, 5): invalid use of _ in number" { t.Error("Bad error message:", err.Error()) } } func TestBinIntegers(t *testing.T) { tree, err := Load(`a = 0b11010110`) assertTree(t, tree, err, map[string]interface{}{"a": int64(214)}) _, err = Load(`a = 0b_1`) if err.Error() != "(1, 5): invalid use of _ in number" { t.Error("Bad error message:", err.Error()) } } func TestBadIntegerBase(t *testing.T) { _, err := Load(`a = 0k1`) if err.Error() != "(1, 5): unknown number base: k. possible options are x (hex) o (octal) b (binary)" { t.Error("Error should have been returned.") } } func TestIntegerNoDigit(t *testing.T) { _, err := Load(`a = 0b`) if err.Error() != "(1, 5): number needs at least one digit" { t.Error("Bad error message:", err.Error()) } } func TestNumbersWithUnderscores(t *testing.T) { tree, err := Load("a = 1_000") assertTree(t, tree, err, map[string]interface{}{ "a": int64(1000), }) tree, err = Load("a = 5_349_221") assertTree(t, tree, err, map[string]interface{}{ "a": int64(5349221), }) tree, err = Load("a = 1_2_3_4_5") assertTree(t, tree, err, map[string]interface{}{ "a": int64(12345), }) tree, err = Load("flt8 = 9_224_617.445_991_228_313") assertTree(t, tree, err, map[string]interface{}{ "flt8": float64(9224617.445991228313), }) tree, err = Load("flt9 = 1e1_00") assertTree(t, tree, err, map[string]interface{}{ "flt9": float64(1e100), }) } func TestFloatsWithExponents(t *testing.T) { tree, err := Load("a = 5e+22\nb = 5E+22\nc = -5e+22\nd = -5e-22\ne = 6.626e-34") assertTree(t, tree, err, map[string]interface{}{ "a": float64(5e+22), "b": float64(5e+22), "c": float64(-5e+22), "d": float64(-5e-22), "e": float64(6.626e-34), }) } func TestSimpleDate(t *testing.T) { tree, err := Load("a = 1979-05-27T07:32:00Z") assertTree(t, tree, err, map[string]interface{}{ "a": time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC), }) } func TestDateOffset(t *testing.T) { tree, err := Load("a = 1979-05-27T00:32:00-07:00") assertTree(t, tree, err, map[string]interface{}{ "a": time.Date(1979, time.May, 27, 0, 32, 0, 0, time.FixedZone("", -7*60*60)), }) } func TestDateNano(t *testing.T) { tree, err := Load("a = 1979-05-27T00:32:00.999999999-07:00") assertTree(t, tree, err, map[string]interface{}{ "a": time.Date(1979, time.May, 27, 0, 32, 0, 999999999, time.FixedZone("", -7*60*60)), }) } func TestLocalDateTime(t *testing.T) { tree, err := Load("a = 1979-05-27T07:32:00") assertTree(t, tree, err, map[string]interface{}{ "a": LocalDateTime{ Date: LocalDate{ Year: 1979, Month: 5, Day: 27, }, Time: LocalTime{ Hour: 7, Minute: 32, Second: 0, Nanosecond: 0, }, }, }) } func TestLocalDateTimeNano(t *testing.T) { tree, err := Load("a = 1979-05-27T07:32:00.999999") assertTree(t, tree, err, map[string]interface{}{ "a": LocalDateTime{ Date: LocalDate{ Year: 1979, Month: 5, Day: 27, }, Time: LocalTime{ Hour: 7, Minute: 32, Second: 0, Nanosecond: 999999000, }, }, }) } func TestLocalDate(t *testing.T) { tree, err := Load("a = 1979-05-27") assertTree(t, tree, err, map[string]interface{}{ "a": LocalDate{ Year: 1979, Month: 5, Day: 27, }, }) } func TestLocalDateError(t *testing.T) { _, err := Load("a = 2020-09-31") if err == nil { t.Fatalf("should error") } } func TestLocalTimeError(t *testing.T) { _, err := Load("a = 07:99:00") if err == nil { t.Fatalf("should error") } } func TestLocalDateTimeError(t *testing.T) { _, err := Load("a = 2020-09-31T07:99:00") if err == nil { t.Fatalf("should error") } } func TestDateTimeOffsetError(t *testing.T) { _, err := Load("a = 2020-09-31T07:99:00Z") if err == nil { t.Fatalf("should error") } } func TestLocalTime(t *testing.T) { tree, err := Load("a = 07:32:00") assertTree(t, tree, err, map[string]interface{}{ "a": LocalTime{ Hour: 7, Minute: 32, Second: 0, Nanosecond: 0, }, }) } func TestLocalTimeNano(t *testing.T) { tree, err := Load("a = 00:32:00.999999") assertTree(t, tree, err, map[string]interface{}{ "a": LocalTime{ Hour: 0, Minute: 32, Second: 0, Nanosecond: 999999000, }, }) } func TestSimpleString(t *testing.T) { tree, err := Load("a = \"hello world\"") assertTree(t, tree, err, map[string]interface{}{ "a": "hello world", }) } func TestSpaceKey(t *testing.T) { tree, err := Load("\"a b\" = \"hello world\"") assertTree(t, tree, err, map[string]interface{}{ "a b": "hello world", }) } func TestDoubleQuotedKey(t *testing.T) { tree, err := Load(` "key" = "a" "\t" = "b" "\U0001F914" = "c" "\u2764" = "d" `) assertTree(t, tree, err, map[string]interface{}{ "key": "a", "\t": "b", "\U0001F914": "c", "\u2764": "d", }) } func TestSingleQuotedKey(t *testing.T) { tree, err := Load(` 'key' = "a" '\t' = "b" '\U0001F914' = "c" '\u2764' = "d" `) assertTree(t, tree, err, map[string]interface{}{ `key`: "a", `\t`: "b", `\U0001F914`: "c", `\u2764`: "d", }) } func TestStringEscapables(t *testing.T) { tree, err := Load("a = \"a \\n b\"") assertTree(t, tree, err, map[string]interface{}{ "a": "a \n b", }) tree, err = Load("a = \"a \\t b\"") assertTree(t, tree, err, map[string]interface{}{ "a": "a \t b", }) tree, err = Load("a = \"a \\r b\"") assertTree(t, tree, err, map[string]interface{}{ "a": "a \r b", }) tree, err = Load("a = \"a \\\\ b\"") assertTree(t, tree, err, map[string]interface{}{ "a": "a \\ b", }) } func TestEmptyQuotedString(t *testing.T) { tree, err := Load(`[""] "" = 1`) assertTree(t, tree, err, map[string]interface{}{ "": map[string]interface{}{ "": int64(1), }, }) } func TestBools(t *testing.T) { tree, err := Load("a = true\nb = false") assertTree(t, tree, err, map[string]interface{}{ "a": true, "b": false, }) } func TestNestedKeys(t *testing.T) { tree, err := Load("[a.b.c]\nd = 42") assertTree(t, tree, err, map[string]interface{}{ "a": map[string]interface{}{ "b": map[string]interface{}{ "c": map[string]interface{}{ "d": int64(42), }, }, }, }) } func TestNestedQuotedUnicodeKeys(t *testing.T) { tree, err := Load("[ j . \"ʞ\" . l ]\nd = 42") assertTree(t, tree, err, map[string]interface{}{ "j": map[string]interface{}{ "ʞ": map[string]interface{}{ "l": map[string]interface{}{ "d": int64(42), }, }, }, }) tree, err = Load("[ g . h . i ]\nd = 42") assertTree(t, tree, err, map[string]interface{}{ "g": map[string]interface{}{ "h": map[string]interface{}{ "i": map[string]interface{}{ "d": int64(42), }, }, }, }) tree, err = Load("[ d.e.f ]\nk = 42") assertTree(t, tree, err, map[string]interface{}{ "d": map[string]interface{}{ "e": map[string]interface{}{ "f": map[string]interface{}{ "k": int64(42), }, }, }, }) } func TestArrayOne(t *testing.T) { tree, err := Load("a = [1]") assertTree(t, tree, err, map[string]interface{}{ "a": []int64{int64(1)}, }) } func TestArrayZero(t *testing.T) { tree, err := Load("a = []") assertTree(t, tree, err, map[string]interface{}{ "a": []interface{}{}, }) } func TestArraySimple(t *testing.T) { tree, err := Load("a = [42, 21, 10]") assertTree(t, tree, err, map[string]interface{}{ "a": []int64{int64(42), int64(21), int64(10)}, }) tree, _ = Load("a = [42, 21, 10,]") assertTree(t, tree, err, map[string]interface{}{ "a": []int64{int64(42), int64(21), int64(10)}, }) } func TestArrayMultiline(t *testing.T) { tree, err := Load("a = [42,\n21, 10,]") assertTree(t, tree, err, map[string]interface{}{ "a": []int64{int64(42), int64(21), int64(10)}, }) } func TestArrayNested(t *testing.T) { tree, err := Load("a = [[42, 21], [10]]") assertTree(t, tree, err, map[string]interface{}{ "a": [][]int64{{int64(42), int64(21)}, {int64(10)}}, }) } func TestNestedArrayComment(t *testing.T) { tree, err := Load(` someArray = [ # does not work ["entry1"] ]`) assertTree(t, tree, err, map[string]interface{}{ "someArray": [][]string{{"entry1"}}, }) } func TestNestedEmptyArrays(t *testing.T) { tree, err := Load("a = [[[]]]") assertTree(t, tree, err, map[string]interface{}{ "a": [][][]interface{}{{{}}}, }) } func TestArrayNestedStrings(t *testing.T) { tree, err := Load("data = [ [\"gamma\", \"delta\"], [\"Foo\"] ]") assertTree(t, tree, err, map[string]interface{}{ "data": [][]string{{"gamma", "delta"}, {"Foo"}}, }) } func TestParseUnknownRvalue(t *testing.T) { _, err := Load("a = !bssss") if err == nil { t.Error("Expecting a parse error") } _, err = Load("a = /b") if err == nil { t.Error("Expecting a parse error") } } func TestMissingValue(t *testing.T) { _, err := Load("a = ") if err.Error() != "(1, 5): expecting a value" { t.Error("Bad error message:", err.Error()) } } func TestUnterminatedArray(t *testing.T) { _, err := Load("a = [1,") if err.Error() != "(1, 8): unterminated array" { t.Error("Bad error message:", err.Error()) } _, err = Load("a = [1") if err.Error() != "(1, 7): unterminated array" { t.Error("Bad error message:", err.Error()) } _, err = Load("a = [1 2") if err.Error() != "(1, 8): missing comma" { t.Error("Bad error message:", err.Error()) } } func TestNewlinesInArrays(t *testing.T) { tree, err := Load("a = [1,\n2,\n3]") assertTree(t, tree, err, map[string]interface{}{ "a": []int64{int64(1), int64(2), int64(3)}, }) } func TestArrayWithExtraComma(t *testing.T) { tree, err := Load("a = [1,\n2,\n3,\n]") assertTree(t, tree, err, map[string]interface{}{ "a": []int64{int64(1), int64(2), int64(3)}, }) } func TestArrayWithExtraCommaComment(t *testing.T) { tree, err := Load("a = [1, # wow\n2, # such items\n3, # so array\n]") assertTree(t, tree, err, map[string]interface{}{ "a": []int64{int64(1), int64(2), int64(3)}, }) } func TestSimpleInlineGroup(t *testing.T) { tree, err := Load("key = {a = 42}") assertTree(t, tree, err, map[string]interface{}{ "key": map[string]interface{}{ "a": int64(42), }, }) } func TestDoubleInlineGroup(t *testing.T) { tree, err := Load("key = {a = 42, b = \"foo\"}") assertTree(t, tree, err, map[string]interface{}{ "key": map[string]interface{}{ "a": int64(42), "b": "foo", }, }) } func TestNestedInlineGroup(t *testing.T) { tree, err := Load("out = {block0 = {x = 99, y = 100}, block1 = {p = \"999\", q = \"1000\"}}") assertTree(t, tree, err, map[string]interface{}{ "out": map[string]interface{}{ "block0": map[string]interface{}{ "x": int64(99), "y": int64(100), }, "block1": map[string]interface{}{ "p": "999", "q": "1000", }, }, }) } func TestArrayInNestedInlineGroup(t *testing.T) { tree, err := Load(`image = {name = "xxx", palette = {id = 100, colors = ["red", "blue", "green"]}}`) assertTree(t, tree, err, map[string]interface{}{ "image": map[string]interface{}{ "name": "xxx", "palette": map[string]interface{}{ "id": int64(100), "colors": []string{ "red", "blue", "green", }, }, }, }) } func TestExampleInlineGroup(t *testing.T) { tree, err := Load(`name = { first = "Tom", last = "Preston-Werner" } point = { x = 1, y = 2 }`) assertTree(t, tree, err, map[string]interface{}{ "name": map[string]interface{}{ "first": "Tom", "last": "Preston-Werner", }, "point": map[string]interface{}{ "x": int64(1), "y": int64(2), }, }) } func TestInlineGroupBareKeysUnderscore(t *testing.T) { tree, err := Load(`foo = { _bar = "buz" }`) assertTree(t, tree, err, map[string]interface{}{ "foo": map[string]interface{}{ "_bar": "buz", }, }) } func TestInlineGroupBareKeysDash(t *testing.T) { tree, err := Load(`foo = { -bar = "buz" }`) assertTree(t, tree, err, map[string]interface{}{ "foo": map[string]interface{}{ "-bar": "buz", }, }) } func TestInlineGroupKeyQuoted(t *testing.T) { tree, err := Load(`foo = { "bar" = "buz" }`) assertTree(t, tree, err, map[string]interface{}{ "foo": map[string]interface{}{ "bar": "buz", }, }) } func TestExampleInlineGroupInArray(t *testing.T) { tree, err := Load(`points = [{ x = 1, y = 2 }]`) assertTree(t, tree, err, map[string]interface{}{ "points": []map[string]interface{}{ { "x": int64(1), "y": int64(2), }, }, }) } func TestInlineTableUnterminated(t *testing.T) { _, err := Load("foo = {") if err.Error() != "(1, 8): unterminated inline table" { t.Error("Bad error message:", err.Error()) } } func TestInlineTableCommaExpected(t *testing.T) { _, err := Load("foo = {hello = 53 test = foo}") if err.Error() != "(1, 19): unexpected token type in inline table: no value can start with t" { t.Error("Bad error message:", err.Error()) } } func TestInlineTableCommaStart(t *testing.T) { _, err := Load("foo = {, hello = 53}") if err.Error() != "(1, 8): unexpected token type in inline table: keys cannot contain , character" { t.Error("Bad error message:", err.Error()) } } func TestInlineTableDoubleComma(t *testing.T) { _, err := Load("foo = {hello = 53,, foo = 17}") if err.Error() != "(1, 19): unexpected token type in inline table: keys cannot contain , character" { t.Error("Bad error message:", err.Error()) } } func TestInlineTableTrailingComma(t *testing.T) { _, err := Load("foo = {hello = 53, foo = 17,}") if err.Error() != "(1, 28): trailing comma at the end of inline table" { t.Error("Bad error message:", err.Error()) } } func TestAddKeyToInlineTable(t *testing.T) { _, err := Load("type = { name = \"Nail\" }\ntype.edible = false") if err.Error() != "(2, 1): could not add key or sub-table to exist inline table or its sub-table : type" { t.Error("Bad error message:", err.Error()) } } func TestAddSubTableToInlineTable(t *testing.T) { _, err := Load("a = { b = \"c\" }\na.d.e = \"f\"") if err.Error() != "(2, 1): could not add key or sub-table to exist inline table or its sub-table : a.d" { t.Error("Bad error message:", err.Error()) } } func TestAddKeyToSubTableOfInlineTable(t *testing.T) { _, err := Load("a = { b = { c = \"d\" } }\na.b.e = \"f\"") if err.Error() != "(2, 1): could not add key or sub-table to exist inline table or its sub-table : a.b" { t.Error("Bad error message:", err.Error()) } } func TestReDefineInlineTable(t *testing.T) { _, err := Load("a = { b = \"c\" }\n[a]\n d = \"e\"") if err.Error() != "(2, 2): could not re-define exist inline table or its sub-table : a" { t.Error("Bad error message:", err.Error()) } } func TestDuplicateGroups(t *testing.T) { _, err := Load("[foo]\na=2\n[foo]b=3") if err.Error() != "(3, 2): duplicated tables" { t.Error("Bad error message:", err.Error()) } } func TestDuplicateKeys(t *testing.T) { _, err := Load("foo = 2\nfoo = 3") if err.Error() != "(2, 1): The following key was defined twice: foo" { t.Error("Bad error message:", err.Error()) } } func TestEmptyIntermediateTable(t *testing.T) { _, err := Load("[foo..bar]") if err.Error() != "(1, 2): invalid table array key: expecting key part after dot" { t.Error("Bad error message:", err.Error()) } } func TestImplicitDeclarationBefore(t *testing.T) { tree, err := Load("[a.b.c]\nanswer = 42\n[a]\nbetter = 43") assertTree(t, tree, err, map[string]interface{}{ "a": map[string]interface{}{ "b": map[string]interface{}{ "c": map[string]interface{}{ "answer": int64(42), }, }, "better": int64(43), }, }) } func TestFloatsWithoutLeadingZeros(t *testing.T) { _, err := Load("a = .42") if err.Error() != "(1, 5): cannot start float with a dot" { t.Error("Bad error message:", err.Error()) } _, err = Load("a = -.42") if err.Error() != "(1, 5): cannot start float with a dot" { t.Error("Bad error message:", err.Error()) } } func TestMissingFile(t *testing.T) { _, err := LoadFile("foo.toml") if err.Error() != "open foo.toml: no such file or directory" && err.Error() != "open foo.toml: The system cannot find the file specified." { t.Error("Bad error message:", err.Error()) } } func TestParseFile(t *testing.T) { tree, err := LoadFile("example.toml") assertTree(t, tree, err, map[string]interface{}{ "title": "TOML Example", "owner": map[string]interface{}{ "name": "Tom Preston-Werner", "organization": "GitHub", "bio": "GitHub Cofounder & CEO\nLikes tater tots and beer.", "dob": time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC), }, "database": map[string]interface{}{ "server": "192.168.1.1", "ports": []int64{8001, 8001, 8002}, "connection_max": 5000, "enabled": true, }, "servers": map[string]interface{}{ "alpha": map[string]interface{}{ "ip": "10.0.0.1", "dc": "eqdc10", }, "beta": map[string]interface{}{ "ip": "10.0.0.2", "dc": "eqdc10", }, }, "clients": map[string]interface{}{ "data": []interface{}{ []string{"gamma", "delta"}, []int64{1, 2}, }, "score": 4e-08, }, }) } func TestParseFileCRLF(t *testing.T) { tree, err := LoadFile("example-crlf.toml") assertTree(t, tree, err, map[string]interface{}{ "title": "TOML Example", "owner": map[string]interface{}{ "name": "Tom Preston-Werner", "organization": "GitHub", "bio": "GitHub Cofounder & CEO\nLikes tater tots and beer.", "dob": time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC), }, "database": map[string]interface{}{ "server": "192.168.1.1", "ports": []int64{8001, 8001, 8002}, "connection_max": 5000, "enabled": true, }, "servers": map[string]interface{}{ "alpha": map[string]interface{}{ "ip": "10.0.0.1", "dc": "eqdc10", }, "beta": map[string]interface{}{ "ip": "10.0.0.2", "dc": "eqdc10", }, }, "clients": map[string]interface{}{ "data": []interface{}{ []string{"gamma", "delta"}, []int64{1, 2}, }, "score": 4e-08, }, }) } func TestParseKeyGroupArray(t *testing.T) { tree, err := Load("[[foo.bar]] a = 42\n[[foo.bar]] a = 69") assertTree(t, tree, err, map[string]interface{}{ "foo": map[string]interface{}{ "bar": []map[string]interface{}{ {"a": int64(42)}, {"a": int64(69)}, }, }, }) } func TestParseKeyGroupArrayUnfinished(t *testing.T) { _, err := Load("[[foo.bar]\na = 42") if err.Error() != "(1, 10): was expecting token [[, but got unclosed table array key instead" { t.Error("Bad error message:", err.Error()) } _, err = Load("[[foo.[bar]\na = 42") if err.Error() != "(1, 3): unexpected token table array key cannot contain ']', was expecting a table array key" { t.Error("Bad error message:", err.Error()) } } func TestParseKeyGroupArrayQueryExample(t *testing.T) { tree, err := Load(` [[book]] title = "The Stand" author = "Stephen King" [[book]] title = "For Whom the Bell Tolls" author = "Ernest Hemmingway" [[book]] title = "Neuromancer" author = "William Gibson" `) assertTree(t, tree, err, map[string]interface{}{ "book": []map[string]interface{}{ {"title": "The Stand", "author": "Stephen King"}, {"title": "For Whom the Bell Tolls", "author": "Ernest Hemmingway"}, {"title": "Neuromancer", "author": "William Gibson"}, }, }) } func TestParseKeyGroupArraySpec(t *testing.T) { tree, err := Load("[[fruit]]\n name=\"apple\"\n [fruit.physical]\n color=\"red\"\n shape=\"round\"\n [[fruit]]\n name=\"banana\"") assertTree(t, tree, err, map[string]interface{}{ "fruit": []map[string]interface{}{ {"name": "apple", "physical": map[string]interface{}{"color": "red", "shape": "round"}}, {"name": "banana"}, }, }) } func TestTomlValueStringRepresentation(t *testing.T) { for idx, item := range []struct { Value interface{} Expect string }{ {int64(12345), "12345"}, {uint64(50), "50"}, {float64(123.45), "123.45"}, {true, "true"}, {"hello world", "\"hello world\""}, {"\b\t\n\f\r\"\\", "\"\\b\\t\\n\\f\\r\\\"\\\\\""}, {"\x05", "\"\\u0005\""}, {time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC), "1979-05-27T07:32:00Z"}, {[]interface{}{"gamma", "delta"}, "[\"gamma\", \"delta\"]"}, {nil, ""}, } { result, err := tomlValueStringRepresentation(item.Value, "", "", OrderAlphabetical, false) if err != nil { t.Errorf("Test %d - unexpected error: %s", idx, err) } if result != item.Expect { t.Errorf("Test %d - got '%s', expected '%s'", idx, result, item.Expect) } } } func TestToStringMapStringString(t *testing.T) { tree, err := TreeFromMap(map[string]interface{}{"m": map[string]interface{}{"v": "abc"}}) if err != nil { t.Fatalf("unexpected error: %s", err) } want := "\n[m]\n v = \"abc\"\n" got := tree.String() if got != want { t.Errorf("want:\n%q\ngot:\n%q", want, got) } } func assertPosition(t *testing.T, text string, ref map[string]Position) { tree, err := Load(text) if err != nil { t.Errorf("Error loading document text: `%v`", text) t.Errorf("Error: %v", err) } for path, pos := range ref { testPos := tree.GetPosition(path) if testPos.Invalid() { t.Errorf("Failed to query tree path or path has invalid position: %s", path) } else if pos != testPos { t.Errorf("Expected position %v, got %v instead", pos, testPos) } } } func TestDocumentPositions(t *testing.T) { assertPosition(t, "[foo]\nbar=42\nbaz=69", map[string]Position{ "": {1, 1}, "foo": {1, 1}, "foo.bar": {2, 1}, "foo.baz": {3, 1}, }) } func TestDocumentPositionsWithSpaces(t *testing.T) { assertPosition(t, " [foo]\n bar=42\n baz=69", map[string]Position{ "": {1, 1}, "foo": {1, 3}, "foo.bar": {2, 3}, "foo.baz": {3, 3}, }) } func TestDocumentPositionsWithGroupArray(t *testing.T) { assertPosition(t, "[[foo]]\nbar=42\nbaz=69", map[string]Position{ "": {1, 1}, "foo": {1, 1}, "foo.bar": {2, 1}, "foo.baz": {3, 1}, }) } func TestNestedTreePosition(t *testing.T) { assertPosition(t, "[foo.bar]\na=42\nb=69", map[string]Position{ "": {1, 1}, "foo": {1, 1}, "foo.bar": {1, 1}, "foo.bar.a": {2, 1}, "foo.bar.b": {3, 1}, }) } func TestInvalidGroupArray(t *testing.T) { _, err := Load("[table#key]\nanswer = 42") if err == nil { t.Error("Should error") } _, err = Load("[foo.[bar]\na = 42") if err.Error() != "(1, 2): unexpected token table key cannot contain ']', was expecting a table key" { t.Error("Bad error message:", err.Error()) } } func TestDoubleEqual(t *testing.T) { _, err := Load("foo= = 2") if err.Error() != "(1, 6): cannot have multiple equals for the same key" { t.Error("Bad error message:", err.Error()) } } func TestGroupArrayReassign(t *testing.T) { _, err := Load("[hello]\n[[hello]]") if err.Error() != "(2, 3): key \"hello\" is already assigned and not of type table array" { t.Error("Bad error message:", err.Error()) } } func TestInvalidFloatParsing(t *testing.T) { _, err := Load("a=1e_2") if err.Error() != "(1, 3): invalid use of _ in number" { t.Error("Bad error message:", err.Error()) } _, err = Load("a=1e2_") if err.Error() != "(1, 3): invalid use of _ in number" { t.Error("Bad error message:", err.Error()) } _, err = Load("a=1__2") if err.Error() != "(1, 3): invalid use of _ in number" { t.Error("Bad error message:", err.Error()) } _, err = Load("a=_1_2") if err.Error() != "(1, 3): no value can start with _" { t.Error("Bad error message:", err.Error()) } } func TestMapKeyIsNum(t *testing.T) { _, err := Load("table={2018=1,2019=2}") if err != nil { t.Error("should be passed") } _, err = Load(`table={"2018"=1,"2019"=2}`) if err != nil { t.Error("should be passed") } } func TestInvalidKeyInlineTable(t *testing.T) { _, err := Load("table={invalid..key = 1}") if err.Error() != "(1, 8): invalid key: expecting key part after dot" { t.Error("Bad error message:", err.Error()) } } func TestDottedKeys(t *testing.T) { tree, err := Load(` name = "Orange" physical.color = "orange" physical.shape = "round" site."google.com" = true`) assertTree(t, tree, err, map[string]interface{}{ "name": "Orange", "physical": map[string]interface{}{ "color": "orange", "shape": "round", }, "site": map[string]interface{}{ "google.com": true, }, }) } func TestInvalidDottedKeyEmptyGroup(t *testing.T) { _, err := Load(`a..b = true`) if err == nil { t.Fatal("should return an error") } if err.Error() != "(1, 1): invalid key: expecting key part after dot" { t.Fatalf("invalid error message: %s", err) } } func TestAccidentalNewlines(t *testing.T) { expected := "The quick brown fox jumps over the lazy dog." tree, err := Load(`str1 = "The quick brown fox jumps over the lazy dog." str2 = """ The quick brown \ fox jumps over \ the lazy dog.""" str3 = """\ The quick brown \` + " " + ` fox jumps over \` + " " + ` the lazy dog.\` + " " + ` """`) if err != nil { t.Fatalf("unexpected error: %v", err) } got := tree.Get("str1") if got != expected { t.Errorf("expected '%s', got '%s'", expected, got) } got = tree.Get("str2") if got != expected { t.Errorf("expected '%s', got '%s'", expected, got) } got = tree.Get("str3") if got != expected { t.Errorf("expected '%s', got '%s'", expected, got) } }