Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/lyaml/spec/spec_helper.lua
178586 views
1
--[[
2
LYAML binding for Lua 5.1, 5.2, 5.3 & 5.4
3
Copyright (C) 2013-2022 Gary V. Vaughan
4
]]
5
6
do
7
local std = require 'specl.std'
8
local spawn = require 'specl.shell'.spawn
9
local objdir = spawn('./build-aux/luke --value=objdir').output
10
11
12
package.path = std.package.normalize(
13
'./lib/?.lua',
14
'./lib/?/init.lua',
15
package.path
16
)
17
package.cpath = std.package.normalize(
18
'./' .. objdir:match("^objdir='(.*)'") .. '/?.so',
19
'./' .. objdir:match("^objdir='(.*)'") .. '/?.dll',
20
package.cpath
21
)
22
end
23
24
local hell = require 'specl.shell'
25
26
27
yaml = require 'yaml'
28
29
BOM = string.char(254, 255) -- UTF-16 Byte Order Mark
30
31
-- Allow use of bare 'pack' and 'unpack' even in Lua > 5.2.
32
pack = table.pack or function(...) return {n = select('#', ...), ...} end
33
unpack = table.unpack or unpack
34
list = pack
35
36
37
function dump(e)
38
print(std.string.prettytostring(e))
39
end
40
41
42
function github_issue(n)
43
return 'see http://github.com/gvvaughan/lyaml/issues/' .. tostring(n)
44
end
45
46
47
-- Output a list of event tables to the given emitter.
48
function emitevents(emitter, list)
49
for _, v in ipairs(list) do
50
if type(v) == 'string' then
51
ok, msg = emitter.emit {type=v}
52
elseif type(v) == 'table' then
53
ok, msg = emitter.emit(v)
54
else
55
error 'expected table or string argument'
56
end
57
58
if not ok then
59
error(msg)
60
elseif ok and msg then
61
return msg
62
end
63
end
64
end
65
66
67
-- Create a new emitter and send STREAM_START, listed events and STREAM_END.
68
function emit(list)
69
local emitter = yaml.emitter()
70
emitter.emit {type='STREAM_START'}
71
emitevents(emitter, list)
72
local _, msg = emitter.emit {type='STREAM_END'}
73
return msg
74
end
75
76
77
-- Create a new parser for STR, and consume the first N events.
78
function consume(n, str)
79
local e = yaml.parser(str)
80
for n = 1, n do
81
e()
82
end
83
return e
84
end
85
86
87
-- Return a new table with only elements of T that have keys listed
88
-- in the following arguments.
89
function filter(t, ...)
90
local u = {}
91
for _, k in ipairs {...} do
92
u[k] = t[k]
93
end
94
return u
95
end
96
97
98
function iscallable(x)
99
return type(x) == 'function' or type((getmetatable(x) or {}).__call) == 'function'
100
end
101
102
103
local function mkscript(code)
104
local f = os.tmpname()
105
local h = io.open(f, 'w')
106
-- TODO: Move this into specl, or expose arguments so that we can
107
-- turn this on and off based on specl `--coverage` arg.
108
h:write "pcall(require, 'luacov')"
109
h:write(code)
110
h:close()
111
return f
112
end
113
114
115
-- Allow user override of LUA binary used by hell.spawn, falling
116
-- back to environment PATH search for 'lua' if nothing else works.
117
local LUA = os.getenv 'LUA' or 'lua'
118
119
120
--- Run some Lua code with the given arguments and input.
121
-- @string code valid Lua code
122
-- @tparam[opt={}] string|table arg single argument, or table of
123
-- arguments for the script invocation.
124
-- @string[opt] stdin standard input contents for the script process
125
-- @treturn specl.shell.Process|nil status of resulting process if
126
-- execution was successful, otherwise nil
127
function luaproc(code, arg, stdin)
128
local f = mkscript(code)
129
if type(arg) ~= 'table' then arg = {arg} end
130
local cmd = {LUA, f, unpack(arg)}
131
-- inject env and stdin keys separately to avoid truncating `...` in
132
-- cmd constructor
133
cmd.env = { LUA_PATH=package.path, LUA_INIT='', LUA_INIT_5_2='' }
134
cmd.stdin = stdin
135
local proc = hell.spawn(cmd)
136
os.remove(f)
137
return proc
138
end
139
140
141
local function tabulate_output(code)
142
local proc = luaproc(code)
143
if proc.status ~= 0 then return error(proc.errout) end
144
local r = {}
145
proc.output:gsub('(%S*)[%s]*',
146
function(x)
147
if x ~= '' then r[x] = true end
148
end)
149
return r
150
end
151
152
153
--- Show changes to tables wrought by a require statement.
154
-- There are a few modes to this function, controlled by what named
155
-- arguments are given. Lists new keys in T1 after `require "import"`:
156
--
157
-- show_apis {added_to=T1, by=import}
158
--
159
-- @tparam table argt one of the combinations above
160
-- @treturn table a list of keys according to criteria above
161
function show_apis(argt)
162
return tabulate_output([[
163
local before, after = {}, {}
164
for k in pairs(]] .. argt.added_to .. [[) do
165
before[k] = true
166
end
167
168
local M = require ']] .. argt.by .. [['
169
for k in pairs(]] .. argt.added_to .. [[) do
170
after[k] = true
171
end
172
173
for k in pairs(after) do
174
if not before[k] then print(k) end
175
end
176
]])
177
end
178
179
180
181
--[[ ========= ]]--
182
--[[ Call Spy. ]]--
183
--[[ ========= ]]--
184
185
186
spy = function(fn)
187
return setmetatable({}, {
188
__call = function(self, ...)
189
self[#self + 1] = list(...)
190
return fn(...)
191
end,
192
})
193
end
194
195
196
do
197
--[[ ================ ]]--
198
--[[ Custom matchers. ]]--
199
--[[ ================ ]]--
200
201
local matchers = require 'specl.matchers'
202
local eqv = require 'specl.std'.operator.eqv
203
local str = require 'specl.std'.string.tostring
204
205
local Matcher, matchers = matchers.Matcher, matchers.matchers
206
local concat = table.concat
207
208
209
matchers.be_called_with = Matcher {
210
function(self, actual, expected)
211
for i,v in ipairs(expected or {}) do
212
if not eqv(actual[i], v) then
213
return false
214
end
215
end
216
return true
217
end,
218
219
actual = 'argmuents',
220
221
format_expect = function(self, expect)
222
return ' arguments (' .. str(expect) .. '), '
223
end,
224
}
225
226
matchers.be_callable = Matcher {
227
function(self, actual, _)
228
return iscallable(actual)
229
end,
230
231
actual = 'callable',
232
233
format_expect = function(self, expect)
234
return ' callable, '
235
end,
236
}
237
238
matchers.be_falsey = Matcher {
239
function(self, actual, _)
240
return not actual and true or false
241
end,
242
243
actual = 'falsey',
244
245
format_expect = function(self, expect)
246
return ' falsey, '
247
end,
248
}
249
250
matchers.be_truthy = Matcher {
251
function(self, actual, _)
252
return actual and true or false
253
end,
254
255
actual = 'truthy',
256
257
format_expect = function(self, expect)
258
return ' truthy, '
259
end,
260
}
261
262
matchers.have_type = Matcher {
263
function(self, actual, expected)
264
return type(actual) == expected or (getmetatable(actual) or {})._type == expected
265
end,
266
267
actual = 'type',
268
269
format_expect = function(self, expect)
270
local article = 'a'
271
if match(expect, '^[aehiou]') then
272
article = 'an'
273
end
274
return concat{' ', article, ' ', expect, ', '}
275
end
276
}
277
end
278
279