1name: test 2on: [push, pull_request] 3 4env: 5 CFLAGS: -Werror 6 MAKEFLAGS: -j 7 8jobs: 9 # run tests 10 test: 11 runs-on: ubuntu-18.04 12 strategy: 13 fail-fast: false 14 matrix: 15 arch: [x86_64, thumb, mips, powerpc] 16 17 steps: 18 - uses: actions/checkout@v2 19 - name: install 20 run: | 21 # need toml, also pip3 isn't installed by default? 22 sudo apt-get update -qq 23 sudo apt-get install -qq python3 python3-pip lcov 24 sudo pip3 install toml 25 gcc --version 26 27 # setup a ram-backed disk to speed up reentrant tests 28 mkdir disks 29 sudo mount -t tmpfs -o size=100m tmpfs disks 30 TESTFLAGS="$TESTFLAGS --disk=disks/disk" 31 32 # collect coverage 33 mkdir -p coverage 34 TESTFLAGS="$TESTFLAGS --coverage=` 35 `coverage/${{github.job}}-${{matrix.arch}}.info" 36 37 echo "TESTFLAGS=$TESTFLAGS" >> $GITHUB_ENV 38 39 # cross-compile with ARM Thumb (32-bit, little-endian) 40 - name: install-thumb 41 if: ${{matrix.arch == 'thumb'}} 42 run: | 43 sudo apt-get install -qq \ 44 gcc-arm-linux-gnueabi \ 45 libc6-dev-armel-cross \ 46 qemu-user 47 echo "CC=arm-linux-gnueabi-gcc -mthumb --static" >> $GITHUB_ENV 48 echo "EXEC=qemu-arm" >> $GITHUB_ENV 49 arm-linux-gnueabi-gcc --version 50 qemu-arm -version 51 # cross-compile with MIPS (32-bit, big-endian) 52 - name: install-mips 53 if: ${{matrix.arch == 'mips'}} 54 run: | 55 sudo apt-get install -qq \ 56 gcc-mips-linux-gnu \ 57 libc6-dev-mips-cross \ 58 qemu-user 59 echo "CC=mips-linux-gnu-gcc --static" >> $GITHUB_ENV 60 echo "EXEC=qemu-mips" >> $GITHUB_ENV 61 mips-linux-gnu-gcc --version 62 qemu-mips -version 63 # cross-compile with PowerPC (32-bit, big-endian) 64 - name: install-powerpc 65 if: ${{matrix.arch == 'powerpc'}} 66 run: | 67 sudo apt-get install -qq \ 68 gcc-powerpc-linux-gnu \ 69 libc6-dev-powerpc-cross \ 70 qemu-user 71 echo "CC=powerpc-linux-gnu-gcc --static" >> $GITHUB_ENV 72 echo "EXEC=qemu-ppc" >> $GITHUB_ENV 73 powerpc-linux-gnu-gcc --version 74 qemu-ppc -version 75 76 # make sure example can at least compile 77 - name: test-example 78 run: | 79 sed -n '/``` c/,/```/{/```/d; p}' README.md > test.c 80 make all CFLAGS+=" \ 81 -Duser_provided_block_device_read=NULL \ 82 -Duser_provided_block_device_prog=NULL \ 83 -Duser_provided_block_device_erase=NULL \ 84 -Duser_provided_block_device_sync=NULL \ 85 -include stdio.h" 86 rm test.c 87 88 # test configurations 89 # normal+reentrant tests 90 - name: test-default 91 run: | 92 make clean 93 make test TESTFLAGS+="-nrk" 94 # NOR flash: read/prog = 1 block = 4KiB 95 - name: test-nor 96 run: | 97 make clean 98 make test TESTFLAGS+="-nrk \ 99 -DLFS_READ_SIZE=1 -DLFS_BLOCK_SIZE=4096" 100 # SD/eMMC: read/prog = 512 block = 512 101 - name: test-emmc 102 run: | 103 make clean 104 make test TESTFLAGS+="-nrk \ 105 -DLFS_READ_SIZE=512 -DLFS_BLOCK_SIZE=512" 106 # NAND flash: read/prog = 4KiB block = 32KiB 107 - name: test-nand 108 run: | 109 make clean 110 make test TESTFLAGS+="-nrk \ 111 -DLFS_READ_SIZE=4096 -DLFS_BLOCK_SIZE=\(32*1024\)" 112 # other extreme geometries that are useful for various corner cases 113 - name: test-no-intrinsics 114 run: | 115 make clean 116 make test TESTFLAGS+="-nrk \ 117 -DLFS_NO_INTRINSICS" 118 - name: test-byte-writes 119 # it just takes too long to test byte-level writes when in qemu, 120 # should be plenty covered by the other configurations 121 if: ${{matrix.arch == 'x86_64'}} 122 run: | 123 make clean 124 make test TESTFLAGS+="-nrk \ 125 -DLFS_READ_SIZE=1 -DLFS_CACHE_SIZE=1" 126 - name: test-block-cycles 127 run: | 128 make clean 129 make test TESTFLAGS+="-nrk \ 130 -DLFS_BLOCK_CYCLES=1" 131 - name: test-odd-block-count 132 run: | 133 make clean 134 make test TESTFLAGS+="-nrk \ 135 -DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256" 136 - name: test-odd-block-size 137 run: | 138 make clean 139 make test TESTFLAGS+="-nrk \ 140 -DLFS_READ_SIZE=11 -DLFS_BLOCK_SIZE=704" 141 142 # upload coverage for later coverage 143 - name: upload-coverage 144 uses: actions/upload-artifact@v2 145 with: 146 name: coverage 147 path: coverage 148 retention-days: 1 149 150 # update results 151 - name: results-code 152 run: | 153 mkdir -p results 154 make clean 155 make code \ 156 CFLAGS+=" \ 157 -DLFS_NO_ASSERT \ 158 -DLFS_NO_DEBUG \ 159 -DLFS_NO_WARN \ 160 -DLFS_NO_ERROR" \ 161 CODEFLAGS+="-o results/code-${{matrix.arch}}.csv" 162 - name: results-code-readonly 163 run: | 164 mkdir -p results 165 make clean 166 make code \ 167 CFLAGS+=" \ 168 -DLFS_NO_ASSERT \ 169 -DLFS_NO_DEBUG \ 170 -DLFS_NO_WARN \ 171 -DLFS_NO_ERROR \ 172 -DLFS_READONLY" \ 173 CODEFLAGS+="-o results/code-${{matrix.arch}}-readonly.csv" 174 - name: results-code-threadsafe 175 run: | 176 mkdir -p results 177 make clean 178 make code \ 179 CFLAGS+=" \ 180 -DLFS_NO_ASSERT \ 181 -DLFS_NO_DEBUG \ 182 -DLFS_NO_WARN \ 183 -DLFS_NO_ERROR \ 184 -DLFS_THREADSAFE" \ 185 CODEFLAGS+="-o results/code-${{matrix.arch}}-threadsafe.csv" 186 - name: results-code-migrate 187 run: | 188 mkdir -p results 189 make clean 190 make code \ 191 CFLAGS+=" \ 192 -DLFS_NO_ASSERT \ 193 -DLFS_NO_DEBUG \ 194 -DLFS_NO_WARN \ 195 -DLFS_NO_ERROR \ 196 -DLFS_MIGRATE" \ 197 CODEFLAGS+="-o results/code-${{matrix.arch}}-migrate.csv" 198 - name: results-code-error-asserts 199 run: | 200 mkdir -p results 201 make clean 202 make code \ 203 CFLAGS+=" \ 204 -DLFS_NO_DEBUG \ 205 -DLFS_NO_WARN \ 206 -DLFS_NO_ERROR \ 207 -D'LFS_ASSERT(test)=do {if(!(test)) {return -1;}} while(0)'" \ 208 CODEFLAGS+="-o results/code-${{matrix.arch}}-error-asserts.csv" 209 - name: upload-results 210 uses: actions/upload-artifact@v2 211 with: 212 name: results 213 path: results 214 # limit reporting to Thumb, otherwise there would be too many numbers 215 # flying around for the results to be easily readable 216 - name: collect-status 217 if: ${{matrix.arch == 'thumb'}} 218 run: | 219 mkdir -p status 220 for f in $(shopt -s nullglob ; echo results/code*.csv) 221 do 222 export STEP="results-code$( 223 echo $f | sed -n 's/.*code-.*-\(.*\).csv/-\1/p')" 224 export CONTEXT="results / code$( 225 echo $f | sed -n 's/.*code-.*-\(.*\).csv/ (\1)/p')" 226 export PREV="$(curl -sS \ 227 "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/status/master" \ 228 | jq -re 'select(.sha != env.GITHUB_SHA) | .statuses[] 229 | select(.context == env.CONTEXT).description 230 | capture("Code size is (?<result>[0-9]+)").result' \ 231 || echo 0)" 232 export DESCRIPTION="$(./scripts/code.py -u $f -s | awk ' 233 NR==2 {printf "Code size is %d B",$2} 234 NR==2 && ENVIRON["PREV"]+0 != 0 { 235 printf " (%+.1f%%)",100*($2-ENVIRON["PREV"])/ENVIRON["PREV"]}')" 236 jq -n '{ 237 state: "success", 238 context: env.CONTEXT, 239 description: env.DESCRIPTION, 240 target_job: "${{github.job}} (${{matrix.arch}})", 241 target_step: env.STEP}' \ 242 | tee status/code$( 243 echo $f | sed -n 's/.*code-.*-\(.*\).csv/-\1/p').json 244 done 245 - name: upload-status 246 if: ${{matrix.arch == 'thumb'}} 247 uses: actions/upload-artifact@v2 248 with: 249 name: status 250 path: status 251 retention-days: 1 252 253 # run under Valgrind to check for memory errors 254 valgrind: 255 runs-on: ubuntu-18.04 256 steps: 257 - uses: actions/checkout@v2 258 - name: install 259 run: | 260 # need toml, also pip3 isn't installed by default? 261 sudo apt-get update -qq 262 sudo apt-get install -qq python3 python3-pip 263 sudo pip3 install toml 264 - name: install-valgrind 265 run: | 266 sudo apt-get update -qq 267 sudo apt-get install -qq valgrind 268 valgrind --version 269 # normal tests, we don't need to test all geometries 270 - name: test-valgrind 271 run: make test TESTFLAGS+="-k --valgrind" 272 273 # self-host with littlefs-fuse for a fuzz-like test 274 fuse: 275 runs-on: ubuntu-18.04 276 if: ${{!endsWith(github.ref, '-prefix')}} 277 steps: 278 - uses: actions/checkout@v2 279 - name: install 280 run: | 281 # need toml, also pip3 isn't installed by default? 282 sudo apt-get update -qq 283 sudo apt-get install -qq python3 python3-pip libfuse-dev 284 sudo pip3 install toml 285 fusermount -V 286 gcc --version 287 - uses: actions/checkout@v2 288 with: 289 repository: littlefs-project/littlefs-fuse 290 ref: v2 291 path: littlefs-fuse 292 - name: setup 293 run: | 294 # copy our new version into littlefs-fuse 295 rm -rf littlefs-fuse/littlefs/* 296 cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs 297 298 # setup disk for littlefs-fuse 299 mkdir mount 300 sudo chmod a+rw /dev/loop0 301 dd if=/dev/zero bs=512 count=128K of=disk 302 losetup /dev/loop0 disk 303 - name: test 304 run: | 305 # self-host test 306 make -C littlefs-fuse 307 308 littlefs-fuse/lfs --format /dev/loop0 309 littlefs-fuse/lfs /dev/loop0 mount 310 311 ls mount 312 mkdir mount/littlefs 313 cp -r $(git ls-tree --name-only HEAD) mount/littlefs 314 cd mount/littlefs 315 stat . 316 ls -flh 317 make -B test 318 319 # test migration using littlefs-fuse 320 migrate: 321 runs-on: ubuntu-18.04 322 if: ${{!endsWith(github.ref, '-prefix')}} 323 steps: 324 - uses: actions/checkout@v2 325 - name: install 326 run: | 327 # need toml, also pip3 isn't installed by default? 328 sudo apt-get update -qq 329 sudo apt-get install -qq python3 python3-pip libfuse-dev 330 sudo pip3 install toml 331 fusermount -V 332 gcc --version 333 - uses: actions/checkout@v2 334 with: 335 repository: littlefs-project/littlefs-fuse 336 ref: v2 337 path: v2 338 - uses: actions/checkout@v2 339 with: 340 repository: littlefs-project/littlefs-fuse 341 ref: v1 342 path: v1 343 - name: setup 344 run: | 345 # copy our new version into littlefs-fuse 346 rm -rf v2/littlefs/* 347 cp -r $(git ls-tree --name-only HEAD) v2/littlefs 348 349 # setup disk for littlefs-fuse 350 mkdir mount 351 sudo chmod a+rw /dev/loop0 352 dd if=/dev/zero bs=512 count=128K of=disk 353 losetup /dev/loop0 disk 354 - name: test 355 run: | 356 # compile v1 and v2 357 make -C v1 358 make -C v2 359 360 # run self-host test with v1 361 v1/lfs --format /dev/loop0 362 v1/lfs /dev/loop0 mount 363 364 ls mount 365 mkdir mount/littlefs 366 cp -r $(git ls-tree --name-only HEAD) mount/littlefs 367 cd mount/littlefs 368 stat . 369 ls -flh 370 make -B test 371 372 # attempt to migrate 373 cd ../.. 374 fusermount -u mount 375 376 v2/lfs --migrate /dev/loop0 377 v2/lfs /dev/loop0 mount 378 379 # run self-host test with v2 right where we left off 380 ls mount 381 cd mount/littlefs 382 stat . 383 ls -flh 384 make -B test 385 386 # collect coverage info 387 coverage: 388 runs-on: ubuntu-18.04 389 needs: [test] 390 steps: 391 - uses: actions/checkout@v2 392 - name: install 393 run: | 394 sudo apt-get update -qq 395 sudo apt-get install -qq python3 python3-pip lcov 396 sudo pip3 install toml 397 # yes we continue-on-error nearly every step, continue-on-error 398 # at job level apparently still marks a job as failed, which isn't 399 # what we want 400 - uses: actions/download-artifact@v2 401 continue-on-error: true 402 with: 403 name: coverage 404 path: coverage 405 - name: results-coverage 406 continue-on-error: true 407 run: | 408 mkdir -p results 409 lcov $(for f in coverage/*.info ; do echo "-a $f" ; done) \ 410 -o results/coverage.info 411 ./scripts/coverage.py results/coverage.info -o results/coverage.csv 412 - name: upload-results 413 uses: actions/upload-artifact@v2 414 with: 415 name: results 416 path: results 417 - name: collect-status 418 run: | 419 mkdir -p status 420 [ -e results/coverage.csv ] || exit 0 421 export STEP="results-coverage" 422 export CONTEXT="results / coverage" 423 export PREV="$(curl -sS \ 424 "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/status/master" \ 425 | jq -re 'select(.sha != env.GITHUB_SHA) | .statuses[] 426 | select(.context == env.CONTEXT).description 427 | capture("Coverage is (?<result>[0-9\\.]+)").result' \ 428 || echo 0)" 429 export DESCRIPTION="$( 430 ./scripts/coverage.py -u results/coverage.csv -s | awk -F '[ /%]+' ' 431 NR==2 {printf "Coverage is %.1f%% of %d lines",$4,$3} 432 NR==2 && ENVIRON["PREV"]+0 != 0 { 433 printf " (%+.1f%%)",$4-ENVIRON["PREV"]}')" 434 jq -n '{ 435 state: "success", 436 context: env.CONTEXT, 437 description: env.DESCRIPTION, 438 target_job: "${{github.job}}", 439 target_step: env.STEP}' \ 440 | tee status/coverage.json 441 - name: upload-status 442 uses: actions/upload-artifact@v2 443 with: 444 name: status 445 path: status 446 retention-days: 1 447