size: 5 KiB
| 1 | -- run tests |
| 2 | package.path = "./?.lua;" .. package.path |
| 3 | local djot = require("djot") |
| 4 | |
| 5 | local testcases = { |
| 6 | "attributes.test", |
| 7 | "blockquote.test", |
| 8 | "code_blocks.test", |
| 9 | "definition_lists.test", |
| 10 | "symbol.test", |
| 11 | "emphasis.test", |
| 12 | "escapes.test", |
| 13 | "fenced_divs.test", |
| 14 | "filters.test", |
| 15 | "footnotes.test", |
| 16 | "headings.test", |
| 17 | "insert_delete_mark.test", |
| 18 | "links_and_images.test", |
| 19 | "lists.test", |
| 20 | "math.test", |
| 21 | "para.test", |
| 22 | "raw.test", |
| 23 | "regression.test", |
| 24 | "smart.test", |
| 25 | "spans.test", |
| 26 | "sourcepos.test", |
| 27 | "super_subscript.test", |
| 28 | "tables.test", |
| 29 | "task_lists.test", |
| 30 | "thematic_breaks.test", |
| 31 | "verbatim.test" |
| 32 | } |
| 33 | |
| 34 | local opts = {} |
| 35 | local i=1 |
| 36 | while i <= #arg do |
| 37 | local thisarg = arg[i] |
| 38 | if string.find(thisarg, "^%-") then |
| 39 | if thisarg == "-v" then |
| 40 | opts.verbose = true |
| 41 | elseif thisarg == "-p" then |
| 42 | opts.pattern = true |
| 43 | elseif thisarg == "--accept" then |
| 44 | opts.accept = true |
| 45 | end |
| 46 | elseif opts.pattern == true then |
| 47 | opts.pattern = thisarg |
| 48 | end |
| 49 | i = i + 1 |
| 50 | end |
| 51 | |
| 52 | local Tests = {} |
| 53 | |
| 54 | function Tests:new() |
| 55 | local contents = { |
| 56 | passed = 0, |
| 57 | failed = 0, |
| 58 | errors = 0, |
| 59 | accept = opts.accept, |
| 60 | verbose = opts.verbose |
| 61 | } |
| 62 | setmetatable(contents, Tests) |
| 63 | Tests.__index = Tests |
| 64 | return contents |
| 65 | end |
| 66 | |
| 67 | function Tests:do_test(test) |
| 68 | if self.verbose then |
| 69 | io.write(string.format("Testing %s at linen %d\n", test.file, test.linenum)) |
| 70 | end |
| 71 | local sourcepos = false |
| 72 | if test.options:match("p") then |
| 73 | sourcepos = true |
| 74 | end |
| 75 | local actual = "" |
| 76 | if test.options:match("m") then |
| 77 | actual = actual .. djot.parse_and_render_events(test.input) |
| 78 | else |
| 79 | local doc = djot.parse(test.input, sourcepos) |
| 80 | for _,filt in ipairs(test.filters) do |
| 81 | local f, err = djot.filter.load_filter(filt) |
| 82 | if not f then |
| 83 | error(err) |
| 84 | end |
| 85 | djot.filter.apply_filter(doc, f) |
| 86 | end |
| 87 | if test.options:match("a") then |
| 88 | actual = actual .. djot.render_ast_pretty(doc) |
| 89 | else -- match 'h' or empty |
| 90 | actual = actual .. djot.render_html(doc) |
| 91 | end |
| 92 | end |
| 93 | if self.accept then |
| 94 | test.output = actual |
| 95 | end |
| 96 | if actual == test.output then |
| 97 | self.passed = self.passed + 1 |
| 98 | return true |
| 99 | else |
| 100 | io.write(string.format("FAILED at %s line %d\n", test.file, test.linenum)) |
| 101 | io.write(string.format("--- INPUT -------------------------------------\n%s--- EXPECTED ----------------------------------\n%s--- GOT ---------------------------------------\n%s-----------------------------------------------\n\n", test.input, test.output, actual)) |
| 102 | self.failed = self.failed + 1 |
| 103 | return false |
| 104 | end |
| 105 | end |
| 106 | |
| 107 | local function read_tests(file) |
| 108 | local f = io.open("test/" .. file,"r") |
| 109 | assert(f ~= nil, "File " .. file .. " cannot be read") |
| 110 | local line |
| 111 | local linenum = 0 |
| 112 | return function() |
| 113 | while true do |
| 114 | local inp = "" |
| 115 | local out = "" |
| 116 | line = f:read() |
| 117 | local pretext = {} |
| 118 | linenum = linenum + 1 |
| 119 | while line and not line:match("^```") do |
| 120 | pretext[#pretext + 1] = line |
| 121 | line = f:read() |
| 122 | linenum = linenum + 1 |
| 123 | end |
| 124 | local testlinenum = linenum |
| 125 | if not line then |
| 126 | break |
| 127 | end |
| 128 | local ticks, options = line:match("^(`+)%s*(.*)") |
| 129 | |
| 130 | -- parse input |
| 131 | line = f:read() |
| 132 | linenum = linenum + 1 |
| 133 | while not line:match("^[%.%!]$") do |
| 134 | inp = inp .. line .. "\n" |
| 135 | line = f:read() |
| 136 | linenum = linenum + 1 |
| 137 | end |
| 138 | |
| 139 | local filters = {} |
| 140 | while line == "!" do -- parse filter |
| 141 | line = f:read() |
| 142 | linenum = linenum + 1 |
| 143 | local filt = "" |
| 144 | while not line:match("^[%.%!]$") do |
| 145 | filt = filt .. line .. "\n" |
| 146 | line = f:read() |
| 147 | linenum = linenum + 1 |
| 148 | end |
| 149 | table.insert(filters, filt) |
| 150 | end |
| 151 | |
| 152 | -- parse output |
| 153 | line = f:read() |
| 154 | linenum = linenum + 1 |
| 155 | while not line:match("^" .. ticks) do |
| 156 | out = out .. line .. "\n" |
| 157 | line = f:read() |
| 158 | linenum = linenum + 1 |
| 159 | end |
| 160 | |
| 161 | return { file = file, |
| 162 | linenum = testlinenum, |
| 163 | pretext = table.concat(pretext, "\n"), |
| 164 | options = options, |
| 165 | filters = filters, |
| 166 | input = inp, |
| 167 | output = out } |
| 168 | end |
| 169 | end |
| 170 | end |
| 171 | |
| 172 | function Tests:do_tests(file) |
| 173 | local tests = {} |
| 174 | for test in read_tests(file) do |
| 175 | tests[#tests + 1] = test |
| 176 | local ok, err = pcall(function() |
| 177 | self:do_test(test) |
| 178 | end) |
| 179 | if not ok then |
| 180 | io.stderr:write(string.format("Error running test %s line %d:\n%s\n", |
| 181 | test.file, test.linenum, err)) |
| 182 | self.errors = self.errors + 1 |
| 183 | end |
| 184 | end |
| 185 | if self.accept then -- rewrite file |
| 186 | local fh = io.open("test/" .. file, "w") |
| 187 | for idx,test in ipairs(tests) do |
| 188 | local numticks = 3 |
| 189 | string.gsub(test.input .. test.output, "(````*)", |
| 190 | function(x) |
| 191 | if #x >= numticks then |
| 192 | numticks = #x + 1 |
| 193 | end |
| 194 | end) |
| 195 | local ticks = string.rep("`", numticks) |
| 196 | local pretext = test.pretext |
| 197 | if #pretext > 0 or idx > 1 then |
| 198 | pretext = pretext .. "\n" |
| 199 | end |
| 200 | |
| 201 | fh:write(string.format("%s%s%s\n%s", |
| 202 | pretext, |
| 203 | ticks, |
| 204 | (test.options == "" and "") or " " .. test.options, |
| 205 | test.input)) |
| 206 | for _,f in ipairs(test.filters) do |
| 207 | fh:write(string.format("!\n%s", f)) |
| 208 | end |
| 209 | fh:write(string.format(".\n%s%s\n", test.output, ticks)) |
| 210 | end |
| 211 | fh:close() |
| 212 | end |
| 213 | end |
| 214 | |
| 215 | local tests = Tests:new() |
| 216 | local starttime = os.clock() |
| 217 | for _,case in ipairs(testcases) do |
| 218 | if not opts.pattern or string.find(case, opts.pattern) then |
| 219 | tests:do_tests(case) |
| 220 | end |
| 221 | end |
| 222 | local endtime = os.clock() |
| 223 | |
| 224 | io.write(string.format("%d tests completed in %0.3f s\n", |
| 225 | tests.passed + tests.failed + tests.errors, endtime - starttime)) |
| 226 | io.write(string.format("PASSED: %4d\n", tests.passed)) |
| 227 | io.write(string.format("FAILED: %4d\n", tests.failed)) |
| 228 | io.write(string.format("ERRORS: %4d\n", tests.errors)) |
| 229 | os.exit(tests.failed + tests.errors) |
| 230 |