Skip to content

Commit e3a6ced

Browse files
committed
ci(performance): Add performance tests to CI
1 parent cf44890 commit e3a6ced

File tree

24 files changed

+175
-20
lines changed

24 files changed

+175
-20
lines changed

.github/scripts/tests_build.sh

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22

33
USAGE="
44
USAGE:
5-
${0} -c <chunk_build_opts>
6-
Example: ${0} -c -t esp32 -i 0 -m 15
5+
${0} -c -type <test_type> <chunk_build_opts>
6+
Example: ${0} -c -type validation -t esp32 -i 0 -m 15
77
${0} -s sketch_name <build_opts>
88
Example: ${0} -s hello_world -t esp32
99
${0} -clean
1010
Remove build and test generated files
1111
"
1212

1313
function clean(){
14-
rm -rf tests/*/build*/
14+
rm -rf tests/**/build*/
1515
rm -rf tests/.pytest_cache
16-
rm -rf tests/*/__pycache__/
17-
rm -rf tests/*/*.xml
16+
rm -rf tests/**/__pycache__/
17+
rm -rf tests/**/*.xml
1818
}
1919

2020
SCRIPTS_DIR="./.github/scripts"
@@ -35,6 +35,10 @@ while [ ! -z "$1" ]; do
3535
echo "$USAGE"
3636
exit 0
3737
;;
38+
-type )
39+
shift
40+
test_type=$1
41+
;;
3842
-clean )
3943
clean
4044
exit 0
@@ -52,12 +56,25 @@ source ${SCRIPTS_DIR}/install-arduino-core-esp32.sh
5256

5357
args="-ai $ARDUINO_IDE_PATH -au $ARDUINO_USR_PATH"
5458

59+
if [[ $test_type == "all" ]] || [[ -z $test_type ]]; then
60+
if [ -n "$sketch" ]; then
61+
tmp_sketch_path=$(find tests -name $sketch.ino)
62+
test_type=$(basename $(dirname $(dirname "$tmp_sketch_path")))
63+
echo "Sketch $sketch test type: $test_type"
64+
test_folder="$PWD/tests/$test_type"
65+
else
66+
test_folder="$PWD/tests"
67+
fi
68+
else
69+
test_folder="$PWD/tests/$test_type"
70+
fi
71+
5572
if [ $chunk_build -eq 1 ]; then
5673
BUILD_CMD="${SCRIPTS_DIR}/sketch_utils.sh chunk_build"
57-
args+=" -p $PWD/tests"
74+
args+=" -p $test_folder"
5875
else
5976
BUILD_CMD="${SCRIPTS_DIR}/sketch_utils.sh build"
60-
args+=" -s $PWD/tests/$sketch"
77+
args+=" -s $test_folder/$sketch"
6178
fi
6279

6380
${BUILD_CMD} ${args} $*

.github/scripts/tests_run.sh

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ function run_test() {
1515
fi
1616

1717
if [ $len -eq 1 ]; then
18-
# build_dir="tests/$sketchname/build"
18+
# build_dir="$sketchdir/build"
1919
build_dir="$HOME/.arduino/tests/$sketchname/build.tmp"
20-
report_file="tests/$sketchname/$sketchname.xml"
20+
report_file="$sketchdir/$sketchname.xml"
2121
fi
2222

2323
for i in `seq 0 $(($len - 1))`
@@ -28,9 +28,9 @@ function run_test() {
2828
fi
2929

3030
if [ $len -ne 1 ]; then
31-
# build_dir="tests/$sketchname/build$i"
31+
# build_dir="$sketchdir/build$i"
3232
build_dir="$HOME/.arduino/tests/$sketchname/build$i.tmp"
33-
report_file="tests/$sketchname/$sketchname$i.xml"
33+
report_file="$sketchdir/$sketchname$i.xml"
3434
fi
3535

3636
pytest tests --build-dir $build_dir -k test_$sketchname --junit-xml=$report_file
@@ -79,6 +79,10 @@ while [ ! -z "$1" ]; do
7979
echo "$USAGE"
8080
exit 0
8181
;;
82+
-type )
83+
shift
84+
test_type=$1
85+
;;
8286
* )
8387
break
8488
;;
@@ -88,8 +92,26 @@ done
8892

8993
source ${SCRIPTS_DIR}/install-arduino-ide.sh
9094

95+
# If sketch is provided and test type is not, test type is inferred from the sketch path
96+
if [[ $test_type == "all" ]] || [[ -z $test_type ]]; then
97+
if [ -n "$sketch" ]; then
98+
tmp_sketch_path=$(find tests -name $sketch.ino)
99+
test_type=$(basename $(dirname $(dirname "$tmp_sketch_path")))
100+
echo "Sketch $sketch test type: $test_type"
101+
test_folder="$PWD/tests/$test_type"
102+
else
103+
test_folder="$PWD/tests"
104+
fi
105+
else
106+
test_folder="$PWD/tests/$test_type"
107+
fi
108+
91109
if [ $chunk_run -eq 0 ]; then
92-
run_test $target $PWD/tests/$sketch/$sketch.ino $options $erase
110+
if [ -z $sketch ]; then
111+
echo "ERROR: Sketch name is required for single test run"
112+
return 1
113+
fi
114+
run_test $target $test_folder/$sketch/$sketch.ino $options $erase
93115
else
94116
if [ "$chunk_max" -le 0 ]; then
95117
echo "ERROR: Chunks count must be positive number"
@@ -102,7 +124,7 @@ else
102124
fi
103125

104126
set +e
105-
${COUNT_SKETCHES} $PWD/tests $target
127+
${COUNT_SKETCHES} $test_folder $target
106128
sketchcount=$?
107129
set -e
108130
sketches=$(cat sketches.txt)

.github/workflows/hil.yml

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ jobs:
1818
gen_chunks:
1919
if: |
2020
contains(github.event.pull_request.labels.*.name, 'hil_test') ||
21+
contains(github.event.pull_request.labels.*.name, 'perf_test') ||
2122
(github.event_name == 'schedule' && github.repository == 'espressif/arduino-esp32')
2223
name: Generate Chunks matrix
2324
runs-on: ubuntu-latest
2425
outputs:
2526
chunks: ${{ steps.gen-chunks.outputs.chunks }}
27+
test_folder: ${{ steps.gen-chunks.outputs.test_folder }}
28+
test_type: ${{ steps.gen-chunks.outputs.test_type }}
2629
steps:
2730
- name: Checkout Repository
2831
uses: actions/checkout@v4
@@ -31,15 +34,29 @@ jobs:
3134
id: gen-chunks
3235
run: |
3336
set +e
34-
.github/scripts/sketch_utils.sh count tests
37+
if [ "${{contains(github.event.pull_request.labels.*.name, 'hil_test')}}" == "true" ] && \
38+
[ "${{contains(github.event.pull_request.labels.*.name, 'perf_test')}}" == "false" ]; then
39+
test_folder="tests/validation"
40+
test_type="validation"
41+
elif [ "${{contains(github.event.pull_request.labels.*.name, 'hil_test')}}" == "false" ] && \
42+
[ "${{contains(github.event.pull_request.labels.*.name, 'perf_test')}}" == "true" ]; then
43+
test_folder="tests/performance"
44+
test_type="performance"
45+
else
46+
test_folder="tests"
47+
test_type="all"
48+
fi
49+
.github/scripts/sketch_utils.sh count $test_folder
3550
sketches=$?
3651
if [[ $sketches -ge ${{env.MAX_CHUNKS}} ]]; then
3752
$sketches=${{env.MAX_CHUNKS}}
3853
fi
3954
set -e
4055
rm sketches.txt
4156
CHUNKS=$(jq -c -n '$ARGS.positional' --args `seq 0 1 $((sketches - 1))`)
42-
echo "chunks=${CHUNKS}" >>$GITHUB_OUTPUT
57+
echo "chunks=${CHUNKS}" >> $GITHUB_OUTPUT
58+
echo "test_folder=${test_folder}" >> $GITHUB_OUTPUT
59+
echo "test_type=${test_type}" >> $GITHUB_OUTPUT
4360
4461
Build:
4562
needs: gen_chunks
@@ -54,14 +71,14 @@ jobs:
5471
uses: actions/checkout@v4
5572
- name: Build sketches
5673
run: |
57-
bash .github/scripts/tests_build.sh -c -t ${{matrix.chip}} -i ${{matrix.chunks}} -m ${{env.MAX_CHUNKS}}
74+
bash .github/scripts/tests_build.sh -c -type ${{ needs.gen_chunks.outputs.test_type }} -t ${{matrix.chip}} -i ${{matrix.chunks}} -m ${{env.MAX_CHUNKS}}
5875
- name: Upload ${{matrix.chip}}-${{matrix.chunks}} artifacts
5976
uses: actions/upload-artifact@v4
6077
with:
6178
name: ${{matrix.chip}}-${{matrix.chunks}}.artifacts
6279
path: |
63-
~/.arduino/tests/*/build*.tmp/*.bin
64-
~/.arduino/tests/*/build*.tmp/*.json
80+
~/.arduino/tests/**/build*.tmp/*.bin
81+
~/.arduino/tests/**/build*.tmp/*.json
6582
if-no-files-found: error
6683
Test:
6784
needs: [gen_chunks, Build]
@@ -94,19 +111,22 @@ jobs:
94111
95112
- name: Run Tests
96113
run: |
97-
bash .github/scripts/tests_run.sh -c -t ${{matrix.chip}} -i ${{matrix.chunks}} -m ${{env.MAX_CHUNKS}} -e
114+
bash .github/scripts/tests_run.sh -c -type ${{ needs.gen_chunks.outputs.test_type }} -t ${{matrix.chip}} -i ${{matrix.chunks}} -m ${{env.MAX_CHUNKS}} -e
98115
99116
- name: Upload test result artifacts
100117
uses: actions/upload-artifact@v4
101118
if: always()
102119
with:
103120
name: test_results-${{matrix.chip}}-${{matrix.chunks}}
104-
path: tests/*/*.xml
121+
path: |
122+
tests/**/*.xml
123+
tests/**/*.json
105124
106125
event_file:
107126
name: "Event File"
108127
if: |
109128
contains(github.event.pull_request.labels.*.name, 'hil_test') ||
129+
contains(github.event.pull_request.labels.*.name, 'perf_test') ||
110130
github.event_name == 'schedule'
111131
needs: Test
112132
runs-on: ubuntu-latest

tests/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
build*/
22
__pycache__/
33
*.xml
4+
result_*.json
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include <Arduino.h>
2+
3+
#define N_RUNS 3 // Number of runs to average
4+
5+
// Fibonacci number to calculate. Keep between 35 and 45.
6+
#define FIB_N 40
7+
8+
uint32_t times[N_RUNS];
9+
10+
uint64_t fib(uint32_t n) {
11+
if (n < 2) return n;
12+
return fib(n - 1) + fib(n - 2);
13+
}
14+
15+
void setup(){
16+
uint64_t fibonacci;
17+
18+
Serial.begin(115200);
19+
while (!Serial) delay(10);
20+
21+
log_d("Starting fibonacci calculation");
22+
Serial.printf("Runs: %d\n", N_RUNS);
23+
for (int i = 0; i < N_RUNS; i++) {
24+
log_d("Run %d", i);
25+
unsigned long start = millis();
26+
fibonacci = fib(FIB_N);
27+
unsigned long end = millis();
28+
times[i] = end - start;
29+
log_d("Run %d: %lu.%03lu s\n", i, times[i]/1000, times[i]%1000);
30+
}
31+
32+
Serial.printf("N: %d\n", FIB_N);
33+
Serial.printf("Fibonacci(N): %llu\n", fibonacci);
34+
uint32_t sum = 0;
35+
for (int i = 0; i < N_RUNS; i++) {
36+
sum += times[i];
37+
}
38+
uint32_t avg = sum / N_RUNS;
39+
Serial.printf("Average time: %lu.%03lu s\n", avg/1000, avg%1000);
40+
}
41+
42+
void loop(){
43+
vTaskDelete(NULL);
44+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import json
2+
import logging
3+
import os
4+
5+
LOGGER = logging.getLogger(__name__)
6+
7+
# Fibonacci results starting from fib(35) to fib(45)
8+
fib_results = [9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733]
9+
10+
def test_fibonacci(dut, request):
11+
# Match "Runs: %d"
12+
res = dut.expect(r"Runs: (\d+)", timeout=60)
13+
runs = int(res.group(0).decode("utf-8").split(" ")[1])
14+
LOGGER.info("Number of runs: {}".format(runs))
15+
16+
# Match "N: %d"
17+
res = dut.expect(r"N: (\d+)", timeout=300)
18+
fib_n = int(res.group(0).decode("utf-8").split(" ")[1])
19+
LOGGER.info("Calculating Fibonacci({})".format(fib_n))
20+
21+
# Match "Fibonacci(N): %llu"
22+
res = dut.expect(r"Fibonacci\(N\): (\d+)", timeout=300)
23+
fib_result = int(res.group(0).decode("utf-8").split(" ")[1])
24+
LOGGER.info("Fibonacci({}) = {}".format(fib_n, fib_result))
25+
26+
# Check if the result is correct
27+
assert fib_result == fib_results[fib_n - 35]
28+
29+
# Match "Average time: %lu.%03lu s"
30+
res = dut.expect(r"Average time: (\d+)\.(\d+) s", timeout=300)
31+
avg_time = float(res.group(0).decode("utf-8").split(" ")[2])
32+
LOGGER.info("Average time on {} runs: {} s".format(runs, avg_time))
33+
34+
# Create JSON with results and write it to file
35+
# Always create a JSON with the format (so it can be merged later on):
36+
# { TEST_NAME_STR: TEST_RESULTS_DICT }
37+
results = {
38+
"fibonacci": {
39+
"runs": runs,
40+
"fib_n": fib_n,
41+
"fib_result": fib_result,
42+
"avg_time": avg_time
43+
}
44+
}
45+
46+
current_folder = os.path.dirname(request.path)
47+
with open(current_folder + "/result_fibonacci.json", "w") as f:
48+
try:
49+
f.write(json.dumps(results))
50+
except Exception as e:
51+
LOGGER.warning("Failed to write results to file: {}".format(e))
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)