#[ allow( unused_imports ) ]
use super::*;
use std::process::Command;
fn program_tools_bin() -> &'static str
{
env!( "CARGO_BIN_EXE_program_tools" )
}
fn run_cli( args : &[ &str ] ) -> std::process::Output
{
Command::new( program_tools_bin() )
.args( args )
.output()
.expect( "failed to invoke program_tools binary" )
}
#[ test ]
fn cli_run_single_file_happy_path()
{
use std::path::PathBuf;
let pid = std::process::id();
let tmp : PathBuf = std::env::temp_dir()
.join( format!( "pt_cli_tc1_{pid}" ) );
std::fs::create_dir_all( &tmp ).expect( "failed to create tmp dir" );
let source = tmp.join( "hello.rs" );
std::fs::write( &source, r#"fn main() { println!( "hello from cli" ); }"# )
.expect( "failed to write source" );
let output = run_cli( &[ "run", source.to_str().expect( "path is valid UTF-8" ) ] );
std::fs::remove_dir_all( &tmp ).ok();
assert_eq!
(
output.status.code(),
Some( 0 ),
"expected exit code 0; stderr: {}",
String::from_utf8_lossy( &output.stderr ),
);
let stdout = String::from_utf8_lossy( &output.stdout );
assert!
(
stdout.contains( "hello from cli" ),
"expected 'hello from cli' in stdout; got: {stdout}",
);
}
#[ test ]
fn cli_run_missing_target_exits_nonzero()
{
let output = run_cli( &[ "run" ] );
assert_ne!
(
output.status.code(),
Some( 0 ),
"expected non-zero exit code when TARGET is missing",
);
let stderr = String::from_utf8_lossy( &output.stderr );
assert!
(
!stderr.is_empty(),
"expected stderr to contain a usage error message",
);
}
#[ test ]
fn cli_run_nonexistent_path_exits_one()
{
let output = run_cli( &[ "run", "/nonexistent/pt_cli_tc4_does_not_exist.rs" ] );
assert_eq!
(
output.status.code(),
Some( 1 ),
"expected exit code 1 for a missing source file",
);
let stderr = String::from_utf8_lossy( &output.stderr );
assert!
(
!stderr.is_empty(),
"expected an error message in stderr for a missing path",
);
}
#[ test ]
fn cli_run_unknown_flag_exits_nonzero()
{
let output = run_cli( &[ "run", "--not-a-real-flag", "dummy.rs" ] );
assert_ne!
(
output.status.code(),
Some( 0 ),
"expected non-zero exit code for an unrecognised flag",
);
let stderr = String::from_utf8_lossy( &output.stderr );
assert!
(
!stderr.is_empty(),
"expected an error message in stderr for an unrecognised flag",
);
}
#[ test ]
fn cli_run_cargo_project_directory()
{
use std::path::PathBuf;
let pid = std::process::id();
let tmp : PathBuf = std::env::temp_dir()
.join( format!( "pt_cli_tc2_{pid}" ) );
let src_dir = tmp.join( "src" );
std::fs::create_dir_all( &src_dir ).expect( "failed to create src dir" );
std::fs::write
(
tmp.join( "Cargo.toml" ),
"[package]\nname = \"pt_tc2\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\n",
).expect( "failed to write Cargo.toml" );
std::fs::write
(
src_dir.join( "main.rs" ),
r#"fn main() { println!( "project output" ); }"#,
).expect( "failed to write main.rs" );
let output = run_cli( &[ "run", tmp.to_str().expect( "path is valid UTF-8" ) ] );
std::fs::remove_dir_all( &tmp ).ok();
assert_eq!
(
output.status.code(),
Some( 0 ),
"expected exit code 0 for project run; stderr: {}",
String::from_utf8_lossy( &output.stderr ),
);
let stdout = String::from_utf8_lossy( &output.stdout );
assert!
(
stdout.contains( "project output" ),
"expected 'project output' in stdout; got: {stdout}",
);
}
#[ test ]
fn cli_run_target_exit_code_forwarded()
{
use std::path::PathBuf;
let pid = std::process::id();
let tmp : PathBuf = std::env::temp_dir()
.join( format!( "pt_cli_tc6_{pid}" ) );
std::fs::create_dir_all( &tmp ).expect( "failed to create tmp dir" );
let source = tmp.join( "exit42.rs" );
std::fs::write( &source, "fn main() { std::process::exit( 42 ); }" )
.expect( "failed to write source" );
let output = run_cli( &[ "run", source.to_str().expect( "path is valid UTF-8" ) ] );
std::fs::remove_dir_all( &tmp ).ok();
assert_eq!
(
output.status.code(),
Some( 42 ),
"expected exit code 42 forwarded from target program; got {:?}",
output.status.code(),
);
}
#[ test ]
fn cli_run_cargo_not_found_exits_one()
{
use std::path::PathBuf;
let pid = std::process::id();
let tmp : PathBuf = std::env::temp_dir()
.join( format!( "pt_cli_tc7_{pid}" ) );
std::fs::create_dir_all( &tmp ).expect( "failed to create tmp dir" );
let source = tmp.join( "hello.rs" );
std::fs::write( &source, r#"fn main() { println!( "hi" ); }"# )
.expect( "failed to write source" );
let output = run_cli
(
&[
"run",
"--cargo",
"/nonexistent/cargo_pt_tc7",
source.to_str().expect( "path is valid UTF-8" ),
]
);
std::fs::remove_dir_all( &tmp ).ok();
assert_eq!
(
output.status.code(),
Some( 1 ),
"expected exit code 1 when Cargo binary is missing",
);
let stderr = String::from_utf8_lossy( &output.stderr );
assert!
(
!stderr.is_empty(),
"expected an error diagnostic in stderr when Cargo is not found",
);
}
#[ test ]
fn cli_run_compilation_error_exits_nonzero()
{
use std::path::PathBuf;
let pid = std::process::id();
let tmp : PathBuf = std::env::temp_dir()
.join( format!( "pt_cli_tc8_{pid}" ) );
std::fs::create_dir_all( &tmp ).expect( "failed to create tmp dir" );
let source = tmp.join( "broken.rs" );
std::fs::write( &source, "fn main() { this_does_not_compile }" )
.expect( "failed to write source" );
let output = run_cli( &[ "run", source.to_str().expect( "path is valid UTF-8" ) ] );
std::fs::remove_dir_all( &tmp ).ok();
assert_ne!
(
output.status.code(),
Some( 0 ),
"expected non-zero exit for a compilation error",
);
let stderr = String::from_utf8_lossy( &output.stderr );
assert!
(
!stderr.is_empty(),
"expected compiler diagnostics in stderr for broken code",
);
}
#[ test ]
fn cli_help_exits_zero()
{
let output = run_cli( &[ "--help" ] );
assert_eq!
(
output.status.code(),
Some( 0 ),
"expected exit code 0 for --help",
);
let stdout = String::from_utf8_lossy( &output.stdout );
assert!
(
!stdout.is_empty(),
"expected usage text in stdout for --help",
);
}
#[ test ]
fn cli_run_help_exits_zero()
{
let output = run_cli( &[ "run", "--help" ] );
assert_eq!
(
output.status.code(),
Some( 0 ),
"expected exit code 0 for 'run --help'",
);
let stdout = String::from_utf8_lossy( &output.stdout );
assert!
(
!stdout.is_empty(),
"expected usage text in stdout for 'run --help'",
);
}
#[ test ]
fn cli_no_subcommand_exits_nonzero()
{
let output = run_cli( &[] );
assert_ne!
(
output.status.code(),
Some( 0 ),
"expected non-zero exit code when no subcommand is given",
);
}
#[ test ]
fn cli_capture_flag_collects_stdout()
{
use std::path::PathBuf;
let pid = std::process::id();
let tmp : PathBuf = std::env::temp_dir()
.join( format!( "pt_cli_tc12_{pid}" ) );
std::fs::create_dir_all( &tmp ).expect( "failed to create tmp dir" );
let source = tmp.join( "capture.rs" );
std::fs::write( &source, r#"fn main() { println!( "captured output" ); }"# )
.expect( "failed to write source" );
let output = run_cli
(
&[ "run", "--capture", source.to_str().expect( "path is valid UTF-8" ) ]
);
std::fs::remove_dir_all( &tmp ).ok();
assert_eq!
(
output.status.code(),
Some( 0 ),
"expected exit code 0 with --capture; stderr: {}",
String::from_utf8_lossy( &output.stderr ),
);
let stdout = String::from_utf8_lossy( &output.stdout );
assert!
(
stdout.contains( "captured output" ),
"expected 'captured output' in CLI stdout when using --capture; got: {stdout}",
);
}
#[ test ]
fn cli_env_flag_passes_env_var_to_script()
{
use std::path::PathBuf;
let pid = std::process::id();
let tmp : PathBuf = std::env::temp_dir()
.join( format!( "pt_cli_tc13_{pid}" ) );
std::fs::create_dir_all( &tmp ).expect( "failed to create tmp dir" );
let source = tmp.join( "env_test.rs" );
std::fs::write
(
&source,
r#"fn main() { println!( "{}", std::env::var( "PT_CLI_VAR" ).unwrap_or( "MISSING".to_string() ) ); }"#,
)
.expect( "failed to write source" );
let output = run_cli
(
&[
"run",
"--capture",
"--env",
"PT_CLI_VAR=from_cli_flag",
source.to_str().expect( "path is valid UTF-8" ),
]
);
std::fs::remove_dir_all( &tmp ).ok();
assert_eq!
(
output.status.code(),
Some( 0 ),
"expected exit code 0; stderr: {}",
String::from_utf8_lossy( &output.stderr ),
);
let stdout = String::from_utf8_lossy( &output.stdout );
assert!
(
stdout.contains( "from_cli_flag" ),
"expected env var value in stdout; got: {stdout}",
);
}