t_options.lua revision 1.8 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