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} , wrkr, test, ctx:: 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 )" , ctx. 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 , " " ), " │" , color = :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 )" , ctx. name_align - textwidth (test) + 1 , " " ), " |" ,
203+ " " ^ ctx. elapsed_align, " crashed at $(now ()) \n " , color = :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