summaryrefslogtreecommitdiffstats
path: root/test/lua/busted/init.lua
blob: da052f503e9b26776e21ab0ca31c58c4a5b44018 (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
local unpack = require 'busted.compatibility'.unpack
local shuffle = require 'busted.utils'.shuffle

local function sort(elements)
  table.sort(elements, function(t1, t2)
    if t1.name and t2.name then
      return t1.name < t2.name
    end
    return t2.name ~= nil
  end)
  return elements
end

local function remove(descriptors, element)
  for _, descriptor in ipairs(descriptors) do
    element.env[descriptor] = function(...)
      error("'" .. descriptor .. "' not supported inside current context block", 2)
    end
  end
end

local function init(busted)
  local function exec(descriptor, element)
    if not element.env then element.env = {} end

    remove({ 'randomize' }, element)
    remove({ 'pending' }, element)
    remove({ 'describe', 'context', 'it', 'spec', 'test' }, element)
    remove({ 'setup', 'teardown', 'before_each', 'after_each' }, element)

    local parent = busted.context.parent(element)
    setmetatable(element.env, {
      __newindex = function(self, key, value)
        if not parent.env then parent.env = {} end
        parent.env[key] = value
      end
    })

    local ret = { busted.safe(descriptor, element.run, element) }
    return unpack(ret)
  end

  local function execAll(descriptor, current, propagate)
    local parent = busted.context.parent(current)

    if propagate and parent then
      local success, ancestor = execAll(descriptor, parent, propagate)
      if not success then
        return success, ancestor
      end
    end

    local list = current[descriptor] or {}

    local success = true
    for _, v in pairs(list) do
      if not exec(descriptor, v):success() then
        success = nil
      end
    end
    return success, current
  end

  local function dexecAll(descriptor, current, propagate)
    local parent = busted.context.parent(current)
    local list = current[descriptor] or {}

    local success = true
    for _, v in pairs(list) do
      if not exec(descriptor, v):success() then
        success = nil
      end
    end

    if propagate and parent then
      if not dexecAll(descriptor, parent, propagate) then
        success = nil
      end
    end
    return success
  end

  local file = function(file)
    busted.publish({ 'file', 'start' }, file.name)

    busted.environment.wrap(file.run)
    if not file.env then file.env = {} end

    local randomize = busted.randomize
    file.env.randomize = function() randomize = true end

    if busted.safe('file', file.run, file):success() then
      if randomize then
        file.randomseed = busted.randomseed
        shuffle(busted.context.children(file), busted.randomseed)
      elseif busted.sort then
        sort(busted.context.children(file))
      end
      if execAll('setup', file) then
        busted.execute(file)
      end
      dexecAll('teardown', file)
    end

    busted.publish({ 'file', 'end' }, file.name)
  end

  local describe = function(describe)
    local parent = busted.context.parent(describe)

    busted.publish({ 'describe', 'start' }, describe, parent)

    if not describe.env then describe.env = {} end

    local randomize = busted.randomize
    describe.env.randomize = function() randomize = true end

    if busted.safe('describe', describe.run, describe):success() then
      if randomize then
        describe.randomseed = busted.randomseed
        shuffle(busted.context.children(describe), busted.randomseed)
      elseif busted.sort then
        sort(busted.context.children(describe))
      end
      if execAll('setup', describe) then
        busted.execute(describe)
      end
      dexecAll('teardown', describe)
    end

    busted.publish({ 'describe', 'end' }, describe, parent)
  end

  local it = function(element)
    local parent = busted.context.parent(element)
    local finally

    busted.publish({ 'test', 'start' }, element, parent)

    if not element.env then element.env = {} end

    remove({ 'randomize' }, element)
    remove({ 'describe', 'context', 'it', 'spec', 'test' }, element)
    remove({ 'setup', 'teardown', 'before_each', 'after_each' }, element)
    element.env.finally = function(fn) finally = fn end
    element.env.pending = function(msg) busted.pending(msg) end

    local status = busted.status('success')
    local updateErrorStatus = function(descriptor)
      if element.message then element.message = element.message .. '\n' end
      element.message = (element.message or '') .. 'Error in ' .. descriptor
      status:update('error')
    end

    local pass, ancestor = execAll('before_each', parent, true)
    if pass then
      status:update(busted.safe('it', element.run, element))
    else
      updateErrorStatus('before_each')
    end

    if not element.env.done then
      remove({ 'pending' }, element)
      if finally then status:update(busted.safe('finally', finally, element)) end
      if not dexecAll('after_each', ancestor, true) then
        updateErrorStatus('after_each')
      end

      busted.publish({ 'test', 'end' }, element, parent, tostring(status))
    end
  end

  local pending = function(element)
    local parent = busted.context.parent(element)
    busted.publish({ 'test', 'start' }, element, parent)
    busted.publish({ 'test', 'end' }, element, parent, 'pending')
  end

  busted.register('file', file)

  busted.register('describe', describe)

  busted.register('it', it)

  busted.register('pending', pending)

  busted.register('setup')
  busted.register('teardown')
  busted.register('before_each')
  busted.register('after_each')

  busted.alias('context', 'describe')
  busted.alias('spec', 'it')
  busted.alias('test', 'it')

  local assert = require 'luassert'
  local spy    = require 'luassert.spy'
  local mock   = require 'luassert.mock'
  local stub   = require 'luassert.stub'

  busted.environment.set('assert', assert)
  busted.environment.set('spy', spy)
  busted.environment.set('mock', mock)
  busted.environment.set('stub', stub)

  busted.replaceErrorWithFail(assert)
  busted.replaceErrorWithFail(assert.True)

  return busted
end

return setmetatable({}, {
  __call = function(self, busted)
    local root = busted.context.get()
    init(busted)

    return setmetatable(self, {
      __index = function(self, key)
        return rawget(root.env, key) or busted.executors[key]
      end,

      __newindex = function(self, key, value)
        error('Attempt to modify busted')
      end
    })
  end
})