6868abstract type  AbstractTestRecord end 
6969
7070struct  TestRecord <:  AbstractTestRecord 
71-     test:: Any 
72-     output:: String 
71+     value:: Any           #  AbstractTestSet or TestSetException
72+     output:: String       #  captured stdout/stderr
73+ 
74+     #  stats
7375    time:: Float64 
7476    bytes:: UInt64 
7577    gctime:: Float64 
@@ -165,7 +167,7 @@ function print_test_finished(record::TestRecord, wrkr, test, ctx::TestIOContext)
165167    end 
166168end 
167169
168- function  print_test_errorred ( :: Type{ TestRecord} :: TestIOContext , test_time )
170+ function  print_test_failed (record :: TestRecord , wrkr, test, ctx:: TestIOContext )
169171    lock (ctx. lock)
170172    try 
171173        printstyled (ctx. stderr , test, color =  :red )
@@ -174,7 +176,7 @@ function print_test_errorred(::Type{TestRecord}, wrkr, test, ctx::TestIOContext,
174176            lpad (" ($wrkr )" . name_align -  textwidth (test) +  1 , "  " "  |" 
175177            , color =  :red 
176178        )
177-         time_str =  @sprintf (" %7.2f" test_time )
179+         time_str =  @sprintf (" %7.2f" record . time )
178180        printstyled (ctx. stderr , lpad (time_str, ctx. elapsed_align +  1 , "  " "  │" =  :red )
179181
180182        failed_str =  " failed at $(now ()) \n " 
@@ -183,6 +185,24 @@ function print_test_errorred(::Type{TestRecord}, wrkr, test, ctx::TestIOContext,
183185        failed_str =  lpad (failed_str, fail_align, "  " 
184186        printstyled (ctx. stderr , failed_str, color =  :red )
185187
188+         #  TODO : print other stats?
189+ 
190+         flush (ctx. stderr )
191+     finally 
192+         unlock (ctx. lock)
193+     end 
194+ end 
195+ 
196+ function  print_test_crashed (:: Type{TestRecord} , wrkr, test, ctx:: TestIOContext )
197+     lock (ctx. lock)
198+     try 
199+         printstyled (ctx. stderr , test, color =  :red )
200+         printstyled (
201+             ctx. stderr ,
202+             lpad (" ($wrkr )" . name_align -  textwidth (test) +  1 , "  " "  |" 
203+             "  " ^ ctx. elapsed_align, "  crashed at $(now ()) \n " =  :red 
204+         )
205+ 
186206        flush (ctx. stderr )
187207    finally 
188208        unlock (ctx. lock)
@@ -209,8 +229,15 @@ function runtest(::Type{TestRecord}, f, name, init_code, color)
209229
210230            mktemp () do  path, io
211231                stats =  redirect_stdio (stdout = io, stderr = io) do 
212-                     @timed  @testset  $ name begin 
213-                         $ f
232+                     @timed  try 
233+                         @testset  $ name begin 
234+                             $ f
235+                         end 
236+                     catch  err
237+                         isa (err, Test. TestSetException) ||  rethrow ()
238+ 
239+                         #  return the error to package it into a TestRecord
240+                         err
214241                    end 
215242                end 
216243                close (io)
@@ -665,13 +692,13 @@ function runtests(mod::Module, ARGS; test_filter = Returns(true), RecordType = T
665692    #  Message types for the printer channel
666693    #  (:started, test_name, worker_id)
667694    #  (:finished, test_name, worker_id, record)
668-     #  (:errored , test_name, worker_id, test_time)
695+     #  (:crashed , test_name, worker_id, test_time)
669696    printer_channel =  Channel {Tuple} (100 )
670697
671698    printer_task =  @async  begin 
672699        last_status_update =  Ref (time ())
673700        try 
674-             while  isopen (printer_channel)
701+             while  isopen (printer_channel)  ||   isready (printer_channel) 
675702                got_message =  false 
676703                while  isready (printer_channel)
677704                    #  Try to get a message from the channel (with timeout)
@@ -692,13 +719,17 @@ function runtests(mod::Module, ARGS; test_filter = Returns(true), RecordType = T
692719                        test_name, wrkr, record =  msg[2 ], msg[3 ], msg[4 ]
693720
694721                        clear_status ()
695-                         print_test_finished (record, wrkr, test_name, io_ctx)
722+                         if  record. value isa  Exception
723+                             print_test_failed (record, wrkr, test_name, io_ctx)
724+                         else 
725+                             print_test_finished (record, wrkr, test_name, io_ctx)
726+                         end 
696727
697-                     elseif  msg_type ==  :errored  
698-                         test_name, wrkr, test_time  =  msg[2 ], msg[3 ], msg[ 4 ]
728+                     elseif  msg_type ==  :crashed  
729+                         test_name, wrkr  =  msg[2 ], msg[3 ]
699730
700731                        clear_status ()
701-                         print_test_errorred (RecordType, wrkr, test_name, io_ctx, test_time )
732+                         print_test_crashed (RecordType, wrkr, test_name, io_ctx)
702733                    end 
703734                end 
704735
@@ -707,7 +738,8 @@ function runtests(mod::Module, ARGS; test_filter = Returns(true), RecordType = T
707738                    update_status ()
708739                    last_status_update[] =  time ()
709740                end 
710-                 sleep (0.1 )
741+ 
742+                 isopen (printer_channel) &&  sleep (0.1 )
711743            end 
712744        catch  ex
713745            if  isa (ex, InterruptException)
@@ -766,16 +798,15 @@ function runtests(mod::Module, ARGS; test_filter = Returns(true), RecordType = T
766798                        break 
767799                    end 
768800
769-                     #  return any other exception as the result
770-                     #  XXX : also put this in a test record?
771801                    ex
772802                end 
773803                test_t1 =  time ()
774804                push! (results, (test, result, test_t0, test_t1))
775805
776806                #  act on the results
777807                if  result isa  AbstractTestRecord
778-                     put! (printer_channel, (:finished , test, wrkr, result:: RecordType ))
808+                     @assert  result isa  RecordType
809+                     put! (printer_channel, (:finished , test, wrkr, result))
779810
780811                    if  memory_usage (result) >  max_worker_rss
781812                        #  the worker has reached the max-rss limit, recycle it
@@ -784,13 +815,12 @@ function runtests(mod::Module, ARGS; test_filter = Returns(true), RecordType = T
784815                    end 
785816                else 
786817                    @assert  result isa  Exception
787-                     put! (printer_channel, (:errored  , test, wrkr, test_t1 - test_t0 ))
818+                     put! (printer_channel, (:crashed  , test, wrkr))
788819                    if  do_quickfail
789820                        stop_work ()
790821                    end 
791822
792-                     #  the worker encountered some failure, recycle it
793-                     #  so future tests get a fresh environment
823+                     #  the worker encountered some serious failure, recycle it
794824                    p =  recycle_worker (p)
795825                end 
796826
@@ -903,32 +933,33 @@ function runtests(mod::Module, ARGS; test_filter = Returns(true), RecordType = T
903933                push! (completed_tests, testname)
904934
905935                #  decode or fake a testset
906-                 if  isa (result, AbstractTestRecord)
907-                     testset =  result. test
908-                     historical_durations[testname] =  stop -  start
909-                 else 
910-                     testset =  create_testset (testname; start, stop)
911-                     if  isa (result, RemoteException) && 
912-                            isa (result. captured. ex, Test. TestSetException)
913-                         for  i in  1 : result. captured. ex. pass
936+                 if  result isa  AbstractTestRecord
937+                     if  result. value isa  Test. AbstractTestSet
938+                         testset =  result. value
939+                         historical_durations[testname] =  stop -  start
940+                     else 
941+                         #  TODO : improve the Test stdlib to keep track of the exact failure
942+                         #        instead of flattening into an exception without provenance
943+                         @assert  result. value isa  Test. TestSetException
944+                         testset =  create_testset (testname; start, stop)
945+                         for  i in  1 : result. value. pass
914946                            Test. record (testset, Test. Pass (:test , nothing , nothing , nothing , LineNumberNode (@__LINE__ , @__FILE__ )))
915947                        end 
916-                         for  i in  1 : result. captured . ex . broken
948+                         for  i in  1 : result. value . broken
917949                            Test. record (testset, Test. Broken (:test , nothing ))
918950                        end 
919-                         for  t in  result. captured . ex . errors_and_fails
951+                         for  t in  result. value . errors_and_fails
920952                            Test. record (testset, t)
921953                        end 
922-                     else 
923-                         if  ! isa (result, Exception)
924-                             result =  ErrorException (string (" Unknown result type : " typeof (result)))
925-                         end 
926-                         #  If this test raised an exception that is not a remote testset exception,
927-                         #  i.e. not a RemoteException capturing a TestSetException that means
928-                         #  the test runner itself had some problem, so we may have hit a segfault,
929-                         #  deserialization errors or something similar.  Record this testset as Errored.
930-                         Test. record (testset, Test. Error (:nontest_error , testname, nothing , Base. ExceptionStack (NamedTuple[(;exception =  result, backtrace =  [])]), LineNumberNode (1 )))
931954                    end 
955+                 else 
956+                     #  If this test raised an exception that is not a remote testset
957+                     #  exception, that means the test runner itself had some problem, so we
958+                     #  may have hit a segfault, deserialization errors or something similar.
959+                     #  Record this testset as Errored.
960+                     @assert  result isa  Exception
961+                     testset =  create_testset (testname; start, stop)
962+                     Test. record (testset, Test. Error (:nontest_error , testname, nothing , Base. ExceptionStack (NamedTuple[(;exception =  result, backtrace =  [])]), LineNumberNode (1 )))
932963                end 
933964
934965                with_testset (testset) do 
0 commit comments