github.com/qiniu/gopher-lua@v0.2017.11/state_test.go (about)

     1  package lua
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  )
     9  
    10  func TestCallStackOverflow(t *testing.T) {
    11  	L := NewState(Options{
    12  		CallStackSize: 3,
    13  	})
    14  	defer L.Close()
    15  	errorIfScriptNotFail(t, L, `
    16      local function a()
    17      end
    18      local function b()
    19        a()
    20      end
    21      local function c()
    22        print(_printregs())
    23        b()
    24      end
    25      c()
    26      `, "stack overflow")
    27  }
    28  
    29  func TestSkipOpenLibs(t *testing.T) {
    30  	L := NewState(Options{SkipOpenLibs: true})
    31  	defer L.Close()
    32  	errorIfScriptNotFail(t, L, `print("")`,
    33  		"attempt to call a non-function object")
    34  	L2 := NewState()
    35  	defer L2.Close()
    36  	errorIfScriptFail(t, L2, `print("")`)
    37  }
    38  
    39  func TestGetAndReplace(t *testing.T) {
    40  	L := NewState()
    41  	defer L.Close()
    42  	L.Push(LString("a"))
    43  	L.Replace(1, LString("b"))
    44  	L.Replace(0, LString("c"))
    45  	errorIfNotEqual(t, LNil, L.Get(0))
    46  	errorIfNotEqual(t, LNil, L.Get(-10))
    47  	errorIfNotEqual(t, L.Env, L.Get(EnvironIndex))
    48  	errorIfNotEqual(t, LString("b"), L.Get(1))
    49  	L.Push(LString("c"))
    50  	L.Push(LString("d"))
    51  	L.Replace(-2, LString("e"))
    52  	errorIfNotEqual(t, LString("e"), L.Get(-2))
    53  	registry := L.NewTable()
    54  	L.Replace(RegistryIndex, registry)
    55  	L.G.Registry = registry
    56  	errorIfGFuncNotFail(t, L, func(L *LState) int {
    57  		L.Replace(RegistryIndex, LNil)
    58  		return 0
    59  	}, "registry must be a table")
    60  	errorIfGFuncFail(t, L, func(L *LState) int {
    61  		env := L.NewTable()
    62  		L.Replace(EnvironIndex, env)
    63  		errorIfNotEqual(t, env, L.Get(EnvironIndex))
    64  		return 0
    65  	})
    66  	errorIfGFuncNotFail(t, L, func(L *LState) int {
    67  		L.Replace(EnvironIndex, LNil)
    68  		return 0
    69  	}, "environment must be a table")
    70  	errorIfGFuncFail(t, L, func(L *LState) int {
    71  		gbl := L.NewTable()
    72  		L.Replace(GlobalsIndex, gbl)
    73  		errorIfNotEqual(t, gbl, L.G.Global)
    74  		return 0
    75  	})
    76  	errorIfGFuncNotFail(t, L, func(L *LState) int {
    77  		L.Replace(GlobalsIndex, LNil)
    78  		return 0
    79  	}, "_G must be a table")
    80  
    81  	L2 := NewState()
    82  	defer L2.Close()
    83  	clo := L2.NewClosure(func(L2 *LState) int {
    84  		L2.Replace(UpvalueIndex(1), LNumber(3))
    85  		errorIfNotEqual(t, LNumber(3), L2.Get(UpvalueIndex(1)))
    86  		return 0
    87  	}, LNumber(1), LNumber(2))
    88  	L2.SetGlobal("clo", clo)
    89  	errorIfScriptFail(t, L2, `clo()`)
    90  }
    91  
    92  func TestRemove(t *testing.T) {
    93  	L := NewState()
    94  	defer L.Close()
    95  	L.Push(LString("a"))
    96  	L.Push(LString("b"))
    97  	L.Push(LString("c"))
    98  
    99  	L.Remove(4)
   100  	errorIfNotEqual(t, LString("a"), L.Get(1))
   101  	errorIfNotEqual(t, LString("b"), L.Get(2))
   102  	errorIfNotEqual(t, LString("c"), L.Get(3))
   103  	errorIfNotEqual(t, 3, L.GetTop())
   104  
   105  	L.Remove(3)
   106  	errorIfNotEqual(t, LString("a"), L.Get(1))
   107  	errorIfNotEqual(t, LString("b"), L.Get(2))
   108  	errorIfNotEqual(t, LNil, L.Get(3))
   109  	errorIfNotEqual(t, 2, L.GetTop())
   110  	L.Push(LString("c"))
   111  
   112  	L.Remove(-10)
   113  	errorIfNotEqual(t, LString("a"), L.Get(1))
   114  	errorIfNotEqual(t, LString("b"), L.Get(2))
   115  	errorIfNotEqual(t, LString("c"), L.Get(3))
   116  	errorIfNotEqual(t, 3, L.GetTop())
   117  
   118  	L.Remove(2)
   119  	errorIfNotEqual(t, LString("a"), L.Get(1))
   120  	errorIfNotEqual(t, LString("c"), L.Get(2))
   121  	errorIfNotEqual(t, LNil, L.Get(3))
   122  	errorIfNotEqual(t, 2, L.GetTop())
   123  }
   124  
   125  func TestToInt(t *testing.T) {
   126  	L := NewState()
   127  	defer L.Close()
   128  	L.Push(LNumber(10))
   129  	L.Push(LString("99.9"))
   130  	L.Push(L.NewTable())
   131  	errorIfNotEqual(t, 10, L.ToInt(1))
   132  	errorIfNotEqual(t, 99, L.ToInt(2))
   133  	errorIfNotEqual(t, 0, L.ToInt(3))
   134  }
   135  
   136  func TestToInt64(t *testing.T) {
   137  	L := NewState()
   138  	defer L.Close()
   139  	L.Push(LNumber(10))
   140  	L.Push(LString("99.9"))
   141  	L.Push(L.NewTable())
   142  	errorIfNotEqual(t, int64(10), L.ToInt64(1))
   143  	errorIfNotEqual(t, int64(99), L.ToInt64(2))
   144  	errorIfNotEqual(t, int64(0), L.ToInt64(3))
   145  }
   146  
   147  func TestToNumber(t *testing.T) {
   148  	L := NewState()
   149  	defer L.Close()
   150  	L.Push(LNumber(10))
   151  	L.Push(LString("99.9"))
   152  	L.Push(L.NewTable())
   153  	errorIfNotEqual(t, LNumber(10), L.ToNumber(1))
   154  	errorIfNotEqual(t, LNumber(99.9), L.ToNumber(2))
   155  	errorIfNotEqual(t, LNumber(0), L.ToNumber(3))
   156  }
   157  
   158  func TestToString(t *testing.T) {
   159  	L := NewState()
   160  	defer L.Close()
   161  	L.Push(LNumber(10))
   162  	L.Push(LString("99.9"))
   163  	L.Push(L.NewTable())
   164  	errorIfNotEqual(t, "10", L.ToString(1))
   165  	errorIfNotEqual(t, "99.9", L.ToString(2))
   166  	errorIfNotEqual(t, "", L.ToString(3))
   167  }
   168  
   169  func TestToTable(t *testing.T) {
   170  	L := NewState()
   171  	defer L.Close()
   172  	L.Push(LNumber(10))
   173  	L.Push(LString("99.9"))
   174  	L.Push(L.NewTable())
   175  	errorIfFalse(t, L.ToTable(1) == nil, "index 1 must be nil")
   176  	errorIfFalse(t, L.ToTable(2) == nil, "index 2 must be nil")
   177  	errorIfNotEqual(t, L.Get(3), L.ToTable(3))
   178  }
   179  
   180  func TestToFunction(t *testing.T) {
   181  	L := NewState()
   182  	defer L.Close()
   183  	L.Push(LNumber(10))
   184  	L.Push(LString("99.9"))
   185  	L.Push(L.NewFunction(func(L *LState) int { return 0 }))
   186  	errorIfFalse(t, L.ToFunction(1) == nil, "index 1 must be nil")
   187  	errorIfFalse(t, L.ToFunction(2) == nil, "index 2 must be nil")
   188  	errorIfNotEqual(t, L.Get(3), L.ToFunction(3))
   189  }
   190  
   191  func TestToUserData(t *testing.T) {
   192  	L := NewState()
   193  	defer L.Close()
   194  	L.Push(LNumber(10))
   195  	L.Push(LString("99.9"))
   196  	L.Push(L.NewUserData())
   197  	errorIfFalse(t, L.ToUserData(1) == nil, "index 1 must be nil")
   198  	errorIfFalse(t, L.ToUserData(2) == nil, "index 2 must be nil")
   199  	errorIfNotEqual(t, L.Get(3), L.ToUserData(3))
   200  }
   201  
   202  func TestToChannel(t *testing.T) {
   203  	L := NewState()
   204  	defer L.Close()
   205  	L.Push(LNumber(10))
   206  	L.Push(LString("99.9"))
   207  	var ch chan LValue
   208  	L.Push(LChannel(ch))
   209  	errorIfFalse(t, L.ToChannel(1) == nil, "index 1 must be nil")
   210  	errorIfFalse(t, L.ToChannel(2) == nil, "index 2 must be nil")
   211  	errorIfNotEqual(t, ch, L.ToChannel(3))
   212  }
   213  
   214  func TestObjLen(t *testing.T) {
   215  	L := NewState()
   216  	defer L.Close()
   217  	errorIfNotEqual(t, 3, L.ObjLen(LString("abc")))
   218  	tbl := L.NewTable()
   219  	tbl.Append(LTrue)
   220  	tbl.Append(LTrue)
   221  	errorIfNotEqual(t, 2, L.ObjLen(tbl))
   222  	mt := L.NewTable()
   223  	L.SetField(mt, "__len", L.NewFunction(func(L *LState) int {
   224  		tbl := L.CheckTable(1)
   225  		L.Push(LNumber(tbl.Len() + 1))
   226  		return 1
   227  	}))
   228  	L.SetMetatable(tbl, mt)
   229  	errorIfNotEqual(t, 3, L.ObjLen(tbl))
   230  	errorIfNotEqual(t, 0, L.ObjLen(LNumber(10)))
   231  }
   232  
   233  func TestConcat(t *testing.T) {
   234  	L := NewState()
   235  	defer L.Close()
   236  	errorIfNotEqual(t, "a1c", L.Concat(LString("a"), LNumber(1), LString("c")))
   237  }
   238  
   239  func TestPCall(t *testing.T) {
   240  	L := NewState()
   241  	defer L.Close()
   242  	L.Register("f1", func(L *LState) int {
   243  		panic("panic!")
   244  		return 0
   245  	})
   246  	errorIfScriptNotFail(t, L, `f1()`, "panic!")
   247  	L.Push(L.GetGlobal("f1"))
   248  	err := L.PCall(0, 0, L.NewFunction(func(L *LState) int {
   249  		L.Push(LString("by handler"))
   250  		return 1
   251  	}))
   252  	errorIfFalse(t, strings.Contains(err.Error(), "by handler"), "")
   253  
   254  	err = L.PCall(0, 0, L.NewFunction(func(L *LState) int {
   255  		L.RaiseError("error!")
   256  		return 1
   257  	}))
   258  	errorIfFalse(t, strings.Contains(err.Error(), "error!"), "")
   259  
   260  	err = L.PCall(0, 0, L.NewFunction(func(L *LState) int {
   261  		panic("panicc!")
   262  		return 1
   263  	}))
   264  	errorIfFalse(t, strings.Contains(err.Error(), "panicc!"), "")
   265  }
   266  
   267  func TestCoroutineApi1(t *testing.T) {
   268  	L := NewState()
   269  	defer L.Close()
   270  	co, _ := L.NewThread()
   271  	errorIfScriptFail(t, L, `
   272        function coro(v)
   273          assert(v == 10)
   274          local ret1, ret2 = coroutine.yield(1,2,3)
   275          assert(ret1 == 11)
   276          assert(ret2 == 12)
   277          coroutine.yield(4)
   278          return 5
   279        end
   280      `)
   281  	fn := L.GetGlobal("coro").(*LFunction)
   282  	st, err, values := L.Resume(co, fn, LNumber(10))
   283  	errorIfNotEqual(t, ResumeYield, st)
   284  	errorIfNotNil(t, err)
   285  	errorIfNotEqual(t, 3, len(values))
   286  	errorIfNotEqual(t, LNumber(1), values[0].(LNumber))
   287  	errorIfNotEqual(t, LNumber(2), values[1].(LNumber))
   288  	errorIfNotEqual(t, LNumber(3), values[2].(LNumber))
   289  
   290  	st, err, values = L.Resume(co, fn, LNumber(11), LNumber(12))
   291  	errorIfNotEqual(t, ResumeYield, st)
   292  	errorIfNotNil(t, err)
   293  	errorIfNotEqual(t, 1, len(values))
   294  	errorIfNotEqual(t, LNumber(4), values[0].(LNumber))
   295  
   296  	st, err, values = L.Resume(co, fn)
   297  	errorIfNotEqual(t, ResumeOK, st)
   298  	errorIfNotNil(t, err)
   299  	errorIfNotEqual(t, 1, len(values))
   300  	errorIfNotEqual(t, LNumber(5), values[0].(LNumber))
   301  
   302  	L.Register("myyield", func(L *LState) int {
   303  		return L.Yield(L.ToNumber(1))
   304  	})
   305  	errorIfScriptFail(t, L, `
   306        function coro_error()
   307          coroutine.yield(1,2,3)
   308          myyield(4)
   309          assert(false, "--failed--")
   310        end
   311      `)
   312  	fn = L.GetGlobal("coro_error").(*LFunction)
   313  	co, _ = L.NewThread()
   314  	st, err, values = L.Resume(co, fn)
   315  	errorIfNotEqual(t, ResumeYield, st)
   316  	errorIfNotNil(t, err)
   317  	errorIfNotEqual(t, 3, len(values))
   318  	errorIfNotEqual(t, LNumber(1), values[0].(LNumber))
   319  	errorIfNotEqual(t, LNumber(2), values[1].(LNumber))
   320  	errorIfNotEqual(t, LNumber(3), values[2].(LNumber))
   321  
   322  	st, err, values = L.Resume(co, fn)
   323  	errorIfNotEqual(t, ResumeYield, st)
   324  	errorIfNotNil(t, err)
   325  	errorIfNotEqual(t, 1, len(values))
   326  	errorIfNotEqual(t, LNumber(4), values[0].(LNumber))
   327  
   328  	st, err, values = L.Resume(co, fn)
   329  	errorIfNotEqual(t, ResumeError, st)
   330  	errorIfNil(t, err)
   331  	errorIfFalse(t, strings.Contains(err.Error(), "--failed--"), "error message must be '--failed--'")
   332  	st, err, values = L.Resume(co, fn)
   333  	errorIfNotEqual(t, ResumeError, st)
   334  	errorIfNil(t, err)
   335  	errorIfFalse(t, strings.Contains(err.Error(), "can not resume a dead thread"), "can not resume a dead thread")
   336  
   337  }
   338  
   339  func TestContextTimeout(t *testing.T) {
   340  	L := NewState()
   341  	defer L.Close()
   342  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   343  	defer cancel()
   344  	L.SetContext(ctx)
   345  	errorIfNotEqual(t, ctx, L.Context())
   346  	err := L.DoString(`
   347  	  local clock = os.clock
   348        function sleep(n)  -- seconds
   349          local t0 = clock()
   350          while clock() - t0 <= n do end
   351        end
   352  	  sleep(3)
   353  	`)
   354  	errorIfNil(t, err)
   355  	errorIfFalse(t, strings.Contains(err.Error(), "context deadline exceeded"), "execution must be canceled")
   356  
   357  	oldctx := L.RemoveContext()
   358  	errorIfNotEqual(t, ctx, oldctx)
   359  	errorIfNotNil(t, L.ctx)
   360  }
   361  
   362  func TestContextCancel(t *testing.T) {
   363  	L := NewState()
   364  	defer L.Close()
   365  	ctx, cancel := context.WithCancel(context.Background())
   366  	errch := make(chan error, 1)
   367  	L.SetContext(ctx)
   368  	go func() {
   369  		errch <- L.DoString(`
   370  	    local clock = os.clock
   371          function sleep(n)  -- seconds
   372            local t0 = clock()
   373            while clock() - t0 <= n do end
   374          end
   375  	    sleep(3)
   376  	  `)
   377  	}()
   378  	time.Sleep(1 * time.Second)
   379  	cancel()
   380  	err := <-errch
   381  	errorIfNil(t, err)
   382  	errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "execution must be canceled")
   383  }
   384  
   385  func TestContextWithCroutine(t *testing.T) {
   386  	L := NewState()
   387  	defer L.Close()
   388  	ctx, cancel := context.WithCancel(context.Background())
   389  	L.SetContext(ctx)
   390  	defer cancel()
   391  	L.DoString(`
   392  	    function coro()
   393  		  local i = 0
   394  		  while true do
   395  		    coroutine.yield(i)
   396  			i = i+1
   397  		  end
   398  		  return i
   399  	    end
   400  	`)
   401  	co, cocancel := L.NewThread()
   402  	defer cocancel()
   403  	fn := L.GetGlobal("coro").(*LFunction)
   404  	_, err, values := L.Resume(co, fn)
   405  	errorIfNotNil(t, err)
   406  	errorIfNotEqual(t, LNumber(0), values[0])
   407  	// cancel the parent context
   408  	cancel()
   409  	_, err, values = L.Resume(co, fn)
   410  	errorIfNil(t, err)
   411  	errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "coroutine execution must be canceled when the parent context is canceled")
   412  
   413  }
   414  
   415  func TestPCallAfterFail(t *testing.T) {
   416  	L := NewState()
   417  	defer L.Close()
   418  	errFn := L.NewFunction(func(L *LState) int {
   419  		L.RaiseError("error!")
   420  		return 0
   421  	})
   422  	changeError := L.NewFunction(func(L *LState) int {
   423  		L.Push(errFn)
   424  		err := L.PCall(0, 0, nil)
   425  		if err != nil {
   426  			L.RaiseError("A New Error")
   427  		}
   428  		return 0
   429  	})
   430  	L.Push(changeError)
   431  	err := L.PCall(0, 0, nil)
   432  	errorIfFalse(t, strings.Contains(err.Error(), "A New Error"), "error not propogated correctly")
   433  }