use std::path::PathBuf;
const UTF8_INPUT_ENCODING_DECLARATION: &str = "[Console]::InputEncoding=[Text.Encoding]::UTF8";
const OEM_DECODING_COMMANDS: &[&str] = &["clip.exe", "$input | Set-Clipboard"];
fn repo_file(relative: &str) -> String {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(relative);
std::fs::read_to_string(&path)
.unwrap_or_else(|error| panic!("reading {} failed: {error}", path.display()))
}
fn windows_copy_command_recipe(contents: &str, source: &str) -> String {
let recipe = contents
.lines()
.filter(|line| line.contains("copy-command") || line.contains("- Windows:"))
.find(|line| line.contains("powershell"))
.unwrap_or_else(|| panic!("{source} no longer documents a Windows copy-command recipe"));
recipe.trim().to_owned()
}
#[test]
fn shipped_windows_copy_command_declares_an_explicit_utf8_input_encoding() {
for source in [
"docs/examples/human-friendly.conf",
"docs/human-friendly-config.md",
] {
let recipe = windows_copy_command_recipe(&repo_file(source), source);
assert!(
recipe.contains(UTF8_INPUT_ENCODING_DECLARATION),
"{source} documents a Windows copy-command that does not declare its input \
encoding, so it decodes RMUX's UTF-8 selection with the OEM code page \
(issue #177): {recipe}"
);
}
}
#[test]
fn shipped_documents_no_longer_recommend_oem_decoding_clipboard_commands() {
for source in [
"docs/examples/human-friendly.conf",
"docs/human-friendly-config.md",
] {
let contents = repo_file(source);
for line in contents.lines() {
if !line.contains("set -s copy-command") {
continue;
}
for rejected in OEM_DECODING_COMMANDS {
assert!(
!line.contains(rejected),
"{source} recommends `{rejected}` as a copy-command; it decodes RMUX's \
UTF-8 selection with the OEM code page (issue #177): {line}"
);
}
}
}
}
#[cfg(windows)]
mod windows_round_trip {
use super::UTF8_INPUT_ENCODING_DECLARATION;
use std::io::Write;
use std::process::{Command, Stdio};
const FIXTURE: &str = "╭─│╯ café 😀 日本";
struct Decoded {
text: String,
default_input_code_page: u32,
}
fn decode_with(declaration: &str) -> Decoded {
let script = format!(
"$cp=[Console]::InputEncoding.CodePage; {declaration} \
$text=[Console]::In.ReadToEnd(); \
[Console]::OutputEncoding=[Text.Encoding]::UTF8; \
[Console]::Out.Write(\"$cp`n$text\")"
);
let mut child = Command::new("powershell.exe")
.args(["-NoProfile", "-NonInteractive", "-Command", &script])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("spawning powershell.exe for the copy-command probe");
child
.stdin
.take()
.expect("powershell stdin is piped")
.write_all(FIXTURE.as_bytes())
.expect("writing the fixture to the probe's stdin");
let output = child.wait_with_output().expect("probe runs to completion");
assert!(
output.status.success(),
"probe failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8(output.stdout).expect("probe emits UTF-8");
let (code_page, text) = stdout
.split_once('\n')
.expect("probe emits the code page then the decoded text");
Decoded {
text: text.trim_end_matches(['\r', '\n']).to_owned(),
default_input_code_page: code_page
.trim()
.parse()
.expect("probe reports a numeric code page"),
}
}
#[test]
fn declared_utf8_input_encoding_round_trips_the_selection_byte_exactly() {
let decoded = decode_with(&format!("{UTF8_INPUT_ENCODING_DECLARATION};"));
assert_eq!(
decoded.text, FIXTURE,
"a copy-command that declares UTF-8 must return RMUX's selection unchanged"
);
}
#[test]
fn undeclared_input_encoding_corrupts_the_selection() {
let decoded = decode_with("");
if decoded.default_input_code_page == 65001 {
eprintln!(
"skipping: this host already defaults to code page 65001, so the OEM \
decode of issue #177 cannot be reproduced here"
);
return;
}
assert_ne!(
decoded.text, FIXTURE,
"expected the undeclared reader to mangle the selection through code page {}",
decoded.default_input_code_page
);
}
}