use super::Directory; use dever::compiler::{project::Project, toolchain::BuildTarget}; use std::path::Path; use std::process::{Command, Output}; mod packaging; mod skill; fn cli(arguments: &[&str]) -> Output { Command::new(env!("CARGO_BIN_EXE_dever")) .args(arguments) .output() .unwrap() } fn success(output: Output) -> String { assert!( output.status.success(), "{}", String::from_utf8_lossy(&output.stderr) ); String::from_utf8(output.stdout).unwrap() } #[test] fn new_creates_plain_source_and_separate_docs_without_overwriting() { let directory = Directory::new(); for name in ["plain", "second"] { let root = directory.0.join(name); let text = root.to_str().unwrap(); let arguments = vec!["new", text]; success(cli(&arguments)); let suffix = "dever"; assert!( root.join(format!("module/hello/greeting/app.{suffix}")) .is_file() ); assert!( root.join(format!("test/hello/greeting/greet.{suffix}")) .is_file() ); assert!(root.join("module/hello/greeting/README.md").is_file()); assert!(root.join("module/hello/greeting/doc/greeting.md").is_file()); let project = Project::load(&root).unwrap(); assert_eq!(project.check().unwrap().commands.len(), 1); success(cli(&["check", text])); success(cli(&["fmt", text, "--check"])); let snapshot = std::fs::read(root.join("README.md")).unwrap(); assert!(!cli(&arguments).status.success()); assert_eq!(std::fs::read(root.join("README.md")).unwrap(), snapshot); } let path = directory.0.join("invalid"); assert!( !cli(&["new", path.to_str().unwrap(), "--markdown"]) .status .success() ); assert!(!path.exists()); let link = directory.0.join("link"); std::os::unix::fs::symlink(directory.0.join("plain"), &link).unwrap(); let rejected = cli(&["new", link.to_str().unwrap()]); assert!(!rejected.status.success()); assert!(String::from_utf8_lossy(&rejected.stderr).contains(link.to_str().unwrap())); assert_eq!( std::fs::read_link(&link).unwrap(), directory.0.join("plain") ); } #[test] fn template_runs_tests_and_builds_a_real_native_executable_for_the_selected_target() { let directory = Directory::new(); let root = directory.0.join("app"); dever::compiler::project::create(&root).unwrap(); let root = root.to_str().unwrap(); success(cli(&["test", root])); let result = success(cli(&[ "run", root, "--", "hello.greeting.greet", r#"{"name":"Dever"}"#, ])); assert_eq!( serde_json::from_str::(&result).unwrap()["data"], "Hello, Dever" ); let binary = directory.0.join("hello"); success(cli(&[ "build", root, "--output", binary.to_str().unwrap(), "--target", BuildTarget::host().unwrap().name(), ])); BuildTarget::host() .unwrap() .validate_executable(&std::fs::read(&binary).unwrap()) .unwrap(); let result = success( Command::new(&binary) .args(["hello.greeting.greet", r#"{"name":"native"}"#]) .output() .unwrap(), ); assert_eq!( serde_json::from_str::(&result).unwrap()["data"], "Hello, native" ); let before = std::fs::read(&binary).unwrap(); assert!( !cli(&["build", root, "--output", binary.to_str().unwrap()]) .status .success() ); assert_eq!(std::fs::read(&binary).unwrap(), before); assert!( !cli(&["run", root, "--target", "linux-aarch64"]) .status .success() ); assert!( !cli(&[ "build", root, "--output", binary.to_str().unwrap(), "--target", "unsupported" ]) .status .success() ); } #[test] fn elf_validation_checks_architecture_and_executable_type() { let mut header = vec![0_u8; 64]; header[..7].copy_from_slice(b"\x7fELF\x02\x01\x01"); header[16..18].copy_from_slice(&2_u16.to_le_bytes()); header[18..20].copy_from_slice(&183_u16.to_le_bytes()); assert!( BuildTarget::LinuxAarch64 .validate_executable(&header) .is_ok() ); assert!( BuildTarget::LinuxX86_64 .validate_executable(&header) .is_err() ); header[16..18].copy_from_slice(&1_u16.to_le_bytes()); assert!( BuildTarget::LinuxAarch64 .validate_executable(&header) .is_err() ); assert!( BuildTarget::LinuxAarch64 .validate_executable(b"short") .is_err() ); assert!(BuildTarget::parse("linux-x86_64").is_ok()); assert!(BuildTarget::parse("esp32").is_err()); } fn copy_tree(source: &Path, destination: &Path) { if source.is_dir() { std::fs::create_dir_all(destination).unwrap(); for entry in std::fs::read_dir(source) .unwrap_or_else(|error| panic!("{}: {error}", source.display())) { let entry = entry.unwrap(); copy_tree(&entry.path(), &destination.join(entry.file_name())); } } else { std::fs::create_dir_all(destination.parent().unwrap()).unwrap(); std::fs::copy(source, destination).unwrap(); } }