aboutsummaryrefslogtreecommitdiff
path: root/spec/util
diff options
context:
space:
mode:
Diffstat (limited to 'spec/util')
-rw-r--r--spec/util/quick.lua384
-rw-r--r--spec/util/test_env.lua16
2 files changed, 399 insertions, 1 deletions
diff --git a/spec/util/quick.lua b/spec/util/quick.lua
new file mode 100644
index 00000000..c8bfb61a
--- /dev/null
+++ b/spec/util/quick.lua
@@ -0,0 +1,384 @@
1local quick = {}
2
3local dir_sep = package.config:sub(1, 1)
4
5local cfg, dir, fs, versions
6local initialized = false
7
8local function initialize()
9 if initialized then
10 return
11 end
12 initialized = true
13
14 cfg = require("luarocks.core.cfg")
15 dir = require("luarocks.dir")
16 fs = require("luarocks.fs")
17 versions = require("spec.util.versions")
18 cfg.init()
19 fs.init()
20end
21
22local function native_slash(pathname)
23 return (pathname:gsub("[/\\]", dir_sep))
24end
25
26local function parse_cmd(line)
27 local cmd, arg = line:match("^%s*([A-Z_]+):%s*(.*)%s*$")
28 return cmd, arg
29end
30
31local function is_blank(line)
32 return not not line:match("^%s*$")
33end
34
35local function is_hr(line)
36 return not not line:match("^%-%-%-%-%-")
37end
38
39local function parse(filename)
40 local fd = assert(io.open(filename, "r"))
41 local input = assert(fd:read("*a"))
42 fd:close()
43
44 initialize()
45
46 local tests = {}
47
48 local cur_line = 0
49 local cur_test
50 local cur_op
51 local cur_block
52 local cur_block_name
53 local stack = { "start" }
54
55 local function start_test(arg)
56 cur_test = {
57 name = arg,
58 ops = {},
59 }
60 cur_op = nil
61 table.insert(tests, cur_test)
62 table.insert(stack, "test")
63 end
64
65 local function fail(msg)
66 io.stderr:write("Error reading " .. filename .. ":" .. cur_line .. ": " .. msg .. "\n")
67 os.exit(1)
68 end
69
70 local function bool_arg(cmd, cur_block, field, arg)
71 if arg ~= "true" and arg ~= "false" then
72 fail(cmd .. " argument must be 'true' or 'false'")
73 end
74 cur_block[field] = (arg == "true")
75 end
76
77 local test_env = require("spec.util.test_env")
78 local function expand_vars(line)
79 if not line then
80 return nil
81 end
82 return (line:gsub("%%%b{}", function(var)
83 var = var:sub(3, -2)
84 local fn, fnarg = var:match("^%s*([a-z_]+)%s*%(%s*([^)]+)%s*%)%s*$")
85
86 local value
87 if var == "tmpdir" then
88 value = "%{tmpdir}"
89 elseif var == "url(tmpdir)" then
90 value = "%{url(tmpdir)}"
91 elseif fn == "url" then
92 value = expand_vars(fnarg)
93 value = value:gsub("\\", "/")
94 elseif fn == "path" then
95 value = expand_vars(fnarg)
96 value = value:gsub("[/\\]", dir_sep)
97 elseif fn == "version" then
98 value = versions[fnarg:lower()] or ""
99 elseif fn == "version_" then
100 value = (versions[fnarg:lower()] or ""):gsub("[%.%-]", "_")
101 else
102 value = test_env.testing_paths[var]
103 or test_env.env_variables[var]
104 or test_env[var]
105 or ""
106 end
107
108 return value
109 end))
110 end
111
112 for line in input:gmatch("[^\n]*") do
113 cur_line = cur_line + 1
114
115 local state = stack[#stack]
116 if state == "start" then
117 local cmd, arg = parse_cmd(line)
118 if cmd == "TEST" then
119 start_test(arg)
120 elseif cmd then
121 fail("expected TEST, got " .. cmd)
122 elseif is_blank(line) then
123 -- skip blank lines and arbitrary text,
124 -- which is interpreted as a comment
125 end
126 elseif state == "test" then
127 local cmd, arg = parse_cmd(line)
128 arg = expand_vars(arg)
129 if cmd == "FILE" then
130 cur_op = {
131 op = "FILE",
132 name = arg,
133 data = {},
134 }
135 table.insert(cur_test.ops, cur_op)
136 cur_block = cur_op
137 cur_block_name = "FILE"
138 table.insert(stack, "block start")
139 elseif cmd == "RUN" then
140 local program, args = arg:match("([^ ]+)%s*(.*)$")
141 if not program then
142 fail("expected a program argument in RUN")
143 end
144
145 cur_op = {
146 op = "RUN",
147 exit = 0,
148 exit_line = cur_line,
149 line = cur_line,
150 program = program,
151 args = args,
152 }
153 table.insert(cur_test.ops, cur_op)
154 elseif cmd == "EXISTS" then
155 cur_op = {
156 op = "EXISTS",
157 file = dir.normalize(arg),
158 }
159 table.insert(cur_test.ops, cur_op)
160 elseif cmd == "NOT_EXISTS" then
161 cur_op = {
162 op = "NOT_EXISTS",
163 file = dir.normalize(arg),
164 }
165 table.insert(cur_test.ops, cur_op)
166 elseif cmd == "MKDIR" then
167 cur_op = {
168 op = "MKDIR",
169 file = dir.normalize(arg),
170 line = cur_line,
171 }
172 table.insert(cur_test.ops, cur_op)
173 elseif cmd == "EXIT" then
174 if not cur_op or cur_op.op ~= "RUN" then
175 fail("EXIT must be given in the context of a RUN")
176 end
177
178 local code = tonumber(arg)
179 if not code and not (code >= 0 and code <= 128) then
180 fail("EXIT code must be a number in the range 0-128, got " .. arg)
181 end
182
183 cur_op.exit = code
184 cur_op.exit_line = cur_line
185 elseif cmd == "STDERR" then
186 if not cur_op or cur_op.op ~= "RUN" then
187 fail("STDERR must be given in the context of a RUN")
188 end
189 if cur_op.stderr then
190 fail("STDERR was already declared")
191 end
192
193 cur_op.stderr = {
194 data = {}
195 }
196 cur_block = cur_op.stderr
197 cur_block_name = "STDERR"
198 table.insert(stack, "block start")
199 elseif cmd == "STDOUT" then
200 if not cur_op or cur_op.op ~= "RUN" then
201 fail("STDOUT must be given in the context of a RUN")
202 end
203 if cur_op.stdout then
204 fail("STDOUT was already declared")
205 end
206
207 cur_op.stdout = {
208 data = {}
209 }
210 cur_block = cur_op.stdout
211 cur_block_name = "STDOUT"
212 table.insert(stack, "block start")
213 elseif cmd == "TEST" then
214 table.remove(stack)
215 start_test(arg)
216 elseif cmd then
217 fail("expected a command, got " .. cmd)
218 else
219 -- skip blank lines and arbitrary text,
220 -- which is interpreted as a comment
221 end
222 elseif state == "block start" then
223 local cmd, arg = parse_cmd(line)
224 if is_blank(line) then
225 -- skip
226 elseif is_hr(line) then
227 stack[#stack] = "block data"
228 cur_block.start = cur_line
229 elseif cmd == "PLAIN" then
230 bool_arg("PLAIN", cur_block, "plain", arg)
231 else
232 fail("expected '-----' to start " .. cur_block_name .. " block")
233 end
234 elseif state == "block data" then
235 if is_hr(line) then
236 cur_block = nil
237 table.remove(stack)
238 else
239 if not cur_block.plain then
240 line = expand_vars(line)
241 end
242 table.insert(cur_block.data, line)
243 end
244 end
245 end
246
247 return tests
248end
249
250function quick.compile(filename, env)
251 local tests = parse(filename)
252
253-- local dev_null = (package.config:sub(1, 1) == "/")
254-- and "/dev/null"
255-- or "NUL"
256
257 local cmd_helpers = {
258 ["luarocks"] = "luarocks_cmd",
259 ["luarocks-admin"] = "luarocks_admin_cmd",
260 }
261
262 for tn, t in ipairs(tests) do
263 local code = {}
264 local function write(...)
265 table.insert(code, table.concat({...}))
266 end
267
268 write([=[ local test_env = require("spec.util.test_env") ]=])
269 write([=[ local lfs = require("lfs") ]=])
270 write([=[ local fs = require("lfs") ]=])
271 write([=[ local dir_sep = package.config:sub(1, 1) ]=])
272 write([=[ local luarocks_cmd = test_env.execute_helper(test_env.Q(test_env.testing_paths.lua) .. " " .. test_env.testing_paths.src_dir .. "/bin/luarocks", false, test_env.env_variables):sub(1, -5) ]=])
273 write([=[ local luarocks_admin_cmd = test_env.execute_helper(test_env.Q(test_env.testing_paths.lua) .. " " .. test_env.testing_paths.src_dir .. "/bin/luarocks-admin", false, test_env.env_variables):sub(1, -5) ]=])
274
275 write(([=[ local function error_message(line, msg, input) ]=]))
276 write(([=[ local out = {"\n\n", %q, ":", line, ": ", msg} ]=]):format(filename))
277 write(([=[ if input then ]=]))
278 write(([=[ if input:match("\n") then ]=]))
279 write(([=[ table.insert(out, "\n") ]=]))
280 write(([=[ table.insert(out, ("-"):rep(40)) ]=]))
281 write(([=[ table.insert(out, "\n") ]=]))
282 write(([=[ table.insert(out, input) ]=]))
283 write(([=[ table.insert(out, ("-"):rep(40)) ]=]))
284 write(([=[ table.insert(out, "\n") ]=]))
285 write(([=[ else ]=]))
286 write(([=[ table.insert(out, ": ") ]=]))
287 write(([=[ table.insert(out, input) ]=]))
288 write(([=[ end ]=]))
289 write(([=[ end ]=]))
290 write(([=[ return table.concat(out) ]=]))
291 write(([=[ end ]=]))
292
293 write([=[ return function() ]=])
294 write([=[ test_env.run_in_tmp(function(tmpdir) ]=])
295 write([=[ local function handle_tmpdir(s) ]=])
296 write([=[ return (s:gsub("%%{url%(tmpdir%)}", (tmpdir:gsub("\\", "/"))) ]=])
297 write([=[ :gsub("%%{tmpdir}", (tmpdir:gsub("[\\/]", dir_sep)))) ]=])
298 write([=[ end ]=])
299 for _, op in ipairs(t.ops) do
300 if op.op == "FILE" then
301 write([=[ test_env.write_file(handle_tmpdir("]=], op.name, [=["), handle_tmpdir([=====[ ]=])
302 for _, line in ipairs(op.data) do
303 write(line)
304 end
305 write([=[ ]=====]), finally) ]=])
306 elseif op.op == "EXISTS" then
307 write(([=[ assert.truthy(lfs.attributes(%q)) ]=]):format(op.file))
308 elseif op.op == "NOT_EXISTS" then
309 write(([=[ assert.falsy(lfs.attributes(%q)) ]=]):format(op.file))
310 elseif op.op == "MKDIR" then
311 local bits = {}
312 op.file = native_slash(op.file)
313 if op.file:sub(1, 1) == dir_sep then bits[1] = "" end
314 write([=[ local ok, err ]=])
315 for p in op.file:gmatch("[^" .. dir_sep .. "]+") do
316 table.insert(bits, p)
317 local d = table.concat(bits, dir_sep)
318 write(([=[ ok, err = lfs.mkdir(%q) ]=]):format(d, d))
319 end
320 write(([=[ assert.truthy((lfs.attributes(%q) or {}).mode == "directory", error_message(%d, "MKDIR failed: " .. %q .. " - " .. (err or "") )) ]=]):format(op.file, op.line, op.file))
321 elseif op.op == "RUN" then
322 local cmd_helper = cmd_helpers[op.program] or op.program
323 local redirs = " 1>stdout.txt 2>stderr.txt "
324 write(([=[ local ok, _, code = os.execute(%s .. " " .. %q .. %q) ]=]):format(cmd_helper, op.args, redirs))
325 write([=[ if type(ok) == "number" then code = (ok >= 256 and ok / 256 or ok) end ]=])
326
327 write([=[ local fd_stderr = assert(io.open("stderr.txt", "r")) ]=])
328 write([=[ local stderr_data = fd_stderr:read("*a") ]=])
329 write([=[ fd_stderr:close() ]=])
330
331 write([=[ if stderr_data:match("please report") then ]=])
332 write(([=[ assert(false, error_message(%d, "RUN crashed: ", stderr_data)) ]=]):format(op.line))
333 write([=[ end ]=])
334
335 if op.stdout then
336 write([=[ local fd_stdout = assert(io.open("stdout.txt", "r")) ]=])
337 write([=[ local stdout_data = fd_stdout:read("*a") ]=])
338 write([=[ fd_stdout:close() ]=])
339
340 write([=[ do ]=])
341 write([=[ local block_at = 1 ]=])
342 write([=[ local s, e, line ]=])
343 for i, line in ipairs(op.stdout.data) do
344 write(([=[ line = %q ]=]):format(line))
345 write(([=[ s, e = string.find(stdout_data, line, block_at, true) ]=]))
346 write(([=[ assert(s, error_message(%d, "STDOUT did not match: " .. line, stdout_data)) ]=]):format(op.stdout.start + i))
347 write(([=[ block_at = e + 1 ]=]):format(i))
348 end
349 write([=[ end ]=])
350 end
351
352 if op.stderr then
353 write([=[ do ]=])
354 write([=[ local block_at = 1 ]=])
355 write([=[ local s, e, line ]=])
356 for i, line in ipairs(op.stderr.data) do
357 write(([=[ line = %q ]=]):format(line))
358 write(([=[ s, e = string.find(stderr_data, line, block_at, true) ]=]))
359 write(([=[ assert(s, error_message(%d, "STDERR did not match: " .. line, stderr_data)) ]=]):format(op.stderr.start + i))
360 write(([=[ block_at = e + 1 ]=]):format(i))
361 end
362 write([=[ end ]=])
363 end
364
365 if op.exit then
366 write(([=[ assert.same(%d, code, error_message(%d, "EXIT did not match: " .. %d, stderr_data)) ]=]):format(op.exit, op.exit_line, op.exit))
367 end
368 end
369 end
370 write([=[ end) ]=])
371 write([=[ end ]=])
372
373 local program = table.concat(code, "\n")
374 local chunk = assert(load(program, "@" .. filename .. ": test " .. tn, "t", env or _ENV))
375 if env and setfenv then
376 setfenv(chunk, env)
377 end
378 t.fn = chunk()
379 end
380
381 return tests
382end
383
384return quick
diff --git a/spec/util/test_env.lua b/spec/util/test_env.lua
index f9f83b8c..dcda6311 100644
--- a/spec/util/test_env.lua
+++ b/spec/util/test_env.lua
@@ -715,6 +715,7 @@ local function create_paths(luaversion_full)
715 testing_paths.util_dir = base_dir .. "/spec/util" 715 testing_paths.util_dir = base_dir .. "/spec/util"
716 testing_paths.testrun_dir = base_dir .. "/testrun" 716 testing_paths.testrun_dir = base_dir .. "/testrun"
717 testing_paths.src_dir = base_dir .. "/src" 717 testing_paths.src_dir = base_dir .. "/src"
718 testing_paths.spec_dir = base_dir .. "/spec"
718 testing_paths.testing_lrprefix = testing_paths.testrun_dir .. "/testing_lrprefix-" .. luaversion_full 719 testing_paths.testing_lrprefix = testing_paths.testrun_dir .. "/testing_lrprefix-" .. luaversion_full
719 testing_paths.testing_tree = testing_paths.testrun_dir .. "/testing-" .. luaversion_full 720 testing_paths.testing_tree = testing_paths.testrun_dir .. "/testing-" .. luaversion_full
720 testing_paths.testing_tree_copy = testing_paths.testrun_dir .. "/testing_copy-" .. luaversion_full 721 testing_paths.testing_tree_copy = testing_paths.testrun_dir .. "/testing_copy-" .. luaversion_full
@@ -753,6 +754,16 @@ function test_env.unload_luarocks()
753 end 754 end
754end 755end
755 756
757local function get_luarocks_platform(variables)
758 local print_arch_script = "\"" ..
759 "cfg = require('luarocks.core.cfg');" ..
760 "cfg.init();" ..
761 "print(cfg.arch)" ..
762 "\""
763 local cmd = test_env.testing_paths.lua .. " -e " .. print_arch_script
764 return execute_output(cmd, false, variables)
765end
766
756--- Function for initial setup of environment, variables, md5sums for spec files 767--- Function for initial setup of environment, variables, md5sums for spec files
757function test_env.setup_specs(extra_rocks) 768function test_env.setup_specs(extra_rocks)
758 -- if global variable about successful creation of testing environment doesn't exist, build environment 769 -- if global variable about successful creation of testing environment doesn't exist, build environment
@@ -770,11 +781,12 @@ function test_env.setup_specs(extra_rocks)
770 781
771 -- preload before meddling with package.path 782 -- preload before meddling with package.path
772 require("spec.util.git_repo") 783 require("spec.util.git_repo")
784 require("spec.util.quick")
773 785
774 package.path = test_env.env_variables.LUA_PATH 786 package.path = test_env.env_variables.LUA_PATH
775 package.cpath = test_env.env_variables.LUA_CPATH 787 package.cpath = test_env.env_variables.LUA_CPATH
776 788
777 test_env.platform = execute_output(test_env.testing_paths.lua .. " -e \"cfg = require('luarocks.core.cfg'); cfg.init(); print(cfg.arch)\"", false, test_env.env_variables) 789 test_env.platform = get_luarocks_platform(test_env.env_variables)
778 test_env.wrapper_extension = test_env.TEST_TARGET_OS == "windows" and ".bat" or "" 790 test_env.wrapper_extension = test_env.TEST_TARGET_OS == "windows" and ".bat" or ""
779 test_env.md5sums = create_md5sums(test_env.testing_paths) 791 test_env.md5sums = create_md5sums(test_env.testing_paths)
780 test_env.setup_done = true 792 test_env.setup_done = true
@@ -1104,5 +1116,7 @@ test_env.env_variables = create_env(test_env.testing_paths)
1104test_env.run = make_run_functions() 1116test_env.run = make_run_functions()
1105test_env.exists = exists 1117test_env.exists = exists
1106test_env.V = V 1118test_env.V = V
1119test_env.Q = Q
1120test_env.platform = get_luarocks_platform(test_env.env_variables)
1107 1121
1108return test_env 1122return test_env