• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FourierTransformer / tested / 26137758217

20 May 2026 02:35AM UTC coverage: 82.364%. Remained the same
26137758217

push

github

web-flow
Merge 1c13d288e into 462bd706b

164 of 169 new or added lines in 6 files covered. (97.04%)

58 existing lines in 3 files now uncovered.

1331 of 1616 relevant lines covered (82.36%)

3342.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.12
/build/tested.lua
1

2
local assert_table = require("tested.assert_table")
156✔
3

4
local tested = { tests = {}, run_only_tests = false }
156✔
5

6
local options_set = { tags = true, expected = true, run_when = true, retries = true, retry_delay = true }
156✔
7

8
local function validate_options(test_name, options, test_src)
9
   local error_prefix = test_src .. " in \"" .. test_name .. "\": "
12,684✔
10

11
   for k, _ in pairs(options) do
25,092✔
12
      if not options_set[k] then
12,408✔
NEW
13
         error(error_prefix .. "options includes unknown value '" .. k .. "'. Options must be one of: " .. "'tags', 'expected', 'run_when', 'retries', or 'retry_delay'")
×
14
      end
15
   end
16

17
   if options.expected ~= nil then
12,684✔
18
      if type(options.expected) ~= "string" then
120✔
19
         error(error_prefix .. "options.expected takes in a 'string', but received '" .. type(options.expected) .. "'", 0)
×
20
      end
21
      if not (options.expected == "FAIL" or options.expected == "EXCEPTION" or options.expected == "UNKNOWN") then
120✔
22
         error(error_prefix .. "options.expected should be one of 'FAIL', 'EXCEPTION', or 'UNKNOWN'", 0)
×
23
      end
24
   end
25

26
   if options.run_when ~= nil then
12,684✔
27
      if type(options.run_when) ~= "boolean" then
36✔
28
         error(error_prefix .. "options.run_when takes in a 'boolean', but received '" .. type(options.run_when) .. "'", 0)
×
29
      end
30
   end
31

32
   if options.tags ~= nil then
12,684✔
33
      if type(options.tags) ~= "table" then
12,168✔
34
         error(error_prefix .. "options.tags takes in a '{string}', but received '" .. type(options.tags) .. "'", 0)
×
35
      end
36
      for i, tag in ipairs(options.tags) do
24,360✔
37
         if type(tag) ~= "string" then
12,192✔
38
            error(error_prefix .. "options.tags[" .. i .. "] should be a 'string', but received '" .. type(tag) .. "'", 0)
×
39
         end
40
         if tag == "and" or tag == "or" or tag == "not" then
12,192✔
41
            error(error_prefix .. "options.tags[" .. i .. "] appears to be a reserved keyword: 'and', 'or', or 'not'. Please change to a different tag name", 0)
×
42
         end
43
      end
44
   end
45

46
   if options.retries ~= nil then
12,684✔
47
      if type(options.retries) ~= "number" or (options.retries) ~= math.floor(options.retries) or (options.retries) < 0 then
72✔
NEW
48
         error(error_prefix .. "options.retries takes in a non-negative integer, but received '" .. tostring(options.retries) .. "'", 0)
×
49
      end
50
   end
51

52
   if options.retry_delay ~= nil then
12,684✔
53
      if type(options.retry_delay) ~= "number" or (options.retry_delay) < 0 then
12✔
NEW
54
         error(error_prefix .. "options.retry_delay takes in a non-negative number, but received '" .. tostring(options.retry_delay) .. "'", 0)
×
55
      end
56
   end
57
end
58

59
local function extract_fn_and_options(test_name, fn_or_options, fn, test_src)
60
   local options = {}
12,684✔
61
   if type(fn_or_options) == "function" then
12,684✔
62
      fn = fn_or_options
336✔
63

64
   elseif type(fn_or_options) == "table" then
12,348✔
65
      options = fn_or_options
12,348✔
66
      if not fn then error("a function must be provided to run a unit test") end
12,348✔
UNCOV
67
      fn = fn
×
68
   end
69

70
   tested.filename = test_src
12,684✔
71

72
   validate_options(test_name, options, test_src or "?")
12,684✔
73

74
   return fn, options
12,684✔
75
end
76

77

78
function tested.test(name, fn_or_options, fn)
156✔
79
   local test_src = debug.getinfo(2, "S").short_src
12,684✔
80
   local func, options = extract_fn_and_options(name, fn_or_options, fn, test_src)
12,684✔
81
   table.insert(tested.tests, { name = name, fn = func, options = options, kind = "test" })
12,684✔
82
end
83

84
function tested.skip(name, fn_or_options, fn)
156✔
UNCOV
85
   local test_src = debug.getinfo(2, "S").short_src
×
UNCOV
86
   local func, options = extract_fn_and_options(name, fn_or_options, fn, test_src)
×
UNCOV
87
   table.insert(tested.tests, { name = name, fn = func, options = options, kind = "skip" })
×
88
end
89

90
function tested.only(name, fn_or_options, fn)
156✔
UNCOV
91
   local test_src = debug.getinfo(2, "S").short_src
×
UNCOV
92
   local func, options = extract_fn_and_options(name, fn_or_options, fn, test_src)
×
UNCOV
93
   table.insert(tested.tests, { name = name, fn = func, options = options, kind = "only" })
×
UNCOV
94
   tested.run_only_tests = true
×
95
end
96

97
function tested.before(fn)
156✔
98
   tested.before_fn = fn
12✔
99
end
100

101
function tested.after(fn)
156✔
102
   tested.after_fn = fn
12✔
103
end
104

105
function tested.before_each(fn)
156✔
106
   tested.before_each_fn = fn
24✔
107
end
108

109
function tested.after_each(fn)
156✔
110
   tested.after_each_fn = fn
24✔
111
end
112

113
function tested.assert(assertion)
156✔
114
   local errors = {}
25,152✔
115
   if assertion.expected == nil then table.insert(errors, "'expected'") end
25,152✔
116
   if assertion.actual == nil then table.insert(errors, "'actual'") end
25,152✔
117
   if #errors ~= 0 then error("The assertion table must include 'expected' and 'actual' whose values cannot be 'nil'. Missing (or 'nil') fields: " .. table.concat(errors, ", "), 0) end
25,152✔
118
   if assertion.given and type(assertion.given) ~= "string" then
25,152✔
UNCOV
119
      table.insert(errors, "In assertion, 'given' should be a 'string'. It appears to be a '" .. type(assertion.given) .. "' with value: '" .. tostring(assertion.given))
×
120
   end
121
   if assertion.should and type(assertion.should) ~= "string" then
25,152✔
UNCOV
122
      table.insert(errors, "In assertion, 'should' should be a 'string'. It appears to be a '" .. type(assertion.should) .. "' with value: " .. tostring(assertion.should))
×
123
   end
124
   if #errors ~= 0 then error(table.concat(errors, ". "), 0) end
25,152✔
125
   local expected_type = type(assertion.expected)
25,152✔
126
   local actual_type = type(assertion.actual)
25,152✔
127

128
   if actual_type ~= expected_type then
25,152✔
UNCOV
129
      return false, "Actual: " .. tostring(assertion.actual) .. " (as '" ..
×
UNCOV
130
      actual_type .. "'). Expected: " .. tostring(assertion.expected) .. " (as '" .. expected_type .. "')"
×
131
   end
132

133

134

135
   if actual_type == "table" and expected_type == "table" then
25,152✔
136
      return assert_table(assertion.expected, assertion.actual)
156✔
137
   end
138

139
   if assertion.actual == assertion.expected then
24,996✔
140
      return true, ""
24,648✔
141
   end
142

143
   return false, "Actual: " .. tostring(assertion.actual) .. "\nExpected: " .. tostring(assertion.expected)
402✔
144
end
145

146
function tested.assert_truthy(assertion)
156✔
147
   return tested.assert({ given = assertion.given, should = assertion.should or "be truthy", expected = true, actual = (not not (assertion.actual)) })
108✔
148
end
149

150
function tested.assert_falsy(assertion)
156✔
151
   return tested.assert({ given = assertion.given, should = assertion.should or "be falsy", expected = false, actual = (not not (assertion.actual)) })
36✔
152
end
153

154
function tested.assert_throws_exception(assertion)
156✔
155
   if assertion.expected then
36✔
156
      local function wrapped_pcall()
157
         local ok, err = pcall(function() assertion.actual() end)
48✔
158
         if type(err) == "string" then
24✔
159
            return { ok, err:match("^.+:%d+: (.+)$") or err }
24✔
160
         else
UNCOV
161
            return { ok, err }
×
162
         end
163
      end
164

165
      return tested.assert({
42✔
166
         given = assertion.given,
24✔
167
         should = assertion.should or "throw exception with error message",
24✔
168
         expected = { false, assertion.expected },
24✔
169
         actual = wrapped_pcall(),
30✔
170
      })
6✔
171
   else
172
      return tested.assert({
21✔
173
         given = assertion.given,
12✔
174
         should = assertion.should or "throw exception",
12✔
175
         expected = false,
9✔
176
         actual = pcall(function() assertion.actual() end),
27✔
177
      })
3✔
178
   end
179
end
180

181
local function fisher_yates_shuffle(t)
UNCOV
182
   for i = #t, 2, -1 do
×
UNCOV
183
      local j = math.random(i)
×
UNCOV
184
      t[i], t[j] = t[j], t[i]
×
185
   end
186
end
187

188
local function should_skip_test(test, run_only, options)
189
   if run_only and test.kind ~= "only" then
12,684✔
UNCOV
190
      return "SKIP", "Only running 'tested.only' tests"
×
191

192
   elseif test.kind == "skip" then
12,684✔
UNCOV
193
      return "SKIP", "Test marked with 'tested.skip'"
×
194

195
   elseif test.options.run_when ~= nil and test.options.run_when == false then
12,684✔
196
      return "SKIP", "Condition in `tested.conditional_skip` returned false. Skipping test."
24✔
197

198
   elseif options and options.filter ~= nil and not string.find(test.name, options.filter) then
12,660✔
UNCOV
199
      return "FILTERED", "Test name does not match filter pattern '" .. options.filter .. "'"
×
200

201
   elseif options and options.tags_filter ~= nil then
12,660✔
202

UNCOV
203
      local tag_set = {}
×
UNCOV
204
      if test.options.tags then
×
205
         for _, tag in ipairs(test.options.tags) do tag_set[tag] = true end
×
206
      end
207

UNCOV
208
      if not options.tags_filter(tag_set) then
×
UNCOV
209
         return "FILTERED", "Test tags do not match tag filter expression"
×
210
      end
211
   end
212
   return nil, nil
12,660✔
213
end
214

215
local function captured_os_exit(code)
216
   local prefix = "os.exit()"
×
217
   if code then prefix = "os.exit(" .. tostring(code) .. ")" end
×
UNCOV
218
   error(prefix .. " intercepted — something tried to exit out of the process", 0)
×
219
end
220

221

222

223

224

225

226
local function wrap_assert(self, test_output)
227
   local original_assert = self.assert
12,744✔
228
   local counters = { total = 0, failed = 0 }
12,744✔
229

230
   self.assert = function(assertion)
231
      local ok, err = original_assert(assertion)
25,152✔
232
      counters.total = counters.total + 1
25,152✔
233

234
      local assertion_result = {
25,152✔
235
         filename = tested.filename,
25,152✔
236
         line_number = debug.getinfo(2, "l").currentline,
25,152✔
237
         given = assertion.given,
25,152✔
238
         should = assertion.should,
25,152✔
239
      }
240
      if ok == false then
25,152✔
241
         counters.failed = counters.failed + 1
396✔
242
         assertion_result.result = "FAIL"
396✔
243
         assertion_result.error_message = err
396✔
244
      else
245
         assertion_result.result = "PASS"
24,756✔
246
      end
247
      table.insert(test_output.assertion_results, assertion_result)
25,152✔
248
      return ok, err
25,152✔
249
   end
250

251
   return counters, original_assert
12,744✔
252
end
253

254

255
local function swap_os_exit(test_fn)
256
   local original_os_exit
257
   if _VERSION == "Lua 5.1" then
12,744✔
258

259
      original_os_exit = getfenv(test_fn).os.exit
6,372✔
260
      getfenv(test_fn).os.exit = captured_os_exit
6,372✔
261
   else
262
      original_os_exit = os.exit
6,372✔
263
      os.exit = captured_os_exit
6,372✔
264
   end
265
   return original_os_exit
12,744✔
266
end
267

268
local function xpcall_handler(e)
269
   local msg = type(e) == "string" and (e) or tostring(e)
36✔
270
   return msg .. debug.traceback("", 2)
36✔
271
end
272

273
local function set_result(ok, err, total_assertions, assert_failed_count, test_output)
274
   if ok == false then
12,744✔
275
      test_output.result = "EXCEPTION"
36✔
276
      test_output.message = err
36✔
277

278
   elseif total_assertions == 0 then
12,708✔
279
      test_output.result = "UNKNOWN"
24✔
280
      test_output.message = "No assertions run during test"
24✔
281

282
   elseif assert_failed_count == 0 then
12,684✔
283
      test_output.result = "PASS"
12,384✔
284
      test_output.message = "All assertions have passed"
12,384✔
285

286
   else
287
      test_output.result = "FAIL"
300✔
288
      test_output.message = assert_failed_count .. " assertions have failed"
300✔
289
   end
290
end
291

292
local function restore_os_exit(test_fn, original)
293
   if _VERSION == "Lua 5.1" then
12,744✔
294
      getfenv(test_fn).os.exit = original
6,372✔
295
   else
296
      os.exit = original
6,372✔
297
   end
298
end
299

300

301
local function adjust_for_expected(expected, test_output)
302
   if expected ~= nil then
12,744✔
303
      if test_output.result == expected then
120✔
304
         if expected == "EXCEPTION" then
84✔
305
            test_output.result = "EXPECTED_EXCEPTION"
12✔
306
         elseif expected == "UNKNOWN" then
72✔
307
            test_output.result = "EXPECTED_UNKNOWN"
12✔
308
         elseif expected == "FAIL" then
60✔
309
            test_output.result = "EXPECTED_FAIL"
60✔
310
         end
311
      else
312
         test_output.message = "Expected test result to be " .. expected .. ", but came back as " .. test_output.result .. "\n" .. test_output.message
36✔
313
         test_output.result = "UNEXPECTED"
36✔
314
      end
315
   end
316
end
317

318
local function add_up_test_results(test_output, test_counts)
319
   if test_output.result == "PASS" then
12,684✔
320
      test_counts.passed = test_counts.passed + 1
12,348✔
321

322
   elseif test_output.result == "FAIL" then
336✔
323
      test_counts.failed = test_counts.failed + 1
168✔
324

325
   elseif test_output.result == "EXPECTED_FAIL" or test_output.result == "EXPECTED_EXCEPTION" or test_output.result == "EXPECTED_UNKNOWN" then
168✔
326
      test_counts.expected = test_counts.expected + 1
84✔
327

328
   elseif test_output.result == "EXCEPTION" or test_output.result == "UNKNOWN" or test_output.result == "UNEXPECTED" then
84✔
329
      test_counts.invalid = test_counts.invalid + 1
60✔
330

331
   elseif test_output.result == "SKIP" then
24✔
332
      test_counts.skipped = test_counts.skipped + 1
24✔
333

UNCOV
334
   elseif test_output.result == "FILTERED" then
×
UNCOV
335
      test_counts.filtered = test_counts.filtered + 1
×
336

337
   end
338
end
339

340
local function run_test(self, test, test_output)
341
   if tested.before_each_fn then tested.before_each_fn() end
12,744✔
342

343
   local assertions, original_assert = wrap_assert(self, test_output)
12,744✔
344
   local original_os_exit = swap_os_exit(test.fn)
12,744✔
345

346
   local ok, err = xpcall(test.fn, xpcall_handler)
12,744✔
347
   set_result(ok, err, assertions.total, assertions.failed, test_output)
12,744✔
348

349
   restore_os_exit(test.fn, original_os_exit)
12,744✔
350
   self.assert = original_assert
12,744✔
351

352

353
   adjust_for_expected(test.options.expected, test_output)
12,744✔
354

355
   if tested.after_each_fn then tested.after_each_fn() end
12,744✔
356
end
357

358

359

360
function tested:run(filename, options)
156✔
361
   if options and options.random then
156✔
UNCOV
362
      math.randomseed(os.time())
×
UNCOV
363
      fisher_yates_shuffle(self.tests)
×
364
   end
365

366
   if self.run_only_tests then print("Only running tests with 'tested.only'") end
156✔
367

368
   local test_results = {
156✔
369
      counts = { passed = 0, failed = 0, expected = 0, skipped = 0, filtered = 0, invalid = 0 },
156✔
370
      tests = {},
156✔
371
      filename = filename,
156✔
372
      fully_tested = false,
117✔
373
      total_time = 0,
117✔
374
   }
375

376
   if tested.before_fn then tested.before_fn() end
156✔
377

378

379
   local retry_attempts = {}
156✔
380

381
   for i, test in ipairs(self.tests) do
12,840✔
382

383

384
      local test_result = { assertion_results = {}, name = test.name, options = test.options }
12,684✔
385

386
      local skip_result, skip_message = should_skip_test(test, self.run_only_tests, options)
12,684✔
387
      local start = options.clock_s()
12,684✔
388

389
      if skip_result then
12,684✔
390
         test_result.result = skip_result
24✔
391
         test_result.message = skip_message
24✔
392

393
      else
394
         run_test(self, test, test_result)
12,660✔
395

396
      end
397

398
      test_result.time = options.clock_s() - start
12,684✔
399

400
      local result = test_result.result
12,684✔
401
      local is_passing = result == "PASS" or result == "EXPECTED_FAIL" or result == "EXPECTED_EXCEPTION" or result == "EXPECTED_UNKNOWN"
12,684✔
402

403

404
      local retry_count = 0
12,684✔
405
      if test.options.retries and not skip_result and not is_passing and test.options.retries > 0 then
12,684✔
406
         while retry_count < test.options.retries do
144✔
407
            table.insert(retry_attempts, test_result)
84✔
408
            retry_count = retry_count + 1
84✔
409
            if test.options.retry_delay and options.sleep_s then
84✔
410
               options.sleep_s(test.options.retry_delay)
12✔
411
            end
412
            test_result = { assertion_results = {}, name = test.name, options = test.options }
84✔
413
            run_test(self, test, test_result)
105✔
414
         end
415
      end
416

417

418
      if retry_count > 0 then
12,684✔
419
         test_result.time = (options.clock_s() - start)
60✔
420
      end
421

422

423
      add_up_test_results(test_result, test_results.counts)
12,684✔
424

425

426
      test_result.retry_attempts = retry_attempts
12,684✔
427
      test_results.tests[i] = test_result
12,684✔
428
      test_results.total_time = test_results.total_time + test_result.time
12,684✔
429
   end
430

431
   if tested.after_fn then tested.after_fn() end
156✔
432

433
   if test_results.counts.failed == 0 and test_results.counts.invalid == 0 then
156✔
434
      test_results.fully_tested = true
108✔
435
   end
436

437
   return test_results
156✔
438
end
439

440
return tested
156✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc