use std::env::current_dir;
use std::path::PathBuf;
use wtools::error::BasicError;
use wca::instruction::Instruction;
use ::wpublisher::manifest::Manifest;
pub fn smoke( instruction : &Instruction ) -> Result< (), BasicError >
{
let mut current_path = current_dir().unwrap();
let subject_path = PathBuf::from( &instruction.subject );
let module_path = if subject_path.is_relative()
{
current_path.push( &instruction.subject );
current_path
}
else
{
subject_path
};
let mut manifest_path = module_path.clone();
manifest_path.push( "Cargo.toml" );
if !manifest_path.exists()
{
let msg = format!( "Current directory {:?} has no file \"Cargo.toml\"", module_path.canonicalize().unwrap() );
return Err( BasicError::new( msg ) );
}
let mut manifest = Manifest::new();
manifest.manifest_path_from_str( &manifest_path ).unwrap();
manifest.load().unwrap();
let data = manifest.manifest_data.as_deref().unwrap();
let module_name = &data[ "package" ][ "name" ].clone();
let module_name = module_name.as_str().unwrap();
let code_path = match instruction.properties_map.get( "code_path" )
{
Some( x ) => PathBuf::from( x.clone().primitive().unwrap() ),
None => PathBuf::default(),
};
let mut data = None;
if code_path.exists()
{
data = Some( std::fs::read_to_string( code_path ).unwrap() );
}
let version = match instruction.properties_map.get( "version" )
{
Some( x ) => x.clone().primitive().unwrap(),
None => "*".to_string(),
};
let smoke = match instruction.properties_map.get( "smoke" )
{
Some( x ) => x.clone().primitive().unwrap(),
None =>
{
if let Ok( x ) = std::env::var( "WITH_SMOKE" )
{
x
}
else
{
"local".to_string()
}
},
};
if smoke != "false" && smoke != "0"
{
let mut threads = vec![];
if smoke == "local" || smoke != "published"
{
let module_name = module_name.clone().to_owned();
let data = data.clone();
let version = version.clone();
let thread = std::thread::spawn( move ||
{
let mut t = SmokeModuleTest::new( module_name );
t.test_postfix( "_test_local" );
if data.is_some()
{
t.code( data.as_ref().unwrap() );
}
t.version( version.as_str() );
t.local_path_clause( module_path.to_str().unwrap() );
t.clean( true ).unwrap();
t.form().unwrap();
t.perform().unwrap();
t.clean( false ).unwrap();
});
threads.push( thread );
}
if smoke == "published" || smoke != "local"
{
let module_name = module_name.clone().to_owned();
let data = data.clone();
let version = version.clone();
let thread = std::thread::spawn( move ||
{
let mut t = SmokeModuleTest::new( module_name );
t.test_postfix( "_test_published" );
if data.is_some()
{
t.code( data.as_ref().unwrap() );
}
t.version( version.as_str() );
t.clean( true ).unwrap();
t.form().unwrap();
t.perform().unwrap();
t.clean( false ).unwrap();
});
threads.push( thread );
}
for thread in threads
{
thread.join().unwrap();
}
}
Ok( () )
}
#[ derive( Debug ) ]
struct SmokeModuleTest< 'a >
{
pub dependency_name : String,
pub version : &'a str,
pub local_path_clause : &'a str,
pub code : String,
pub test_path : std::path::PathBuf,
pub test_postfix : &'a str,
}
impl< 'a > SmokeModuleTest< 'a >
{
fn new( dependency_name : String ) -> SmokeModuleTest< 'a >
{
let test_postfix = "_smoke_test";
let smoke_test_path = format!( "{}{}", dependency_name, test_postfix );
let mut test_path = std::env::temp_dir();
test_path.push( smoke_test_path );
SmokeModuleTest
{
dependency_name,
version : "*",
local_path_clause : "",
code : "".to_string(),
test_path,
test_postfix,
}
}
fn version( &mut self, version : &'a str ) -> &mut SmokeModuleTest< 'a >
{
self.version = version.as_ref();
self
}
fn local_path_clause( &mut self, local_path_clause : &'a str ) -> &mut SmokeModuleTest< 'a >
{
self.local_path_clause = local_path_clause;
self
}
fn test_postfix( &mut self, test_postfix : &'a str ) -> &mut SmokeModuleTest< 'a >
{
self.test_postfix = test_postfix;
let smoke_test_path = format!( "{}{}", self.dependency_name, test_postfix );
self.test_path.pop();
self.test_path.push( smoke_test_path );
self
}
fn code( &mut self, code : impl AsRef< str > + 'a ) -> &mut SmokeModuleTest< 'a >
{
self.code = code.as_ref().into();
self
}
fn form( &mut self ) -> Result< (), &'static str >
{
std::fs::create_dir( &self.test_path ).unwrap();
let mut test_path = self.test_path.clone();
let test_name = format!( "{}{}", self.dependency_name, self.test_postfix );
let output = std::process::Command::new( "cargo" )
.current_dir( &test_path )
.args([ "new", "--bin", &test_name ])
.output()
.expect( "Failed to execute command" );
println!( "Creating smoke binary module :\n\n{}", std::str::from_utf8( &output.stderr ).expect( "Found invalid UTF-8" ) );
test_path.push( &test_name );
#[ cfg( target_os = "windows" ) ]
let local_path_clause = if self.local_path_clause == "" { "".to_string() } else { format!( ", path = \"{}\"", self.local_path_clause.escape_default() ) };
#[ cfg( not( target_os = "windows" ) ) ]
let local_path_clause = if self.local_path_clause == "" { "".to_string() } else { format!( ", path = \"{}\"", self.local_path_clause ) };
let dependencies_section = format!( "{} = {{ version = \"{}\" {} }}", self.dependency_name, self.version, &local_path_clause );
let config_data = format!
(
"[package]
edition = \"2021\"
name = \"{}_smoke_test\"
version = \"0.0.1\"
[dependencies]
{}",
&self.dependency_name,
&dependencies_section
);
let mut config_path = test_path.clone();
config_path.push( "Cargo.toml" );
println!( "Manifest of module \"{}\" :\n\n {}\n", test_name, config_data );
std::fs::write( config_path, config_data ).unwrap();
test_path.push( "src" );
test_path.push( "main.rs" );
if self.code == ""
{
self.code = format!( "use ::{}::*;", self.dependency_name );
}
let code = format!
(
"#[ allow( unused_imports ) ]
fn main()
{{
{}
}}",
self.code,
);
self.code = code;
std::fs::write( &test_path, &self.code ).unwrap();
Ok( () )
}
fn perform( &self ) -> Result<(), BasicError>
{
let mut test_path = self.test_path.clone();
let test_name = format!( "{}{}", self.dependency_name, self.test_postfix );
test_path.push( test_name );
let output = std::process::Command::new( "cargo" )
.current_dir( test_path )
.args([ "run", "--release" ])
.output()
.unwrap();
println!( "{}", std::str::from_utf8( &output.stdout ).expect( "Found invalid UTF-8" ) );
println!( "{}", std::str::from_utf8( &output.stderr ).expect( "Found invalid UTF-8" ) );
println!( "Process status :\n {}\n", output.status );
println!( "Code :\n\n {}\n", self.code );
if !output.status.success()
{
return Err( BasicError::new( "Smoke test failed" ) );
}
Ok( () )
}
fn clean( &self, force : bool ) -> Result<(), &'static str>
{
let result = std::fs::remove_dir_all( &self.test_path );
if force
{
result.unwrap_or_default();
}
else
{
let msg = format!( "Cannot remove temporary directory {}. Please, remove it manually", &self.test_path.display() );
result.expect( &msg );
}
Ok( () )
}
}