aboutsummaryrefslogtreecommitdiff
path: root/testes/memerr.lua
blob: 77cb47cb1ee312c45e778a3acbcf5dbbb6826b58 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
-- $Id: testes/memerr.lua $
-- See Copyright Notice in file lua.h


local function checkerr (msg, f, ...)
  local stat, err = pcall(f, ...)
  assert(not stat and string.find(err, msg))
end

if T==nil then
  (Message or print)
      ('\n >>> testC not active: skipping memory error tests <<<\n')
  return
end

print("testing memory-allocation errors")

local debug = require "debug"

local pack = table.pack

-- standard error message for memory errors
local MEMERRMSG = "not enough memory"


-- memory error in panic function
T.totalmem(T.totalmem()+10000)   -- set low memory limit (+10k)
assert(T.checkpanic("newuserdata 20000") == MEMERRMSG)
T.totalmem(0)          -- restore high limit



-- {==================================================================
-- Testing memory limits
-- ===================================================================

checkerr("block too big", T.newuserdata, math.maxinteger)
collectgarbage()
local f = load"local a={}; for i=1,100000 do a[i]=i end"
T.alloccount(10)
checkerr(MEMERRMSG, f)
T.alloccount()          -- remove limit


-- test memory errors; increase limit for maximum memory by steps,
-- o that we get memory errors in all allocations of a given
-- task, until there is enough memory to complete the task without
-- errors.
local function testbytes (s, f)
  collectgarbage()
  local M = T.totalmem()
  local oldM = M
  local a,b = nil
  while true do
    collectgarbage(); collectgarbage()
    T.totalmem(M)
    a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
    T.totalmem(0)  -- remove limit
    if a and b == "OK" then break end       -- stop when no more errors
    if b ~= "OK" and b ~= MEMERRMSG then    -- not a memory error?
      error(a, 0)   -- propagate it
    end
    M = M + 7   -- increase memory limit
  end
  print(string.format("minimum memory for %s: %d bytes", s, M - oldM))
  return a
end

-- test memory errors; increase limit for number of allocations one
-- by one, so that we get memory errors in all allocations of a given
-- task, until there is enough allocations to complete the task without
-- errors.

local function testalloc (s, f)
  collectgarbage()
  local M = 0
  local a,b = nil
  while true do
    collectgarbage(); collectgarbage()
    T.alloccount(M)
    a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
    T.alloccount()  -- remove limit
    if a and b == "OK" then break end       -- stop when no more errors
    if b ~= "OK" and b ~= MEMERRMSG then    -- not a memory error?
      error(a, 0)   -- propagate it
    end
    M = M + 1   -- increase allocation limit
  end
  print(string.format("minimum allocations for %s: %d allocations", s, M))
  return a
end


local function testamem (s, f)
  testalloc(s, f)
  return testbytes(s, f)
end


-- doing nothing
b = testamem("doing nothing", function () return 10 end)
assert(b == 10)

-- testing memory errors when creating a new state

testamem("state creation", function ()
  local st = T.newstate()
  if st then T.closestate(st) end   -- close new state
  return st
end)

testamem("empty-table creation", function ()
  return {}
end)

testamem("string creation", function ()
  return "XXX" .. "YYY"
end)

testamem("coroutine creation", function()
           return coroutine.create(print)
end)


-- testing to-be-closed variables
testamem("to-be-closed variables", function()
  local flag
  do
    local x <close> =
              setmetatable({}, {__close = function () flag = true end})
    flag = false
    local x = {}
  end
  return flag
end)


-- testing threads

-- get main thread from registry
local mt = T.testC("rawgeti R !M; return 1")
assert(type(mt) == "thread" and coroutine.running() == mt)



local function expand (n,s)
  if n==0 then return "" end
  local e = string.rep("=", n)
  return string.format("T.doonnewstack([%s[ %s;\n collectgarbage(); %s]%s])\n",
                              e, s, expand(n-1,s), e)
end

G=0; collectgarbage(); a =collectgarbage("count")
load(expand(20,"G=G+1"))()
assert(G==20); collectgarbage();  -- assert(gcinfo() <= a+1)
G = nil

testamem("running code on new thread", function ()
  return T.doonnewstack("local x=1") == 0  -- try to create thread
end)


-- testing memory x compiler

testamem("loadstring", function ()
  return load("x=1")  -- try to do load a string
end)


local testprog = [[
local function foo () return end
local t = {"x"}
AA = "aaa"
for i = 1, #t do AA = AA .. t[i] end
return true
]]

-- testing memory x dofile
_G.AA = nil
local t =os.tmpname()
local f = assert(io.open(t, "w"))
f:write(testprog)
f:close()
testamem("dofile", function ()
  local a = loadfile(t)
  return a and a()
end)
assert(os.remove(t))
assert(_G.AA == "aaax")


-- other generic tests

testamem("gsub", function ()
  local a, b = string.gsub("alo alo", "(a)", function (x) return x..'b' end)
  return (a == 'ablo ablo')
end)

testamem("dump/undump", function ()
  local a = load(testprog)
  local b = a and string.dump(a)
  a = b and load(b)
  return a and a()
end)

_G.AA = nil

local t = os.tmpname()
testamem("file creation", function ()
  local f = assert(io.open(t, 'w'))
  assert (not io.open"nomenaoexistente")
  io.close(f);
  return not loadfile'nomenaoexistente'
end)
assert(os.remove(t))

testamem("table creation", function ()
  local a, lim = {}, 10
  for i=1,lim do a[i] = i; a[i..'a'] = {} end
  return (type(a[lim..'a']) == 'table' and a[lim] == lim)
end)

testamem("constructors", function ()
  local a = {10, 20, 30, 40, 50; a=1, b=2, c=3, d=4, e=5}
  return (type(a) == 'table' and a.e == 5)
end)

local a = 1
local close = nil
testamem("closure creation", function ()
  function close (b)
   return function (x) return b + x end
  end
  return (close(2)(4) == 6)
end)

testamem("using coroutines", function ()
  local a = coroutine.wrap(function ()
              coroutine.yield(string.rep("a", 10))
              return {}
            end)
  assert(string.len(a()) == 10)
  return a()
end)

do   -- auxiliary buffer
  local lim = 100
  local a = {}; for i = 1, lim do a[i] = "01234567890123456789" end
  testamem("auxiliary buffer", function ()
    return (#table.concat(a, ",") == 20*lim + lim - 1)
  end)
end

testamem("growing stack", function ()
  local function foo (n)
    if n == 0 then return 1 else return 1 + foo(n - 1) end
  end
  return foo(100)
end)

-- }==================================================================


print "Ok"