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