From eae4ee2b6d63d7d6379ad1f4b0257381965fe02d Mon Sep 17 00:00:00 2001 From: Alvin Peters Date: Sun, 16 Jun 2024 16:35:11 +1000 Subject: [PATCH 1/2] Add multi-platform tests using VMs --- .github/workflows/ci.yml | 47 ++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 607bac7..1ca5e98 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,32 +1,55 @@ on: + workflow_dispatch: pull_request: push: + tags: ["v*.*.*"] branches-ignore: - '**.tmp' -name: ci +name: Continuous Integration jobs: test: - runs-on: ubuntu-latest + name: Build / ${{matrix.target}} + + runs-on: ${{matrix.host_os}} + strategy: + fail-fast: false matrix: - rust: - - stable - - beta - - nightly - - 1.47.0 + include: + - target: x86_64-unknown-linux-gnu + host_os: ubuntu-latest + - target: x86_64-apple-darwin + host_os: macos-latest + - target: x86_64-unknown-freebsd + host_os: ubuntu-latest + vm_action: vmactions/freebsd-vm@v1 + - target: x86_64-unknown-openbsd + host_os: ubuntu-latest + vm_action: vmactions/openbsd-vm@v1 + steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - toolchain: ${{ matrix.rust }} - - uses: Swatinem/rust-cache@v2 - - run: cargo test --all + key: ${{matrix.host_os}}-${{matrix.target}}-ioctl-sys + - name: Test for target ${{matrix.target}} with a virtual machine + if: matrix.vm_action != null + uses: vmactions/openbsd-vm@v1 + with: + usesh: true + prepare: | + pkg_add rust + run: | + cargo test --all + - name: Test for target ${{matrix.target}} + if: matrix.vm_action == null + run: cargo test --all check: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@beta From 86939204d9362a4aa1a2b989967a5486bde50c3d Mon Sep 17 00:00:00 2001 From: Alvin Peters Date: Thu, 27 Jun 2024 22:45:34 +1000 Subject: [PATCH 2/2] Enable on FreeBSD and rest of BSDs BSD-derived systems share the same constants so enabled it for other, less known BSDs like NetBSD and DragonflyBSD. Only tested on macOS, OpenBSD, and FreeBSD for now. Added tests and CI jobs to test ioctl-sys with BSDs. --- .github/workflows/ci.yml | 45 +++++++-- ioctl-sys/Cargo.toml | 4 + ioctl-sys/src/lib.rs | 24 ++--- ioctl-sys/src/platform/{macos.rs => bsd.rs} | 1 + ioctl-sys/src/platform/mod.rs | 14 +-- ioctl-sys/src/platform/openbsd.rs | 19 ---- ioctl-sys/tests/ioctl_test.rs | 101 ++++++++++++++++++++ 7 files changed, 156 insertions(+), 52 deletions(-) rename ioctl-sys/src/platform/{macos.rs => bsd.rs} (82%) delete mode 100644 ioctl-sys/src/platform/openbsd.rs create mode 100644 ioctl-sys/tests/ioctl_test.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ca5e98..5288c96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,11 +9,9 @@ on: name: Continuous Integration jobs: - test: - name: Build / ${{matrix.target}} - + test-sys: + name: Build and test the sys crate / ${{matrix.target}} runs-on: ${{matrix.host_os}} - strategy: fail-fast: false matrix: @@ -28,14 +26,13 @@ jobs: - target: x86_64-unknown-openbsd host_os: ubuntu-latest vm_action: vmactions/openbsd-vm@v1 - steps: - uses: actions/checkout@v4 - name: Rust Cache uses: Swatinem/rust-cache@v2 with: key: ${{matrix.host_os}}-${{matrix.target}}-ioctl-sys - - name: Test for target ${{matrix.target}} with a virtual machine + - name: Build for target ${{matrix.target}} with a virtual machine if: matrix.vm_action != null uses: vmactions/openbsd-vm@v1 with: @@ -43,10 +40,10 @@ jobs: prepare: | pkg_add rust run: | - cargo test --all - - name: Test for target ${{matrix.target}} + cargo test --package ioctl-sys + - name: Build for target ${{matrix.target}} if: matrix.vm_action == null - run: cargo test --all + run: cargo test --package ioctl-sys check: runs-on: ubuntu-latest @@ -56,5 +53,33 @@ jobs: with: components: rustfmt, clippy - uses: Swatinem/rust-cache@v2 - - run: cargo fmt --all -- --check + - run: cargo fmt --all #- run: cargo clippy --all --all-targets -- -D warnings + + test: + name: Build / ${{matrix.target}} + runs-on: ${{matrix.host_os}} + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + host_os: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + key: ${{matrix.host_os}}-${{matrix.target}}-ioctl-sys + - name: Test for target ${{matrix.target}} with a virtual machine + if: matrix.vm_action != null + uses: vmactions/openbsd-vm@v1 + with: + usesh: true + prepare: | + pkg_add rust + run: | + cargo test --all + - name: Test for target ${{matrix.target}} + if: matrix.vm_action == null + run: cargo test --all \ No newline at end of file diff --git a/ioctl-sys/Cargo.toml b/ioctl-sys/Cargo.toml index 12f5f03..6d4b676 100644 --- a/ioctl-sys/Cargo.toml +++ b/ioctl-sys/Cargo.toml @@ -9,3 +9,7 @@ documentation = "https://docs.rs/ioctl-sys" include = ["Cargo.toml", "**/*.rs"] [dependencies] + +[dev-dependencies] +# Only for the ioctl test +libc = "0.2" diff --git a/ioctl-sys/src/lib.rs b/ioctl-sys/src/lib.rs index 73d7da7..c839f9a 100644 --- a/ioctl-sys/src/lib.rs +++ b/ioctl-sys/src/lib.rs @@ -1,20 +1,18 @@ use std::os::raw::{c_int, c_ulong}; -#[cfg(any( +#[cfg(not(any( target_os = "linux", target_os = "macos", target_os = "openbsd", + target_os = "freebsd", + target_os = "netbsd", + target_os = "dragonfly", target_os = "android" -))] -#[macro_use] +)))] +compile_error!("This platform is not supported!"); + mod platform; -#[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "openbsd", - target_os = "android" -))] pub use platform::*; extern "C" { @@ -31,14 +29,6 @@ pub fn check_res(res: c_int) -> std::io::Result<()> { } } -#[cfg(not(any( - target_os = "linux", - target_os = "macos", - target_os = "openbsd", - target_os = "android" -)))] -use platform_not_supported; - #[cfg(doctest)] mod test_readme { macro_rules! external_doc_test { diff --git a/ioctl-sys/src/platform/macos.rs b/ioctl-sys/src/platform/bsd.rs similarity index 82% rename from ioctl-sys/src/platform/macos.rs rename to ioctl-sys/src/platform/bsd.rs index 6331440..cc0db8b 100644 --- a/ioctl-sys/src/platform/macos.rs +++ b/ioctl-sys/src/platform/bsd.rs @@ -1,3 +1,4 @@ +/// Constants for BSD-derived operating systems. Confirmed to work on macOS, FreeBSD, OpenBSD. #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] mod consts { #[doc(hidden)] diff --git a/ioctl-sys/src/platform/mod.rs b/ioctl-sys/src/platform/mod.rs index cc8838e..4323483 100644 --- a/ioctl-sys/src/platform/mod.rs +++ b/ioctl-sys/src/platform/mod.rs @@ -7,12 +7,14 @@ pub const TYPEBITS: u32 = 8; #[path = "linux.rs"] mod consts; -#[cfg(target_os = "macos")] -#[path = "macos.rs"] -mod consts; - -#[cfg(target_os = "openbsd")] -#[path = "openbsd.rs"] +#[cfg(any( + target_os = "macos", + target_os = "openbsd", + target_os = "freebsd", + target_os = "netbsd", + target_os = "dragonfly", +))] +#[path = "bsd.rs"] mod consts; #[doc(hidden)] diff --git a/ioctl-sys/src/platform/openbsd.rs b/ioctl-sys/src/platform/openbsd.rs deleted file mode 100644 index 6331440..0000000 --- a/ioctl-sys/src/platform/openbsd.rs +++ /dev/null @@ -1,19 +0,0 @@ -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] -mod consts { - #[doc(hidden)] - pub const NONE: u8 = 1; - #[doc(hidden)] - pub const READ: u8 = 2; - #[doc(hidden)] - pub const WRITE: u8 = 4; - #[doc(hidden)] - pub const SIZEBITS: u8 = 13; - #[doc(hidden)] - pub const DIRBITS: u8 = 3; -} - -#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))] -use this_arch_not_supported; - -#[doc(hidden)] -pub use self::consts::*; diff --git a/ioctl-sys/tests/ioctl_test.rs b/ioctl-sys/tests/ioctl_test.rs new file mode 100644 index 0000000..9c9e4a9 --- /dev/null +++ b/ioctl-sys/tests/ioctl_test.rs @@ -0,0 +1,101 @@ +extern crate ioctl_sys; +extern crate libc; + +const TEMP_FILE_PATH: &str = concat!(env!("CARGO_TARGET_TMPDIR"), "/ioctl_test"); + +// BSD ioctl tests. Shamelessly stolen from the nix crate +#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "openbsd"))] +mod bsd_ioctls { + use std::fs::File; + use std::{io, mem}; + use std::os::fd::IntoRawFd; + use std::os::raw::c_int; + + use libc::termios; + + + // From: + // macOS: /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/ttycom.h + // FreeBSD, OpenBSD: + use ioctl_sys::ioctl; + use TEMP_FILE_PATH; + ioctl!(none tiocnxcl with b't', 14); + ioctl!(read tiocgeta with b't', 19; termios); + ioctl!(write tiocseta with b't', 20; termios); + + // Common function + fn test_ioctl( + expected_err_code: Option<(c_int, &str)>, + f: fn(temp_file_fd: c_int, stdout_fd: c_int) -> c_int + ) { + let temp_file_fd = File::create(TEMP_FILE_PATH) + .expect("create temp file").into_raw_fd(); + let stdout_fd = 1; + let result = f(temp_file_fd, stdout_fd); + match expected_err_code { + Some((code, name)) => { + let fail_err_code = io::Error::last_os_error().raw_os_error() + .expect("ioctl error code"); + assert_eq!(result, -1, "expected fail code (-1)"); + assert_eq!(fail_err_code, code, "expected error code {} ({})", name, code); + }, + None => { + assert_eq!(result, 0, "expected success code (0)"); + } + } + } + + #[test] + fn test_ioctl_none_fail() { + test_ioctl(Some((25, "ERRNOTTY")), |file_fd, _stdout_fd| { + unsafe { tiocnxcl( file_fd ) } + }); + } + + #[test] + fn test_ioctl_read_fail() { + test_ioctl(Some((25, "ERRNOTTY")), |file_fd, _stdout_fd| { + let mut termios = unsafe { mem::zeroed() }; + unsafe { tiocgeta( file_fd, &mut termios ) } + }); + } + + #[test] + fn test_ioctl_write_fail() { + test_ioctl(Some((25, "ERRNOTTY")), |file_fd, _stdout_fd| { + let mut termios = unsafe { mem::zeroed() }; + unsafe { tiocseta( file_fd, &mut termios ) } + }); + } + + // Ignored because it need doesn't work on GitHub actions + #[ignore] + #[test] + fn test_ioctl_none_pass() { + test_ioctl(Some((25, "ERRNOTTY")), |_file_fd, stdout_fd| { + unsafe { tiocnxcl( stdout_fd ) } + }); + } + + // Ignored because it need doesn't work on GitHub actions + #[ignore] + #[test] + fn test_ioctl_read_pass() { + test_ioctl(Some((25, "ERRNOTTY")), |_file_fd, stdout_fd| { + let mut termios = unsafe { mem::zeroed() }; + unsafe { tiocgeta( stdout_fd, &mut termios ) } + }); + } + + // Ignored because it need doesn't work on GitHub actions + // Also ignored because TIOCSETA with zeroed termios will destroy your current terminal session + // If you decide to test it, just restart your terminal after + #[ignore] + #[test] + fn test_ioctl_write_pass() { + test_ioctl(Some((25, "ERRNOTTY")), |_file_fd, stdout_fd| { + let mut termios = unsafe { mem::zeroed() }; + unsafe { tiocseta( stdout_fd, &mut termios ) } + }); + } +} \ No newline at end of file