t_options.lua revision 1.4 1 1.4 rillig -- $NetBSD: t_options.lua,v 1.4 2023/05/22 06:35:56 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.4 rillig local cmd = indent .. " " .. args .. " 2>&1"
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.1 rillig end
138 1.1 rillig
139 1.1 rillig local function handle_empty_section(line)
140 1.1 rillig if line == "" then
141 1.1 rillig curr_empty_lines = curr_empty_lines + 1
142 1.1 rillig else
143 1.1 rillig if curr_empty_lines > max_empty_lines then
144 1.1 rillig max_empty_lines = curr_empty_lines
145 1.1 rillig end
146 1.1 rillig if curr_empty_lines > 0 then
147 1.1 rillig if prev_empty_lines > 1 then
148 1.1 rillig warn(lineno - curr_empty_lines - 1,
149 1.1 rillig prev_empty_lines .. " empty lines a few "
150 1.1 rillig .. "lines above, should be only 1")
151 1.1 rillig end
152 1.1 rillig prev_empty_lines = curr_empty_lines
153 1.1 rillig end
154 1.1 rillig curr_empty_lines = 0
155 1.1 rillig end
156 1.1 rillig end
157 1.1 rillig
158 1.2 rillig local function handle_indent_input()
159 1.2 rillig if prev_empty_lines ~= 2 and seen_input_section then
160 1.2 rillig warn(lineno, "input section needs 2 empty lines "
161 1.2 rillig .. "above, not " .. prev_empty_lines)
162 1.2 rillig end
163 1.2 rillig check_empty_lines_block(2)
164 1.2 rillig check_unused_input()
165 1.2 rillig section = "input"
166 1.2 rillig section_excl_comm = ""
167 1.2 rillig section_incl_comm = ""
168 1.2 rillig unused_input_lineno = lineno
169 1.2 rillig seen_input_section = true
170 1.2 rillig output_excl_comm = ""
171 1.2 rillig output_incl_comm = ""
172 1.2 rillig output_lineno = 0
173 1.2 rillig end
174 1.1 rillig
175 1.2 rillig local function handle_indent_run(args)
176 1.2 rillig if section ~= "" then
177 1.2 rillig warn(lineno, "unfinished section '" .. section .. "'")
178 1.2 rillig end
179 1.2 rillig check_empty_lines_block(1)
180 1.2 rillig if prev_empty_lines ~= 1 then
181 1.2 rillig warn(lineno, "run section needs 1 empty line above, "
182 1.2 rillig .. "not " .. prev_empty_lines)
183 1.2 rillig end
184 1.2 rillig section = "run"
185 1.2 rillig output_lineno = lineno
186 1.2 rillig section_excl_comm = ""
187 1.2 rillig section_incl_comm = ""
188 1.1 rillig
189 1.2 rillig run_indent(input_excl_comm, args)
190 1.2 rillig unused_input_lineno = 0
191 1.2 rillig end
192 1.1 rillig
193 1.2 rillig local function handle_indent_run_equals_input(args)
194 1.2 rillig check_empty_lines_block(1)
195 1.2 rillig run_indent(input_excl_comm, args)
196 1.2 rillig expected_out:write(input_excl_comm)
197 1.2 rillig unused_input_lineno = 0
198 1.2 rillig max_empty_lines = 0
199 1.2 rillig end
200 1.1 rillig
201 1.2 rillig local function handle_indent_run_equals_prev_output(args)
202 1.2 rillig check_empty_lines_block(1)
203 1.2 rillig run_indent(input_excl_comm, args)
204 1.2 rillig expected_out:write(output_excl_comm)
205 1.2 rillig max_empty_lines = 0
206 1.2 rillig end
207 1.1 rillig
208 1.2 rillig local function handle_indent_end_input()
209 1.2 rillig if section_incl_comm == input_incl_comm then
210 1.2 rillig warn(lineno, "duplicate input; remove this section")
211 1.2 rillig end
212 1.1 rillig
213 1.2 rillig input_excl_comm = section_excl_comm
214 1.2 rillig input_incl_comm = section_incl_comm
215 1.2 rillig section = ""
216 1.2 rillig max_empty_lines = 0
217 1.2 rillig end
218 1.1 rillig
219 1.2 rillig local function handle_indent_end_run()
220 1.2 rillig if section_incl_comm == input_incl_comm then
221 1.2 rillig warn(output_lineno,
222 1.2 rillig "output == input; use run-equals-input")
223 1.2 rillig end
224 1.2 rillig if section_incl_comm == output_incl_comm then
225 1.2 rillig warn(output_lineno,
226 1.2 rillig "duplicate output; use run-equals-prev-output")
227 1.2 rillig end
228 1.1 rillig
229 1.2 rillig output_excl_comm = section_excl_comm
230 1.2 rillig output_incl_comm = section_incl_comm
231 1.2 rillig section = ""
232 1.2 rillig max_empty_lines = 0
233 1.2 rillig end
234 1.1 rillig
235 1.2 rillig local function handle_indent_directive(line, command, args)
236 1.2 rillig print(line)
237 1.2 rillig expected_out:write(line .. "\n")
238 1.1 rillig
239 1.4 rillig if command == "input" and args ~= "" then
240 1.4 rillig warn(lineno, "'//indent input' does not take arguments")
241 1.4 rillig elseif command == "input" then
242 1.2 rillig handle_indent_input()
243 1.2 rillig elseif command == "run" then
244 1.2 rillig handle_indent_run(args)
245 1.2 rillig elseif command == "run-equals-input" then
246 1.2 rillig handle_indent_run_equals_input(args)
247 1.2 rillig elseif command == "run-equals-prev-output" then
248 1.2 rillig handle_indent_run_equals_prev_output(args)
249 1.2 rillig elseif command == "end" and args ~= "" then
250 1.2 rillig warn(lineno, "'//indent end' does not take arguments")
251 1.2 rillig elseif command == "end" and section == "input" then
252 1.2 rillig handle_indent_end_input()
253 1.2 rillig elseif command == "end" and section == "run" then
254 1.2 rillig handle_indent_end_run()
255 1.1 rillig elseif command == "end" then
256 1.1 rillig warn(lineno, "misplaced '//indent end'")
257 1.1 rillig else
258 1.1 rillig die(lineno, "invalid line '" .. line .. "'")
259 1.1 rillig end
260 1.1 rillig
261 1.1 rillig prev_empty_lines = 0
262 1.1 rillig curr_empty_lines = 0
263 1.1 rillig end
264 1.1 rillig
265 1.1 rillig local function handle_line(line)
266 1.1 rillig if section == "" then
267 1.1 rillig handle_empty_section(line)
268 1.1 rillig end
269 1.1 rillig
270 1.1 rillig -- Hide comments starting with dollar from indent; they are used for
271 1.1 rillig -- marking bugs and adding other remarks directly in the input or
272 1.1 rillig -- output sections.
273 1.1 rillig if line:match("^%s*/[*]%s*[$].*[*]/$")
274 1.1 rillig or line:match("^%s*//%s*[$]") then
275 1.1 rillig if section ~= "" then
276 1.1 rillig section_incl_comm = section_incl_comm .. line .. "\n"
277 1.1 rillig end
278 1.1 rillig return
279 1.1 rillig end
280 1.1 rillig
281 1.1 rillig local cmd, args = line:match("^//indent%s+([^%s]+)%s*(.*)$")
282 1.1 rillig if cmd then
283 1.1 rillig handle_indent_directive(line, cmd, args)
284 1.1 rillig return
285 1.1 rillig end
286 1.1 rillig
287 1.1 rillig if section == "input" or section == "run" then
288 1.1 rillig section_excl_comm = section_excl_comm .. line .. "\n"
289 1.1 rillig section_incl_comm = section_incl_comm .. line .. "\n"
290 1.1 rillig end
291 1.1 rillig
292 1.1 rillig if section == "run" then
293 1.1 rillig expected_out:write(line .. "\n")
294 1.1 rillig end
295 1.1 rillig
296 1.1 rillig if section == ""
297 1.1 rillig and line ~= ""
298 1.1 rillig and line:sub(1, 1) ~= "/"
299 1.1 rillig and line:sub(1, 2) ~= " *" then
300 1.1 rillig warn(lineno, "non-comment line outside 'input' or 'run' "
301 1.1 rillig .. "section")
302 1.1 rillig end
303 1.1 rillig end
304 1.1 rillig
305 1.1 rillig local function handle_file(fname)
306 1.1 rillig init_file(fname)
307 1.1 rillig local f = assert(io.open(fname))
308 1.1 rillig for line in f:lines() do
309 1.1 rillig lineno = lineno + 1
310 1.1 rillig handle_line(line)
311 1.1 rillig end
312 1.1 rillig f:close()
313 1.1 rillig end
314 1.1 rillig
315 1.1 rillig local function main()
316 1.1 rillig for _, arg in ipairs(arg) do
317 1.1 rillig handle_file(arg)
318 1.1 rillig end
319 1.1 rillig if section ~= "" then
320 1.1 rillig die(lineno, "still in section '" .. section .. "'")
321 1.1 rillig end
322 1.1 rillig check_unused_input()
323 1.1 rillig expected_out:close()
324 1.1 rillig os.exit(not warned)
325 1.1 rillig end
326 1.1 rillig
327 1.1 rillig main()
328