t_options.lua revision 1.5 1 1.5 rillig -- $NetBSD: t_options.lua,v 1.5 2023/06/14 17:07:32 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.4 rillig -- usage: [INDENT=...] lua t_options.lua <file>...
28 1.4 rillig --
29 1.1 rillig -- Test driver for indent that runs indent on several inputs, checks the
30 1.1 rillig -- output and can run indent with different command line options on the same
31 1.1 rillig -- input.
32 1.1 rillig --
33 1.1 rillig -- The test files contain the input to be formatted, the formatting options
34 1.1 rillig -- and the output, all as close together as possible. The test files use the
35 1.1 rillig -- following directives:
36 1.1 rillig --
37 1.4 rillig -- //indent input
38 1.1 rillig -- Specifies the input to be formatted.
39 1.1 rillig -- //indent run [options]
40 1.1 rillig -- Runs indent on the input, using the given options.
41 1.4 rillig -- //indent end
42 1.1 rillig -- Finishes an '//indent input' or '//indent run' section.
43 1.1 rillig -- //indent run-equals-input [options]
44 1.1 rillig -- Runs indent on the input, expecting unmodified output.
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.1 rillig -- test output is written to 'expected.out'.
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.1 rillig local function die(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 local lines = n ~= 1 and "lines" or "line"
116 1.1 rillig warn(lineno, ("expecting %d empty %s, got %d")
117 1.1 rillig :format(n, lines, max_empty_lines))
118 1.1 rillig end
119 1.1 rillig end
120 1.1 rillig
121 1.1 rillig local function check_unused_input()
122 1.1 rillig if unused_input_lineno ~= 0 then
123 1.1 rillig warn(unused_input_lineno, "input is not used")
124 1.1 rillig end
125 1.1 rillig end
126 1.1 rillig
127 1.1 rillig local function run_indent(inp, args)
128 1.1 rillig local indent = os.getenv("INDENT") or "indent"
129 1.5 rillig local cmd = indent .. " " .. args .. " 2>t_options.err"
130 1.1 rillig
131 1.4 rillig local indent_in = assert(io.popen(cmd, "w"))
132 1.1 rillig indent_in:write(inp)
133 1.4 rillig local ok, kind, info = indent_in:close()
134 1.1 rillig if not ok then
135 1.1 rillig print(kind .. " " .. info)
136 1.1 rillig end
137 1.5 rillig for line in io.lines("t_options.err") do
138 1.5 rillig print(line)
139 1.5 rillig end
140 1.1 rillig end
141 1.1 rillig
142 1.1 rillig local function handle_empty_section(line)
143 1.1 rillig if line == "" then
144 1.1 rillig curr_empty_lines = curr_empty_lines + 1
145 1.1 rillig else
146 1.1 rillig if curr_empty_lines > max_empty_lines then
147 1.1 rillig max_empty_lines = curr_empty_lines
148 1.1 rillig end
149 1.1 rillig if curr_empty_lines > 0 then
150 1.1 rillig if prev_empty_lines > 1 then
151 1.1 rillig warn(lineno - curr_empty_lines - 1,
152 1.1 rillig prev_empty_lines .. " empty lines a few "
153 1.1 rillig .. "lines above, should be only 1")
154 1.1 rillig end
155 1.1 rillig prev_empty_lines = curr_empty_lines
156 1.1 rillig end
157 1.1 rillig curr_empty_lines = 0
158 1.1 rillig end
159 1.1 rillig end
160 1.1 rillig
161 1.2 rillig local function handle_indent_input()
162 1.2 rillig if prev_empty_lines ~= 2 and seen_input_section then
163 1.2 rillig warn(lineno, "input section needs 2 empty lines "
164 1.2 rillig .. "above, not " .. prev_empty_lines)
165 1.2 rillig end
166 1.2 rillig check_empty_lines_block(2)
167 1.2 rillig check_unused_input()
168 1.2 rillig section = "input"
169 1.2 rillig section_excl_comm = ""
170 1.2 rillig section_incl_comm = ""
171 1.2 rillig unused_input_lineno = lineno
172 1.2 rillig seen_input_section = true
173 1.2 rillig output_excl_comm = ""
174 1.2 rillig output_incl_comm = ""
175 1.2 rillig output_lineno = 0
176 1.2 rillig end
177 1.1 rillig
178 1.2 rillig local function handle_indent_run(args)
179 1.2 rillig if section ~= "" then
180 1.2 rillig warn(lineno, "unfinished section '" .. section .. "'")
181 1.2 rillig end
182 1.2 rillig check_empty_lines_block(1)
183 1.2 rillig if prev_empty_lines ~= 1 then
184 1.2 rillig warn(lineno, "run section needs 1 empty line above, "
185 1.2 rillig .. "not " .. prev_empty_lines)
186 1.2 rillig end
187 1.2 rillig section = "run"
188 1.2 rillig output_lineno = lineno
189 1.2 rillig section_excl_comm = ""
190 1.2 rillig section_incl_comm = ""
191 1.1 rillig
192 1.2 rillig run_indent(input_excl_comm, args)
193 1.2 rillig unused_input_lineno = 0
194 1.2 rillig end
195 1.1 rillig
196 1.2 rillig local function handle_indent_run_equals_input(args)
197 1.2 rillig check_empty_lines_block(1)
198 1.2 rillig run_indent(input_excl_comm, args)
199 1.2 rillig expected_out:write(input_excl_comm)
200 1.2 rillig unused_input_lineno = 0
201 1.2 rillig max_empty_lines = 0
202 1.2 rillig end
203 1.1 rillig
204 1.2 rillig local function handle_indent_run_equals_prev_output(args)
205 1.2 rillig check_empty_lines_block(1)
206 1.2 rillig run_indent(input_excl_comm, args)
207 1.2 rillig expected_out:write(output_excl_comm)
208 1.2 rillig max_empty_lines = 0
209 1.2 rillig end
210 1.1 rillig
211 1.2 rillig local function handle_indent_end_input()
212 1.2 rillig if section_incl_comm == input_incl_comm then
213 1.2 rillig warn(lineno, "duplicate input; remove this section")
214 1.2 rillig end
215 1.1 rillig
216 1.2 rillig input_excl_comm = section_excl_comm
217 1.2 rillig input_incl_comm = section_incl_comm
218 1.2 rillig section = ""
219 1.2 rillig max_empty_lines = 0
220 1.2 rillig end
221 1.1 rillig
222 1.2 rillig local function handle_indent_end_run()
223 1.2 rillig if section_incl_comm == input_incl_comm then
224 1.2 rillig warn(output_lineno,
225 1.2 rillig "output == input; use run-equals-input")
226 1.2 rillig end
227 1.2 rillig if section_incl_comm == output_incl_comm then
228 1.2 rillig warn(output_lineno,
229 1.2 rillig "duplicate output; use run-equals-prev-output")
230 1.2 rillig end
231 1.1 rillig
232 1.2 rillig output_excl_comm = section_excl_comm
233 1.2 rillig output_incl_comm = section_incl_comm
234 1.2 rillig section = ""
235 1.2 rillig max_empty_lines = 0
236 1.2 rillig end
237 1.1 rillig
238 1.2 rillig local function handle_indent_directive(line, command, args)
239 1.2 rillig print(line)
240 1.2 rillig expected_out:write(line .. "\n")
241 1.1 rillig
242 1.4 rillig if command == "input" and args ~= "" then
243 1.4 rillig warn(lineno, "'//indent input' does not take arguments")
244 1.4 rillig elseif command == "input" then
245 1.2 rillig handle_indent_input()
246 1.2 rillig elseif command == "run" then
247 1.2 rillig handle_indent_run(args)
248 1.2 rillig elseif command == "run-equals-input" then
249 1.2 rillig handle_indent_run_equals_input(args)
250 1.2 rillig elseif command == "run-equals-prev-output" then
251 1.2 rillig handle_indent_run_equals_prev_output(args)
252 1.2 rillig elseif command == "end" and args ~= "" then
253 1.2 rillig warn(lineno, "'//indent end' does not take arguments")
254 1.2 rillig elseif command == "end" and section == "input" then
255 1.2 rillig handle_indent_end_input()
256 1.2 rillig elseif command == "end" and section == "run" then
257 1.2 rillig handle_indent_end_run()
258 1.1 rillig elseif command == "end" then
259 1.1 rillig warn(lineno, "misplaced '//indent end'")
260 1.1 rillig else
261 1.1 rillig die(lineno, "invalid line '" .. line .. "'")
262 1.1 rillig end
263 1.1 rillig
264 1.1 rillig prev_empty_lines = 0
265 1.1 rillig curr_empty_lines = 0
266 1.1 rillig end
267 1.1 rillig
268 1.1 rillig local function handle_line(line)
269 1.1 rillig if section == "" then
270 1.1 rillig handle_empty_section(line)
271 1.1 rillig end
272 1.1 rillig
273 1.1 rillig -- Hide comments starting with dollar from indent; they are used for
274 1.1 rillig -- marking bugs and adding other remarks directly in the input or
275 1.1 rillig -- output sections.
276 1.1 rillig if line:match("^%s*/[*]%s*[$].*[*]/$")
277 1.1 rillig or line:match("^%s*//%s*[$]") then
278 1.1 rillig if section ~= "" then
279 1.1 rillig section_incl_comm = section_incl_comm .. line .. "\n"
280 1.1 rillig end
281 1.1 rillig return
282 1.1 rillig end
283 1.1 rillig
284 1.1 rillig local cmd, args = line:match("^//indent%s+([^%s]+)%s*(.*)$")
285 1.1 rillig if cmd then
286 1.1 rillig handle_indent_directive(line, cmd, args)
287 1.1 rillig return
288 1.1 rillig end
289 1.1 rillig
290 1.1 rillig if section == "input" or section == "run" then
291 1.1 rillig section_excl_comm = section_excl_comm .. line .. "\n"
292 1.1 rillig section_incl_comm = section_incl_comm .. line .. "\n"
293 1.1 rillig end
294 1.1 rillig
295 1.1 rillig if section == "run" then
296 1.1 rillig expected_out:write(line .. "\n")
297 1.1 rillig end
298 1.1 rillig
299 1.1 rillig if section == ""
300 1.1 rillig and line ~= ""
301 1.1 rillig and line:sub(1, 1) ~= "/"
302 1.1 rillig and line:sub(1, 2) ~= " *" then
303 1.1 rillig warn(lineno, "non-comment line outside 'input' or 'run' "
304 1.1 rillig .. "section")
305 1.1 rillig end
306 1.1 rillig end
307 1.1 rillig
308 1.1 rillig local function handle_file(fname)
309 1.1 rillig init_file(fname)
310 1.1 rillig local f = assert(io.open(fname))
311 1.1 rillig for line in f:lines() do
312 1.1 rillig lineno = lineno + 1
313 1.1 rillig handle_line(line)
314 1.1 rillig end
315 1.1 rillig f:close()
316 1.1 rillig end
317 1.1 rillig
318 1.1 rillig local function main()
319 1.1 rillig for _, arg in ipairs(arg) do
320 1.1 rillig handle_file(arg)
321 1.1 rillig end
322 1.1 rillig if section ~= "" then
323 1.1 rillig die(lineno, "still in section '" .. section .. "'")
324 1.1 rillig end
325 1.1 rillig check_unused_input()
326 1.1 rillig expected_out:close()
327 1.1 rillig os.exit(not warned)
328 1.1 rillig end
329 1.1 rillig
330 1.1 rillig main()
331