1010-- every requested nvim binary, so CI correctly fails when any spec
1111-- fails.
1212
13- local fn = vim .fn
13+ --- @class parallel_test.worker
14+ --- @field id integer
15+ --- @field spec string
16+ --- @field cmd string[]
17+ --- @field env table<string , string>
18+ --- @field buf string[]
19+ --- @field started boolean
20+ --- @field finished boolean
21+ --- @field code integer
22+ --- @field signal integer
1423
15- --- @class parallel_test_opts
24+ --- @class parallel_test.opts
1625--- @field jobs integer
1726--- @field glob ? string
1827--- @field filter ? string
1928--- @field serial boolean
2029--- @field nvim_execs string[]
2130
2231--- @param args string[]
23- --- @return parallel_test_opts
32+ --- @return parallel_test.opts
2433local function parse_args (args )
34+ --- @type parallel_test.opts
2535 local opts = { jobs = 0 , serial = false , nvim_execs = {} }
2636 local i = 1
2737 while i <= # args do
2838 local a = args [i ]
2939 if a == " --jobs" or a == " -j" then
3040 i = i + 1
31- opts .jobs = tonumber (args [i ]) or 0
41+ local raw = args [i ] and tonumber (args [i ]) or nil
42+ --- @cast raw integer ?
43+ opts .jobs = raw or 0
3244 elseif a == " --glob" then
3345 i = i + 1
3446 opts .glob = args [i ]
@@ -49,34 +61,38 @@ Options:
4961 --help, -h Show this help
5062]] )
5163 os.exit (0 )
52- elseif a :sub (1 , 1 ) == " -" then
64+ elseif type ( a ) == " string " and a :sub (1 , 1 ) == " -" then
5365 io.stderr :write (" parallel_test: unknown option: " .. a .. " \n " )
5466 os.exit (2 )
55- else
67+ elseif type ( a ) == " string " then
5668 table.insert (opts .nvim_execs , a )
5769 end
5870 i = i + 1
5971 end
6072
6173 if opts .jobs <= 0 then
62- if vim .uv and vim .uv .os_getenv (" NUMBER_OF_PROCESSORS" ) then
63- opts .jobs = tonumber (vim .uv .os_getenv (" NUMBER_OF_PROCESSORS" )) or 1
74+ local env = vim .uv and vim .uv .os_getenv (" NUMBER_OF_PROCESSORS" ) or nil
75+ if env then
76+ local raw = tonumber (env )
77+ --- @cast raw integer ?
78+ opts .jobs = raw or 1
6479 elseif jit and jit .os == " Windows" then
65- opts .jobs = tonumber ( vim . uv . os_getenv ( " NUMBER_OF_PROCESSORS " )) or 4
80+ opts .jobs = 4
6681 else
67- -- POSIX fallback: count online processors.
68- local n = 0
82+ local count = 0
6983 local f = io.open (" /proc/cpuinfo" , " r" )
7084 if f then
7185 for line in f :lines () do
72- if line :match (" ^processor%s*:" ) then n = n + 1 end
86+ if type (line ) == " string" and line :match (" ^processor%s*:" ) then
87+ count = count + 1
88+ end
7389 end
7490 f :close ()
7591 end
76- opts .jobs = n > 0 and n or 4
92+ opts .jobs = count > 0 and count or 4
7793 end
7894 end
79- opts .jobs = math.max ( 1 , opts .jobs )
95+ if opts .jobs < 1 then opts .jobs = 1 end
8096 return opts
8197end
8298
@@ -95,36 +111,24 @@ local function list_specs(root, pattern)
95111end
96112
97113--- @param nvim_exec string
98- --- @param spec string
99114--- @return string[]
100- local function build_cmd (nvim_exec , spec )
115+ local function build_cmd (nvim_exec )
101116 return {
102117 nvim_exec , " --headless" , " --noplugin" ,
103118 " -u" , " ./scripts/minimal_init.lua" ,
104119 " -l" , " ./scripts/make_cli.lua" ,
105120 }
106121end
107122
108- --- @class worker
109- --- @field id integer
110- --- @field spec string
111- --- @field cmd string[]
112- --- @field env table<string , string>
113- --- @field buf string[]
114- --- @field started boolean
115- --- @field finished boolean
116- --- @field code integer
117- --- @field signal integer
118-
119- --- @param w worker
123+ --- @param w parallel_test.worker
120124--- @return fun ( _err : string ?, data : string ?)
121125local function make_data_cb (w )
122126 return function (_err , data )
123127 if data then table.insert (w .buf , data ) end
124128 end
125129end
126130
127- --- @param w worker
131+ --- @param w parallel_test. worker
128132--- @return fun ( obj : vim.SystemCompleted )
129133local function make_exit_cb (w )
130134 return vim .schedule_wrap (function (obj )
@@ -134,7 +138,7 @@ local function make_exit_cb(w)
134138 end )
135139end
136140
137- --- @param workers worker[]
141+ --- @param workers parallel_test. worker[]
138142--- @param jobs integer
139143local function drive (workers , jobs )
140144 local next_idx = 1
@@ -143,21 +147,25 @@ local function drive(workers, jobs)
143147 local function spawn_one ()
144148 while in_flight < jobs and next_idx <= # workers do
145149 local w = workers [next_idx ]
146- next_idx = next_idx + 1
147- in_flight = in_flight + 1
148- w .started = true
149- vim .system (w .cmd , {
150- cwd = " ." ,
151- text = true ,
152- env = w .env ,
153- stdout = make_data_cb (w ),
154- stderr = make_data_cb (w ),
155- }, vim .schedule_wrap (function (obj )
156- w .finished = true
157- w .code = obj .code
158- w .signal = obj .signal
159- in_flight = in_flight - 1
160- end ))
150+ if w then
151+ next_idx = next_idx + 1
152+ in_flight = in_flight + 1
153+ w .started = true
154+ vim .system (w .cmd , {
155+ cwd = " ." ,
156+ text = true ,
157+ env = w .env ,
158+ stdout = make_data_cb (w ),
159+ stderr = make_data_cb (w ),
160+ }, vim .schedule_wrap (function (obj )
161+ w .finished = true
162+ w .code = obj .code
163+ w .signal = obj .signal
164+ in_flight = in_flight - 1
165+ end ))
166+ else
167+ next_idx = next_idx + 1
168+ end
161169 end
162170 end
163171
@@ -168,12 +176,12 @@ local function drive(workers, jobs)
168176 while true do
169177 local all_done = true
170178 for _ , w in ipairs (workers ) do
171- if not w .finished then all_done = false ; break end
179+ if w and not w .finished then all_done = false ; break end
172180 end
173181 if all_done then break end
174182 vim .wait (200 , function ()
175183 for _ , w in ipairs (workers ) do
176- if not w .finished then return false end
184+ if w and not w .finished then return false end
177185 end
178186 return true
179187 end )
@@ -182,10 +190,9 @@ local function drive(workers, jobs)
182190 vim .wait (50 )
183191end
184192
185- --- @param workers worker[]
186- --- @param opts parallel_test_opts
193+ --- @param workers parallel_test.worker[]
187194--- @return integer fails
188- local function print_and_collect (workers , opts )
195+ local function print_and_collect (workers )
189196 local fails = 0
190197 for _ , w in ipairs (workers ) do
191198 io.write (string.format (" \n ----- %s -----\n " , w .spec ))
@@ -204,7 +211,7 @@ local function print_and_collect(workers, opts)
204211 return fails
205212end
206213
207- --- @param opts parallel_test_opts
214+ --- @param opts parallel_test.opts
208215--- @param nvim_exec string
209216--- @return integer fails
210217local function run_one_nvim (opts , nvim_exec )
@@ -219,7 +226,7 @@ local function run_one_nvim(opts, nvim_exec)
219226 io.write (string.format (" Found %d spec file(s) (jobs=%d)\n " , # specs , opts .jobs ))
220227 io.flush ()
221228
222- --- @type worker[]
229+ --- @type parallel_test. worker[]
223230 local workers = {}
224231 for i , spec in ipairs (specs ) do
225232 -- Derive the per-spec `glob` (a basename prefix consumed by
@@ -234,7 +241,7 @@ local function run_one_nvim(opts, nvim_exec)
234241 workers [i ] = {
235242 id = i ,
236243 spec = spec ,
237- cmd = build_cmd (nvim_exec , spec ),
244+ cmd = build_cmd (nvim_exec ),
238245 env = env ,
239246 buf = {},
240247 started = false ,
@@ -260,28 +267,32 @@ local function run_one_nvim(opts, nvim_exec)
260267 drive (workers , opts .jobs )
261268 end
262269
263- local fails = print_and_collect (workers , opts )
270+ local fails = print_and_collect (workers )
264271 io.write (string.format (" \n [parallel_test] fails=%d/%d\n " , fails , # workers ))
265272 io.flush ()
266273 return fails
267274end
268275
276+ --- @param opts parallel_test.opts
277+ local function main (opts )
278+ local total_fails = 0
279+ for _ , nvim_exec in ipairs (opts .nvim_execs ) do
280+ io.write (string.format (" \n ====== %s ======\n " ,
281+ vim .fn .system ({ nvim_exec , " --version" }):gsub (" \n .*$" , " " )))
282+ io.flush ()
283+ total_fails = total_fails + run_one_nvim (opts , nvim_exec )
284+ end
285+
286+ if total_fails > 0 then
287+ io.write (string.format (" FAIL: %d spec file(s) failed\n " , total_fails ))
288+ os.exit (1 )
289+ end
290+ io.write (" PASS: all spec files green\n " )
291+ end
292+
269293local opts = parse_args (_G .arg )
270294if # opts .nvim_execs == 0 then
271295 io.stderr :write (" parallel_test: at least one NVIM_EXEC is required\n " )
272296 os.exit (2 )
273297end
274-
275- local total_fails = 0
276- for _ , nvim_exec in ipairs (opts .nvim_execs ) do
277- io.write (string.format (" \n ====== %s ======\n " ,
278- fn .system ({ nvim_exec , " --version" }):gsub (" \n .*$" , " " )))
279- io.flush ()
280- total_fails = total_fails + run_one_nvim (opts , nvim_exec )
281- end
282-
283- if total_fails > 0 then
284- io.write (string.format (" FAIL: %d spec file(s) failed\n " , total_fails ))
285- os.exit (1 )
286- end
287- io.write (" PASS: all spec files green\n " )
298+ main (opts )
0 commit comments