1 1.8 rillig -- $NetBSD: t_options.lua,v 1.8 2024/12/12 05:33:47 rillig Exp $ 2 1.1 rillig -- 3 1.1 rillig -- Copyright (c) 2023 The NetBSD Foundation, Inc. 4 1.1 rillig -- All rights reserved. 5 1.1 rillig -- 6 1.1 rillig -- Redistribution and use in source and binary forms, with or without 7 1.1 rillig -- modification, are permitted provided that the following conditions 8 1.1 rillig -- are met: 9 1.1 rillig -- 1. Redistributions of source code must retain the above copyright 10 1.1 rillig -- notice, this list of conditions and the following disclaimer. 11 1.1 rillig -- 2. Redistributions in binary form must reproduce the above copyright 12 1.1 rillig -- notice, this list of conditions and the following disclaimer in the 13 1.1 rillig -- documentation and/or other materials provided with the distribution. 14 1.1 rillig -- 15 1.1 rillig -- THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 16 1.1 rillig -- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17 1.1 rillig -- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 1.1 rillig -- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 19 1.1 rillig -- BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 1.1 rillig -- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 1.1 rillig -- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 1.1 rillig -- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 1.1 rillig -- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 1.1 rillig -- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 1.1 rillig -- POSSIBILITY OF SUCH DAMAGE. 26 1.1 rillig 27 1.8 rillig -- usage: [INDENT=...] lua t_options.lua <file.c>... 28 1.4 rillig -- 29 1.8 rillig -- Run indent on several inputs with different command line options, verifying 30 1.8 rillig -- that the actual output equals the expected output. 31 1.1 rillig -- 32 1.1 rillig -- The test files contain the input to be formatted, the formatting options 33 1.1 rillig -- and the output, all as close together as possible. The test files use the 34 1.1 rillig -- following directives: 35 1.1 rillig -- 36 1.4 rillig -- //indent input 37 1.1 rillig -- Specifies the input to be formatted. 38 1.1 rillig -- //indent run [options] 39 1.1 rillig -- Runs indent on the input, using the given options. 40 1.4 rillig -- //indent end 41 1.1 rillig -- Finishes an '//indent input' or '//indent run' section. 42 1.1 rillig -- //indent run-equals-input [options] 43 1.8 rillig -- Runs indent on the input, expecting that the output is the 44 1.8 rillig -- same as the input. 45 1.1 rillig -- //indent run-equals-prev-output [options] 46 1.1 rillig -- Runs indent on the input, expecting the same output as from 47 1.1 rillig -- the previous run. 48 1.1 rillig -- 49 1.1 rillig -- All text outside input..end or run..end directives is not passed to indent. 50 1.1 rillig -- 51 1.1 rillig -- Inside the input..end or run..end sections, comments that start with '$' 52 1.1 rillig -- are filtered out, they can be used for remarks near the affected code. 53 1.1 rillig -- 54 1.1 rillig -- The actual output from running indent is written to stdout, the expected 55 1.8 rillig -- test output is written to 'expected.out', ready to be compared using diff. 56 1.1 rillig 57 1.1 rillig local warned = false 58 1.1 rillig 59 1.1 rillig local filename = "" 60 1.1 rillig local lineno = 0 61 1.1 rillig 62 1.1 rillig local prev_empty_lines = 0 -- the finished "block" of empty lines 63 1.1 rillig local curr_empty_lines = 0 -- the ongoing "block" of empty lines 64 1.1 rillig local max_empty_lines = 0 -- between sections 65 1.1 rillig local seen_input_section = false-- the first input section is not checked 66 1.1 rillig 67 1.1 rillig local section = "" -- "", "input" or "run" 68 1.1 rillig local section_excl_comm = "" -- without dollar comments 69 1.1 rillig local section_incl_comm = "" -- with dollar comments 70 1.1 rillig 71 1.1 rillig local input_excl_comm = "" -- the input text for indent 72 1.1 rillig local input_incl_comm = "" -- used for duplicate checks 73 1.1 rillig local unused_input_lineno = 0 74 1.1 rillig 75 1.1 rillig local output_excl_comm = "" -- expected output 76 1.1 rillig local output_incl_comm = "" -- used for duplicate checks 77 1.1 rillig local output_lineno = 0 78 1.1 rillig 79 1.1 rillig local expected_out = assert(io.open("expected.out", "w")) 80 1.1 rillig 81 1.8 rillig local function err(ln, msg) 82 1.1 rillig io.stderr:write(("%s:%d: error: %s\n"):format(filename, ln, msg)) 83 1.1 rillig os.exit(false) 84 1.1 rillig end 85 1.1 rillig 86 1.1 rillig local function warn(ln, msg) 87 1.1 rillig io.stderr:write(("%s:%d: warning: %s\n"):format(filename, ln, msg)) 88 1.1 rillig warned = true 89 1.1 rillig end 90 1.1 rillig 91 1.1 rillig local function init_file(fname) 92 1.1 rillig filename = fname 93 1.1 rillig lineno = 0 94 1.1 rillig 95 1.1 rillig prev_empty_lines = 0 96 1.1 rillig curr_empty_lines = 0 97 1.1 rillig max_empty_lines = 0 98 1.1 rillig seen_input_section = false 99 1.1 rillig 100 1.1 rillig section = "" 101 1.1 rillig section_excl_comm = "" 102 1.1 rillig section_incl_comm = "" 103 1.1 rillig 104 1.1 rillig input_excl_comm = "" 105 1.1 rillig input_incl_comm = "" 106 1.1 rillig unused_input_lineno = 0 107 1.1 rillig 108 1.1 rillig output_excl_comm = "" 109 1.1 rillig output_incl_comm = "" 110 1.1 rillig output_lineno = 0 111 1.1 rillig end 112 1.1 rillig 113 1.1 rillig local function check_empty_lines_block(n) 114 1.1 rillig if max_empty_lines ~= n and seen_input_section then 115 1.1 rillig warn(lineno, ("expecting %d empty %s, got %d") 116 1.8 rillig :format(n, n ~= 1 and "lines" or "line", max_empty_lines)) 117 1.1 rillig end 118 1.1 rillig end 119 1.1 rillig 120 1.1 rillig local function check_unused_input() 121 1.1 rillig if unused_input_lineno ~= 0 then 122 1.1 rillig warn(unused_input_lineno, "input is not used") 123 1.1 rillig end 124 1.1 rillig end 125 1.1 rillig 126 1.1 rillig local function run_indent(inp, args) 127 1.1 rillig local indent = os.getenv("INDENT") or "indent" 128 1.5 rillig local cmd = indent .. " " .. args .. " 2>t_options.err" 129 1.1 rillig 130 1.4 rillig local indent_in = assert(io.popen(cmd, "w")) 131 1.1 rillig indent_in:write(inp) 132 1.4 rillig local ok, kind, info = indent_in:close() 133 1.1 rillig if not ok then 134 1.6 rillig print("// " .. kind .. " " .. info) 135 1.1 rillig end 136 1.5 rillig for line in io.lines("t_options.err") do 137 1.6 rillig print("// " .. line) 138 1.5 rillig end 139 1.1 rillig end 140 1.1 rillig 141 1.8 rillig local function handle_line_outside_section(line) 142 1.1 rillig if line == "" then 143 1.1 rillig curr_empty_lines = curr_empty_lines + 1 144 1.8 rillig return 145 1.8 rillig end 146 1.8 rillig if curr_empty_lines > max_empty_lines then 147 1.8 rillig max_empty_lines = curr_empty_lines 148 1.8 rillig end 149 1.8 rillig if curr_empty_lines > 0 then 150 1.8 rillig if prev_empty_lines > 1 then 151 1.8 rillig warn(lineno - curr_empty_lines - 1, 152 1.8 rillig prev_empty_lines .. " empty lines a few " 153 1.8 rillig .. "lines above, should be only 1") 154 1.1 rillig end 155 1.8 rillig prev_empty_lines = curr_empty_lines 156 1.1 rillig end 157 1.8 rillig curr_empty_lines = 0 158 1.1 rillig end 159 1.1 rillig 160 1.2 rillig local function handle_indent_input() 161 1.2 rillig if prev_empty_lines ~= 2 and seen_input_section then 162 1.2 rillig warn(lineno, "input section needs 2 empty lines " 163 1.2 rillig .. "above, not " .. prev_empty_lines) 164 1.2 rillig end 165 1.2 rillig check_empty_lines_block(2) 166 1.2 rillig check_unused_input() 167 1.2 rillig section = "input" 168 1.2 rillig section_excl_comm = "" 169 1.2 rillig section_incl_comm = "" 170 1.2 rillig unused_input_lineno = lineno 171 1.2 rillig seen_input_section = true 172 1.2 rillig output_excl_comm = "" 173 1.2 rillig output_incl_comm = "" 174 1.2 rillig output_lineno = 0 175 1.2 rillig end 176 1.1 rillig 177 1.2 rillig local function handle_indent_run(args) 178 1.2 rillig if section ~= "" then 179 1.2 rillig warn(lineno, "unfinished section '" .. section .. "'") 180 1.2 rillig end 181 1.2 rillig check_empty_lines_block(1) 182 1.2 rillig if prev_empty_lines ~= 1 then 183 1.2 rillig warn(lineno, "run section needs 1 empty line above, " 184 1.2 rillig .. "not " .. prev_empty_lines) 185 1.2 rillig end 186 1.2 rillig section = "run" 187 1.2 rillig output_lineno = lineno 188 1.2 rillig section_excl_comm = "" 189 1.2 rillig section_incl_comm = "" 190 1.1 rillig 191 1.2 rillig run_indent(input_excl_comm, args) 192 1.2 rillig unused_input_lineno = 0 193 1.2 rillig end 194 1.1 rillig 195 1.2 rillig local function handle_indent_run_equals_input(args) 196 1.2 rillig check_empty_lines_block(1) 197 1.2 rillig run_indent(input_excl_comm, args) 198 1.2 rillig expected_out:write(input_excl_comm) 199 1.2 rillig unused_input_lineno = 0 200 1.2 rillig max_empty_lines = 0 201 1.7 rillig output_incl_comm = "" 202 1.7 rillig output_excl_comm = "" 203 1.2 rillig end 204 1.1 rillig 205 1.2 rillig local function handle_indent_run_equals_prev_output(args) 206 1.7 rillig if output_incl_comm == "" then 207 1.7 rillig warn(lineno, 208 1.7 rillig "no previous output; use run-equals-input instead") 209 1.7 rillig end 210 1.2 rillig check_empty_lines_block(1) 211 1.2 rillig run_indent(input_excl_comm, args) 212 1.2 rillig expected_out:write(output_excl_comm) 213 1.2 rillig max_empty_lines = 0 214 1.2 rillig end 215 1.1 rillig 216 1.2 rillig local function handle_indent_end_input() 217 1.2 rillig if section_incl_comm == input_incl_comm then 218 1.2 rillig warn(lineno, "duplicate input; remove this section") 219 1.2 rillig end 220 1.1 rillig 221 1.2 rillig input_excl_comm = section_excl_comm 222 1.2 rillig input_incl_comm = section_incl_comm 223 1.2 rillig section = "" 224 1.2 rillig max_empty_lines = 0 225 1.2 rillig end 226 1.1 rillig 227 1.2 rillig local function handle_indent_end_run() 228 1.2 rillig if section_incl_comm == input_incl_comm then 229 1.2 rillig warn(output_lineno, 230 1.2 rillig "output == input; use run-equals-input") 231 1.2 rillig end 232 1.2 rillig if section_incl_comm == output_incl_comm then 233 1.2 rillig warn(output_lineno, 234 1.2 rillig "duplicate output; use run-equals-prev-output") 235 1.2 rillig end 236 1.1 rillig 237 1.2 rillig output_excl_comm = section_excl_comm 238 1.2 rillig output_incl_comm = section_incl_comm 239 1.2 rillig section = "" 240 1.2 rillig max_empty_lines = 0 241 1.2 rillig end 242 1.1 rillig 243 1.2 rillig local function handle_indent_directive(line, command, args) 244 1.2 rillig print(line) 245 1.2 rillig expected_out:write(line .. "\n") 246 1.1 rillig 247 1.4 rillig if command == "input" and args ~= "" then 248 1.4 rillig warn(lineno, "'//indent input' does not take arguments") 249 1.4 rillig elseif command == "input" then 250 1.2 rillig handle_indent_input() 251 1.2 rillig elseif command == "run" then 252 1.2 rillig handle_indent_run(args) 253 1.2 rillig elseif command == "run-equals-input" then 254 1.2 rillig handle_indent_run_equals_input(args) 255 1.2 rillig elseif command == "run-equals-prev-output" then 256 1.2 rillig handle_indent_run_equals_prev_output(args) 257 1.2 rillig elseif command == "end" and args ~= "" then 258 1.2 rillig warn(lineno, "'//indent end' does not take arguments") 259 1.2 rillig elseif command == "end" and section == "input" then 260 1.2 rillig handle_indent_end_input() 261 1.2 rillig elseif command == "end" and section == "run" then 262 1.2 rillig handle_indent_end_run() 263 1.1 rillig elseif command == "end" then 264 1.1 rillig warn(lineno, "misplaced '//indent end'") 265 1.1 rillig else 266 1.8 rillig err(lineno, "invalid line '" .. line .. "'") 267 1.1 rillig end 268 1.1 rillig 269 1.1 rillig prev_empty_lines = 0 270 1.1 rillig curr_empty_lines = 0 271 1.1 rillig end 272 1.1 rillig 273 1.1 rillig local function handle_line(line) 274 1.1 rillig if section == "" then 275 1.8 rillig handle_line_outside_section(line) 276 1.1 rillig end 277 1.1 rillig 278 1.1 rillig -- Hide comments starting with dollar from indent; they are used for 279 1.1 rillig -- marking bugs and adding other remarks directly in the input or 280 1.1 rillig -- output sections. 281 1.1 rillig if line:match("^%s*/[*]%s*[$].*[*]/$") 282 1.1 rillig or line:match("^%s*//%s*[$]") then 283 1.1 rillig if section ~= "" then 284 1.1 rillig section_incl_comm = section_incl_comm .. line .. "\n" 285 1.1 rillig end 286 1.1 rillig return 287 1.1 rillig end 288 1.1 rillig 289 1.1 rillig local cmd, args = line:match("^//indent%s+([^%s]+)%s*(.*)$") 290 1.1 rillig if cmd then 291 1.1 rillig handle_indent_directive(line, cmd, args) 292 1.1 rillig return 293 1.1 rillig end 294 1.1 rillig 295 1.1 rillig if section == "input" or section == "run" then 296 1.1 rillig section_excl_comm = section_excl_comm .. line .. "\n" 297 1.1 rillig section_incl_comm = section_incl_comm .. line .. "\n" 298 1.1 rillig end 299 1.1 rillig 300 1.1 rillig if section == "run" then 301 1.1 rillig expected_out:write(line .. "\n") 302 1.1 rillig end 303 1.1 rillig 304 1.1 rillig if section == "" 305 1.1 rillig and line ~= "" 306 1.1 rillig and line:sub(1, 1) ~= "/" 307 1.1 rillig and line:sub(1, 2) ~= " *" then 308 1.1 rillig warn(lineno, "non-comment line outside 'input' or 'run' " 309 1.1 rillig .. "section") 310 1.1 rillig end 311 1.1 rillig end 312 1.1 rillig 313 1.1 rillig local function handle_file(fname) 314 1.1 rillig init_file(fname) 315 1.1 rillig local f = assert(io.open(fname)) 316 1.1 rillig for line in f:lines() do 317 1.1 rillig lineno = lineno + 1 318 1.1 rillig handle_line(line) 319 1.1 rillig end 320 1.1 rillig f:close() 321 1.1 rillig end 322 1.1 rillig 323 1.1 rillig local function main() 324 1.1 rillig for _, arg in ipairs(arg) do 325 1.1 rillig handle_file(arg) 326 1.1 rillig end 327 1.1 rillig if section ~= "" then 328 1.8 rillig err(lineno, "still in section '" .. section .. "'") 329 1.1 rillig end 330 1.1 rillig check_unused_input() 331 1.1 rillig expected_out:close() 332 1.1 rillig os.exit(not warned) 333 1.1 rillig end 334 1.1 rillig 335 1.1 rillig main() 336