Skip to content

Commit b3f98c7

Browse files
Copilotjosecelano
andcommitted
feat: [#138] add buffering control with flush() method
- Add flush() method to UserOutput that flushes both stdout and stderr - Update module-level documentation with buffering behavior section - Add comprehensive unit tests for flush functionality: - Test successful flush - Test multiple flush calls (idempotency) - Test empty buffer flushing - Test flushing both channels - Test sequential flush patterns - Follow Rust's Write trait pattern for error handling - Maintain backward compatibility with existing API - All 101 tests pass (96 original + 5 new buffering tests) Co-authored-by: josecelano <58816+josecelano@users.noreply.github.com>
1 parent b537a8f commit b3f98c7

1 file changed

Lines changed: 140 additions & 0 deletions

File tree

src/presentation/user_output.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@
2323
//! The newtype pattern is a zero-cost abstraction - it has the same memory layout and performance
2424
//! characteristics as the wrapped type, but provides type safety benefits.
2525
//!
26+
//! ## Buffering Behavior
27+
//!
28+
//! Output is line-buffered by default. Messages are typically flushed automatically
29+
//! after each newline. For cases where immediate output is critical (e.g., before
30+
//! long-running operations), call `flush()` explicitly:
31+
//!
32+
//! ```rust,ignore
33+
//! output.progress("Starting long operation...");
34+
//! output.flush()?; // Ensure message appears before operation starts
35+
//! perform_long_operation();
36+
//! ```
37+
//!
2638
//! ## Example Usage
2739
//!
2840
//! ```rust
@@ -1083,6 +1095,32 @@ impl UserOutput {
10831095
}
10841096
}
10851097

1098+
/// Flush all pending output to stdout and stderr
1099+
///
1100+
/// This is typically not needed as writes are line-buffered by default,
1101+
/// but can be useful for ensuring output appears immediately before
1102+
/// long-running operations or in test scenarios.
1103+
///
1104+
/// # Errors
1105+
///
1106+
/// Returns an error if flushing either stream fails.
1107+
///
1108+
/// # Examples
1109+
///
1110+
/// ```rust
1111+
/// use torrust_tracker_deployer_lib::presentation::user_output::{UserOutput, VerbosityLevel};
1112+
///
1113+
/// let mut output = UserOutput::new(VerbosityLevel::Normal);
1114+
/// output.progress("Starting long operation...");
1115+
/// output.flush().expect("Failed to flush output");
1116+
/// // Now perform long operation...
1117+
/// ```
1118+
pub fn flush(&mut self) -> std::io::Result<()> {
1119+
self.stdout.0.flush()?;
1120+
self.stderr.0.flush()?;
1121+
Ok(())
1122+
}
1123+
10861124
/// Display progress message to stderr (Normal level and above)
10871125
///
10881126
/// Progress messages go to stderr following cargo/docker patterns.
@@ -2744,4 +2782,106 @@ mod tests {
27442782
assert!(json.is_ok(), "Invalid JSON in stdout");
27452783
}
27462784
}
2785+
2786+
// ============================================================================
2787+
// Buffering Tests
2788+
// ============================================================================
2789+
2790+
mod buffering {
2791+
use super::super::*;
2792+
use crate::presentation::user_output::test_support::TestUserOutput;
2793+
2794+
#[test]
2795+
fn it_should_flush_all_writers() {
2796+
let mut test_output = TestUserOutput::new(VerbosityLevel::Normal);
2797+
test_output.output.progress("Test message");
2798+
2799+
// Flush should succeed
2800+
test_output.output.flush().expect("Flush should succeed");
2801+
2802+
// Verify output is present (flushed)
2803+
assert!(!test_output.stderr().is_empty());
2804+
assert!(test_output.stderr().contains("Test message"));
2805+
}
2806+
2807+
#[test]
2808+
fn it_should_be_safe_to_flush_multiple_times() {
2809+
let mut test_output = TestUserOutput::new(VerbosityLevel::Normal);
2810+
test_output.output.progress("Test message");
2811+
2812+
// Multiple flushes should be safe
2813+
test_output
2814+
.output
2815+
.flush()
2816+
.expect("First flush should succeed");
2817+
test_output
2818+
.output
2819+
.flush()
2820+
.expect("Second flush should succeed");
2821+
test_output
2822+
.output
2823+
.flush()
2824+
.expect("Third flush should succeed");
2825+
2826+
// Output should still be present
2827+
assert!(!test_output.stderr().is_empty());
2828+
}
2829+
2830+
#[test]
2831+
fn it_should_flush_empty_buffers_safely() {
2832+
let mut test_output = TestUserOutput::new(VerbosityLevel::Normal);
2833+
2834+
// Flushing with no output should be safe
2835+
test_output
2836+
.output
2837+
.flush()
2838+
.expect("Flushing empty buffers should succeed");
2839+
2840+
// No output should be present
2841+
assert_eq!(test_output.stderr(), "");
2842+
assert_eq!(test_output.stdout(), "");
2843+
}
2844+
2845+
#[test]
2846+
fn it_should_flush_both_stdout_and_stderr() {
2847+
let mut test_output = TestUserOutput::new(VerbosityLevel::Normal);
2848+
2849+
// Write to both channels
2850+
test_output.output.progress("Progress message");
2851+
test_output.output.result("Result data");
2852+
2853+
// Flush should handle both channels
2854+
test_output
2855+
.output
2856+
.flush()
2857+
.expect("Flush should succeed for both channels");
2858+
2859+
// Verify both outputs are present
2860+
assert!(test_output.stderr().contains("Progress message"));
2861+
assert!(test_output.stdout().contains("Result data"));
2862+
}
2863+
2864+
#[test]
2865+
fn it_should_work_with_sequential_flush_calls() {
2866+
let mut test_output = TestUserOutput::new(VerbosityLevel::Normal);
2867+
2868+
// Write, flush, write, flush pattern
2869+
test_output.output.progress("Message 1");
2870+
test_output
2871+
.output
2872+
.flush()
2873+
.expect("First flush should succeed");
2874+
2875+
test_output.output.progress("Message 2");
2876+
test_output
2877+
.output
2878+
.flush()
2879+
.expect("Second flush should succeed");
2880+
2881+
// Both messages should be present
2882+
let stderr = test_output.stderr();
2883+
assert!(stderr.contains("Message 1"));
2884+
assert!(stderr.contains("Message 2"));
2885+
}
2886+
}
27472887
}

0 commit comments

Comments
 (0)