Skip to content

Commit 759974e

Browse files
committed
ci(wokwi): Add wokwi emulator to CI
1 parent 86b3163 commit 759974e

File tree

8 files changed

+209
-13
lines changed

8 files changed

+209
-13
lines changed

.github/scripts/tests_run.sh

+19-1
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" ]]; then
12+
echo "Skipping $sketchname test in $target"
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,13 @@ 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 == "wokwi" ]; then
42+
extra_args="--target $target --embedded-services arduino,wokwi --wokwi-timeout=$wokwi_timeout"
43+
else
44+
extra_args="--embedded-services esp,arduino"
45+
fi
46+
47+
pytest tests --build-dir $build_dir -k test_$sketchname --junit-xml=$report_file $extra_args
3748
result=$?
3849
if [ $result -ne 0 ]; then
3950
return $result
@@ -44,6 +55,8 @@ function run_test() {
4455
SCRIPTS_DIR="./.github/scripts"
4556
COUNT_SKETCHES="${SCRIPTS_DIR}/sketch_utils.sh count"
4657

58+
platform="hardware"
59+
wokwi_timeout=60000
4760
chunk_run=0
4861
options=0
4962
erase=0
@@ -53,6 +66,11 @@ while [ ! -z "$1" ]; do
5366
-c )
5467
chunk_run=1
5568
;;
69+
-w )
70+
shift
71+
wokwi_timeout=$1
72+
platform="wokwi"
73+
;;
5674
-o )
5775
options=1
5876
;;

.github/workflows/hil.yml

+53-10
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:
@@ -9,16 +9,15 @@ on:
99

1010
env:
1111
MAX_CHUNKS: 15
12+
WOKWI_TIMEOUT: 120000 # Milliseconds
1213

1314
concurrency:
1415
group: hil-${{github.event.pull_request.number || github.ref}}
1516
cancel-in-progress: true
1617

1718
jobs:
1819
gen_chunks:
19-
if: |
20-
contains(github.event.pull_request.labels.*.name, 'hil_test') ||
21-
(github.event_name == 'schedule' && github.repository == 'espressif/arduino-esp32')
20+
if: github.repository == 'espressif/arduino-esp32'
2221
name: Generate Chunks matrix
2322
runs-on: ubuntu-latest
2423
outputs:
@@ -41,7 +40,7 @@ jobs:
4140
CHUNKS=$(jq -c -n '$ARGS.positional' --args `seq 0 1 $((sketches - 1))`)
4241
echo "chunks=${CHUNKS}" >>$GITHUB_OUTPUT
4342
44-
Build:
43+
build:
4544
needs: gen_chunks
4645
name: ${{matrix.chip}}-Build#${{matrix.chunks}}
4746
runs-on: ubuntu-latest
@@ -63,9 +62,53 @@ jobs:
6362
~/.arduino/tests/*/build*.tmp/*.bin
6463
~/.arduino/tests/*/build*.tmp/*.json
6564
if-no-files-found: error
66-
Test:
67-
needs: [gen_chunks, Build]
68-
name: ${{matrix.chip}}-Test#${{matrix.chunks}}
65+
66+
wokwi-test:
67+
needs: [gen_chunks, build]
68+
name: ${{matrix.chip}}-Wokwi_Test#${{matrix.chunks}}
69+
strategy:
70+
fail-fast: false
71+
matrix:
72+
chip: ['esp32', 'esp32s2', 'esp32s3', 'esp32c3', 'esp32c6', 'esp32h2']
73+
chunks: ${{fromJson(needs.gen_chunks.outputs.chunks)}}
74+
runs-on: ubuntu-latest
75+
steps:
76+
- name: Checkout repository
77+
uses: actions/checkout@v4
78+
79+
- name: Download ${{matrix.chip}}-${{matrix.chunks}} artifacts
80+
uses: actions/download-artifact@v4
81+
with:
82+
name: ${{matrix.chip}}-${{matrix.chunks}}.artifacts
83+
path: ~/.arduino/tests/
84+
85+
- name: Install Wokwi CLI
86+
run: curl -L https://wokwi.com/ci/install.sh | sh
87+
88+
- name: Install dependencies
89+
run: |
90+
pip install -U pip
91+
pip install -r tests/requirements.txt --extra-index-url https://dl.espressif.com/pypi
92+
apt update && apt install -y -qq jq
93+
94+
- name: Run Tests
95+
env:
96+
WOKWI_CLI_TOKEN: ${{ secrets.WOKWI_CLI_TOKEN }}
97+
run: |
98+
bash .github/scripts/tests_run.sh -c -t ${{matrix.chip}} -i ${{matrix.chunks}} -m ${{env.MAX_CHUNKS}} -w ${{env.WOKWI_TIMEOUT}}
99+
100+
- name: Upload test result artifacts
101+
uses: actions/upload-artifact@v4
102+
if: always()
103+
with:
104+
name: wokwi_results-${{matrix.chip}}-${{matrix.chunks}}
105+
path: tests/*/*.xml
106+
107+
hardware-test:
108+
needs: [gen_chunks, build]
109+
name: ${{matrix.chip}}-Hardware_Test#${{matrix.chunks}}
110+
if: |
111+
contains(github.event.pull_request.labels.*.name, 'hil_test') || github.event_name == 'schedule'
69112
strategy:
70113
fail-fast: false
71114
matrix:
@@ -100,15 +143,15 @@ jobs:
100143
uses: actions/upload-artifact@v4
101144
if: always()
102145
with:
103-
name: test_results-${{matrix.chip}}-${{matrix.chunks}}
146+
name: hw_results-${{matrix.chip}}-${{matrix.chunks}}
104147
path: tests/*/*.xml
105148

106149
event_file:
107150
name: "Event File"
108151
if: |
109152
contains(github.event.pull_request.labels.*.name, 'hil_test') ||
110153
github.event_name == 'schedule'
111-
needs: Test
154+
needs: hardware-test
112155
runs-on: ubuntu-latest
113156
steps:
114157
- name: Upload

.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:

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,wokwi
33

44
# log related
55
log_cli = True

tests/requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ cryptography>=2.1.4
33
pytest-cov
44
pytest-embedded-serial-esp>=1.3.4
55
pytest-embedded-arduino>=1.3.4
6+
pytest-embedded-wokwi>=1.3.5

tests/wifi/.skip.hardware

Whitespace-only changes.

tests/wifi/test_wifi.py

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def test_wifi(dut):
2+
dut.expect_unity_test_output(timeout=240)

tests/wifi/wifi.ino

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/* HW Timer test */
2+
#include <unity.h>
3+
4+
#define TIMER_FREQUENCY 4000000
5+
#define TIMER_FREQUENCY_XTAL_CLK 1000
6+
7+
/*
8+
* ESP32 - APB clk only (1kHz not possible)
9+
* C3 - APB + XTAL clk
10+
* S2 - APB + XTAL clk
11+
* S3 - APB + XTAL clk
12+
*/
13+
14+
static hw_timer_t *timer = NULL;
15+
static volatile bool alarm_flag;
16+
17+
/* setUp / tearDown functions are intended to be called before / after each test. */
18+
void setUp(void) {
19+
timer = timerBegin(TIMER_FREQUENCY);
20+
if (timer == NULL) {
21+
TEST_FAIL_MESSAGE("Timer init failed in setUp()");
22+
}
23+
timerStop(timer);
24+
timerRestart(timer);
25+
}
26+
27+
void tearDown(void) {
28+
timerEnd(timer);
29+
}
30+
31+
void ARDUINO_ISR_ATTR onTimer() {
32+
alarm_flag = true;
33+
}
34+
35+
void timer_interrupt_test(void) {
36+
37+
alarm_flag = false;
38+
timerAttachInterrupt(timer, &onTimer);
39+
timerAlarm(timer, (1.2 * TIMER_FREQUENCY), true, 0);
40+
timerStart(timer);
41+
42+
delay(2000);
43+
44+
TEST_ASSERT_EQUAL(true, alarm_flag);
45+
46+
timerStop(timer);
47+
timerRestart(timer);
48+
alarm_flag = false;
49+
timerDetachInterrupt(timer);
50+
timerStart(timer);
51+
52+
delay(2000);
53+
TEST_ASSERT_EQUAL(false, alarm_flag);
54+
}
55+
56+
void timer_divider_test(void) {
57+
58+
uint64_t time_val;
59+
uint64_t comp_time_val;
60+
61+
timerStart(timer);
62+
63+
delay(1000);
64+
time_val = timerRead(timer);
65+
66+
// compare divider 16 and 8, value should be double
67+
timerEnd(timer);
68+
69+
timer = timerBegin(2 * TIMER_FREQUENCY);
70+
if (timer == NULL) {
71+
TEST_FAIL_MESSAGE("Timer init failed!");
72+
}
73+
timerRestart(timer);
74+
delay(1000);
75+
comp_time_val = timerRead(timer);
76+
77+
TEST_ASSERT_INT_WITHIN(4000, 4000000, time_val);
78+
TEST_ASSERT_INT_WITHIN(8000, 8000000, comp_time_val);
79+
80+
// divider is 256, value should be 2^4
81+
timerEnd(timer);
82+
83+
timer = timerBegin(TIMER_FREQUENCY / 16);
84+
if (timer == NULL) {
85+
TEST_FAIL_MESSAGE("Timer init failed!");
86+
}
87+
timerRestart(timer);
88+
delay(1000);
89+
comp_time_val = timerRead(timer);
90+
91+
TEST_ASSERT_INT_WITHIN(4000, 4000000, time_val);
92+
TEST_ASSERT_INT_WITHIN(2500, 250000, comp_time_val);
93+
}
94+
95+
void timer_read_test(void) {
96+
97+
uint64_t set_timer_val = 0xFF;
98+
uint64_t get_timer_val = 0;
99+
100+
timerWrite(timer, set_timer_val);
101+
get_timer_val = timerRead(timer);
102+
103+
TEST_ASSERT_EQUAL(set_timer_val, get_timer_val);
104+
}
105+
106+
void timer_clock_select_test(void) {
107+
// Set timer frequency that can be achieved using XTAL clock source (autoselected)
108+
timer = timerBegin(TIMER_FREQUENCY_XTAL_CLK);
109+
110+
uint32_t resolution = timerGetFrequency(timer);
111+
TEST_ASSERT_EQUAL(TIMER_FREQUENCY_XTAL_CLK, resolution);
112+
}
113+
114+
void setup() {
115+
116+
// Open serial communications and wait for port to open:
117+
Serial.begin(115200);
118+
while (!Serial) {
119+
;
120+
}
121+
122+
UNITY_BEGIN();
123+
RUN_TEST(timer_read_test);
124+
RUN_TEST(timer_interrupt_test);
125+
RUN_TEST(timer_divider_test);
126+
#if !CONFIG_IDF_TARGET_ESP32
127+
RUN_TEST(timer_clock_select_test);
128+
#endif
129+
UNITY_END();
130+
}
131+
132+
void loop() {}

0 commit comments

Comments
 (0)