diff options
Diffstat (limited to 'spec/util')
-rw-r--r-- | spec/util/quick.lua | 384 | ||||
-rw-r--r-- | spec/util/test_env.lua | 16 |
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 @@ | |||
1 | local quick = {} | ||
2 | |||
3 | local dir_sep = package.config:sub(1, 1) | ||
4 | |||
5 | local cfg, dir, fs, versions | ||
6 | local initialized = false | ||
7 | |||
8 | local 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() | ||
20 | end | ||
21 | |||
22 | local function native_slash(pathname) | ||
23 | return (pathname:gsub("[/\\]", dir_sep)) | ||
24 | end | ||
25 | |||
26 | local function parse_cmd(line) | ||
27 | local cmd, arg = line:match("^%s*([A-Z_]+):%s*(.*)%s*$") | ||
28 | return cmd, arg | ||
29 | end | ||
30 | |||
31 | local function is_blank(line) | ||
32 | return not not line:match("^%s*$") | ||
33 | end | ||
34 | |||
35 | local function is_hr(line) | ||
36 | return not not line:match("^%-%-%-%-%-") | ||
37 | end | ||
38 | |||
39 | local 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 | ||
248 | end | ||
249 | |||
250 | function 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 | ||
382 | end | ||
383 | |||
384 | return 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 |
754 | end | 755 | end |
755 | 756 | ||
757 | local 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) | ||
765 | end | ||
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 |
757 | function test_env.setup_specs(extra_rocks) | 768 | function 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) | |||
1104 | test_env.run = make_run_functions() | 1116 | test_env.run = make_run_functions() |
1105 | test_env.exists = exists | 1117 | test_env.exists = exists |
1106 | test_env.V = V | 1118 | test_env.V = V |
1119 | test_env.Q = Q | ||
1120 | test_env.platform = get_luarocks_platform(test_env.env_variables) | ||
1107 | 1121 | ||
1108 | return test_env | 1122 | return test_env |