Skip to content

Commit e4d3c58

Browse files
committed
ci(qemu): Add QEMU emulator to CI
1 parent cf44890 commit e4d3c58

File tree

12 files changed

+138
-28
lines changed

12 files changed

+138
-28
lines changed

Diff for: .github/scripts/sketch_utils.sh

+9-9
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ function build_sketch(){ # build_sketch <ide_path> <user_path> <path-to-ino> [ex
7878

7979
# Default FQBN options if none were passed in the command line.
8080

81-
esp32_opts="PSRAM=enabled,PartitionScheme=huge_app"
81+
esp32_opts="FlashMode=dio,PSRAM=enabled,PartitionScheme=huge_app"
8282
esp32s2_opts="PSRAM=enabled,PartitionScheme=huge_app"
8383
esp32s3_opts="PSRAM=opi,USBMode=default,PartitionScheme=huge_app"
84-
esp32c3_opts="PartitionScheme=huge_app"
84+
esp32c3_opts="FlashMode=dio,PartitionScheme=huge_app"
8585
esp32c6_opts="PartitionScheme=huge_app"
8686
esp32h2_opts="PartitionScheme=huge_app"
8787

@@ -139,7 +139,7 @@ function build_sketch(){ # build_sketch <ide_path> <user_path> <path-to-ino> [ex
139139
echo "Skipping $sketchname for target $target"
140140
exit 0
141141
fi
142-
142+
143143
ARDUINO_CACHE_DIR="$HOME/.arduino/cache.tmp"
144144
if [ -n "$ARDUINO_BUILD_DIR" ]; then
145145
build_dir="$ARDUINO_BUILD_DIR"
@@ -177,7 +177,7 @@ function build_sketch(){ # build_sketch <ide_path> <user_path> <path-to-ino> [ex
177177
--build-path "$build_dir" \
178178
$xtra_opts "${sketchdir}" \
179179
> $output_file
180-
180+
181181
exit_status=$?
182182
if [ $exit_status -ne 0 ]; then
183183
echo ""ERROR: Compilation failed with error code $exit_status""
@@ -198,11 +198,11 @@ function build_sketch(){ # build_sketch <ide_path> <user_path> <path-to-ino> [ex
198198
# Extract the desired substring using sed
199199
lib_sketch_name=$(echo "$directory_path" | sed "s|$constant_part||")
200200
#append json file where key is fqbn, sketch name, sizes -> extracted values
201-
echo "{\"name\": \"$lib_sketch_name\",
201+
echo "{\"name\": \"$lib_sketch_name\",
202202
\"sizes\": [{
203-
\"flash_bytes\": $flash_bytes,
204-
\"flash_percentage\": $flash_percentage,
205-
\"ram_bytes\": $ram_bytes,
203+
\"flash_bytes\": $flash_bytes,
204+
\"flash_percentage\": $flash_percentage,
205+
\"ram_bytes\": $ram_bytes,
206206
\"ram_percentage\": $ram_percentage
207207
}]
208208
}," >> "$sizes_file"
@@ -386,7 +386,7 @@ function build_sketches(){ # build_sketches <ide_path> <user_path> <target> <pat
386386
if [ $log_compilation ]; then
387387
#echo board,target and start of sketches to sizes_file json
388388
echo "{ \"board\": \"$fqbn\",
389-
\"target\": \"$target\",
389+
\"target\": \"$target\",
390390
\"sketches\": [" >> "$sizes_file"
391391
fi
392392

Diff for: .github/scripts/tests_run.sh

+34-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ function run_test() {
88
local sketchdir=$(dirname $sketch)
99
local sketchname=$(basename $sketchdir)
1010

11+
if [[ -f "$sketchdir/.skip.$platform" ]] || [[ -f "$sketchdir/.skip.$target" ]] || [[ -f "$sketchdir/.skip.$platform.$target" ]]; then
12+
echo "Skipping $sketchname test in $target for $platform"
13+
exit 0
14+
fi
15+
1116
if [ $options -eq 0 ] && [ -f $sketchdir/cfg.json ]; then
1217
len=`jq -r --arg chip $target '.targets[] | select(.name==$chip) | .fqbn | length' $sketchdir/cfg.json`
1318
else
@@ -33,7 +38,24 @@ function run_test() {
3338
report_file="tests/$sketchname/$sketchname$i.xml"
3439
fi
3540

36-
pytest tests --build-dir $build_dir -k test_$sketchname --junit-xml=$report_file
41+
if [ $platform == "qemu" ]; then
42+
PATH=$HOME/qemu/bin:$PATH
43+
extra_args="--embedded-services qemu --qemu-image-path $build_dir/$sketchname.ino.merged.bin"
44+
45+
if [ $target == "esp32" ] || [ $target == "esp32s3" ]; then
46+
extra_args+=" --qemu-prog-path qemu-system-xtensa --qemu-cli-args=\"-machine $target -m 4M -nographic\""
47+
elif [ $target == "esp32c3" ]; then
48+
extra_args+=" --qemu-prog-path qemu-system-riscv32 --qemu-cli-args=\"-machine $target -icount 3 -nographic\""
49+
else
50+
echo "Unsupported QEMU target: $target"
51+
exit 1
52+
fi
53+
else
54+
extra_args="--embedded-services esp,arduino"
55+
fi
56+
57+
echo "pytest tests --build-dir $build_dir -k test_$sketchname --junit-xml=$report_file $extra_args"
58+
bash -c "pytest tests --build-dir $build_dir -k test_$sketchname --junit-xml=$report_file $extra_args"
3759
result=$?
3860
if [ $result -ne 0 ]; then
3961
return $result
@@ -44,6 +66,7 @@ function run_test() {
4466
SCRIPTS_DIR="./.github/scripts"
4567
COUNT_SKETCHES="${SCRIPTS_DIR}/sketch_utils.sh count"
4668

69+
platform="hardware"
4770
chunk_run=0
4871
options=0
4972
erase=0
@@ -53,6 +76,13 @@ while [ ! -z "$1" ]; do
5376
-c )
5477
chunk_run=1
5578
;;
79+
-q )
80+
if [ ! -d $QEMU_PATH ]; then
81+
echo "QEMU path $QEMU_PATH does not exist"
82+
exit 1
83+
fi
84+
platform="qemu"
85+
;;
5686
-o )
5787
options=1
5888
;;
@@ -86,7 +116,9 @@ while [ ! -z "$1" ]; do
86116
shift
87117
done
88118

89-
source ${SCRIPTS_DIR}/install-arduino-ide.sh
119+
if [ ! $platform == "qemu" ]; then
120+
source ${SCRIPTS_DIR}/install-arduino-ide.sh
121+
fi
90122

91123
if [ $chunk_run -eq 0 ]; then
92124
run_test $target $PWD/tests/$sketch/$sketch.ino $options $erase

Diff for: .github/workflows/hil.yml

+79-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Run tests in hardware
1+
name: Run tests
22

33
on:
44
pull_request:
@@ -16,9 +16,7 @@ concurrency:
1616

1717
jobs:
1818
gen_chunks:
19-
if: |
20-
contains(github.event.pull_request.labels.*.name, 'hil_test') ||
21-
(github.event_name == 'schedule' && github.repository == 'espressif/arduino-esp32')
19+
if: github.repository == 'espressif/arduino-esp32'
2220
name: Generate Chunks matrix
2321
runs-on: ubuntu-latest
2422
outputs:
@@ -41,7 +39,7 @@ jobs:
4139
CHUNKS=$(jq -c -n '$ARGS.positional' --args `seq 0 1 $((sketches - 1))`)
4240
echo "chunks=${CHUNKS}" >>$GITHUB_OUTPUT
4341
44-
Build:
42+
build:
4543
needs: gen_chunks
4644
name: ${{matrix.chip}}-Build#${{matrix.chunks}}
4745
runs-on: ubuntu-latest
@@ -63,9 +61,79 @@ jobs:
6361
~/.arduino/tests/*/build*.tmp/*.bin
6462
~/.arduino/tests/*/build*.tmp/*.json
6563
if-no-files-found: error
66-
Test:
67-
needs: [gen_chunks, Build]
68-
name: ${{matrix.chip}}-Test#${{matrix.chunks}}
64+
65+
qemu-test:
66+
needs: [gen_chunks, build]
67+
name: ${{matrix.chip}}-QEMU_Test#${{matrix.chunks}}
68+
strategy:
69+
fail-fast: false
70+
matrix:
71+
chip: ['esp32', 'esp32c3'] # Currently only ESP32 and ESP32-C3 are supported by QEMU
72+
chunks: ${{fromJson(needs.gen_chunks.outputs.chunks)}}
73+
runs-on: ubuntu-latest
74+
env:
75+
QEMU_INSTALL_PATH: "$HOME"
76+
steps:
77+
- name: Checkout repository
78+
uses: actions/checkout@v4
79+
80+
- name: Get QEMU version
81+
uses: pozetroninc/[email protected]
82+
id: get-qemu-version
83+
with:
84+
token: ${{secrets.GITHUB_TOKEN}}
85+
owner: espressif
86+
repo: qemu
87+
excludes: prerelease, draft
88+
89+
- name: Cache tools
90+
id: cache-linux
91+
uses: actions/cache@v4
92+
with:
93+
path: |
94+
~/qemu
95+
~/.cache/pip
96+
key: ${{ steps.get-qemu-version.outputs.release }}-${{ hashFiles('.github/workflows/hil.yml') }}
97+
98+
- name: Install dependencies
99+
run: |
100+
pip install -U pip
101+
pip install -r tests/requirements.txt --extra-index-url https://dl.espressif.com/pypi
102+
sudo apt update && sudo apt install libpixman-1-0 libnuma1 libglib2.0-0 libslirp0 libsdl2-2.0-0
103+
104+
- name: Download QEMU
105+
if: steps.cache-linux.outputs.cache-hit != 'true'
106+
run: |
107+
cd ${{ env.QEMU_INSTALL_PATH }}
108+
underscore_release=$(echo ${{ steps.get-qemu-version.outputs.release }} | sed 's/\-/_/g')
109+
curl -L https://github.com/espressif/qemu/releases/download/${{ steps.get-qemu-version.outputs.release }}/qemu-riscv32-softmmu-${underscore_release}-x86_64-linux-gnu.tar.xz > qemu-riscv32.tar.xz
110+
curl -L https://github.com/espressif/qemu/releases/download/${{ steps.get-qemu-version.outputs.release }}/qemu-xtensa-softmmu-${underscore_release}-x86_64-linux-gnu.tar.xz > qemu-xtensa.tar.xz
111+
tar -xf qemu-riscv32.tar.xz
112+
tar -xf qemu-xtensa.tar.xz
113+
rm qemu-*
114+
echo "QEMU_PATH=${{ env.QEMU_INSTALL_PATH }}/qemu" >> $GITHUB_ENV
115+
116+
- name: Download ${{matrix.chip}}-${{matrix.chunks}} artifacts
117+
uses: actions/download-artifact@v4
118+
with:
119+
name: ${{matrix.chip}}-${{matrix.chunks}}.artifacts
120+
path: ~/.arduino/tests/
121+
122+
- name: Run Tests
123+
run: QEMU_PATH="${{env.QEMU_PATH}}" bash .github/scripts/tests_run.sh -c -t ${{matrix.chip}} -i ${{matrix.chunks}} -m ${{env.MAX_CHUNKS}} -q
124+
125+
- name: Upload test result artifacts
126+
uses: actions/upload-artifact@v4
127+
if: always()
128+
with:
129+
name: qemu_results-${{matrix.chip}}-${{matrix.chunks}}
130+
path: tests/*/*.xml
131+
132+
hardware-test:
133+
needs: [gen_chunks, build]
134+
name: ${{matrix.chip}}-Hardware_Test#${{matrix.chunks}}
135+
if: |
136+
contains(github.event.pull_request.labels.*.name, 'hil_test') || github.event_name == 'schedule'
69137
strategy:
70138
fail-fast: false
71139
matrix:
@@ -100,15 +168,13 @@ jobs:
100168
uses: actions/upload-artifact@v4
101169
if: always()
102170
with:
103-
name: test_results-${{matrix.chip}}-${{matrix.chunks}}
171+
name: hw_results-${{matrix.chip}}-${{matrix.chunks}}
104172
path: tests/*/*.xml
105173

106174
event_file:
107175
name: "Event File"
108-
if: |
109-
contains(github.event.pull_request.labels.*.name, 'hil_test') ||
110-
github.event_name == 'schedule'
111-
needs: Test
176+
if: ${{ always() && !failure() && !cancelled() }}
177+
needs: [hardware-test, qemu-test]
112178
runs-on: ubuntu-latest
113179
steps:
114180
- name: Upload

Diff for: .github/workflows/publish.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Unit Test Results
22

33
on:
44
workflow_run:
5-
workflows: [Run tests in hardware]
5+
workflows: [Run tests]
66
branches-ignore: [master]
77

88
types:

Diff for: platform.txt

+11
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,17 @@ recipe.hooks.objcopy.postobjcopy.1.pattern.windows=cmd /c if exist "{build.path}
169169
recipe.hooks.objcopy.postobjcopy.2.pattern=/usr/bin/env bash -c "[ ! -d "{build.path}"/libraries/ESP_SR ] || [ ! -f "{compiler.sdk.path}"/esp_sr/srmodels.bin ] || cp -f "{compiler.sdk.path}"/esp_sr/srmodels.bin "{build.path}"/srmodels.bin"
170170
recipe.hooks.objcopy.postobjcopy.2.pattern.windows=cmd /c if exist "{build.path}\libraries\ESP_SR" if exist "{compiler.sdk.path}\esp_sr\srmodels.bin" COPY /y "{compiler.sdk.path}\esp_sr\srmodels.bin" "{build.path}\srmodels.bin"
171171

172+
## Create flash_args file
173+
flash_args.path={build.path}/flash_args
174+
recipe.hooks.objcopy.postobjcopy.3.pattern_args=(echo --fill-flash-size {build.flash_size} --flash_mode keep --flash_freq keep --flash_size keep; echo {build.bootloader_addr} "{build.path}/{build.project_name}.bootloader.bin"; echo 0x8000 "{build.path}/{build.project_name}.partitions.bin"; echo 0xe000 "{runtime.platform.path}/tools/partitions/boot_app0.bin"; echo 0x10000 "{build.path}/{build.project_name}.bin") > "{flash_args.path}"
175+
recipe.hooks.objcopy.postobjcopy.3.pattern=/usr/bin/env bash -c "{recipe.hooks.objcopy.postobjcopy.3.pattern_args}"
176+
recipe.hooks.objcopy.postobjcopy.3.pattern.windows=cmd /c {recipe.hooks.objcopy.postobjcopy.3.pattern_args}
177+
178+
# Create merged binary
179+
recipe.hooks.objcopy.postobjcopy.4.pattern_args=--chip {build.mcu} merge_bin -o "{build.path}/{build.project_name}.merged.bin" "@{flash_args.path}"
180+
recipe.hooks.objcopy.postobjcopy.4.pattern="{tools.esptool_py.path}/{tools.esptool_py.cmd}" {recipe.hooks.objcopy.postobjcopy.4.pattern_args}
181+
recipe.hooks.objcopy.postobjcopy.4.pattern.linux=python3 "{tools.esptool_py.path}/{tools.esptool_py.cmd}" {recipe.hooks.objcopy.postobjcopy.4.pattern_args}
182+
172183
## Save bin
173184
recipe.output.tmp_file={build.project_name}.bin
174185
recipe.output.save_file={build.project_name}.{build.variant}.bin

Diff for: tests/democfg/.skip.qemu

Whitespace-only changes.

Diff for: tests/nvs/.skip.qemu

Whitespace-only changes.

Diff for: tests/periman/.skip.qemu

Whitespace-only changes.

Diff for: tests/pytest.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[pytest]
2-
addopts = --embedded-services esp,arduino
2+
addopts = --embedded-services esp,arduino,qemu
33

44
# log related
55
log_cli = True

Diff for: tests/requirements.txt

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
cryptography>=2.1.4
22
--only-binary cryptography
33
pytest-cov
4-
pytest-embedded-serial-esp>=1.3.4
5-
pytest-embedded-arduino>=1.3.4
4+
pytest-embedded-serial-esp>=1.10.1
5+
pytest-embedded-arduino>=1.10.1
6+
pytest-embedded-qemu>=1.10.1

Diff for: tests/touch/.skip.qemu

Whitespace-only changes.

Diff for: tests/uart/.skip.qemu

Whitespace-only changes.

0 commit comments

Comments
 (0)