diff --git a/lib/cli/ui/spinner/spin_group.rb b/lib/cli/ui/spinner/spin_group.rb index ab459921..898027d1 100644 --- a/lib/cli/ui/spinner/spin_group.rb +++ b/lib/cli/ui/spinner/spin_group.rb @@ -100,7 +100,7 @@ class Task # * +title+ - Title of the task # * +block+ - Block for the task, will be provided with an instance of the spinner # - #: (String title, final_glyph: ^(bool success) -> (Glyph | String), merged_output: bool, duplicate_output_to: IO, work_queue: WorkQueue) { (Task task) -> untyped } -> void + #: (String title, final_glyph: ^(bool success) -> (Glyph | String), merged_output: bool, duplicate_output_to: io_like?, work_queue: WorkQueue) { (Task task) -> untyped } -> void def initialize(title, final_glyph:, merged_output:, duplicate_output_to:, work_queue:, &block) @title = title @final_glyph = final_glyph @@ -300,12 +300,12 @@ def inset_width # spin_group.add('Title') { |spinner| sleep 1.0 } # spin_group.wait # - #: (String title, ?final_glyph: ^(bool success) -> (Glyph | String), ?merged_output: bool, ?duplicate_output_to: IO) { (Task task) -> void } -> void + #: (String title, ?final_glyph: ^(bool success) -> (Glyph | String), ?merged_output: bool, ?duplicate_output_to: io_like?) { (Task task) -> void } -> void def add( title, final_glyph: DEFAULT_FINAL_GLYPH, merged_output: false, - duplicate_output_to: File.new(File::NULL, 'w'), + duplicate_output_to: nil, &block ) @m.synchronize do diff --git a/lib/cli/ui/stdout_router.rb b/lib/cli/ui/stdout_router.rb index 20f8144b..97677d1d 100644 --- a/lib/cli/ui/stdout_router.rb +++ b/lib/cli/ui/stdout_router.rb @@ -184,11 +184,11 @@ def outermost_uncaptured? end end - #: (?with_frame_inset: bool, ?merged_output: bool, ?duplicate_output_to: IO) { -> void } -> void + #: (?with_frame_inset: bool, ?merged_output: bool, ?duplicate_output_to: io_like?) { -> void } -> void def initialize( with_frame_inset: true, merged_output: false, - duplicate_output_to: File.open(File::NULL, 'w'), + duplicate_output_to: nil, &block ) @with_frame_inset = with_frame_inset @@ -225,7 +225,11 @@ def run case stream when :stdout @out.write(data) - @duplicate_output_to.write(data) + begin + @duplicate_output_to&.write(data) + rescue IOError + # Ignore + end when :stderr @err.write(data) else raise diff --git a/test/cli/ui/spinner/spin_group_test.rb b/test/cli/ui/spinner/spin_group_test.rb index 4f262568..43f43e05 100644 --- a/test/cli/ui/spinner/spin_group_test.rb +++ b/test/cli/ui/spinner/spin_group_test.rb @@ -36,6 +36,18 @@ def test_spin_group_auto_debrief_false assert_equal('', err) end + def test_spin_group_tasks_do_not_open_null_device + capture_io do + CLI::UI::StdoutRouter.ensure_activated + File.expects(:new).with(File::NULL, 'w').never + + sg = SpinGroup.new(auto_debrief: false) + sg.add('task') { true } + + assert(sg.wait) + end + end + def test_spin_group_success_debrief capture_io do CLI::UI::StdoutRouter.ensure_activated diff --git a/test/cli/ui/stdout_router_test.rb b/test/cli/ui/stdout_router_test.rb index 23be5aa5..bf9fe485 100644 --- a/test/cli/ui/stdout_router_test.rb +++ b/test/cli/ui/stdout_router_test.rb @@ -24,6 +24,40 @@ def test_current_id end end + def test_capture_does_not_open_null_device + File.expects(:open).with(File::NULL, 'w').never + StdoutRouter::Capture.new {} + end + + def test_capture_duplicate_output_to + capture_io do + StdoutRouter.with_enabled do + dup = StringIO.new + cap = StdoutRouter::Capture.new(duplicate_output_to: dup) { print('hello') } + cap.run + assert_equal('hello', cap.stdout) + assert_equal('hello', dup.string) + refute_predicate(dup, :closed?) # the capture doesn't own the handle + end + end + end + + def test_capture_ignores_closed_duplicate_output + capture_io do + StdoutRouter.with_enabled do + dup = StringIO.new + cap = StdoutRouter::Capture.new(duplicate_output_to: dup) do + dup.close + print('hello') + end + + cap.run + + assert_equal('hello', cap.stdout) + end + end + end + def test_frame_can_autoload_after_router_is_enabled script = <<~RUBY require 'stringio'