1*9e94795aSAndroid Build Coastguard Worker// Copyright 2021 Google LLC 2*9e94795aSAndroid Build Coastguard Worker// 3*9e94795aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*9e94795aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*9e94795aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*9e94795aSAndroid Build Coastguard Worker// 7*9e94795aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*9e94795aSAndroid Build Coastguard Worker// 9*9e94795aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*9e94795aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*9e94795aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9e94795aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*9e94795aSAndroid Build Coastguard Worker// limitations under the License. 14*9e94795aSAndroid Build Coastguard Worker 15*9e94795aSAndroid Build Coastguard Workerpackage rbcrun 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard Workerimport ( 18*9e94795aSAndroid Build Coastguard Worker "fmt" 19*9e94795aSAndroid Build Coastguard Worker "os" 20*9e94795aSAndroid Build Coastguard Worker "path/filepath" 21*9e94795aSAndroid Build Coastguard Worker "runtime" 22*9e94795aSAndroid Build Coastguard Worker "strings" 23*9e94795aSAndroid Build Coastguard Worker "testing" 24*9e94795aSAndroid Build Coastguard Worker 25*9e94795aSAndroid Build Coastguard Worker "go.starlark.net/resolve" 26*9e94795aSAndroid Build Coastguard Worker "go.starlark.net/starlark" 27*9e94795aSAndroid Build Coastguard Worker "go.starlark.net/starlarktest" 28*9e94795aSAndroid Build Coastguard Worker) 29*9e94795aSAndroid Build Coastguard Worker 30*9e94795aSAndroid Build Coastguard Worker// In order to use "assert.star" from go/starlark.net/starlarktest in the tests, 31*9e94795aSAndroid Build Coastguard Worker// provide: 32*9e94795aSAndroid Build Coastguard Worker// * load function that handles "assert.star" 33*9e94795aSAndroid Build Coastguard Worker// * starlarktest.DataFile function that finds its location 34*9e94795aSAndroid Build Coastguard Worker 35*9e94795aSAndroid Build Coastguard Workerfunc init() { 36*9e94795aSAndroid Build Coastguard Worker starlarktestSetup() 37*9e94795aSAndroid Build Coastguard Worker} 38*9e94795aSAndroid Build Coastguard Worker 39*9e94795aSAndroid Build Coastguard Workerfunc starlarktestSetup() { 40*9e94795aSAndroid Build Coastguard Worker resolve.AllowLambda = true 41*9e94795aSAndroid Build Coastguard Worker starlarktest.DataFile = func(pkgdir, filename string) string { 42*9e94795aSAndroid Build Coastguard Worker // The caller expects this function to return the path to the 43*9e94795aSAndroid Build Coastguard Worker // data file. The implementation assumes that the source file 44*9e94795aSAndroid Build Coastguard Worker // containing the caller and the data file are in the same 45*9e94795aSAndroid Build Coastguard Worker // directory. It's ugly. Not sure what's the better way. 46*9e94795aSAndroid Build Coastguard Worker // TODO(asmundak): handle Bazel case 47*9e94795aSAndroid Build Coastguard Worker _, starlarktestSrcFile, _, _ := runtime.Caller(1) 48*9e94795aSAndroid Build Coastguard Worker if filepath.Base(starlarktestSrcFile) != "starlarktest.go" { 49*9e94795aSAndroid Build Coastguard Worker panic(fmt.Errorf("this function should be called from starlarktest.go, got %s", 50*9e94795aSAndroid Build Coastguard Worker starlarktestSrcFile)) 51*9e94795aSAndroid Build Coastguard Worker } 52*9e94795aSAndroid Build Coastguard Worker return filepath.Join(filepath.Dir(starlarktestSrcFile), filename) 53*9e94795aSAndroid Build Coastguard Worker } 54*9e94795aSAndroid Build Coastguard Worker} 55*9e94795aSAndroid Build Coastguard Worker 56*9e94795aSAndroid Build Coastguard Worker// Common setup for the tests: create thread, change to the test directory 57*9e94795aSAndroid Build Coastguard Workerfunc testSetup(t *testing.T) *starlark.Thread { 58*9e94795aSAndroid Build Coastguard Worker thread := &starlark.Thread{ 59*9e94795aSAndroid Build Coastguard Worker Load: func(thread *starlark.Thread, module string) (starlark.StringDict, error) { 60*9e94795aSAndroid Build Coastguard Worker if module == "assert.star" { 61*9e94795aSAndroid Build Coastguard Worker return starlarktest.LoadAssertModule() 62*9e94795aSAndroid Build Coastguard Worker } 63*9e94795aSAndroid Build Coastguard Worker return nil, fmt.Errorf("load not implemented") 64*9e94795aSAndroid Build Coastguard Worker }} 65*9e94795aSAndroid Build Coastguard Worker starlarktest.SetReporter(thread, t) 66*9e94795aSAndroid Build Coastguard Worker if err := os.Chdir(dataDir()); err != nil { 67*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 68*9e94795aSAndroid Build Coastguard Worker } 69*9e94795aSAndroid Build Coastguard Worker return thread 70*9e94795aSAndroid Build Coastguard Worker} 71*9e94795aSAndroid Build Coastguard Worker 72*9e94795aSAndroid Build Coastguard Workerfunc dataDir() string { 73*9e94795aSAndroid Build Coastguard Worker _, thisSrcFile, _, _ := runtime.Caller(0) 74*9e94795aSAndroid Build Coastguard Worker return filepath.Join(filepath.Dir(thisSrcFile), "testdata") 75*9e94795aSAndroid Build Coastguard Worker} 76*9e94795aSAndroid Build Coastguard Worker 77*9e94795aSAndroid Build Coastguard Workerfunc exerciseStarlarkTestFile(t *testing.T, starFile string) { 78*9e94795aSAndroid Build Coastguard Worker // In order to use "assert.star" from go/starlark.net/starlarktest in the tests, provide: 79*9e94795aSAndroid Build Coastguard Worker // * load function that handles "assert.star" 80*9e94795aSAndroid Build Coastguard Worker // * starlarktest.DataFile function that finds its location 81*9e94795aSAndroid Build Coastguard Worker if err := os.Chdir(dataDir()); err != nil { 82*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 83*9e94795aSAndroid Build Coastguard Worker } 84*9e94795aSAndroid Build Coastguard Worker thread := &starlark.Thread{ 85*9e94795aSAndroid Build Coastguard Worker Load: func(thread *starlark.Thread, module string) (starlark.StringDict, error) { 86*9e94795aSAndroid Build Coastguard Worker if module == "assert.star" { 87*9e94795aSAndroid Build Coastguard Worker return starlarktest.LoadAssertModule() 88*9e94795aSAndroid Build Coastguard Worker } 89*9e94795aSAndroid Build Coastguard Worker return nil, fmt.Errorf("load not implemented") 90*9e94795aSAndroid Build Coastguard Worker }} 91*9e94795aSAndroid Build Coastguard Worker starlarktest.SetReporter(thread, t) 92*9e94795aSAndroid Build Coastguard Worker _, thisSrcFile, _, _ := runtime.Caller(0) 93*9e94795aSAndroid Build Coastguard Worker filename := filepath.Join(filepath.Dir(thisSrcFile), starFile) 94*9e94795aSAndroid Build Coastguard Worker thread.SetLocal(executionModeKey, ExecutionModeRbc) 95*9e94795aSAndroid Build Coastguard Worker thread.SetLocal(shellKey, "/bin/sh") 96*9e94795aSAndroid Build Coastguard Worker if _, err := starlark.ExecFile(thread, filename, nil, rbcBuiltins); err != nil { 97*9e94795aSAndroid Build Coastguard Worker if err, ok := err.(*starlark.EvalError); ok { 98*9e94795aSAndroid Build Coastguard Worker t.Fatal(err.Backtrace()) 99*9e94795aSAndroid Build Coastguard Worker } 100*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 101*9e94795aSAndroid Build Coastguard Worker } 102*9e94795aSAndroid Build Coastguard Worker} 103*9e94795aSAndroid Build Coastguard Worker 104*9e94795aSAndroid Build Coastguard Workerfunc TestFileOps(t *testing.T) { 105*9e94795aSAndroid Build Coastguard Worker // TODO(asmundak): convert this to use exerciseStarlarkTestFile 106*9e94795aSAndroid Build Coastguard Worker thread := testSetup(t) 107*9e94795aSAndroid Build Coastguard Worker if _, err := starlark.ExecFile(thread, "file_ops.star", nil, rbcBuiltins); err != nil { 108*9e94795aSAndroid Build Coastguard Worker if err, ok := err.(*starlark.EvalError); ok { 109*9e94795aSAndroid Build Coastguard Worker t.Fatal(err.Backtrace()) 110*9e94795aSAndroid Build Coastguard Worker } 111*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 112*9e94795aSAndroid Build Coastguard Worker } 113*9e94795aSAndroid Build Coastguard Worker} 114*9e94795aSAndroid Build Coastguard Worker 115*9e94795aSAndroid Build Coastguard Workerfunc TestLoad(t *testing.T) { 116*9e94795aSAndroid Build Coastguard Worker // TODO(asmundak): convert this to use exerciseStarlarkTestFile 117*9e94795aSAndroid Build Coastguard Worker thread := testSetup(t) 118*9e94795aSAndroid Build Coastguard Worker thread.Load = func(thread *starlark.Thread, module string) (starlark.StringDict, error) { 119*9e94795aSAndroid Build Coastguard Worker if module == "assert.star" { 120*9e94795aSAndroid Build Coastguard Worker return starlarktest.LoadAssertModule() 121*9e94795aSAndroid Build Coastguard Worker } else { 122*9e94795aSAndroid Build Coastguard Worker return loader(thread, module) 123*9e94795aSAndroid Build Coastguard Worker } 124*9e94795aSAndroid Build Coastguard Worker } 125*9e94795aSAndroid Build Coastguard Worker dir := dataDir() 126*9e94795aSAndroid Build Coastguard Worker if err := os.Chdir(filepath.Dir(dir)); err != nil { 127*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 128*9e94795aSAndroid Build Coastguard Worker } 129*9e94795aSAndroid Build Coastguard Worker thread.SetLocal(allowExternalEntrypointKey, false) 130*9e94795aSAndroid Build Coastguard Worker thread.SetLocal(callingFileKey, "testdata/load.star") 131*9e94795aSAndroid Build Coastguard Worker thread.SetLocal(executionModeKey, ExecutionModeRbc) 132*9e94795aSAndroid Build Coastguard Worker if _, err := starlark.ExecFile(thread, "testdata/load.star", nil, rbcBuiltins); err != nil { 133*9e94795aSAndroid Build Coastguard Worker if err, ok := err.(*starlark.EvalError); ok { 134*9e94795aSAndroid Build Coastguard Worker t.Fatal(err.Backtrace()) 135*9e94795aSAndroid Build Coastguard Worker } 136*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 137*9e94795aSAndroid Build Coastguard Worker } 138*9e94795aSAndroid Build Coastguard Worker} 139*9e94795aSAndroid Build Coastguard Worker 140*9e94795aSAndroid Build Coastguard Workerfunc TestBzlLoadsScl(t *testing.T) { 141*9e94795aSAndroid Build Coastguard Worker moduleCache = make(map[string]*modentry) 142*9e94795aSAndroid Build Coastguard Worker dir := dataDir() 143*9e94795aSAndroid Build Coastguard Worker if err := os.Chdir(filepath.Dir(dir)); err != nil { 144*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 145*9e94795aSAndroid Build Coastguard Worker } 146*9e94795aSAndroid Build Coastguard Worker vars, _, err := Run("testdata/bzl_loads_scl.bzl", nil, ExecutionModeRbc, false) 147*9e94795aSAndroid Build Coastguard Worker if err != nil { 148*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 149*9e94795aSAndroid Build Coastguard Worker } 150*9e94795aSAndroid Build Coastguard Worker if val, ok := vars["foo"]; !ok { 151*9e94795aSAndroid Build Coastguard Worker t.Fatalf("Failed to load foo variable") 152*9e94795aSAndroid Build Coastguard Worker } else if val.(starlark.String) != "bar" { 153*9e94795aSAndroid Build Coastguard Worker t.Fatalf("Expected \"bar\", got %q", val) 154*9e94795aSAndroid Build Coastguard Worker } 155*9e94795aSAndroid Build Coastguard Worker} 156*9e94795aSAndroid Build Coastguard Worker 157*9e94795aSAndroid Build Coastguard Workerfunc TestNonEntrypointBzlLoadsScl(t *testing.T) { 158*9e94795aSAndroid Build Coastguard Worker moduleCache = make(map[string]*modentry) 159*9e94795aSAndroid Build Coastguard Worker dir := dataDir() 160*9e94795aSAndroid Build Coastguard Worker if err := os.Chdir(filepath.Dir(dir)); err != nil { 161*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 162*9e94795aSAndroid Build Coastguard Worker } 163*9e94795aSAndroid Build Coastguard Worker vars, _, err := Run("testdata/bzl_loads_scl_2.bzl", nil, ExecutionModeRbc, false) 164*9e94795aSAndroid Build Coastguard Worker if err != nil { 165*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 166*9e94795aSAndroid Build Coastguard Worker } 167*9e94795aSAndroid Build Coastguard Worker if val, ok := vars["foo"]; !ok { 168*9e94795aSAndroid Build Coastguard Worker t.Fatalf("Failed to load foo variable") 169*9e94795aSAndroid Build Coastguard Worker } else if val.(starlark.String) != "bar" { 170*9e94795aSAndroid Build Coastguard Worker t.Fatalf("Expected \"bar\", got %q", val) 171*9e94795aSAndroid Build Coastguard Worker } 172*9e94795aSAndroid Build Coastguard Worker} 173*9e94795aSAndroid Build Coastguard Worker 174*9e94795aSAndroid Build Coastguard Workerfunc TestSclLoadsBzl(t *testing.T) { 175*9e94795aSAndroid Build Coastguard Worker moduleCache = make(map[string]*modentry) 176*9e94795aSAndroid Build Coastguard Worker dir := dataDir() 177*9e94795aSAndroid Build Coastguard Worker if err := os.Chdir(filepath.Dir(dir)); err != nil { 178*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 179*9e94795aSAndroid Build Coastguard Worker } 180*9e94795aSAndroid Build Coastguard Worker _, _, err := Run("testdata/scl_incorrectly_loads_bzl.scl", nil, ExecutionModeScl, false) 181*9e94795aSAndroid Build Coastguard Worker if err == nil { 182*9e94795aSAndroid Build Coastguard Worker t.Fatal("Expected failure") 183*9e94795aSAndroid Build Coastguard Worker } 184*9e94795aSAndroid Build Coastguard Worker if !strings.Contains(err.Error(), ".scl files can only load other .scl files") { 185*9e94795aSAndroid Build Coastguard Worker t.Fatalf("Expected error to contain \".scl files can only load other .scl files\": %q", err.Error()) 186*9e94795aSAndroid Build Coastguard Worker } 187*9e94795aSAndroid Build Coastguard Worker} 188*9e94795aSAndroid Build Coastguard Worker 189*9e94795aSAndroid Build Coastguard Workerfunc TestCantLoadSymlink(t *testing.T) { 190*9e94795aSAndroid Build Coastguard Worker moduleCache = make(map[string]*modentry) 191*9e94795aSAndroid Build Coastguard Worker dir := dataDir() 192*9e94795aSAndroid Build Coastguard Worker if err := os.Chdir(filepath.Dir(dir)); err != nil { 193*9e94795aSAndroid Build Coastguard Worker t.Fatal(err) 194*9e94795aSAndroid Build Coastguard Worker } 195*9e94795aSAndroid Build Coastguard Worker _, _, err := Run("testdata/test_scl_symlink.scl", nil, ExecutionModeScl, false) 196*9e94795aSAndroid Build Coastguard Worker if err == nil { 197*9e94795aSAndroid Build Coastguard Worker t.Fatal("Expected failure") 198*9e94795aSAndroid Build Coastguard Worker } 199*9e94795aSAndroid Build Coastguard Worker if !strings.Contains(err.Error(), "symlinks to starlark files are not allowed") { 200*9e94795aSAndroid Build Coastguard Worker t.Fatalf("Expected error to contain \"symlinks to starlark files are not allowed\": %q", err.Error()) 201*9e94795aSAndroid Build Coastguard Worker } 202*9e94795aSAndroid Build Coastguard Worker} 203*9e94795aSAndroid Build Coastguard Worker 204*9e94795aSAndroid Build Coastguard Workerfunc TestShell(t *testing.T) { 205*9e94795aSAndroid Build Coastguard Worker exerciseStarlarkTestFile(t, "testdata/shell.star") 206*9e94795aSAndroid Build Coastguard Worker} 207