@@ -27,6 +27,7 @@ defmodule ExUnit.CLIFormatter do
2727 test_counter: % { } ,
2828 test_timings: [ ] ,
2929 failure_counter: 0 ,
30+ failure_type_counter: % { } ,
3031 skipped_counter: 0 ,
3132 excluded_counter: 0 ,
3233 invalid_counter: 0
@@ -139,7 +140,14 @@ defmodule ExUnit.CLIFormatter do
139140
140141 test_counter = update_test_counter ( config . test_counter , test )
141142 failure_counter = config . failure_counter + 1
142- config = % { config | test_counter: test_counter , failure_counter: failure_counter }
143+ failure_type_counter = update_test_counter ( config . failure_type_counter , test )
144+
145+ config = % {
146+ config
147+ | test_counter: test_counter ,
148+ failure_counter: failure_counter ,
149+ failure_type_counter: failure_type_counter
150+ }
143151
144152 { :noreply , update_test_timings ( config , test ) }
145153 end
@@ -176,8 +184,16 @@ defmodule ExUnit.CLIFormatter do
176184 # The failed tests have already contributed to the counter,
177185 # so we should only add the successful tests to the count
178186 config =
179- update_in ( config . failure_counter , fn counter ->
180- counter + Enum . count ( test_module . tests , & is_nil ( & 1 . state ) )
187+ Enum . reduce ( test_module . tests , config , fn
188+ % { state: nil } = test , acc ->
189+ % {
190+ acc
191+ | failure_counter: acc . failure_counter + 1 ,
192+ failure_type_counter: update_test_counter ( acc . failure_type_counter , test )
193+ }
194+
195+ _test , acc ->
196+ acc
181197 end )
182198
183199 formatted =
@@ -352,11 +368,36 @@ defmodule ExUnit.CLIFormatter do
352368 defp print_summary ( config , force_failures? ) do
353369 test_type_counts = collect_test_type_counts ( config )
354370 test_counter = test_counter_or_default ( config , test_type_counts )
355- formatted_test_type_counts = format_test_type_counts ( test_counter )
356- failure_pl = pluralize ( config . failure_counter , "failure" , "failures" )
371+
372+ passed_total =
373+ test_type_counts - config . failure_counter - config . skipped_counter - config . invalid_counter
374+
375+ # Passed line: "Passed: 447/455 (53/54 doctests, 393/403 tests)" or
376+ # "Passed: 455 (70 tests, 14 properties)" when all pass
377+ all_passed? = passed_total == test_type_counts
378+
379+ passed_breakdown =
380+ format_passed_breakdown ( test_counter , config . failure_type_counter , all_passed? )
381+
382+ passed_line =
383+ if all_passed? do
384+ "Passed: #{ passed_total } "
385+ else
386+ "Passed: #{ passed_total } /#{ test_type_counts } "
387+ end
388+ |> if_true ( passed_breakdown != "" , & ( & 1 <> " (#{ passed_breakdown } )" ) )
389+
390+ # Failed line: "Failed: 8 tests, 1 property"
391+ failed_line =
392+ if config . failure_counter > 0 do
393+ failed_breakdown = format_type_counts ( config . failure_type_counter )
394+ "\n " <> failure ( "Failed: #{ failed_breakdown } " , config )
395+ else
396+ ""
397+ end
357398
358399 message =
359- " #{ formatted_test_type_counts } #{ config . failure_counter } #{ failure_pl } "
400+ ( " \n " <> passed_line )
360401 |> if_true (
361402 config . invalid_counter > 0 ,
362403 & ( & 1 <> ", #{ config . invalid_counter } invalid" )
@@ -372,7 +413,7 @@ defmodule ExUnit.CLIFormatter do
372413
373414 cond do
374415 config . failure_counter > 0 or force_failures? ->
375- IO . puts ( failure ( message , config ) )
416+ IO . puts ( message <> failed_line )
376417
377418 config . invalid_counter > 0 ->
378419 IO . puts ( invalid ( message , config ) )
@@ -404,14 +445,40 @@ defmodule ExUnit.CLIFormatter do
404445 IO . puts ( formatted )
405446 end
406447
407- defp format_test_type_counts ( test_counter ) do
408- test_counter
448+ defp format_type_counts ( type_counter ) do
449+ type_counter
409450 |> Enum . sort ( )
410451 |> Enum . map ( fn { test_type , count } ->
411- type_pluralized = pluralize ( count , test_type , ExUnit . plural_rule ( test_type |> to_string ( ) ) )
412-
413- "#{ count } #{ type_pluralized } , "
452+ "#{ count } #{ pluralize_type ( count , test_type ) } "
414453 end )
454+ |> Enum . join ( ", " )
455+ end
456+
457+ defp format_passed_breakdown ( test_counter , failure_type_counter , all_passed? ) do
458+ # If there are no different test types, we just print "Passed: N/N"
459+ # without the type.
460+ if map_size ( test_counter ) in 0 .. 1 do
461+ ""
462+ else
463+ test_counter
464+ |> Map . keys ( )
465+ |> Enum . sort ( )
466+ |> Enum . map_join ( ", " , fn type ->
467+ total = Map . fetch! ( test_counter , type )
468+
469+ if all_passed? do
470+ "#{ total } #{ pluralize_type ( total , type ) } "
471+ else
472+ failed = Map . get ( failure_type_counter , type , 0 )
473+ passed = total - failed
474+ "#{ passed } /#{ total } #{ pluralize_type ( total , type ) } "
475+ end
476+ end )
477+ end
478+ end
479+
480+ defp pluralize_type ( count , type ) do
481+ pluralize ( count , type , ExUnit . plural_rule ( to_string ( type ) ) )
415482 end
416483
417484 defp test_counter_or_default ( _config , 0 ) do
0 commit comments