-
Notifications
You must be signed in to change notification settings - Fork 2
176 lines (161 loc) · 6.07 KB
/
fuzz-test.yml
File metadata and controls
176 lines (161 loc) · 6.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
name: fuzz test
permissions:
pull-requests: read
contents: read
on:
workflow_call:
defaults:
run:
shell: bash
jobs:
fuzz-matrix:
name: Fuzz matrix
# Known limitation: go test -fuzz cannot run fuzz with multiple packages.
#
# We have to identify which package contains fuzz tests then execute them.
# See https://github.com/golang/go/issues/46312.
# An alternative to using jq and matrix is to use (or rewrite): https://github.com/demurky/gofuzz/
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.get-fuzz-tests.outputs.matrix }}
steps:
-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: stable
check-latest: true
cache: true
-
name: Fuzz tests matrix
id: get-fuzz-tests
run: |
# Suppports: multiple tests, multiple packages, multiple modules
fuzz_tests=$(\
go test work -list Fuzz -json ./... |\
jq '.|select(.Action == "output")|select( (.Output|test("^(ok)|(\\?)"))|not )|{package: .Package, test: (.Output|gsub("\n";""))}'|\
jq -c --slurp \
)
export fuzz_tests
echo "matrix<<EOF" >> "${GITHUB_OUTPUT}"
printenv fuzz_tests >> "${GITHUB_OUTPUT}"
echo "EOF" >> "${GITHUB_OUTPUT}"
echo "::notice title=Fuzz tests found::${fuzz_tests}"
fuzz-test:
name: fuzz test
runs-on: ubuntu-latest
needs: [fuzz-matrix]
if: ${{ needs.fuzz-matrix.outputs.matrix != '' && needs.fuzz-matrix.outputs.matrix != '[]' }}
strategy:
matrix:
include: ${{ fromJSON(needs.fuzz-matrix.outputs.matrix) }}
env:
CORPUS_MAX_SIZE_MB: 250
FUZZ_TIME: 1m30s
FUZZ_MINIMIZE_TIME: 5m
steps:
-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: stable
check-latest: true
cache: true
-
name: Locate go fuzz cache
run: |
GOCACHE=$(go env GOCACHE)
echo "CORPUS_DIR=${GOCACHE}/fuzz" >> "${GITHUB_ENV}"
-
name: Retrieve fuzz corpus from cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
key: ${{ runner.os }}-go-fuzz
path:
${{ env.CORPUS_DIR }}
-
name: Manage fuzz corpus cache size
run: |
mkdir -p "${CORPUS_DIR}"
# This script checks that the size of the corpus cache doesn't exceed ${CORPUS_MAX_SIZE_MB},
# and if it does, it removes all oldest files beyond that size.
function size() {
local location=$1
local unit=$2
du -s"${unit}" "${location}"|cut -f1
}
function purge() {
local location=$1
local max_size_b=$2
declare -i current_size_b=0 file_size_b=0 purged_files=0
while read -r filename ; do
file_size_b="$(size "${filename}" "b")"
((current_size_b+=file_size_b))
if [[ ${current_size_b} -le ${max_size_b} ]] ; then
continue
fi
if [[ ${file_size_b} -eq 0 ]] ; then
continue
fi
rm -f "${filename}"
((purged_files+=1))
done < <(find "${location}" -type f -print0 | xargs -0 ls -t)
echo ${purged_files}
}
CURRENT_SIZE_MB="$(size "${CORPUS_DIR}" "m")"
if [[ "${CURRENT_SIZE_MB}" -lt "${MAX_SIZE_MB}" ]] ; then
echo "::notice:cache size remains under the accepted size of ${MAX_SIZE_MB} MB: ${CURRENT_SIZE_MB} MB"
exit 0
fi
declare -i max_size_b=$(("${CORPUS_MAX_SIZE_MB}" * 1024 * 1024))
purged_files=$(purge "${CORPUS_DIR}" "${max_size_b}");
echo "::notice:cache size is ${CURRENT_SIZE_MB} MB: purging oldest files to keep it under ${CORPUS_MAX_SIZE_MB} MB"
if [[ ${purged_files} -gt 0 ]] ; then
echo "::notice:removed ${purged_files} files to keep the cache size below ${CORPUS_MAX_SIZE_MB} MB"
fi
FINAL_SIZE_MB="$(size "${CORPUS_DIR}" "m")"
echo "::notice:purged cache size: ${FINAL_SIZE_MB} MB"
-
name: Run go fuzz test ${{ matrix.test }}
run: >
go test
-fuzz=Fuzz
-run='${{ matrix.test }}$'
-fuzztime='${{ env.FUZZ_TIME }}'
-fuzzminimizetime='${{ env.FUZZ_MINIMIZE_TIME }}'
${{ matrix.package }}
-
name: Archive failed cases
if: ${{ failure() }}
run: |
# Package name needs to be reworked to account for v2 suffixes
folder=$(go list -f '{{ printf "%s" .Dir }}' '${{ matrix.package }}')
tar czf "fuzzcase-${{ matrix.test }}.tgz" $(find "${folder}/testdata/fuzz" -type f)
-
name: Upload failed cases
if: ${{ failure() }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
# TODO(fredbi): ideally, after uploading, we should fire a pull request to add
# this corpus to testdata.
with:
path: fuzzcase-${{ matrix.test }}.tgz
name: '${{ runner.os }}-fuzz-corpus-failure-${{ github.run_id }}'
retention-days: 60
overwrite: true
-
name: Upload current corpus
# This is the current corpus, it does not contain the latest failed case
if: ${{ always() }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
path: ${{ env.CORPUS_DIR }}
name: '${{ runner.os }}-fuzz-corpus-${{ matrix.test }}'
retention-days: 60
overwrite: true
-
name: Report fuzz corpus cache size
run: |
FINAL_SIZE=$(du -sm "${CORPUS_DIR}"|cut -f1)
echo "::notice title=fuzz corpus size::${FINAL_SIZE}MB"