@@ -16,11 +16,16 @@ export interface CheckConfig {
1616 env ?: Record < string , string > ;
1717}
1818
19+ export interface NextPass extends AsyncDisposable {
20+ input : CopyInFile ;
21+ state ?: Record < string , CopyInFile > ;
22+ }
23+
1924type Checker = ( config : CheckConfig ) => Promise < {
2025 status : number ;
2126 score : number ;
2227 message : string | { message : string , params ?: any [ ] } ;
23- nextPass ?: { input : CopyInFile , state ?: Record < string , CopyInFile > } ;
28+ nextPass ?: NextPass ;
2429} > ;
2530
2631function parseDiffMsg ( msg : string ) {
@@ -237,7 +242,7 @@ const checkers: Record<string, Checker> = new Proxy({
237242 } ,
238243
239244 async testlib ( config ) {
240- const { stderr , status , code , fileIds } = await runQueued ( `${ config . execute } /w/in /w/user_out /w/answer` , {
245+ const res = await runQueued ( `${ config . execute } /w/in /w/user_out /w/answer` , {
241246 copyIn : {
242247 in : config . input ,
243248 user_out : config . user_stdout ,
@@ -248,41 +253,52 @@ const checkers: Record<string, Checker> = new Proxy({
248253 env : config . env ,
249254 copyOutCached : [ 'nextpass.in?' , 'state.txt?' ] ,
250255 } ) ;
251- if ( [ STATUS . STATUS_SYSTEM_ERROR , STATUS . STATUS_TIME_LIMIT_EXCEEDED , STATUS . STATUS_MEMORY_LIMIT_EXCEEDED ] . includes ( status ) ) {
252- const message = {
253- [ STATUS . STATUS_SYSTEM_ERROR ] : stderr ,
254- [ STATUS . STATUS_TIME_LIMIT_EXCEEDED ] : 'Checker Time Limit Exceeded' ,
255- [ STATUS . STATUS_MEMORY_LIMIT_EXCEEDED ] : 'Checker Memory Limit Exceeded' ,
256- } [ status ] ;
257- return {
258- status : STATUS . STATUS_SYSTEM_ERROR ,
259- score : 0 ,
260- message,
261- } ;
256+ const cleanup = res [ Symbol . asyncDispose ] ;
257+ let cleanupTransferred = false ;
258+ try {
259+ const {
260+ stderr, status, code, fileIds,
261+ } = res ;
262+ if ( [ STATUS . STATUS_SYSTEM_ERROR , STATUS . STATUS_TIME_LIMIT_EXCEEDED , STATUS . STATUS_MEMORY_LIMIT_EXCEEDED ] . includes ( status ) ) {
263+ const message = {
264+ [ STATUS . STATUS_SYSTEM_ERROR ] : stderr ,
265+ [ STATUS . STATUS_TIME_LIMIT_EXCEEDED ] : 'Checker Time Limit Exceeded' ,
266+ [ STATUS . STATUS_MEMORY_LIMIT_EXCEEDED ] : 'Checker Memory Limit Exceeded' ,
267+ } [ status ] ;
268+ return {
269+ status : STATUS . STATUS_SYSTEM_ERROR ,
270+ score : 0 ,
271+ message,
272+ } ;
273+ }
274+ if ( status === STATUS . STATUS_RUNTIME_ERROR && ! stderr ?. trim ( ) ) {
275+ return {
276+ status : STATUS . STATUS_SYSTEM_ERROR ,
277+ score : 0 ,
278+ message : `Checker exited with code ${ code } ` ,
279+ } ;
280+ }
281+ const result = parse ( stderr , config . score , config . detail ) ;
282+ if ( result . status === STATUS . STATUS_ACCEPTED && fileIds [ 'nextpass.in' ] ) {
283+ cleanupTransferred = true ;
284+ return {
285+ ...result ,
286+ nextPass : {
287+ input : { fileId : fileIds [ 'nextpass.in' ] } ,
288+ state : fileIds [ 'state.txt' ] ? { 'state.txt' : { fileId : fileIds [ 'state.txt' ] } } : undefined ,
289+ [ Symbol . asyncDispose ] : cleanup ,
290+ } ,
291+ } ;
292+ }
293+ return result ;
294+ } finally {
295+ if ( ! cleanupTransferred ) await cleanup ( ) ;
262296 }
263- if ( status === STATUS . STATUS_RUNTIME_ERROR && ! stderr ?. trim ( ) ) {
264- return {
265- status : STATUS . STATUS_SYSTEM_ERROR ,
266- score : 0 ,
267- message : `Checker exited with code ${ code } ` ,
268- } ;
269- }
270- const result = parse ( stderr , config . score , config . detail ) ;
271- if ( result . status === STATUS . STATUS_ACCEPTED && fileIds [ 'nextpass.in' ] ) {
272- return {
273- ...result ,
274- nextPass : {
275- input : { fileId : fileIds [ 'nextpass.in' ] } ,
276- state : fileIds [ 'state.txt' ] ? { 'state.txt' : { fileId : fileIds [ 'state.txt' ] } } : undefined ,
277- } ,
278- } ;
279- }
280- return result ;
281297 } ,
282298
283299 // https://www.kattis.com/problem-package-format/spec/2023-07-draft.html#output-validator
284300 async kattis ( config ) {
285- const { files , fileIds , code } = await runQueued ( `${ config . execute } input answer_file feedback_dir` , {
301+ const res = await runQueued ( `${ config . execute } input answer_file feedback_dir` , {
286302 copyIn : {
287303 input : config . input ,
288304 answer_file : config . output ,
@@ -301,38 +317,46 @@ const checkers: Record<string, Checker> = new Proxy({
301317 'feedback_dir/state.txt?' ,
302318 ] ,
303319 } ) ;
320+ const cleanup = res [ Symbol . asyncDispose ] ;
321+ let cleanupTransferred = false ;
322+ try {
323+ const { files, fileIds, code } = res ;
324+ const status = code === 42
325+ ? STATUS . STATUS_ACCEPTED
326+ : code === 43
327+ ? STATUS . STATUS_WRONG_ANSWER
328+ : STATUS . STATUS_SYSTEM_ERROR ;
304329
305- const status = code === 42
306- ? STATUS . STATUS_ACCEPTED
307- : code === 43
308- ? STATUS . STATUS_WRONG_ANSWER
309- : STATUS . STATUS_SYSTEM_ERROR ;
330+ const score = status === STATUS . STATUS_ACCEPTED
331+ ? config . score
332+ : + files [ 'feedback_dir/score.txt' ] || 0 ;
310333
311- const score = status === STATUS . STATUS_ACCEPTED
312- ? config . score
313- : + files [ 'feedback_dir/score.txt' ] || 0 ;
334+ const message = status === STATUS . STATUS_SYSTEM_ERROR
335+ ? files [ 'feedback_dir/judgeerror.txt' ] || `Checker exited with code ${ code } `
336+ : config . detail === 'full'
337+ ? files [ 'feedback_dir/teammessage.txt' ] || files [ 'feedback_dir/judgemessage.txt' ] || ''
338+ : '' ;
314339
315- const message = status === STATUS . STATUS_SYSTEM_ERROR
316- ? files [ 'feedback_dir/judgeerror.txt' ] || `Checker exited with code ${ code } `
317- : config . detail === 'full'
318- ? files [ 'feedback_dir/teammessage.txt' ] || files [ 'feedback_dir/judgemessage.txt' ] || ''
319- : '' ;
340+ if ( status === STATUS . STATUS_ACCEPTED && fileIds [ 'feedback_dir/nextpass.in' ] ) {
341+ cleanupTransferred = true ;
342+ return {
343+ status,
344+ score,
345+ message,
346+ nextPass : {
347+ input : { fileId : fileIds [ 'feedback_dir/nextpass.in' ] } ,
348+ state : fileIds [ 'feedback_dir/state.txt' ]
349+ ? { 'feedback_dir/state.txt' : { fileId : fileIds [ 'feedback_dir/state.txt' ] } }
350+ : undefined ,
351+ [ Symbol . asyncDispose ] : cleanup ,
352+ } ,
353+ } ;
354+ }
320355
321- if ( status === STATUS . STATUS_ACCEPTED && fileIds [ 'feedback_dir/nextpass.in' ] ) {
322- return {
323- status,
324- score,
325- message,
326- nextPass : {
327- input : { fileId : fileIds [ 'feedback_dir/nextpass.in' ] } ,
328- state : fileIds [ 'feedback_dir/state.txt' ]
329- ? { 'feedback_dir/state.txt' : { fileId : fileIds [ 'feedback_dir/state.txt' ] } }
330- : undefined ,
331- } ,
332- } ;
356+ return { status, score, message } ;
357+ } finally {
358+ if ( ! cleanupTransferred ) await cleanup ( ) ;
333359 }
334-
335- return { status, score, message } ;
336360 } ,
337361} , {
338362 get ( self , key ) {
0 commit comments