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  }