1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
//! BUG-005: Broken Progress Output - RED Phase Tests
//!
//! These tests verify that progress output updates in place (single line)
//! instead of creating multiple lines.
//!
//! Current Status: 🔴 RED - These tests will FAIL until progress is fixed
//!
//! Test Strategy (Extreme TDD):
//! 1. RED: Write failing tests that verify single-line progress updates
//! 2. GREEN: Implement proper ANSI escape codes or indicatif library
//! 3. REFACTOR: Ensure clean code
//! 4. COMMIT: Single atomic commit with fix
use std::process::{Command, Stdio};
use tempfile::TempDir;
// =============================================================================
// RED TEST 1: Progress Should Not Create Multiple Lines
// =============================================================================
#[test]
#[ignore = "BUG-005: RED test - will fail until progress output fixed"]
fn test_progress_uses_single_line() {
// Arrange: Create a simple project
let project = create_simple_rust_project();
// Act: Run context command and capture stderr
let output = Command::new("cargo")
.args([
"run",
"--bin",
"pmat",
"--",
"context",
"--language",
"rust",
])
.current_dir(project.path())
.stderr(Stdio::piped())
.output()
.expect("Failed to run pmat context");
let stderr = String::from_utf8_lossy(&output.stderr);
// Assert: Progress should use carriage return (\r) for single-line updates
// Count lines that start with progress indicators
let progress_lines: Vec<&str> = stderr
.lines()
.filter(|line| line.contains("Auto-detecting") || line.contains("Detected:"))
.collect();
// Should only show final "Detected:" line, not "Auto-detecting" + "Detected:"
// because "Auto-detecting" should be overwritten
assert!(
progress_lines.len() <= 1,
"Progress should update single line, found {} lines: {:?}",
progress_lines.len(),
progress_lines
);
}
// =============================================================================
// RED TEST 2: Progress Should Use ANSI Escape Codes
// =============================================================================
#[test]
#[ignore = "BUG-005: RED test - will fail until ANSI codes implemented"]
fn test_progress_uses_ansi_codes() {
// Arrange: Create a simple project
let project = create_simple_rust_project();
// Act: Run context command and capture raw stderr
let output = Command::new("cargo")
.args([
"run",
"--bin",
"pmat",
"--",
"context",
"--language",
"rust",
])
.current_dir(project.path())
.stderr(Stdio::piped())
.output()
.expect("Failed to run pmat context");
let stderr = String::from_utf8_lossy(&output.stderr);
// Assert: Should contain carriage return (\r) or ANSI clear line code (\x1b[K)
let has_carriage_return = stderr.contains('\r');
let has_clear_line = stderr.contains("\x1b[K");
assert!(
has_carriage_return || has_clear_line,
"Progress should use ANSI escape codes (\\r or \\x1b[K) for in-place updates"
);
}
// =============================================================================
// RED TEST 3: Auto-detecting Line Should Be Overwritten
// =============================================================================
#[test]
#[ignore = "BUG-005: RED test - will fail until overwrite implemented"]
fn test_auto_detecting_line_overwritten() {
// Arrange: Create a simple project
let project = create_simple_rust_project();
// Act: Run context command
let output = Command::new("cargo")
.args([
"run",
"--bin",
"pmat",
"--",
"context",
"--language",
"rust",
])
.current_dir(project.path())
.stderr(Stdio::piped())
.output()
.expect("Failed to run pmat context");
let stderr = String::from_utf8_lossy(&output.stderr);
// Assert: If we see "Detected:" we should NOT also see "Auto-detecting" on separate line
// Because the "Auto-detecting" line should have been overwritten
if stderr.contains("Detected:") {
// Split by carriage returns to see actual visible output
let visible_lines: Vec<&str> = stderr.split('\n').filter(|line| !line.is_empty()).collect();
let auto_detect_count = visible_lines
.iter()
.filter(|line| line.contains("Auto-detecting"))
.count();
let detected_count = visible_lines
.iter()
.filter(|line| line.contains("Detected:"))
.count();
// If both appear as separate lines, that's the bug
assert!(
!(auto_detect_count > 0 && detected_count > 0),
"Bug: Both 'Auto-detecting' and 'Detected:' visible on separate lines\n\
Expected: 'Auto-detecting' should be overwritten by 'Detected:'\n\
Found {} 'Auto-detecting' and {} 'Detected:' lines",
auto_detect_count,
detected_count
);
}
}
// =============================================================================
// RED TEST 4: No Visual Corruption in Output
// =============================================================================
#[test]
#[ignore = "BUG-005: RED test - will fail until corruption fixed"]
fn test_no_visual_corruption() {
// Arrange: Create a simple project
let project = create_simple_rust_project();
// Act: Run context command
let output = Command::new("cargo")
.args([
"run",
"--bin",
"pmat",
"--",
"context",
"--language",
"rust",
])
.current_dir(project.path())
.stderr(Stdio::piped())
.output()
.expect("Failed to run pmat context");
let stderr = String::from_utf8_lossy(&output.stderr);
// Assert: No lines should have overlapping text
// This is hard to test directly, but we can check for common corruption patterns
for line in stderr.lines() {
// Check for multiple progress indicators on same line (corruption)
let indicator_count =
line.matches("🔍").count() + line.matches("✅").count() + line.matches("⚠️").count();
assert!(
indicator_count <= 1,
"Visual corruption detected: Multiple indicators on same line: {}",
line
);
}
}
// =============================================================================
// RED TEST 5: Final Output Should Be Clean
// =============================================================================
#[test]
#[ignore = "BUG-005: RED test - will fail until clean output implemented"]
fn test_final_output_is_clean() {
// Arrange: Create a simple project
let project = create_simple_rust_project();
// Act: Run context command
let output = Command::new("cargo")
.args([
"run",
"--bin",
"pmat",
"--",
"context",
"--language",
"rust",
])
.current_dir(project.path())
.stderr(Stdio::piped())
.output()
.expect("Failed to run pmat context");
let stderr = String::from_utf8_lossy(&output.stderr);
// Assert: Final visible output should be clean
// Count total lines that contain progress indicators
let progress_line_count = stderr
.lines()
.filter(|line| {
line.contains("Auto-detecting")
|| line.contains("Detected:")
|| line.contains("Discovering")
})
.count();
// Should see at most 2-3 clean lines (detecting, detected, maybe discovering)
// NOT multiple instances of the same progress message
assert!(
progress_line_count <= 3,
"Too many progress lines ({}), suggests not updating in place",
progress_line_count
);
}
// =============================================================================
// Helper Functions (Test Support)
// =============================================================================
fn create_simple_rust_project() -> TempDir {
use std::fs;
let temp = TempDir::new().unwrap();
// Create a simple main.rs file
let code = r#"
fn main() {
println!("Hello, world!");
}
"#;
fs::create_dir_all(temp.path().join("src")).unwrap();
fs::write(temp.path().join("src/main.rs"), code).unwrap();
fs::write(
temp.path().join("Cargo.toml"),
"[package]\nname = \"test\"\nversion = \"0.1.0\"\n",
)
.unwrap();
temp
}