|
7 | 7 | // SPDX-License-Identifier: MIT |
8 | 8 | // |
9 | 9 |
|
10 | | -use plib::testing::{run_test, TestPlan}; |
| 10 | +use plib::testing::{run_test, run_test_with_checker, TestPlan}; |
11 | 11 |
|
12 | 12 | fn xargs_test(test_data: &str, expected_output: &str, args: Vec<&str>) { |
13 | 13 | run_test(TestPlan { |
@@ -211,3 +211,141 @@ fn xargs_escaped_chars() { |
211 | 211 | // Test escaped characters |
212 | 212 | xargs_test("hello\\ world\n", "hello world\n", vec!["echo"]); |
213 | 213 | } |
| 214 | + |
| 215 | +#[test] |
| 216 | +fn xargs_single_quotes() { |
| 217 | + // Test single quote (apostrophe) handling |
| 218 | + xargs_test("'hello world'\n", "hello world\n", vec!["echo"]); |
| 219 | +} |
| 220 | + |
| 221 | +#[test] |
| 222 | +fn xargs_mixed_quotes() { |
| 223 | + // Test mix of single and double quotes |
| 224 | + xargs_test( |
| 225 | + "'single' \"double\" plain\n", |
| 226 | + "single double plain\n", |
| 227 | + vec!["echo"], |
| 228 | + ); |
| 229 | +} |
| 230 | + |
| 231 | +#[test] |
| 232 | +fn xargs_empty_input() { |
| 233 | + // Empty input should not execute the utility |
| 234 | + run_test(TestPlan { |
| 235 | + cmd: String::from("xargs"), |
| 236 | + args: vec!["echo".to_string()], |
| 237 | + stdin_data: String::from(""), |
| 238 | + expected_out: String::from(""), |
| 239 | + expected_err: String::from(""), |
| 240 | + expected_exit_code: 0, |
| 241 | + }); |
| 242 | +} |
| 243 | + |
| 244 | +#[test] |
| 245 | +fn xargs_line_continuation() { |
| 246 | + // -L with trailing blank should continue to next line |
| 247 | + run_test(TestPlan { |
| 248 | + cmd: String::from("xargs"), |
| 249 | + args: vec!["-L".to_string(), "1".to_string(), "echo".to_string()], |
| 250 | + stdin_data: String::from("one \ntwo\nthree\n"), |
| 251 | + expected_out: String::from("one two\nthree\n"), |
| 252 | + expected_err: String::from(""), |
| 253 | + expected_exit_code: 0, |
| 254 | + }); |
| 255 | +} |
| 256 | + |
| 257 | +#[test] |
| 258 | +fn xargs_insert_multiple_replstr() { |
| 259 | + // -I with multiple occurrences of replstr in same argument |
| 260 | + run_test(TestPlan { |
| 261 | + cmd: String::from("xargs"), |
| 262 | + args: vec![ |
| 263 | + "-I".to_string(), |
| 264 | + "{}".to_string(), |
| 265 | + "echo".to_string(), |
| 266 | + "{}-{}-{}".to_string(), |
| 267 | + ], |
| 268 | + stdin_data: String::from("test\n"), |
| 269 | + expected_out: String::from("test-test-test\n"), |
| 270 | + expected_err: String::from(""), |
| 271 | + expected_exit_code: 0, |
| 272 | + }); |
| 273 | +} |
| 274 | + |
| 275 | +#[test] |
| 276 | +fn xargs_insert_five_args_with_replstr() { |
| 277 | + // -I with replstr in 5 arguments (POSIX requires at least 5) |
| 278 | + run_test(TestPlan { |
| 279 | + cmd: String::from("xargs"), |
| 280 | + args: vec![ |
| 281 | + "-I".to_string(), |
| 282 | + "{}".to_string(), |
| 283 | + "echo".to_string(), |
| 284 | + "{}".to_string(), |
| 285 | + "{}".to_string(), |
| 286 | + "{}".to_string(), |
| 287 | + "{}".to_string(), |
| 288 | + "{}".to_string(), |
| 289 | + ], |
| 290 | + stdin_data: String::from("x\n"), |
| 291 | + expected_out: String::from("x x x x x\n"), |
| 292 | + expected_err: String::from(""), |
| 293 | + expected_exit_code: 0, |
| 294 | + }); |
| 295 | +} |
| 296 | + |
| 297 | +#[test] |
| 298 | +fn xargs_combine_n_and_s() { |
| 299 | + // Combining -n and -s should work together |
| 300 | + run_test(TestPlan { |
| 301 | + cmd: String::from("xargs"), |
| 302 | + args: vec![ |
| 303 | + "-n".to_string(), |
| 304 | + "2".to_string(), |
| 305 | + "-s".to_string(), |
| 306 | + "20".to_string(), |
| 307 | + "echo".to_string(), |
| 308 | + ], |
| 309 | + stdin_data: String::from("a b c d e\n"), |
| 310 | + expected_out: String::from("a b\nc d\ne\n"), |
| 311 | + expected_err: String::from(""), |
| 312 | + expected_exit_code: 0, |
| 313 | + }); |
| 314 | +} |
| 315 | + |
| 316 | +#[test] |
| 317 | +fn xargs_insert_arg_size_limit() { |
| 318 | + // -I mode should enforce size limit on constructed arguments |
| 319 | + // Create a string that when repeated will exceed 4096 bytes |
| 320 | + let long_input = "x".repeat(5000); |
| 321 | + let test_plan = TestPlan { |
| 322 | + cmd: String::from("xargs"), |
| 323 | + args: vec![ |
| 324 | + "-I".to_string(), |
| 325 | + "{}".to_string(), |
| 326 | + "echo".to_string(), |
| 327 | + "{}".to_string(), |
| 328 | + ], |
| 329 | + stdin_data: format!("{}\n", long_input), |
| 330 | + expected_out: String::from(""), |
| 331 | + expected_err: String::from(""), |
| 332 | + expected_exit_code: 1, |
| 333 | + }; |
| 334 | + |
| 335 | + // Run with custom checker since error format includes Rust error wrapper |
| 336 | + run_test_with_checker(test_plan, |plan, output| { |
| 337 | + assert_eq!(output.status.code(), Some(plan.expected_exit_code)); |
| 338 | + assert_eq!(String::from_utf8_lossy(&output.stdout), plan.expected_out); |
| 339 | + // Just verify stderr contains the key message parts |
| 340 | + let stderr = String::from_utf8_lossy(&output.stderr); |
| 341 | + assert!( |
| 342 | + stderr.contains("constructed argument"), |
| 343 | + "Expected error about constructed argument" |
| 344 | + ); |
| 345 | + assert!(stderr.contains("5000 bytes"), "Expected size in error"); |
| 346 | + assert!( |
| 347 | + stderr.contains("4096 byte limit"), |
| 348 | + "Expected limit in error" |
| 349 | + ); |
| 350 | + }); |
| 351 | +} |
0 commit comments