github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/go/gen_program_expression_test.go (about) 1 package gen 2 3 import ( 4 "bytes" 5 "io" 6 "path/filepath" 7 "strings" 8 "testing" 9 10 "github.com/hashicorp/hcl/v2" 11 "github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/model" 12 "github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/syntax" 13 "github.com/stretchr/testify/assert" 14 ) 15 16 type exprTestCase struct { 17 hcl2Expr string 18 goCode string 19 } 20 21 type environment map[string]interface{} 22 23 func (e environment) scope() *model.Scope { 24 s := model.NewRootScope(syntax.None) 25 for name, typeOrFunction := range e { 26 switch typeOrFunction := typeOrFunction.(type) { 27 case *model.Function: 28 s.DefineFunction(name, typeOrFunction) 29 case model.Type: 30 s.Define(name, &model.Variable{Name: name, VariableType: typeOrFunction}) 31 } 32 } 33 return s 34 } 35 36 func TestLiteralExpression(t *testing.T) { 37 t.Parallel() 38 39 cases := []exprTestCase{ 40 {hcl2Expr: "false", goCode: "false"}, 41 {hcl2Expr: "true", goCode: "true"}, 42 {hcl2Expr: "0", goCode: "0"}, 43 {hcl2Expr: "3.14", goCode: "3.14"}, 44 {hcl2Expr: "\"foo\"", goCode: "\"foo\""}, 45 {hcl2Expr: `"foo: ${bar}"`, goCode: `fmt.Sprintf("foo: %v", bar)`}, 46 {hcl2Expr: `"fizz${bar}buzz"`, goCode: `fmt.Sprintf("fizz%vbuzz", bar)`}, 47 {hcl2Expr: `"foo ${bar} %baz"`, goCode: `fmt.Sprintf("foo %v %vbaz", bar, "%")`}, 48 {hcl2Expr: strings.ReplaceAll(`"{ 49 \"Version\": \"2008-10-17\", 50 \"Statement\": [ 51 { 52 ${Sid}: ${newpolicy}, 53 ${Effect}: ${Allow}, 54 \"Principal\": \"*\", 55 } 56 ] 57 }"`, "\n", "\\n"), goCode: "fmt.Sprintf(`" + `{ 58 "Version": "2008-10-17", 59 "Statement": [ 60 { 61 %v: %v, 62 %v: %v, 63 "Principal": "*", 64 } 65 ] 66 }` + "`, Sid, newpolicy, Effect, Allow)"}, 67 } 68 for _, c := range cases { 69 c := c 70 testGenerateExpression(t, c.hcl2Expr, c.goCode, nil, nil) 71 } 72 } 73 74 func TestBinaryOpExpression(t *testing.T) { 75 t.Parallel() 76 77 env := environment(map[string]interface{}{ 78 "a": model.BoolType, 79 "b": model.BoolType, 80 "c": model.NumberType, 81 "d": model.NumberType, 82 }) 83 scope := env.scope() 84 85 cases := []exprTestCase{ 86 {hcl2Expr: "0 == 0", goCode: "0 == 0"}, 87 {hcl2Expr: "0 != 0", goCode: "0 != 0"}, 88 {hcl2Expr: "0 < 0", goCode: "0 < 0"}, 89 {hcl2Expr: "0 > 0", goCode: "0 > 0"}, 90 {hcl2Expr: "0 <= 0", goCode: "0 <= 0"}, 91 {hcl2Expr: "0 >= 0", goCode: "0 >= 0"}, 92 {hcl2Expr: "0 + 0", goCode: "0 + 0"}, 93 {hcl2Expr: "0 * 0", goCode: "0 * 0"}, 94 {hcl2Expr: "0 / 0", goCode: "0 / 0"}, 95 {hcl2Expr: "0 % 0", goCode: "0 % 0"}, 96 {hcl2Expr: "false && false", goCode: "false && false"}, 97 {hcl2Expr: "false || false", goCode: "false || false"}, 98 {hcl2Expr: "a == true", goCode: "a == true"}, 99 {hcl2Expr: "b == true", goCode: "b == true"}, 100 {hcl2Expr: "c + 0", goCode: "c + 0"}, 101 {hcl2Expr: "d + 0", goCode: "d + 0"}, 102 {hcl2Expr: "a && true", goCode: "a && true"}, 103 {hcl2Expr: "b && true", goCode: "b && true"}, 104 } 105 for _, c := range cases { 106 testGenerateExpression(t, c.hcl2Expr, c.goCode, scope, nil) 107 } 108 } 109 110 func TestUnaryOpExrepssion(t *testing.T) { 111 t.Parallel() 112 113 env := environment(map[string]interface{}{ 114 "a": model.NumberType, 115 "b": model.BoolType, 116 }) 117 scope := env.scope() 118 119 cases := []exprTestCase{ 120 {hcl2Expr: "-1", goCode: "-1"}, 121 {hcl2Expr: "!true", goCode: "!true"}, 122 {hcl2Expr: "-a", goCode: "-a"}, 123 {hcl2Expr: "!b", goCode: "!b"}, 124 } 125 126 for _, c := range cases { 127 testGenerateExpression(t, c.hcl2Expr, c.goCode, scope, nil) 128 } 129 } 130 131 // nolint: lll 132 func TestConditionalExpression(t *testing.T) { 133 t.Parallel() 134 135 cases := []exprTestCase{ 136 { 137 hcl2Expr: "true ? 1 : 0", 138 goCode: "var tmp0 float64\nif true {\ntmp0 = 1\n} else {\ntmp0 = 0\n}\ntmp0", 139 }, 140 { 141 hcl2Expr: "true ? 1 : true ? 0 : -1", 142 goCode: "var tmp0 float64\nif true {\ntmp0 = 0\n} else {\ntmp0 = -1\n}\nvar tmp1 float64\nif true {\ntmp1 = 1\n} else {\ntmp1 = tmp0\n}\ntmp1", 143 }, 144 { 145 hcl2Expr: "true ? true ? 0 : -1 : 0", 146 goCode: "var tmp0 float64\nif true {\ntmp0 = 0\n} else {\ntmp0 = -1\n}\nvar tmp1 float64\nif true {\ntmp1 = tmp0\n} else {\ntmp1 = 0\n}\ntmp1", 147 }, 148 { 149 hcl2Expr: "{foo = true ? 2 : 0}", 150 goCode: "var tmp0 float64\nif true {\ntmp0 = 2\n} else {\ntmp0 = 0\n}\nmap[string]interface{}{\n\"foo\": tmp0,\n}", 151 }, 152 } 153 genFunc := func(w io.Writer, g *generator, e model.Expression) { 154 e, temps := g.lowerExpression(e, e.Type()) 155 g.genTemps(w, temps) 156 g.Fgenf(w, "%v", e) 157 } 158 for _, c := range cases { 159 testGenerateExpression(t, c.hcl2Expr, c.goCode, nil, genFunc) 160 } 161 } 162 163 func TestObjectConsExpression(t *testing.T) { 164 t.Parallel() 165 166 env := environment(map[string]interface{}{ 167 "a": model.StringType, 168 }) 169 scope := env.scope() 170 cases := []exprTestCase{ 171 { 172 // TODO probably a bug in the binder. Single value objects should just be maps 173 hcl2Expr: "{foo = 1}", 174 goCode: "map[string]interface{}{\n\"foo\": 1,\n}", 175 }, 176 { 177 hcl2Expr: "{\"foo\" = 1}", 178 goCode: "map[string]interface{}{\n\"foo\": 1,\n}", 179 }, 180 { 181 hcl2Expr: "{1 = 1}", 182 goCode: "map[string]interface{}{\n\"1\": 1,\n}", 183 }, 184 { 185 hcl2Expr: "{(a) = 1}", 186 goCode: "map[string]float64{\na: 1,\n}", 187 }, 188 { 189 hcl2Expr: "{(a+a) = 1}", 190 goCode: "map[string]float64{\na + a: 1,\n}", 191 }, 192 } 193 for _, c := range cases { 194 testGenerateExpression(t, c.hcl2Expr, c.goCode, scope, nil) 195 } 196 } 197 198 func TestTupleConsExpression(t *testing.T) { 199 t.Parallel() 200 201 env := environment(map[string]interface{}{ 202 "a": model.StringType, 203 }) 204 scope := env.scope() 205 cases := []exprTestCase{ 206 { 207 hcl2Expr: "[\"foo\"]", 208 goCode: "[]string{\n\"foo\",\n}", 209 }, 210 { 211 hcl2Expr: "[\"foo\", \"bar\", \"baz\"]", 212 goCode: "[]string{\n\"foo\",\n\"bar\",\n\"baz\",\n}", 213 }, 214 { 215 hcl2Expr: "[1]", 216 goCode: "[]float64{\n1,\n}", 217 }, 218 { 219 hcl2Expr: "[1,2,3]", 220 goCode: "[]float64{\n1,\n2,\n3,\n}", 221 }, 222 { 223 hcl2Expr: "[1,\"foo\"]", 224 goCode: "[]interface{}{\n1,\n\"foo\",\n}", 225 }, 226 } 227 for _, c := range cases { 228 c := c 229 testGenerateExpression(t, c.hcl2Expr, c.goCode, scope, nil) 230 } 231 } 232 233 func testGenerateExpression( 234 t *testing.T, 235 hcl2Expr, goCode string, 236 scope *model.Scope, 237 gen func(w io.Writer, g *generator, e model.Expression), 238 ) { 239 t.Run(hcl2Expr, func(t *testing.T) { 240 t.Parallel() 241 242 // test program is only for schema info 243 g := newTestGenerator(t, filepath.Join("aws-s3-logging-pp", "aws-s3-logging.pp")) 244 var index bytes.Buffer 245 expr, _ := model.BindExpressionText(hcl2Expr, scope, hcl.Pos{}) 246 if gen != nil { 247 gen(&index, g, expr) 248 } else { 249 g.Fgenf(&index, "%v", expr) 250 } 251 252 assert.Equal(t, goCode, index.String()) 253 }) 254 }