diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 9380186..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "i686-pc-windows-msvc" diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 72b73c0..0f5f3a3 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -22,10 +22,10 @@ jobs: uses: actions/checkout@v4 - name: Setup Toolchains - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: toolchain: stable - target: ${{ matrix.target_name }} + targets: ${{ matrix.target_name }} - name: Install g++ multilib (Ubuntu) run: | @@ -54,32 +54,59 @@ jobs: toolchain: stable command: fmt args: --all -- --check - # TODO: write linux tests - run_test: - name: Run test + + run_test_windows: + name: Run test (Windows) runs-on: windows-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Toolchains - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: toolchain: stable - target: i686-pc-windows-msvc + targets: i686-pc-windows-msvc - name: Restore BYOND cache uses: actions/cache@v3 with: path: ~/BYOND - key: linux-byond - - - name: Set up byond - run: bash ./test_byond.sh + key: windows-byond - name: Run tests - uses: actions-rs/cargo@v1 + run: | + bash ./tools/setup_byond_windows.sh + $Env:BYOND_LOCATION = "$HOME\BYOND\byond\bin" + cargo test --package byondapi-test --target i686-pc-windows-msvc --test test -- test_byondapi_with_dreamdaemon --exact --nocapture + + run_test_linux: + name: Run test (Ubuntu) + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Toolchains + uses: dtolnay/rust-toolchain@stable with: toolchain: stable - command: test - args: test_byondapi_with_dreamdaemon --target i686-pc-windows-msvc + targets: i686-unknown-linux-gnu + + - name: Install g++ multilib + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install build-essential g++-multilib libc6-i386 libstdc++6:i386 + + - name: Restore BYOND cache + uses: actions/cache@v3 + with: + path: ~/BYOND + key: linux-byond + + - name: Run tests + run: | + bash ./tools/setup_byond_linux.sh + source $HOME/BYOND/byond/bin/byondsetup + cargo test --package byondapi-test --target i686-unknown-linux-gnu --test test -- test_byondapi_with_dreamdaemon --exact --nocapture diff --git a/.gitignore b/.gitignore index 6985cf1..448d64d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ Cargo.lock # MSVC Windows builds of rustc generate these, which store debugging information *.pdb + +# Must be defined by users, since they may be on linux +.cargo/ \ No newline at end of file diff --git a/crates/byondapi-rs-test/Cargo.toml b/crates/byondapi-rs-test/Cargo.toml index b3af029..5f390ad 100644 --- a/crates/byondapi-rs-test/Cargo.toml +++ b/crates/byondapi-rs-test/Cargo.toml @@ -12,4 +12,4 @@ crate-type = ["cdylib"] byondapi = { path = "../byondapi-rs" } byondapi-sys = { path = "../byondapi-sys" } tempfile = "3.8.1" -test-cdylib = "1.1.0" +cargo_metadata = "0.18.1" diff --git a/crates/byondapi-rs-test/dm_project/dm_project.dme b/crates/byondapi-rs-test/dm_project/dm_project.dme index abbda4b..bd57742 100644 --- a/crates/byondapi-rs-test/dm_project/dm_project.dme +++ b/crates/byondapi-rs-test/dm_project/dm_project.dme @@ -28,9 +28,17 @@ throw EXCEPTION("Object did not make it through FFI") /test/proc/test_ref() + world.maxz = 1 + world.maxx = 1 + world.maxy = 1 + var/turf/T = locate(1,1,1) var/ret = call_ext("byondapi_test.dll", "byond:test_ref")(T) + world.maxz = 0 + world.maxx = 0 + world.maxy = 0 + /test/proc/test_ptr() var/x = "meow" var/ptr = &x @@ -55,6 +63,20 @@ /datum/data var/name = "test name" +/test/proc/test_block() + world.maxz = 1 + world.maxx = 2 + world.maxy = 2 + + var/ret = call_ext("byondapi_test.dll", "byond:test_block")() + + if(ret != 4) + throw EXCEPTION("Block failed [json_encode(ret)]") + + world.maxz = 0 + world.maxx = 0 + world.maxy = 0 + /test/proc/test_readwrite_var() var/datum/data/stub = new() @@ -102,20 +124,6 @@ if(ret != 5) throw EXCEPTION("List length failed [json_encode(ret)]") -/test/proc/test_block() - world.maxz = 1 - world.maxx = 2 - world.maxy = 2 - - var/ret = call_ext("byondapi_test.dll", "byond:test_block")() - - if(ret != 4) - throw EXCEPTION("Block failed [json_encode(ret)]") - - world.maxz = 0 - world.maxx = 0 - world.maxy = 0 - /test/proc/test_length_with_str() var/str = "meowman" @@ -136,6 +144,11 @@ if(L["parrot"] != 14) throw EXCEPTION("list modification by key failed") + +/test/proc/test_list_read() + var/list/L = list("cat" = 0, "dog" = 1, "parrot" = 5) + call_ext("byondapi_test.dll", "byond:test_list_read")(L) + // BEGIN_INTERNALS // END_INTERNALS // BEGIN_FILE_DIR diff --git a/crates/byondapi-rs-test/src/lib.rs b/crates/byondapi-rs-test/src/lib.rs index faefdf3..f82b05e 100644 --- a/crates/byondapi-rs-test/src/lib.rs +++ b/crates/byondapi-rs-test/src/lib.rs @@ -310,3 +310,26 @@ pub unsafe extern "C" fn test_non_assoc_list( ByondValue::new() } + +#[no_mangle] +pub unsafe extern "C" fn test_list_read( + argc: byondapi_sys::u4c, + argv: *mut ByondValue, +) -> ByondValue { + setup_panic_handler(); + let args = parse_args(argc, argv); + let list = args.get(0).unwrap(); + + let map = list.get_list().unwrap(); + let values = map + .into_iter() + .map(|item| item.get_string().unwrap()) + .collect::>(); + + assert_eq!( + values, + vec!["cat".to_owned(), "dog".to_owned(), "parrot".to_owned()] + ); + + ByondValue::new() +} diff --git a/crates/byondapi-rs-test/tests/test.rs b/crates/byondapi-rs-test/tests/test.rs index 6176193..fb3f71d 100644 --- a/crates/byondapi-rs-test/tests/test.rs +++ b/crates/byondapi-rs-test/tests/test.rs @@ -1,59 +1,62 @@ use std::{ path::{Path, PathBuf}, - process::Command, + process::{Command, Output}, }; use tempfile::TempDir; #[test] -#[cfg(windows)] fn test_byondapi_with_dreamdaemon() { let dll = build_dylib(); compile(); let tempdir = tempfile::tempdir().expect("Failed to create temporary directory"); + copy_to_tmp(&dll, &tempdir); - run_dreamdaemon(&tempdir); + + let stderr = run_dreamdaemon(&tempdir); + check_output_rust(&tempdir); check_output_dd(&tempdir); + + #[cfg(unix)] + const LINE_COUNT: usize = 3; + #[cfg(windows)] + const LINE_COUNT: usize = 5; + + if stderr.lines().count() > LINE_COUNT { + panic!("Stderr contains more than 3 lines, an error message might be printed!") + } } fn bin_path() -> PathBuf { - Path::new(env!("CARGO_MANIFEST_DIR")) - .join("dm_project") - .join("byond") - .join("bin") + match std::env::var("BYOND_LOCATION") { + Ok(value) => { + println!("Using byond from dir {value}"); + value.into() + } + Err(_) => { + println!("Byond not found, using default location"); + println!("To set a location for byond, set the BYOND_LOCATION environment variable to a path"); + println!("Keep in mind that this path has to point to the /bin folder of byond"); + "C:\\Program Files (x86)\\BYOND\\bin".into() + } + } } -fn find_dm() -> Result { - let base_path = bin_path(); - - let path = if cfg!(windows) { - base_path.join("dm.exe") - } else { - base_path.join("DreamMaker") - }; - - if path.exists() { - Ok(path) +fn find_dm() -> PathBuf { + if cfg!(windows) { + bin_path().join("dm.exe") } else { - Err(()) + "DreamMaker".into() } } -fn find_dd() -> Result { - let base_path = bin_path(); - - let path = if cfg!(windows) { - base_path.join("dd.exe") - } else { - base_path.join("DreamDaemon") - }; - - if path.exists() { - Ok(path) +fn find_dd() -> PathBuf { + if cfg!(windows) { + bin_path().join("dd.exe") } else { - Err(()) + "DreamDaemon".into() } } @@ -62,11 +65,35 @@ fn project_dir() -> PathBuf { } fn build_dylib() -> PathBuf { - test_cdylib::build_current_project() + let mut cmd = Command::new(option_env!("CARGO").unwrap_or("cargo")); + + cmd.arg("build").arg("--message-format=json").arg("--lib"); + #[cfg(windows)] + cmd.arg("--target=i686-pc-windows-msvc"); + #[cfg(unix)] + cmd.arg("--target=i686-unknown-linux-gnu"); + cmd.stderr(std::process::Stdio::inherit()); + parse_output(cmd.output().unwrap()) +} + +fn parse_output(res: Output) -> PathBuf { + let mut artifact = None; + for message in cargo_metadata::Message::parse_stream(res.stdout.as_slice()) { + match message.unwrap() { + cargo_metadata::Message::CompilerMessage(m) => eprintln!("{}", m), + cargo_metadata::Message::CompilerArtifact(a) => artifact = Some(a), + _ => (), + } + } + + if !res.status.success() { + panic!("Failed to build") + } + artifact.unwrap().filenames[0].clone().into() } fn compile() { - let dm_compiler = find_dm().expect("To run this integration test you must place a copy of BYOND binaries in dm_project/byond/bin"); + let dm_compiler = find_dm(); let output = Command::new(dm_compiler) .current_dir(project_dir()) @@ -98,17 +125,27 @@ fn copy_to_tmp(dll: &Path, tempdir: &TempDir) { std::fs::copy(dll, target.join("byondapi_test.dll")).expect("Failed to copy byondapi_test.dll"); } -fn run_dreamdaemon(tempdir: &TempDir) { - let dream_daemon = find_dd().expect("To run this integration test you must place a copy of BYOND binaries in dm_project/byond/bin"); +fn run_dreamdaemon(tempdir: &TempDir) -> String { + let dream_daemon = find_dd(); - let _dd_output = Command::new(dream_daemon) + let dd_output = Command::new(dream_daemon) .current_dir(tempdir.path()) .arg("dm_project.dmb") .arg("-trusted") .output() .expect("DreamDaemon crashed"); + let stdout = std::str::from_utf8(&dd_output.stdout).unwrap(); + let stderr = std::str::from_utf8(&dd_output.stderr).unwrap(); + if !stdout.is_empty() { + eprintln!("Stdout:-------------------------------------------------------------------"); + eprintln!("{stdout}"); + } - // println!("{:#?}", _dd_output); + if !stderr.is_empty() { + eprintln!("Stderr:-------------------------------------------------------------------"); + eprintln!("{stderr}"); + } + stderr.to_owned() } fn check_output_dd(tempdir: &TempDir) { @@ -118,6 +155,7 @@ fn check_output_dd(tempdir: &TempDir) { let log = std::fs::read_to_string(log).expect("Failed to read log"); + eprintln!("DDlogs:-------------------------------------------------------------------"); eprintln!("{}", log); assert!( @@ -131,6 +169,7 @@ fn check_output_rust(tempdir: &TempDir) { if log.exists() { let log = std::fs::read_to_string(log).expect("Failed to read log"); + eprintln!("Rustlogs:-----------------------------------------------------------------"); eprintln!("{}", log); panic!("Rust error log was produced!"); } diff --git a/crates/byondapi-rs/src/list.rs b/crates/byondapi-rs/src/list.rs index 866bbc6..7f84761 100644 --- a/crates/byondapi-rs/src/list.rs +++ b/crates/byondapi-rs/src/list.rs @@ -20,7 +20,7 @@ impl ByondValue { unsafe { byond().Byond_ReadList(&self.0, buff.as_mut_ptr().cast(), &mut len) }; match (initial_res, len) { (false, 1..) => { - buff.reserve_exact(len as usize - buff.capacity()); + buff.reserve_exact(len as usize); // Safety: buffer capacity is passed to byond, which makes sure it writes in-bound unsafe { map_byond_error!(byond().Byond_ReadList( @@ -103,9 +103,7 @@ impl ByondValue { if !self.is_list() { return Err(Error::NotAList); } - let mut list_copy = self.get_list()?; - list_copy.push(value); - self.write_list(&list_copy)?; + self.call("Add", &[value])?; Ok(()) } @@ -114,9 +112,12 @@ impl ByondValue { if !self.is_list() { return Err(Error::NotAList); } - let mut list_copy = self.get_list()?; - let value = list_copy.pop(); - self.write_list(&list_copy)?; - Ok(value) + let len = self.builtin_length()?.get_number()? as usize; + if len == 0 { + return Ok(None); + } + let value = self.read_list_index(len as f32)?; + self.call("Remove", &[value])?; + Ok(Some(value)) } } diff --git a/crates/byondapi-rs/src/map.rs b/crates/byondapi-rs/src/map.rs index f146f1b..c2fd384 100644 --- a/crates/byondapi-rs/src/map.rs +++ b/crates/byondapi-rs/src/map.rs @@ -45,7 +45,7 @@ pub fn byond_block(corner1: ByondXYZ, corner2: ByondXYZ) -> Result { - buff.reserve_exact(len as usize - buff.capacity()); + buff.reserve_exact(len as usize); // Safety: buffer capacity is passed to byond, which makes sure it writes in-bound unsafe { map_byond_error!(byond().Byond_Block( @@ -55,6 +55,8 @@ pub fn byond_block(corner1: ByondXYZ, corner2: ByondXYZ) -> Result "$HOME/BYOND/version.txt" + cd ~/ +fi diff --git a/test_byond.sh b/tools/setup_byond_windows.sh similarity index 62% rename from test_byond.sh rename to tools/setup_byond_windows.sh index 601ab96..a087016 100644 --- a/test_byond.sh +++ b/tools/setup_byond_windows.sh @@ -1,8 +1,8 @@ #!/bin/bash set -euo pipefail -export BYOND_MAJOR=515 -export BYOND_MINOR=1620 +BYOND_MAJOR=515 +BYOND_MINOR=1620 if [ -d "$HOME/BYOND/byond/bin" ] && grep -Fxq "${BYOND_MAJOR}.${BYOND_MINOR}" $HOME/BYOND/version.txt; then @@ -18,7 +18,4 @@ else cd byond echo "$BYOND_MAJOR.$BYOND_MINOR" > "$HOME/BYOND/version.txt" cd ~/ -fi -mkdir -p "$GITHUB_WORKSPACE/crates/byondapi-rs-test/dm_project/byond" -cp -r "$HOME/BYOND/byond/bin" "$GITHUB_WORKSPACE/crates/byondapi-rs-test/dm_project/byond" -echo "Written byond bin to $GITHUB_WORKSPACE/crates/byondapi-rs-test/dm_project/byond" +fi \ No newline at end of file