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
// SPDX-License-Identifier: MIT OR Apache-2.0
//! v0.1.28 GAP-CLI-SURFACE-DRIFT — `edit` stdin-mode tty guard, E2E layer.
//!
//! `edit --after-line/--before-line/--range/--after-match/--before-match/
//! --between/--multi` read new content from stdin. Without a guard, running
//! one of these under an interactive terminal (stdin is a tty) would hang
//! forever waiting for input the user never intended to type. The guard
//! (`read_stdin_text_guarded` in `src/commands/mod.rs`) rejects a tty stdin
//! immediately with `InvalidInput` (exit 65) instead of blocking.
//!
//! `read_stdin_text_guarded` is `pub(crate)`, so its `stdin_is_tty == true`
//! branch is unit-tested directly in `src/commands/mod.rs` (`tty_guard_tests`
//! module) where the crate-private function is reachable. `assert_cmd`
//! spawns a real child process whose stdin cannot be attached to a
//! synthetic pseudo-terminal from this test harness, so the `true` branch
//! is not re-exercised here. This file instead proves the two things that
//! ARE observable from outside the crate: the happy path (piped, non-tty
//! stdin) works end-to-end, and a closed/empty stdin (also non-tty) never
//! hangs — the guard only ever blocks on an actual terminal, never on an
//! ordinary pipe.
mod common;
use std::time::{Duration, Instant};
#[test]
fn after_match_with_piped_stdin_succeeds_end_to_end() {
let dir = tempfile::tempdir().expect("tempdir");
let path = common::create_test_file(dir.path(), "f.txt", "before\nmarker\nafter\n");
let output = common::atomwrite()
.args([
"--workspace",
dir.path().to_str().unwrap(),
"edit",
"f.txt",
"--after-match",
"marker",
])
.write_stdin("inserted\n")
.output()
.expect("run");
assert!(
output.status.success(),
"edit --after-match com stdin via pipe deve suceder: {:?}",
String::from_utf8_lossy(&output.stderr)
);
let content = std::fs::read_to_string(&path).unwrap();
assert!(content.contains("inserted"));
}
#[test]
fn between_mode_with_closed_stdin_completes_quickly_without_hanging() {
let dir = tempfile::tempdir().expect("tempdir");
common::create_test_file(dir.path(), "f.txt", "start\nold\nend\n");
let start = Instant::now();
let output = common::atomwrite()
.args([
"--workspace",
dir.path().to_str().unwrap(),
"edit",
"f.txt",
"--between",
"start",
"end",
])
.write_stdin("")
.output()
.expect("run");
let elapsed = start.elapsed();
assert!(
elapsed < Duration::from_secs(5),
"edit com stdin fechado (nao-tty) nao deve travar; levou {elapsed:?}"
);
assert!(
output.status.success(),
"stdin fechado (EOF imediato) e nao-tty: deve suceder com conteudo vazio, nao travar"
);
}
#[test]
fn range_mode_with_closed_stdin_completes_quickly_without_hanging() {
let dir = tempfile::tempdir().expect("tempdir");
common::create_test_file(dir.path(), "f.txt", "a\nb\nc\n");
let start = Instant::now();
let output = common::atomwrite()
.args([
"--workspace",
dir.path().to_str().unwrap(),
"edit",
"f.txt",
"--range",
"2:3",
])
.write_stdin("")
.output()
.expect("run");
let elapsed = start.elapsed();
assert!(
elapsed < Duration::from_secs(5),
"edit --range com stdin fechado nao deve travar; levou {elapsed:?}"
);
assert!(output.status.success());
}