@@ -602,6 +602,174 @@ def(kw::Symbol) =
602
602
retest_defaults[kw]
603
603
end
604
604
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
+ previewchan != = nothing || 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
605
773
606
774
"""
607
775
retest(mod..., pattern...;
@@ -883,118 +1051,17 @@ function retest(@nospecialize(args::ArgType...);
883
1051
many = hasmany (tests)
884
1052
885
1053
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
+ )
910
1057
911
1058
gotprinted = false
912
1059
align_overflow = 0
913
1060
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
+ )
998
1065
999
1066
# TODO : move printer task out of worker?
1000
1067
worker = @task begin
0 commit comments