diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso
index 8a846d3d9b5a..2f4a2bef3735 100644
--- a/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso
+++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso
@@ -14,26 +14,25 @@ import project.Test.Test
import project.Test_Reporter
import project.Test_Result.Test_Result
-run_group : Group -> Vector Test_Result
-run_group (group : Group) =
- assert group.is_pending.not
- run_specs_from_group group.specs group
-
-run_specs_from_group : Vector Spec -> Group -> Vector Test_Result
-run_specs_from_group (specs : Vector Spec) (group : Group) =
+run_specs_from_group : Vector Spec -> Group -> Any -> Vector Test_Result
+run_specs_from_group (specs : Vector Spec) (group : Group) progress_reporter =
assert (group.is_pending.not)
case specs.is_empty of
True -> []
False ->
test_results = specs.map spec->
assert (group_contains_spec group spec)
+ progress_reporter.report_progress (group.name+": "+spec.name)
pair = run_spec spec
spec_res = pair.second
time_taken = pair.first
Test_Result.Impl group.name spec.name spec_res time_taken
+
+ progress_reporter.report_progress (group.name+": (Teardown)") increment=0
# Invoke the teardown of the group
group.teardown Nothing
+ progress_reporter.clear
test_results
diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Suite.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Suite.enso
index 66c63e5a4eca..1ff92a5ea5ca 100644
--- a/distribution/lib/Standard/Test/0.0.0-dev/src/Suite.enso
+++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Suite.enso
@@ -61,46 +61,54 @@ type Suite
run_with_filter self (filter : (Text | Nothing) = Nothing) (should_exit : Boolean = True) -> (Boolean | Nothing) =
config = Suite_Config.from_environment
- # Map of groups to vector of specs that match the filter
- matching_specs = self.groups.fold Map.empty map-> group->
+ # List of pairs of groups and their specs that match the filter
+ matching_specs = self.groups.flat_map group->
group_matches = name_matches group.name filter
case group_matches of
True ->
# Include all the specs from the group
- map.insert group group.specs
+ [[group, group.specs]]
False ->
# Try to include only some specs from the group
matched_specs = group.specs.filter spec->
name_matches spec.name filter
case matched_specs.is_empty of
- True -> map
+ True -> []
False ->
- assert (map.contains_key group . not)
- map.insert group matched_specs
+ [[group, matched_specs]]
+ progress_reporter = case Test_Reporter.is_terminal_interactive of
+ True ->
+ matching_spec_count = matching_specs.map (p-> p.second.length) . fold 0 (+)
+ Test_Reporter.Command_Line_Progress_Reporter.make matching_spec_count
+ False ->
+ Test_Reporter.Ignore_Progress_Reporter
all_results_bldr = Vector.new_builder
junit_sb_builder = if config.should_output_junit then StringBuilder.new else Nothing
Test_Reporter.wrap_junit_testsuites config junit_sb_builder <|
- matching_specs.each_with_key group-> specs->
- if group.is_pending.not then
- results = Helpers.run_specs_from_group specs group
- Test_Reporter.print_report results config junit_sb_builder
- all_results_bldr.append_vector_range results
+ matching_specs.each p->
+ group = p.first
+ specs = p.second
+ case group.is_pending of
+ False ->
+ results = Helpers.run_specs_from_group specs group progress_reporter
+ Test_Reporter.print_report results config junit_sb_builder
+ all_results_bldr.append_vector_range results
+ True ->
+ Test_Reporter.print_pending_group group config junit_sb_builder
+
all_results = all_results_bldr.to_vector
succ_tests = all_results.filter (r-> r.is_success) . length
failed_tests = all_results.filter (r-> r.is_fail) . length
skipped_tests = all_results.filter (r-> r.is_pending) . length
+ pending_groups = matching_specs.filter (p-> p.first.is_pending) . length
case should_exit of
True ->
IO.println <| succ_tests.to_text + " tests succeeded."
IO.println <| failed_tests.to_text + " tests failed."
IO.println <| skipped_tests.to_text + " tests skipped."
- pending_groups = matching_specs.keys.filter (group-> group.is_pending)
- pending_groups_details = case pending_groups.is_empty of
- True -> "."
- False -> ": " + (pending_groups.map (it-> it.name) . to_text)
- IO.println <| pending_groups.length.to_text + " groups skipped" + pending_groups_details
+ IO.println <| pending_groups.to_text + " groups skipped."
exit_code = if failed_tests > 0 then 1 else 0
System.exit exit_code
False ->
diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Test_Reporter.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Test_Reporter.enso
index 4d6824c2ceca..16e6f9979811 100644
--- a/distribution/lib/Standard/Test/0.0.0-dev/src/Test_Reporter.enso
+++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Test_Reporter.enso
@@ -2,8 +2,10 @@ private
from Standard.Base import all
import Standard.Base.Runtime.Context
+import Standard.Base.Runtime.Ref.Ref
from Standard.Base.Runtime import assert
+import project.Group.Group
import project.Internal.Stack_Trace_Helpers
import project.Spec_Result.Spec_Result
import project.Suite_Config.Suite_Config
@@ -11,6 +13,7 @@ import project.Test.Test
import project.Test_Result.Test_Result
polyglot java import java.lang.StringBuilder
+polyglot java import java.lang.System as Java_System
## PRIVATE
Write the JUnit XML header.
@@ -41,6 +44,9 @@ green text =
highlighted text =
'\u001b[1;1m' + text + '\u001b[0m'
+grey text =
+ '\u001b[90m' + text + '\u001b[0m'
+
maybe_red_text (text : Text) (config : Suite_Config) =
if config.use_ansi_colors then (red text) else text
@@ -50,6 +56,9 @@ maybe_green_text (text : Text) (config : Suite_Config) =
maybe_highlighted_text (text : Text) (config : Suite_Config) =
if config.use_ansi_colors then (highlighted text) else text
+maybe_grey_text (text : Text) (config : Suite_Config) =
+ if config.use_ansi_colors then (grey text) else text
+
## Print result for a single Spec run
print_single_result : Test_Result -> Suite_Config -> Nothing
print_single_result (test_result : Test_Result) (config : Suite_Config) =
@@ -74,7 +83,7 @@ print_single_result (test_result : Test_Result) (config : Suite_Config) =
IO.println (decorate_stack_trace details)
Spec_Result.Pending reason ->
if config.print_only_failures.not then
- IO.println (" - [PENDING] " + test_result.spec_name)
+ IO.println (maybe_grey_text (" - [PENDING] " + test_result.spec_name) config)
IO.println (" Reason: " + reason)
@@ -96,6 +105,20 @@ print_report (test_results : Vector Test_Result) (config : Suite_Config) (builde
results_per_group.each_with_key group_name-> group_results->
print_group_report group_name group_results config builder
+## Prints a pending group, optionally writing it to a jUnit XML output.
+print_pending_group : Group -> Vector -> Suite_Config -> (StringBuilder | Nothing) -> Nothing
+print_pending_group group config builder =
+ assert group.pending.is_nothing.not "Group in print_pending_group should be pending"
+ if config.should_output_junit then
+ assert builder.is_nothing.not "Builder must be specified when JUnit output is enabled"
+ builder.append (' \n')
+ builder.append (' \n')
+ builder.append (' \n')
+ builder.append (' \n')
+ builder.append ' \n'
+ IO.println <| maybe_grey_text ("[PENDING] " + group.name) config
+ IO.println (" Reason: " + group.pending)
## Prints report for test_results from a single group.
@@ -109,7 +132,7 @@ print_group_report group_name test_results config builder =
acc + res.time_taken
if config.should_output_junit then
assert builder.is_nothing.not "Builder must be specified when JUnit output is enabled"
- builder.append (' \n')
test_results.each result->
- builder.append (' ')
+ builder.append (' ')
case result.spec_result of
Spec_Result.Success -> Nothing
Spec_Result.Failure msg details ->
- escaped_message = escape_xml msg . replace '\n' '
'
- builder.append ('\n \n')
+ builder.append ('\n \n')
# We always print the message again as content - otherwise the GitHub action may fail to parse it.
builder.append (escape_xml msg)
if details.is_nothing.not then
@@ -130,7 +152,7 @@ print_group_report group_name test_results config builder =
builder.append '\n\n'
builder.append (escape_xml details)
builder.append '\n \n'
- Spec_Result.Pending msg -> builder.append ('\n \n ')
+ Spec_Result.Pending msg -> builder.append ('\n \n ')
builder.append ' \n'
builder.append ' \n'
@@ -154,6 +176,63 @@ print_group_report group_name test_results config builder =
## PRIVATE
Escape Text for XML
-escape_xml : Text -> Text
-escape_xml input =
- input.replace '&' '&' . replace '"' '"' . replace "'" ''' . replace '<' '<' . replace '>' '>'
+escape_xml : Text -> Boolean -> Text
+escape_xml input inside_attribute=False =
+ escaped = input.replace '&' '&' . replace '"' '"' . replace "'" ''' . replace '<' '<' . replace '>' '>'
+ if inside_attribute then escaped.replace '\n' '
' else escaped
+
+## PRIVATE
+progress_width = 70
+
+## PRIVATE
+print_progress current_progress total_count status_text =
+ total_count_as_text = total_count.to_text
+ counter_width = total_count_as_text.length
+ current_progress_as_text = current_progress.to_text.pad counter_width at=Location.Start
+ line = " ("+ current_progress_as_text + " / " + total_count_as_text + ") " + status_text
+ truncated_line = if line.length <= progress_width then line else
+ line.take (progress_width - 3) + '...'
+
+ Java_System.out.print '\r'
+ Java_System.out.print (' ' * progress_width)
+ Java_System.out.print '\r'
+ Java_System.out.print truncated_line
+ Java_System.out.print '\r'
+
+## PRIVATE
+clear_progress =
+ Java_System.out.print '\r'
+ Java_System.out.print (' ' * progress_width)
+ Java_System.out.print '\r'
+
+## PRIVATE
+type Ignore_Progress_Reporter
+ ## PRIVATE
+ report_progress self (status_text : Text) (increment : Integer = 1) =
+ _ = [increment, status_text]
+ Nothing
+
+ ## PRIVATE
+ clear = Nothing
+
+## PRIVATE
+type Command_Line_Progress_Reporter
+ ## PRIVATE
+ Value current_progress total_count
+
+ ## PRIVATE
+ make total_expected =
+ Command_Line_Progress_Reporter.Value (Ref.new 0) total_expected
+
+ ## PRIVATE
+ report_progress self (status_text : Text) (increment : Integer = 1) =
+ self.current_progress.modify (+increment)
+ print_progress self.current_progress.get self.total_count status_text
+
+ ## PRIVATE
+ clear self = clear_progress
+
+## PRIVATE
+ Checks if the current process is running in an interactive terminal session.
+is_terminal_interactive -> Boolean =
+ Java_System.console != Nothing