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

FourierTransformer / tested / 29217509047

13 Jul 2026 01:29AM UTC coverage: 89.56% (-0.01%) from 89.571%
29217509047

push

github

web-flow
Merge 87e5b4e1a into 2d7f05a6f

239 of 247 new or added lines in 12 files covered. (96.76%)

76 existing lines in 5 files now uncovered.

1690 of 1887 relevant lines covered (89.56%)

1582.55 hits per line

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

70.37
/build/tested/cli.lua
1
local argparse = require("argparse")
5✔
2
local lfs = require("lfs")
5✔
3

4
local logging = require("tested.libs.logging")
5✔
5
local logger = logging.get_logger("tested.cli")
5✔
6
local util = require("tested.util")
5✔
7

8

9

10
local cli = { CLIOptions = {} }
5✔
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
local cli_to_display = {
5✔
58
   ["skip"] = "SKIP",
5✔
59
   ["pass"] = "PASS",
5✔
60
   ["fail"] = "FAIL",
5✔
61
   ["exception"] = "EXCEPTION",
5✔
62
   ["unknown"] = "UNKNOWN",
5✔
63
   ["unexpected"] = "UNEXPECTED",
5✔
64

65
}
66

67
function cli.parse_args(version)
5✔
68
   local parser = argparse("tested", "A Lua/Teal Unit Testing Framework", "For more info see https://fouriertransformer.github.io/tested")
5✔
69

70
   parser:flag("-c --coverage"):
9✔
71
   description("Enable code coverage - will generate luacov.stats.out (default: not-set)"):
5✔
72
   default(false)
5✔
73

74
   parser:flag("-r --random"):
9✔
75
   description("Randomize the order of the tests (default: not-set)"):
5✔
76
   default(false)
5✔
77

78
   parser:option("-F --filter"):
7✔
79
   description("Only run tests whose name matches this Lua pattern (default: not-set)")
5✔
80

81
   parser:option("-t --tags"):
7✔
82
   description("Only run tests matching a tag expression, e.g. 'integration' or '(unit or integration) and not slow' (default: not-set)")
5✔
83

84
   parser:option("-s --show"):
11✔
85
   description("What test results to display (default: '-s fail -s exception -s unknown')"):
5✔
86
   choices({ "all", "valid", "invalid", "skip", "pass", "fail", "exception", "unknown", "expected", "unexpected" }):
5✔
87
   count("*")
5✔
88

89
   parser:mutex(
10✔
90
   parser:option("-f --display-format"):
11✔
91
   description("What format to output the results in (default: 'terminal')"):
5✔
92
   choices({ "terminal", "plain", "tap" }):
5✔
93
   default("terminal"),
5✔
94
   parser:option("-z --custom-formatter"):
7✔
95
   description("File that loads a custom formatter to use for terminal output"))
5✔
96

97

98
   parser:option("-o --output-file"):
9✔
99
   description("Output file to save test results in (currently supported extensions: '.txt' and '.json')"):
5✔
100
   count("*")
5✔
101

102
   parser:option("-n --instances"):
11✔
103
   description("Set the number of concurrent tests to run (default: 4). Due to threading behaviour, this is now a noop. All tests run seqeuntially."):
5✔
104
   default(4):
5✔
105
   convert(tonumber)
5✔
106

107
   parser:option("-x --language-handler"):
9✔
108
   description("File that loads custom language that is Lua-compatible"):
5✔
109
   count("*")
5✔
110

111
   parser:option("-d --debug"):
11✔
112
   description("Set the log level - mostly for debugging issues with tested (default: 'WARNING')"):
5✔
113
   choices({ "DEBUG", "INFO", "WARNING" }):
5✔
114
   default("WARNING")
5✔
115

116
   parser:flag("--version"):
9✔
117
   description("Show version information"):
5✔
118
   action(function() print(version); os.exit(0) end)
5✔
119

120
   parser:flag("-p --process"):
11✔
121
   description("Internal flag used to indicate that this test is running as a process"):
5✔
122
   default(false):
5✔
123
   hidden(true)
5✔
124

125
   parser:argument("paths", "Path(s) to directories or files with tests to run (default: 'tests')"):
7✔
126
   args("*")
5✔
127

128
   logger:info("Parsing Arguments...")
5✔
129

130
   local args = parser:parse()
5✔
131
   return args
5✔
132
end
133

134
function cli.set_defaults(args)
5✔
135
   logger:info("Setting Defaults...")
5✔
136
   if #args.show == 0 then
5✔
137
      args.show = { "fail", "exception", "unknown", "unexpected" }
5✔
138
      args.specified_show = false
5✔
139
   else
UNCOV
140
      args.specified_show = true
×
141
   end
142
   if #args.paths == 0 then args.paths = { "tests" } end
5✔
143
   args.test_files = {}
5✔
144
   args.test_directories = {}
5✔
145

146
   local show_all = false
5✔
147
   for _, display_option in ipairs(args.show) do if display_option == "all" then show_all = true; break end end
25✔
148
   if show_all then args.show = { "skip", "pass", "fail", "exception", "unknown", "expected", "unexpected" } end
5✔
149

150
   if #args.output_file > 0 then
5✔
UNCOV
151
      for _, output_file in ipairs(args.output_file) do
×
UNCOV
152
         if not (util.get_file_extension(output_file) == ".txt" or util.get_file_extension(output_file) == ".json") then
×
UNCOV
153
            error("The given output file does not have a supported file extension: '" .. output_file .. "'. Supported file extensions are: '.txt', '.json'", 0)
×
154
         end
155
      end
156
   end
157
end
158

159
function cli.validate_args(args)
5✔
160
   logger:info("Validating args...")
5✔
161
   for _, path in ipairs(args.paths) do
10✔
162
      local info, err = lfs.attributes(path)
5✔
163
      if err then error("The file or directory '" .. path .. "' does not appear to exist. Unable to run tests") end
5✔
164
      if not (info.mode == "directory" or info.mode == "file") then error("tested requires the paths passed in to be a directory or file", 0) end
5✔
165
      if info.mode == "directory" then table.insert(args.test_directories, path) end
5✔
166
      if info.mode == "file" then table.insert(args.test_files, path) end
5✔
167
   end
168
   if args.filter then
5✔
169
      local ok, err = pcall(string.find, "", args.filter)
×
170
      if not ok then
×
171
         error("Invalid --filter pattern '" .. args.filter .. "': " .. tostring(err), 0)
×
172
      end
173
   end
174
   if args.tags then
5✔
UNCOV
175
      if args.tags:match("[^a-zA-Z0-9_ ()]") then
×
UNCOV
176
         error("Invalid --tags expression: only letters, digits, underscores, spaces, and parentheses are allowed", 0)
×
177
      end
UNCOV
178
      local lua_expr = (args.tags:gsub("([a-zA-Z_][a-zA-Z0-9_]*)", function(word)
×
UNCOV
179
         if word == "and" or word == "or" or word == "not" then return word end
×
UNCOV
180
         return 'tags["' .. word .. '"]'
×
181
      end))
UNCOV
182
      local tag_filter = "local tags = ... \nreturn " .. lua_expr
×
183

184

UNCOV
185
      local string_loader = loadstring or load
×
UNCOV
186
      local loaded = string_loader(tag_filter)
×
187
      if not loaded then
×
188
         error("Invalid --tags expression '" .. args.tags .. "': Be sure to use boolean expressions ('or', 'and', 'not', etc) when combining tags", 0)
×
189
      end
UNCOV
190
      args.tags_filter = loaded
×
191
   end
192

193
   if args.custom_formatter then
5✔
194
      if not (util.is_valid_filename(args.custom_formatter) or util.is_valid_lua_module_name(args.custom_formatter)) then
×
UNCOV
195
         error("Invalid --custom-formatter '" .. args.custom_formatter .. "'. It does not appear to be a valid filepath nor Lua module name")
×
196
      end
197
   end
198

199
   if args.language_handler then
5✔
200
      for _, language_handler in ipairs(args.language_handler) do
5✔
UNCOV
201
         if not (util.is_valid_filename(language_handler) or util.is_valid_lua_module_name(language_handler)) then
×
UNCOV
202
            error("Invalid --format-handler '" .. language_handler .. "'. It does not appear to be a valid filepath nor Lua module name")
×
203
         end
204
      end
205
   end
206
end
207

208
function cli.display_types(options)
5✔
209
   local to_display = {}
110✔
210
   for _, cli_option in ipairs(options) do
550✔
211
      if cli_to_display[cli_option] then
440✔
212
         to_display[cli_to_display[cli_option]] = true
440✔
213
         if cli_option == "skip" then
440✔
UNCOV
214
            to_display["SKIP"] = true
×
215
         end
216
      else
UNCOV
217
         if cli_option == "invalid" then
×
UNCOV
218
            to_display["EXCEPTION"] = true
×
219
            to_display["UNKNOWN"] = true
×
220
            to_display["TIMEOUT"] = true
×
UNCOV
221
            to_display["UNEXPECTED"] = true
×
UNCOV
222
         elseif cli_option == "valid" then
×
UNCOV
223
            to_display["PASS"] = true
×
UNCOV
224
            to_display["SKIP"] = true
×
UNCOV
225
            to_display["FILTERED"] = true
×
UNCOV
226
            to_display["FAIL"] = true
×
UNCOV
227
            to_display["EXPECTED_FAIL"] = true
×
UNCOV
228
            to_display["EXPECTED_EXCEPTION"] = true
×
UNCOV
229
            to_display["EXPECTED_UNKNOWN"] = true
×
UNCOV
230
         elseif cli_option == "expected" then
×
UNCOV
231
            to_display["EXPECTED_FAIL"] = true
×
232
            to_display["EXPECTED_EXCEPTION"] = true
×
UNCOV
233
            to_display["EXPECTED_UNKNOWN"] = true
×
234
         end
235
      end
236
   end
237
   return to_display
110✔
238
end
239

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

© 2026 Coveralls, Inc