github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/parse/asp/interpreter_test.go (about) 1 // This is essentially an end-to-end test on the whole thing; since it's 2 // quite tedious to write out the AST by hand we interpret sample BUILD files directly. 3 4 package asp 5 6 import ( 7 "reflect" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "core" 14 "parse/rules" 15 ) 16 17 func parseFileToStatements(filename string) (*scope, []*Statement, error) { 18 state := core.NewBuildState(1, nil, 4, core.DefaultConfiguration()) 19 state.Config.BuildConfig = map[string]string{"parser-engine": "python27"} 20 pkg := core.NewPackage("test/package") 21 parser := NewParser(state) 22 parser.MustLoadBuiltins("builtins.build_defs", nil, rules.MustAsset("builtins.build_defs.gob")) 23 statements, err := parser.parse(filename) 24 if err != nil { 25 panic(err) 26 } 27 statements = parser.optimise(statements) 28 parser.interpreter.optimiseExpressions(reflect.ValueOf(statements)) 29 s, err := parser.interpreter.interpretAll(pkg, statements) 30 return s, statements, err 31 } 32 33 func parseFile(filename string) (*scope, error) { 34 s, _, err := parseFileToStatements(filename) 35 return s, err 36 } 37 38 func TestBasic(t *testing.T) { 39 s, err := parseFile("src/parse/asp/test_data/basic.build") 40 require.NoError(t, err) 41 assert.NotNil(t, s.Lookup("test")) 42 assert.Panics(t, func() { s.Lookup("wibble") }) 43 assert.NotNil(t, s.Lookup("True")) 44 assert.NotNil(t, s.Lookup("False")) 45 assert.NotNil(t, s.Lookup("None")) 46 } 47 48 func TestFunctionDef(t *testing.T) { 49 s, err := parseFile("src/parse/asp/test_data/function_def.build") 50 require.NoError(t, err) 51 require.NotNil(t, s.Lookup("cc_library")) 52 f := s.Lookup("cc_library").(*pyFunc) 53 assert.Equal(t, 14, len(f.args)) 54 assert.Equal(t, 14, len(f.constants)) 55 assert.Equal(t, 0, len(f.defaults)) 56 assert.Equal(t, "name", f.args[0]) 57 assert.Nil(t, f.constants[0]) 58 assert.Equal(t, "srcs", f.args[1]) 59 assert.NotNil(t, f.constants[1]) 60 } 61 62 func TestOperators(t *testing.T) { 63 s, err := parseFile("src/parse/asp/test_data/interpreter/operators.build") 64 require.NoError(t, err) 65 require.NotNil(t, s.Lookup("y")) 66 i := s.Lookup("y").(pyInt) 67 assert.EqualValues(t, 7, i) 68 assert.True(t, s.Lookup("z").IsTruthy()) 69 } 70 71 func TestInterpolation(t *testing.T) { 72 s, err := parseFile("src/parse/asp/test_data/interpreter/interpolation.build") 73 require.NoError(t, err) 74 assert.EqualValues(t, "//abc:123", s.Lookup("x")) 75 } 76 77 func TestCollections(t *testing.T) { 78 s, err := parseFile("src/parse/asp/test_data/interpreter/collections.build") 79 require.NoError(t, err) 80 assert.EqualValues(t, True, s.Lookup("x")) 81 assert.EqualValues(t, True, s.Lookup("y")) 82 assert.EqualValues(t, False, s.Lookup("z")) 83 } 84 85 func TestArguments(t *testing.T) { 86 s, err := parseFile("src/parse/asp/test_data/interpreter/arguments.build") 87 require.NoError(t, err) 88 assert.EqualValues(t, "a:b:True", s.Lookup("x")) 89 assert.EqualValues(t, "a:b:c", s.Lookup("y")) 90 assert.EqualValues(t, "a:b:c", s.Lookup("z")) 91 } 92 93 func TestMutableArguments(t *testing.T) { 94 s, err := parseFile("src/parse/asp/test_data/interpreter/mutable_arguments.build") 95 require.NoError(t, err) 96 assert.EqualValues(t, 8, s.Lookup("y")) 97 } 98 99 func TestBuiltins(t *testing.T) { 100 s, err := parseFile("src/parse/asp/test_data/interpreter/builtins.build") 101 require.NoError(t, err) 102 assert.Equal(t, 1, s.pkg.NumTargets()) 103 assert.NotNil(t, s.pkg.Target("lib")) 104 } 105 106 func TestParentheses(t *testing.T) { 107 s, err := parseFile("src/parse/asp/test_data/interpreter/parentheses.build") 108 require.NoError(t, err) 109 assert.EqualValues(t, 1, s.Lookup("x")) 110 } 111 112 func TestComprehensions(t *testing.T) { 113 s, err := parseFile("src/parse/asp/test_data/interpreter/comprehensions.build") 114 require.NoError(t, err) 115 assert.EqualValues(t, pyList{pyString("file1"), pyString("file2")}, s.Lookup("file_srcs")) 116 assert.EqualValues(t, pyList{pyString("file1+file1"), pyString("file1+file2"), pyString("file1+:rule1"), 117 pyString("file2+file1"), pyString("file2+file2"), pyString("file2+:rule1")}, s.Lookup("pairs")) 118 } 119 120 func TestEquality(t *testing.T) { 121 s, err := parseFile("src/parse/asp/test_data/interpreter/equality.build") 122 require.NoError(t, err) 123 assert.Equal(t, True, s.Lookup("a")) 124 assert.Equal(t, True, s.Lookup("b")) 125 assert.Equal(t, False, s.Lookup("c")) 126 assert.Equal(t, False, s.Lookup("d")) 127 assert.Equal(t, True, s.Lookup("e")) 128 assert.Equal(t, False, s.Lookup("f")) 129 assert.Equal(t, False, s.Lookup("g")) 130 } 131 132 func TestSlicing(t *testing.T) { 133 s, err := parseFile("src/parse/asp/test_data/interpreter/slicing.build") 134 require.NoError(t, err) 135 assert.Equal(t, pyInt(2), s.Lookup("a")) 136 assert.Equal(t, pyList{pyInt(2), pyInt(3)}, s.Lookup("b")) 137 assert.Equal(t, pyList{pyInt(1)}, s.Lookup("c")) 138 assert.Equal(t, pyList{pyInt(2)}, s.Lookup("d")) 139 assert.Equal(t, pyInt(3), s.Lookup("e")) 140 assert.Equal(t, pyList{pyInt(1), pyInt(2)}, s.Lookup("f")) 141 assert.Equal(t, pyList{pyInt(1), pyInt(2), pyInt(3)}, s.Lookup("g")) 142 } 143 144 func TestSorting(t *testing.T) { 145 s, err := parseFile("src/parse/asp/test_data/interpreter/sorted.build") 146 require.NoError(t, err) 147 assert.Equal(t, pyList{pyInt(1), pyInt(2), pyInt(3)}, s.Lookup("y")) 148 // N.B. sorted() sorts in-place, unlike Python's one. We may change that later. 149 } 150 151 func TestUnpacking(t *testing.T) { 152 s, err := parseFile("src/parse/asp/test_data/interpreter/unpacking.build") 153 require.NoError(t, err) 154 assert.EqualValues(t, "a", s.Lookup("a")) 155 assert.EqualValues(t, "b", s.Lookup("b")) 156 assert.EqualValues(t, "c", s.Lookup("c")) 157 assert.EqualValues(t, "abc", s.Lookup("d")) 158 assert.EqualValues(t, ".", s.Lookup("e")) 159 assert.EqualValues(t, "def", s.Lookup("f")) 160 } 161 162 func TestPrecedence(t *testing.T) { 163 s, err := parseFile("src/parse/asp/test_data/interpreter/precedence.build") 164 require.NoError(t, err) 165 assert.EqualValues(t, pyList{pyString("a.go")}, s.Lookup("file_srcs")) 166 } 167 168 func TestPrecedence2(t *testing.T) { 169 s, err := parseFile("src/parse/asp/test_data/interpreter/precedence2.build") 170 require.NoError(t, err) 171 assert.True(t, s.Lookup("y").IsTruthy()) 172 } 173 174 func TestZip(t *testing.T) { 175 s, err := parseFile("src/parse/asp/test_data/interpreter/zip.build") 176 require.NoError(t, err) 177 expected := pyList{ 178 pyList{pyInt(1), pyInt(4), pyInt(7)}, 179 pyList{pyInt(2), pyInt(5), pyInt(8)}, 180 pyList{pyInt(3), pyInt(6), pyInt(9)}, 181 } 182 assert.EqualValues(t, expected, s.Lookup("x")) 183 } 184 185 func TestOptimisations(t *testing.T) { 186 s, err := parseFile("src/parse/asp/test_data/interpreter/optimisations.build") 187 require.NoError(t, err) 188 assert.EqualValues(t, "python2.7", s.Lookup("PARSER_LIB_NAME")) 189 } 190 191 func TestContinue(t *testing.T) { 192 s, err := parseFile("src/parse/asp/test_data/interpreter/continue.build") 193 require.NoError(t, err) 194 assert.EqualValues(t, pyList{pyInt(4), pyInt(5)}, s.Lookup("a")) 195 } 196 197 func TestAliases(t *testing.T) { 198 s, err := parseFile("src/parse/asp/test_data/interpreter/aliases.build") 199 require.NoError(t, err) 200 assert.EqualValues(t, 42, s.Lookup("v")) 201 } 202 203 func TestPaths(t *testing.T) { 204 s, err := parseFile("src/parse/asp/test_data/interpreter/paths.build") 205 require.NoError(t, err) 206 assert.EqualValues(t, "a/b/c", s.Lookup("a")) 207 assert.EqualValues(t, "a/c", s.Lookup("b")) 208 assert.EqualValues(t, pyList{pyString("a/b"), pyString("c")}, s.Lookup("c")) 209 assert.EqualValues(t, pyList{pyString(""), pyString("a")}, s.Lookup("d")) 210 assert.EqualValues(t, pyList{pyString("a/test"), pyString(".txt")}, s.Lookup("e")) 211 assert.EqualValues(t, pyList{pyString("a/test"), pyString("")}, s.Lookup("f")) 212 assert.EqualValues(t, "c", s.Lookup("g")) 213 assert.EqualValues(t, "a", s.Lookup("h")) 214 assert.EqualValues(t, "a/b", s.Lookup("i")) 215 assert.EqualValues(t, "", s.Lookup("j")) 216 } 217 218 func TestStrings(t *testing.T) { 219 s, err := parseFile("src/parse/asp/test_data/interpreter/strings.build") 220 require.NoError(t, err) 221 assert.EqualValues(t, pyList{ 222 pyString("acpi"), pyString("base64"), pyString("basename"), pyString("blkid"), pyString("blockdev"), 223 pyString("bunzip2"), pyString("bzcat"), pyString("cal"), pyString("cat"), pyString("catv"), 224 pyString("chattr"), pyString("whoami"), pyString("xargs"), pyString("xxd"), pyString("yes"), 225 }, s.Lookup("TOYS2")) 226 assert.EqualValues(t, "acpi base64 basename blkid blockdev bunzip2 bzcat cal cat catv chattr\nwhoami xargs xxd yes", s.Lookup("TOYS3")) 227 } 228 229 func TestArgumentCompatibility(t *testing.T) { 230 // This isn't a totally obvious property of the interpreter, but when an argument specifies 231 // a type and is given None, we adopt the default. This allows external functions to use None 232 // for various arguments (e.g. deps), but internally we can treat them as lists. 233 s, err := parseFile("src/parse/asp/test_data/interpreter/argument_compatibility.build") 234 require.NoError(t, err) 235 assert.EqualValues(t, pyList{pyInt(1)}, s.Lookup("x")) 236 } 237 238 func TestOptimiseConfig(t *testing.T) { 239 s, statements, err := parseFileToStatements("src/parse/asp/test_data/interpreter/optimise_config.build") 240 assert.NoError(t, err) 241 assert.Equal(t, 1, len(statements)) 242 assert.NotNil(t, statements[0].Ident) 243 assert.NotNil(t, statements[0].Ident.Action) 244 assert.NotNil(t, statements[0].Ident.Action.Assign) 245 assert.Equal(t, "GO_TOOL", statements[0].Ident.Action.Assign.Optimised.Config) 246 assert.EqualValues(t, "go", s.Lookup("x")) 247 }