@@ -602,6 +602,174 @@ def(kw::Symbol) =
602602 retest_defaults[kw]
603603 end
604604
605+ """
606+ get_previewer_channel(
607+ spin, stdout, version, nthreads, nprocs
608+ )::Maybe{RemoteChannel}
609+
610+ Optionally returns a channel for previewing testsets yet to be completed.
611+
612+ # Arguments:
613+ - `spin::Bool`: Whether to show an active 'spinner' along with the description
614+ of the currently executing testset.
615+ - `stdout::DataType`: Output stream to print to.
616+ - `version::VersionNumber`: Current version of Julia being used.
617+ - `nthreads::Integer`: Number of threads available to ReTest.
618+ - `nprocs::Integer`: Number of processes available to ReTest.
619+ """
620+ function get_previewer_channel (spin, stdout , version, nthreads, nprocs)
621+ can_call_thread_pin = nthreads > 1 && version >= v " 1.3"
622+ if spin && stdout isa Base. TTY && (can_call_thread_pin || nprocs > 1 )
623+ RemoteChannel (() -> Channel {Maybe{Tuple{Int64,String}}} (Inf ))
624+ # Needs to be "remote" in the case nprocs() == 2, as then nworkers() ==
625+ # 1, which means the one remote worker will put descriptions on
626+ # previewchan (if nworkers() > 1, descriptions are not put because we
627+ # can't predict the order in which they complete, and then the previewer
628+ # will not show the descriptions, just the spinning wheel)
629+
630+ # On VERSION < v"1.3" : we can't call `thread_pin` (see below), and in
631+ # this case previewing doesn't work well, as the worker and previewer
632+ # tasks can end up in the same thread, and the previewer is not
633+ # responsive.
634+
635+ # channel size: if nworkers() == 1, then 2 would suffice (one for the
636+ # "compilation step", one for @testset execution step, and then the
637+ # printer would empty the channel; but for two workers and more, this
638+ # second step is not done, so the buffer needs a size of at least
639+ # `nworkers()`
640+ else
641+ # Otherwise, the previewing doesn't work well, because the worker task
642+ # keeps the thread busy and doesn't yield enough for previewing to be
643+ # useful.
644+ nothing
645+ end
646+ end
647+
648+ """
649+ take_latest!(previewchan)::Tuple{Int64, Union{String, Nothing}}
650+
651+ Gets the ID and description of the latest testset to preview.
652+
653+ # Arguments:
654+ - `previewchan::Maybe{RemoteChannel}`: Object to get ID/description pairs from.
655+ """
656+ function take_latest! (previewchan)
657+ local id_desc
658+ while isready (previewchan)
659+ # printer/previewer can't take! it, as we locked
660+ id_desc = take! (previewchan)
661+ end
662+ if @isdefined (id_desc)
663+ something (id_desc, (Int64 (0 ), nothing ))
664+ else
665+ (Int64 (0 ), " " )
666+ end
667+ end
668+
669+ # TODO : Clarify purpose of `align_overflow`.
670+ # TODO : Clarify purpose of `maxidw` - maximum indentation width?
671+ """
672+ get_previewer(
673+ previewchan, interrupted, printlock, gotprinted, align_overflow,
674+ verbose, format, module_header, many, maxidw
675+ )::Maybe{Task}
676+
677+ Optionally schedules and returns a task for previewing the completion of
678+ testsets for the current module, if previewing is enabled.
679+
680+ # Arguments:
681+ - `previewchan::Maybe{RemoteChannel}`: Object for acquiring the latest testset
682+ to preview.
683+ - `interrupted::Threads.Atomic{Bool}`: Whether or not the previewer was
684+ interrupted during execution.
685+ - `printlock::ReentrantLock`: Lock for printing to the output stream.
686+ - `gotprinted::Bool`: Whether the latest testset was printed.
687+ - `align_overflow::Integer`: How many characters overflow.
688+ - `verbose::Bool`: Whether to preview nested testsets.
689+ - `format::.Testset.Format`: Container for formatting information.
690+ - `module_header::Bool`: Whether to print module header before testsets
691+ belonging to that module.
692+ - `many::Bool`: Whether there are multiple testsets, individually or in loops.
693+ - `maxidw::Ref{Int}`: Visual width for showing testset IDs.
694+ """
695+ function get_previewer (
696+ previewchan, interrupted, printlock, gotprinted, align_overflow, verbose,
697+ format, module_header, many, maxidw
698+ )
699+ ! isnothing (previewchan) || return nothing
700+ previewer = @async try
701+ timer = [' |' , ' /' , ' -' , ' \\ ' ]
702+ cursor = 0
703+ desc = " "
704+ id = Int64 (0 )
705+ finito = false
706+
707+ while ! finito && ! interrupted[]
708+ lock (printlock) do
709+ newid, newdesc = take_latest! (previewchan)
710+ if newdesc === nothing
711+ finito = true
712+ return # no need to sleep before looping
713+ elseif newdesc != " "
714+ desc = newdesc
715+ id = newid
716+ cursor = 0
717+ gotprinted = false
718+ elseif gotprinted
719+ desc = " "
720+ gotprinted = false
721+ align_overflow = 0
722+ elseif desc != " "
723+ align = format. desc_align
724+ if nworkers () > 1
725+ description = align >= 3 ? " ..." : " "
726+ style = NamedTuple ()
727+ elseif startswith (desc, ' \0 ' )
728+ description = chop (desc, head= 1 , tail= 0 )
729+ style = (color = :light_black , bold= true )
730+ else
731+ description = desc
732+ style = NamedTuple ()
733+ end
734+ if isindented (verbose, module_header, many)
735+ description = " " * description
736+ end
737+ cursor += 1
738+
739+ # when verbose == 0, we still can print the currently run
740+ # testset, but then its description might be larger than
741+ # `align`, because it was not taken into account for
742+ # computing `align`;
743+ # `align_overflow` computes how many characters do overflow,
744+ # so that the printer can "erase" them later on;
745+ # once we overflow, we don't go back (leftwards) until the
746+ # printer prints
747+ align_overflow =
748+ max (align_overflow, textwidth (description) - align)
749+ print (' \r ' )
750+ print_id (id, maxidw[])
751+ printstyled (rpad (" $description " , align+ align_overflow, " " ),
752+ ' ' ,
753+ timer[mod1 (cursor, end )];
754+ style... )
755+ end
756+ end
757+ previewer_refresh_duration_seconds = 0.13
758+ sleep (previewer_refresh_duration_seconds)
759+ end
760+ catch ex
761+ # TODO : clarify what is the correct thing to do here
762+ if ex isa InterruptException
763+ interrupted[] = true
764+ rethrow ()
765+ else
766+ # then there is probably a bug in the previewer code, but it might
767+ # be fine for the worker/printer to continue?
768+ rethrow ()
769+ end
770+ end # previewer task
771+ previewer
772+ end
605773
606774"""
607775 retest(mod..., pattern...;
@@ -883,118 +1051,17 @@ function retest(@nospecialize(args::ArgType...);
8831051 many = hasmany (tests)
8841052
8851053 printlock = ReentrantLock ()
886- previewchan =
887- if spin && stdout isa Base. TTY && (nthreads () > 1 && VERSION >= v " 1.3" ||
888- nprocs () > 1 )
889- RemoteChannel (() -> Channel {Maybe{Tuple{Int64,String}}} (Inf ))
890- # needs to be "remote" in the case nprocs() == 2, as then nworkers() == 1,
891- # which means the one remote worker will put descriptions on previewchan
892- # (if nworkers() > 1, descriptions are not put because we can't predict
893- # the order in which they complete, and then the previewer will
894- # not show the descriptions, just the spinning wheel)
895-
896- # on VERSION < v"1.3" : we can't call `thread_pin` (see below), and in this
897- # case previewing doesn't work well, as the worker and previewer tasks
898- # can end up in the same thread, and the previewer is not responsive
899-
900- # channel size: if nworkers() == 1, then 2 would suffice (one for
901- # the "compilation step", one for @testset execution step, and then
902- # the printer would empty the channel; but for two workers and more,
903- # this second step is not done, so the buffer needs a size of at least
904- # `nworkers()`
905- else
906- # otherwise, the previewing doesn't work well, because the worker task
907- # keeps the thread busy and doesn't yield enough for previewing to be useful
908- nothing
909- end
1054+ previewchan = get_previewer_channel (
1055+ spin, stdout , VERSION , nthreads (), nprocs ()
1056+ )
9101057
9111058 gotprinted = false
9121059 align_overflow = 0
9131060
914- function take_latest! (previewchan)
915- local id_desc
916- while isready (previewchan)
917- # printer/previewer can't take! it, as we locked
918- id_desc = take! (previewchan)
919- end
920- if @isdefined (id_desc)
921- something (id_desc, (Int64 (0 ), nothing ))
922- else
923- (Int64 (0 ), " " )
924- end
925- end
926-
927- previewer = previewchan === nothing ? nothing :
928- @async try
929- timer = [' |' , ' /' , ' -' , ' \\ ' ]
930- cursor = 0
931- desc = " "
932- id = Int64 (0 )
933- finito = false
934-
935- while ! finito && ! interrupted[]
936- lock (printlock) do
937- newid, newdesc = take_latest! (previewchan)
938- if newdesc === nothing
939- finito = true
940- return # no need to sleep before looping
941- elseif newdesc != " "
942- desc = newdesc
943- id = newid
944- cursor = 0
945- gotprinted = false
946- elseif gotprinted
947- desc = " "
948- gotprinted = false
949- align_overflow = 0
950- elseif desc != " "
951- align = format. desc_align
952- if nworkers () > 1
953- description = align >= 3 ? " ..." : " "
954- style = NamedTuple ()
955- elseif startswith (desc, ' \0 ' )
956- description = chop (desc, head= 1 , tail= 0 )
957- style = (color = :light_black , bold= true )
958- else
959- description = desc
960- style = NamedTuple ()
961- end
962- if isindented (verbose, module_header, many)
963- description = " " * description
964- end
965- cursor += 1
966-
967- # when verbose == 0, we still can print the currently run
968- # testset, but then its description might be larger than
969- # `align`, because it was not taken into account for computing
970- # `align`;
971- # `align_overflow` computes how many characters do overflow,
972- # so that the printer can "erase" them later on;
973- # once we overflow, we don't go back (leftwards) until the
974- # printer prints
975- align_overflow =
976- max (align_overflow, textwidth (description) - align)
977- print (' \r ' )
978- print_id (id, maxidw[])
979- printstyled (rpad (" $description " , align+ align_overflow, " " ),
980- ' ' ,
981- timer[mod1 (cursor, end )];
982- style... )
983- end
984- end
985- sleep (0.13 )
986- end
987- catch ex
988- # TODO : clarify what is the correct thing to do here
989- if ex isa InterruptException
990- interrupted[] = true
991- rethrow ()
992- else
993- # then there is probably a bug in the previewer code, but it might be fine
994- # for the worker/printer to continue?
995- rethrow ()
996- end
997- end # previewer task
1061+ previewer = get_previewer (
1062+ previewchan, interrupted, printlock, gotprinted, align_overflow,
1063+ verbose, format, module_header, many, maxidw
1064+ )
9981065
9991066 # TODO : move printer task out of worker?
10001067 worker = @task begin
0 commit comments