From 399d37afa23d2594d7b4583005a3a3c1b83343da Mon Sep 17 00:00:00 2001 From: Kurtis Dinelle Date: Fri, 8 May 2026 13:34:38 -0700 Subject: [PATCH 1/4] dev-qemu: Add integration test to CI Assisted-by: GitHub Copilot:claude-opus-4.6 Signed-off-by: Kurtis Dinelle --- .github/workflows/check.yml | 32 +++++++++ platform/dev-qemu/.cargo/config.toml | 15 ++++ scripts/integration-test.sh | 100 +++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100755 scripts/integration-test.sh diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 29b708b..5196c5d 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -311,3 +311,35 @@ jobs: - name: cargo build --locked working-directory: platform/${{ matrix.platform }} run: cargo build --locked + + integration-test: + # Runs `dev-qemu` and exercises all `ec-test-cli` commands against it + runs-on: ubuntu-latest + name: dev-qemu / integration-test + env: + # We keep this pinned to a specific commit since we don't want to accidentally break our CI + # if there are breaking changes in `ec-test-cli` + # + # We can update this commit when we want to pull in a new `ec-test-cli` + EC_TEST_CLI_REV: e674ca93 + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Install stable + uses: dtolnay/rust-toolchain@stable + + - name: rustup target add riscv32imac-unknown-none-elf + run: rustup target add riscv32imac-unknown-none-elf + + # Note: qemu-system-misc contains the `qemu-system-riscv32` binary which is needed to run `dev-qemu` + - name: Install qemu-system-misc + run: sudo apt-get update && sudo apt-get install -y qemu-system-misc + + - name: Install ec-test-cli + run: cargo install --git https://github.com/OpenDevicePartnership/odp-platform-common --locked --rev ${{ env.EC_TEST_CLI_REV }} ec-test-cli + + - name: Run integration tests + run: ./scripts/integration-test.sh diff --git a/platform/dev-qemu/.cargo/config.toml b/platform/dev-qemu/.cargo/config.toml index c1fa1f7..502289d 100644 --- a/platform/dev-qemu/.cargo/config.toml +++ b/platform/dev-qemu/.cargo/config.toml @@ -8,3 +8,18 @@ target = "riscv32imac-unknown-none-elf" [env] DEFMT_LOG = "trace" + +[alias] +# This disables defmt logging (hence `headless` since there is no output), +# which allows us to turn off semihosting +# +# Main use-case for this is running `dev-qemu` in CI where we don't care about logging +# and don't want to have to install `defmt-print` for it to run +run-headless = [ + "run", + "--release", + "--config", + 'env.DEFMT_LOG="off"', + "--config", + 'target.riscv32imac-unknown-none-elf.runner="qemu-system-riscv32 -machine virt -bios none -serial pty -nographic -monitor none -kernel"', +] diff --git a/scripts/integration-test.sh b/scripts/integration-test.sh new file mode 100755 index 0000000..87ed2f9 --- /dev/null +++ b/scripts/integration-test.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# +# This script spawns an instance of `dev-qemu` and runs the `ec-test-cli` commands against it. +# Mainly used within CI, but can be run locally assuming `ec-test-cli` is installed with: +# `cargo install --git https://github.com/OpenDevicePartnership/odp-platform-common ec-test-cli --locked` +# +# Currently this only checks the status code of the commands, not the actual returned result. +# So this won't catch if requests are responding successfully but with garbage data. +# Will need to add support to `ec-test-cli` to accept a range of expected outputs to check against +# for that. + +# Causes script to exit non-zero on the first test failure, +# which will then cause entire script to return non-zero and fail CI +set -eo pipefail + +REPO_ROOT=$(git rev-parse --show-toplevel) + +# Dump all `dev-qemu` output here so we can extract info or print it on failure +QEMU_LOG=$(mktemp) + +# The dev-qemu process is launched in the background, so we need to ensure it gets killed when this +# script exits regardless of whether it succeeded or failed +cleanup() { + # Print the dev-qemu log if we encountered error + if [[ $? -ne 0 && -f "$QEMU_LOG" ]]; then + echo "dev-qemu output:" + cat "$QEMU_LOG" + fi + if [[ -n "$QEMU_PID" ]]; then + kill "$QEMU_PID" 2>/dev/null || true + wait "$QEMU_PID" 2>/dev/null || true + fi + rm -f "$QEMU_LOG" +} +trap cleanup EXIT + +# Build `dev-qemu` (with DEFMT disabled since we don't need logs) +cd "$REPO_ROOT/platform/dev-qemu" +echo "Building dev-qemu..." +cargo build --release --config 'env.DEFMT_LOG="off"' + +# Then launch it in "headless mode" (again, DEFMT disabled), +# and sleeping a bit to ensure serial comms are ready +echo "Starting dev-qemu..." +cargo run-headless > "$QEMU_LOG" 2>&1 & +QEMU_PID=$! +sleep 1 + +# Verify QEMU didn't exit for some reason +if ! kill -0 "$QEMU_PID" 2>/dev/null; then + echo "ERROR: dev-qemu exited unexpectedly" + exit 1 +fi + +# Extract PTY path and pass it to `ec-test-cli` +PTY_PATH=$(grep -oP '/dev/pts/\d+' "$QEMU_LOG" || true) +if [[ -z "$PTY_PATH" ]]; then + echo "ERROR: Could not find PTY path in dev-qemu output" + exit 1 +fi +echo "dev-qemu serial port: $PTY_PATH" +CLI="ec-test-cli --port $PTY_PATH" + +# Helper to run a ec-test-cli request and print the result +run_test() { + echo "$*" + $CLI "$@" + # Note: Sleep is needed since otherwise we seem to get a timeout error on next request + sleep 0.01 +} + +# Thermal +echo "Running thermal tests..." +run_test thermal get-temperature +run_test thermal get-rpm +run_test thermal get-min-rpm +run_test thermal get-max-rpm +run_test thermal get-threshold on +run_test thermal get-threshold ramping +run_test thermal get-threshold max +run_test thermal set-rpm 1000 + +# Battery +echo "Running battery tests..." +run_test battery get-bst +run_test battery get-bix +run_test battery set-btp 50 + +# RTC +echo "Running RTC tests..." +run_test rtc get-capabilities +run_test rtc get-real-time +run_test rtc get-wake-status ac +run_test rtc get-wake-status dc +run_test rtc get-expired-timer-wake-policy ac +run_test rtc get-expired-timer-wake-policy dc +run_test rtc get-timer-value ac +run_test rtc get-timer-value dc + +echo "SUCCESS: Integration test passed!" From 2c36231b8a16307d8b8ab95d2f7fb4202354f85e Mon Sep 17 00:00:00 2001 From: Kurtis Dinelle Date: Fri, 8 May 2026 14:04:04 -0700 Subject: [PATCH 2/4] dev-qemu: Address Copilot's suggestion Assisted-by: GitHub Copilot:claude-opus-4.6 Signed-off-by: Kurtis Dinelle --- scripts/integration-test.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/integration-test.sh b/scripts/integration-test.sh index 87ed2f9..448c7e5 100755 --- a/scripts/integration-test.sh +++ b/scripts/integration-test.sh @@ -11,12 +11,13 @@ # Causes script to exit non-zero on the first test failure, # which will then cause entire script to return non-zero and fail CI -set -eo pipefail +set -euo pipefail REPO_ROOT=$(git rev-parse --show-toplevel) # Dump all `dev-qemu` output here so we can extract info or print it on failure QEMU_LOG=$(mktemp) +QEMU_PID="" # The dev-qemu process is launched in the background, so we need to ensure it gets killed when this # script exits regardless of whether it succeeded or failed @@ -37,12 +38,12 @@ trap cleanup EXIT # Build `dev-qemu` (with DEFMT disabled since we don't need logs) cd "$REPO_ROOT/platform/dev-qemu" echo "Building dev-qemu..." -cargo build --release --config 'env.DEFMT_LOG="off"' +cargo build --locked --release --config 'env.DEFMT_LOG="off"' # Then launch it in "headless mode" (again, DEFMT disabled), # and sleeping a bit to ensure serial comms are ready echo "Starting dev-qemu..." -cargo run-headless > "$QEMU_LOG" 2>&1 & +cargo run-headless --locked > "$QEMU_LOG" 2>&1 & QEMU_PID=$! sleep 1 @@ -53,7 +54,7 @@ if ! kill -0 "$QEMU_PID" 2>/dev/null; then fi # Extract PTY path and pass it to `ec-test-cli` -PTY_PATH=$(grep -oP '/dev/pts/\d+' "$QEMU_LOG" || true) +PTY_PATH=$(grep -oE '/dev/pts/[0-9]+' "$QEMU_LOG" || true) if [[ -z "$PTY_PATH" ]]; then echo "ERROR: Could not find PTY path in dev-qemu output" exit 1 From 3ee1bf913b103b63355731e2805e386a8d6e00e3 Mon Sep 17 00:00:00 2001 From: Kurtis Dinelle Date: Mon, 11 May 2026 09:10:32 -0700 Subject: [PATCH 3/4] dev-qemu: Install libudev-dev in job Assisted-by: GitHub Copilot:claude-opus-4.6 Signed-off-by: Kurtis Dinelle --- .github/workflows/check.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 5196c5d..78cedc0 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -335,8 +335,9 @@ jobs: run: rustup target add riscv32imac-unknown-none-elf # Note: qemu-system-misc contains the `qemu-system-riscv32` binary which is needed to run `dev-qemu` - - name: Install qemu-system-misc - run: sudo apt-get update && sudo apt-get install -y qemu-system-misc + # libudev-dev is needed by `ec-test-cli` (via libudev-sys) + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y qemu-system-misc libudev-dev - name: Install ec-test-cli run: cargo install --git https://github.com/OpenDevicePartnership/odp-platform-common --locked --rev ${{ env.EC_TEST_CLI_REV }} ec-test-cli From 9c3554bc1c10415cc21b38ca89d88d7f8e5a095c Mon Sep 17 00:00:00 2001 From: Kurtis Dinelle Date: Tue, 12 May 2026 09:50:56 -0700 Subject: [PATCH 4/4] Add timeout/polling --- .github/workflows/check.yml | 3 +++ scripts/integration-test.sh | 31 +++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 78cedc0..9e367ec 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -316,6 +316,9 @@ jobs: # Runs `dev-qemu` and exercises all `ec-test-cli` commands against it runs-on: ubuntu-latest name: dev-qemu / integration-test + # Failsafe in case the integration test script hangs for some reason + # If it takes longer than 30 minutes something definitely went wrong + timeout-minutes: 30 env: # We keep this pinned to a specific commit since we don't want to accidentally break our CI # if there are breaking changes in `ec-test-cli` diff --git a/scripts/integration-test.sh b/scripts/integration-test.sh index 448c7e5..9b4aaaa 100755 --- a/scripts/integration-test.sh +++ b/scripts/integration-test.sh @@ -41,22 +41,33 @@ echo "Building dev-qemu..." cargo build --locked --release --config 'env.DEFMT_LOG="off"' # Then launch it in "headless mode" (again, DEFMT disabled), -# and sleeping a bit to ensure serial comms are ready +# and poll until serial comms are ready echo "Starting dev-qemu..." cargo run-headless --locked > "$QEMU_LOG" 2>&1 & QEMU_PID=$! -sleep 1 -# Verify QEMU didn't exit for some reason -if ! kill -0 "$QEMU_PID" 2>/dev/null; then - echo "ERROR: dev-qemu exited unexpectedly" - exit 1 -fi +# Poll for the PTY path to appear in dev-qemu output, indicating serial is ready +# Note: We do this poll-sleep loop in case the CI is under heavy load +MAX_RETRIES=10 +WAIT_INTERVAL=1 +PTY_PATH="" +for i in $(seq 1 $MAX_RETRIES); do + if ! kill -0 "$QEMU_PID" 2>/dev/null; then + echo "ERROR: dev-qemu exited unexpectedly" + exit 1 + fi + + PTY_PATH=$(grep -oE '/dev/pts/[0-9]+' "$QEMU_LOG" || true) + if [[ -n "$PTY_PATH" ]]; then + break + fi + + echo "Waiting for dev-qemu to be ready... ($i/$MAX_RETRIES)" + sleep $WAIT_INTERVAL +done -# Extract PTY path and pass it to `ec-test-cli` -PTY_PATH=$(grep -oE '/dev/pts/[0-9]+' "$QEMU_LOG" || true) if [[ -z "$PTY_PATH" ]]; then - echo "ERROR: Could not find PTY path in dev-qemu output" + echo "ERROR: Could not find PTY path in dev-qemu output after ${MAX_RETRIES}s" exit 1 fi echo "dev-qemu serial port: $PTY_PATH"