github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/config/parse_test.go (about) 1 // Copyright 2019 GRAIL, Inc. All rights reserved. 2 // Use of this source code is governed by the Apache 2.0 3 // license that can be found in the LICENSE file. 4 5 package config 6 7 import ( 8 "strings" 9 "testing" 10 ) 11 12 // TestParseEmpty verifies that parsing a (logically) empty file is valid. 13 func TestParseEmpty(t *testing.T) { 14 for _, c := range []struct { 15 name string 16 text string 17 }{ 18 {"Empty", ""}, 19 {"Whitespace", "\t\n \n\t \n"}, 20 {"Semicolons", ";;"}, 21 {"Mix", " \t \n \n;\n ;"}, 22 } { 23 t.Run(c.name, func(t *testing.T) { 24 instances, err := parse(strings.NewReader(c.text)) 25 if err != nil { 26 t.Fatal(err) 27 } 28 if got, want := len(instances), 0; got != want { 29 t.Errorf("got %v, want %v", got, want) 30 } 31 }) 32 } 33 } 34 35 func TestParse(t *testing.T) { 36 got, err := parse(strings.NewReader(strings.ReplaceAll(` 37 param x y = "okay" 38 param y z = 123 39 param y a = "a"; param y b = b 40 param y c = nil 41 42 param y ( 43 x = "blah" 44 y = 333 45 fprec = 0.123456789 46 raw = ${"it's json": 47 12.3}$ 48 ) 49 50 instance z blah ( 51 bloop = 123 52 negint = -3 53 negfloat = -3.14 54 ) 55 56 param z x = 89898 57 58 instance bigslice/system blah ( 59 region = "us-west-2" 60 ) 61 62 param zero-params () 63 `, "$", "`"))) 64 if err != nil { 65 t.Fatal(err) 66 } 67 want := instances{ 68 "x": &instance{ 69 name: "x", 70 params: map[string]interface{}{ 71 "y": "okay", 72 }, 73 }, 74 "y": &instance{ 75 name: "y", 76 params: map[string]interface{}{ 77 "x": "blah", 78 "y": 333, 79 "z": 123, 80 "a": "a", 81 "b": indirect("b"), 82 "c": indirect(""), 83 "fprec": 0.123456789, 84 "raw": `{"it's json": 85 12.3}`, 86 }, 87 }, 88 "z": &instance{ 89 name: "z", 90 parent: "blah", 91 params: map[string]interface{}{ 92 "bloop": 123, 93 "x": 89898, 94 "negint": -3, 95 "negfloat": -3.14, 96 }, 97 }, 98 "bigslice/system": &instance{ 99 name: "bigslice/system", 100 parent: "blah", 101 params: map[string]interface{}{ 102 "region": "us-west-2", 103 }, 104 }, 105 "zero-params": &instance{ 106 name: "zero-params", 107 parent: "", 108 params: map[string]interface{}{}, 109 }, 110 } 111 if !got.Equal(want) { 112 t.Errorf("got %v, want %v", got, want) 113 } 114 for name, wantInst := range want { 115 t.Run(name, func(t *testing.T) { 116 syntax := wantInst.SyntaxString(nil) 117 insts, err := parse(strings.NewReader(syntax)) 118 if err != nil { 119 t.Fatalf("%v. syntax:\n%s", err, syntax) 120 } 121 if gotInst := insts[wantInst.name]; !wantInst.Equal(gotInst) { 122 t.Errorf("got %v, want %v, syntax:\n%s", got, want, syntax) 123 } 124 }) 125 } 126 } 127 128 func TestParseError(t *testing.T) { 129 testError(t, `parm x y = 1`, `parse error: <input>:1:1: unrecognized toplevel clause: parm`) 130 testError(t, `param x _y = "hey"`, `parse error: <input>:1:9: unexpected: "_"`) 131 testError(t, `param x 123 = blah`, `parse error: <input>:1:9: unexpected: Int`) 132 testError(t, `param x y z`, `parse error: <input>:1:11: expected "="`) 133 } 134 135 func testError(t *testing.T, s, expect string) { 136 t.Helper() 137 _, err := parse(strings.NewReader(s)) 138 if err == nil { 139 t.Error("expected error") 140 return 141 } 142 if got, want := err.Error(), expect; got != want { 143 t.Errorf("got %q, want %q", got, want) 144 } 145 }