go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/quota/internal/luatest/lua_test.go (about) 1 // Copyright 2022 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 luatest 16 17 import ( 18 "fmt" 19 "os" 20 "path/filepath" 21 "regexp" 22 "strconv" 23 "strings" 24 "testing" 25 26 luajson "github.com/alicebob/gopher-json" 27 lua "github.com/yuin/gopher-lua" 28 29 . "github.com/smartystreets/goconvey/convey" 30 ) 31 32 func intMin(a, b int) int { 33 if a < b { 34 return a 35 } 36 return b 37 } 38 39 var hexFinder = regexp.MustCompile(`\\x[a-f0-9]{2}`) 40 41 // hackedFormat is like the built in implementation of string.format, except 42 // that it correctly implements %q on strings containing binary characters. 43 func hackedFormat(L *lua.LState) int { 44 str := L.CheckString(1) 45 args := make([]any, L.GetTop()-1) 46 top := L.GetTop() 47 for i := 2; i <= top; i++ { 48 args[i-2] = L.Get(i) 49 } 50 npat := strings.Count(str, "%") - strings.Count(str, "%%") 51 52 ret := hexFinder.ReplaceAllStringFunc(fmt.Sprintf(str, args[:intMin(npat, len(args))]...), func(s string) string { 53 dec, err := strconv.ParseUint(s[2:], 16, 8) 54 if err != nil { 55 panic(err) 56 } 57 return fmt.Sprintf("\\%03d", dec) 58 }) 59 60 L.Push(lua.LString(ret)) 61 return 1 62 } 63 64 func check(err error) { 65 if err != nil { 66 panic(err) 67 } 68 } 69 70 func TestLua(t *testing.T) { 71 // gopher-lua doesn't implement os.date correctly, so having this unset 72 // results in a crash because luaunt passes the result of os.getenv as the 73 // first argument to `os.date` which doesn't like `args.n == 1` with the arg 74 // value being nil. So, we set it to the real default explicitly. 75 check(os.Setenv("LUAUNIT_DATEFMT", "%c")) 76 defer func() { 77 check(os.Unsetenv("LUAUNIT_DATEFMT")) 78 }() 79 oldLuaPath := os.Getenv("LUA_PATH") 80 if os.Unsetenv("LUA_PATH") == nil { 81 defer func() { 82 check(os.Setenv("LUA_PATH", oldLuaPath)) 83 }() 84 } 85 check(os.Chdir("testdata")) 86 defer func() { 87 check(os.Chdir("..")) 88 }() 89 90 matches, err := filepath.Glob("*_test.lua") 91 if err != nil { 92 t.Fatalf("could not glob: %s", err) 93 } 94 if len(matches) == 0 { 95 t.Fatal("found no lua tests") 96 } 97 98 Convey(`lua`, t, func() { 99 for _, filename := range matches { 100 Convey(filename, func(c C) { 101 L := lua.NewState(lua.Options{}) 102 defer L.Close() 103 104 // install `cjson` global; used for DUMP debugging function. 105 luajson.Loader(L) 106 mod := L.Get(1) 107 L.Pop(1) 108 L.SetGlobal("cjson", mod) 109 110 L.GetGlobal("string").(*lua.LTable).RawSet(lua.LString("format"), L.NewFunction(hackedFormat)) 111 So(L.DoFile(filename), ShouldBeNil) 112 SoMsg("luaunit tests failed", L.CheckInt(1), ShouldEqual, 0) 113 _, err := Println() // space between luaunit outputs 114 So(err, ShouldBeNil) 115 }) 116 } 117 }) 118 }