go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/starlark/builtins/native_test.go (about) 1 // Copyright 2020 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package builtins 16 17 import ( 18 "testing" 19 20 "go.starlark.net/resolve" 21 "go.starlark.net/starlark" 22 23 . "github.com/smartystreets/goconvey/convey" 24 . "go.chromium.org/luci/common/testing/assertions" 25 ) 26 27 func init() { 28 resolve.AllowFloat = true 29 resolve.AllowSet = true 30 } 31 32 func TestToGoNative(t *testing.T) { 33 t.Parallel() 34 35 runScript := func(code string) (starlark.StringDict, error) { 36 return starlark.ExecFile(&starlark.Thread{}, "main", code, nil) 37 } 38 39 run := func(code string, expected any) { 40 out, err := runScript(code) 41 So(err, ShouldBeNil) 42 result, err := ToGoNative(out["val"]) 43 So(err, ShouldBeNil) 44 So(result, ShouldResemble, expected) 45 } 46 47 mustFail := func(code string, expectErr string) { 48 out, err := runScript(code) 49 _, err = ToGoNative(out["val"]) 50 So(err, ShouldErrLike, expectErr) 51 } 52 53 Convey("Happy cases", t, func() { 54 Convey("Scalar types", func() { 55 run(`val = None`, nil) 56 run(`val = True`, true) 57 run(`val = 123`, int64(123)) 58 run(`val = 123.5`, 123.5) 59 run(`val = "hi"`, "hi") 60 }) 61 62 Convey("Dict", func() { 63 run(`val = {}`, map[string]any{}) 64 run(`val = {"a": 1, "b": 2}`, map[string]any{"a": int64(1), "b": int64(2)}) 65 }) 66 67 Convey("Iterables", func() { 68 run(`val = []`, []any{}) 69 run(`val = ()`, []any{}) 70 run(`val = set()`, []any{}) 71 run(`val = [1, 2, 3]`, []any{int64(1), int64(2), int64(3)}) 72 run(`val = (1, 2, 3)`, []any{int64(1), int64(2), int64(3)}) 73 run(`val = set([1, 2, 3])`, []any{int64(1), int64(2), int64(3)}) 74 }) 75 76 Convey("Everything at once", func() { 77 run(`val = {"a": None, "b": ["c", "d", ["e"]]}`, map[string]any{ 78 "a": nil, 79 "b": []any{"c", "d", []any{"e"}}, 80 }) 81 }) 82 }) 83 84 Convey("Unhappy cases", t, func() { 85 mustFail(`val = list`, `unsupported type builtin_function_or_method`) 86 mustFail(`val = 18446744073709551616`, `can't convert "18446744073709551616" to int64`) 87 mustFail(`val = {1: 2}`, `dict keys should be strings, got int`) 88 89 // Errors propagate when serializing containers. 90 mustFail(`val = {"a": list}`, `unsupported type builtin_function_or_method`) 91 mustFail(`val = [list]`, `unsupported type builtin_function_or_method`) 92 mustFail(`val = (list, list)`, `unsupported type builtin_function_or_method`) 93 mustFail(`val = set([list])`, `unsupported type builtin_function_or_method`) 94 }) 95 96 Convey("Detects recursive structures", t, func() { 97 const msg = "detected recursion in the data structure" 98 99 // With dict. 100 mustFail(`val = {}; val['k'] = val`, msg) 101 // With list. 102 mustFail(`val = []; val.append(val)`, msg) 103 // With tuple. 104 mustFail(`l = []; val = (1, l); l.append(val)`, msg) 105 106 // And there can't be recursion involving sets, since Starlark sets require 107 // all items to be hashable, and this automatically forbids containers that 108 // can host recursive structures. 109 }) 110 }