name: test on: [push, pull_request] env: CFLAGS: -Werror MAKEFLAGS: -j jobs: # run tests test: runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: arch: [x86_64, thumb, mips, powerpc] steps: - uses: actions/checkout@v2 - name: install run: | # need a few additional tools # # note this includes gcc-10, which is required for -fcallgraph-info=su sudo apt-get update -qq sudo apt-get install -qq gcc-10 python3 python3-pip lcov sudo pip3 install toml echo "CC=gcc-10" >> $GITHUB_ENV gcc-10 --version lcov --version python3 --version # need newer lcov version for gcc-10 #sudo apt-get remove lcov #wget https://launchpad.net/ubuntu/+archive/primary/+files/lcov_1.15-1_all.deb #sudo apt install ./lcov_1.15-1_all.deb #lcov --version #which lcov #ls -lha /usr/bin/lcov wget https://github.com/linux-test-project/lcov/releases/download/v1.15/lcov-1.15.tar.gz tar xf lcov-1.15.tar.gz sudo make -C lcov-1.15 install # setup a ram-backed disk to speed up reentrant tests mkdir disks sudo mount -t tmpfs -o size=100m tmpfs disks TESTFLAGS="$TESTFLAGS --disk=disks/disk" # collect coverage mkdir -p coverage TESTFLAGS="$TESTFLAGS --coverage=` `coverage/${{github.job}}-${{matrix.arch}}.info" echo "TESTFLAGS=$TESTFLAGS" >> $GITHUB_ENV # cross-compile with ARM Thumb (32-bit, little-endian) - name: install-thumb if: ${{matrix.arch == 'thumb'}} run: | sudo apt-get install -qq \ gcc-10-arm-linux-gnueabi \ libc6-dev-armel-cross \ qemu-user echo "CC=arm-linux-gnueabi-gcc-10 -mthumb --static" >> $GITHUB_ENV echo "EXEC=qemu-arm" >> $GITHUB_ENV arm-linux-gnueabi-gcc-10 --version qemu-arm -version # cross-compile with MIPS (32-bit, big-endian) - name: install-mips if: ${{matrix.arch == 'mips'}} run: | sudo apt-get install -qq \ gcc-10-mips-linux-gnu \ libc6-dev-mips-cross \ qemu-user echo "CC=mips-linux-gnu-gcc-10 --static" >> $GITHUB_ENV echo "EXEC=qemu-mips" >> $GITHUB_ENV mips-linux-gnu-gcc-10 --version qemu-mips -version # cross-compile with PowerPC (32-bit, big-endian) - name: install-powerpc if: ${{matrix.arch == 'powerpc'}} run: | sudo apt-get install -qq \ gcc-10-powerpc-linux-gnu \ libc6-dev-powerpc-cross \ qemu-user echo "CC=powerpc-linux-gnu-gcc-10 --static" >> $GITHUB_ENV echo "EXEC=qemu-ppc" >> $GITHUB_ENV powerpc-linux-gnu-gcc-10 --version qemu-ppc -version # make sure example can at least compile - name: test-example run: | sed -n '/``` c/,/```/{/```/d; p}' README.md > test.c make all CFLAGS+=" \ -Duser_provided_block_device_read=NULL \ -Duser_provided_block_device_prog=NULL \ -Duser_provided_block_device_erase=NULL \ -Duser_provided_block_device_sync=NULL \ -include stdio.h" rm test.c # test configurations # normal+reentrant tests - name: test-default run: | make clean make test TESTFLAGS+="-nrk" # NOR flash: read/prog = 1 block = 4KiB - name: test-nor run: | make clean make test TESTFLAGS+="-nrk \ -DLFS_READ_SIZE=1 -DLFS_BLOCK_SIZE=4096" # SD/eMMC: read/prog = 512 block = 512 - name: test-emmc run: | make clean make test TESTFLAGS+="-nrk \ -DLFS_READ_SIZE=512 -DLFS_BLOCK_SIZE=512" # NAND flash: read/prog = 4KiB block = 32KiB - name: test-nand run: | make clean make test TESTFLAGS+="-nrk \ -DLFS_READ_SIZE=4096 -DLFS_BLOCK_SIZE=\(32*1024\)" # other extreme geometries that are useful for various corner cases - name: test-no-intrinsics run: | make clean make test TESTFLAGS+="-nrk \ -DLFS_NO_INTRINSICS" - name: test-byte-writes # it just takes too long to test byte-level writes when in qemu, # should be plenty covered by the other configurations if: ${{matrix.arch == 'x86_64'}} run: | make clean make test TESTFLAGS+="-nrk \ -DLFS_READ_SIZE=1 -DLFS_CACHE_SIZE=1" - name: test-block-cycles run: | make clean make test TESTFLAGS+="-nrk \ -DLFS_BLOCK_CYCLES=1" - name: test-odd-block-count run: | make clean make test TESTFLAGS+="-nrk \ -DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256" - name: test-odd-block-size run: | make clean make test TESTFLAGS+="-nrk \ -DLFS_READ_SIZE=11 -DLFS_BLOCK_SIZE=704" # upload coverage for later coverage - name: upload-coverage uses: actions/upload-artifact@v2 with: name: coverage path: coverage retention-days: 1 # update results - name: results run: | mkdir -p results make clean make lfs.csv \ CFLAGS+=" \ -DLFS_NO_ASSERT \ -DLFS_NO_DEBUG \ -DLFS_NO_WARN \ -DLFS_NO_ERROR" cp lfs.csv results/${{matrix.arch}}.csv ./scripts/summary.py results/${{matrix.arch}}.csv - name: results-readonly run: | mkdir -p results make clean make lfs.csv \ CFLAGS+=" \ -DLFS_NO_ASSERT \ -DLFS_NO_DEBUG \ -DLFS_NO_WARN \ -DLFS_NO_ERROR \ -DLFS_READONLY" cp lfs.csv results/${{matrix.arch}}-readonly.csv ./scripts/summary.py results/${{matrix.arch}}-readonly.csv - name: results-threadsafe run: | mkdir -p results make clean make lfs.csv \ CFLAGS+=" \ -DLFS_NO_ASSERT \ -DLFS_NO_DEBUG \ -DLFS_NO_WARN \ -DLFS_NO_ERROR \ -DLFS_THREADSAFE" cp lfs.csv results/${{matrix.arch}}-threadsafe.csv ./scripts/summary.py results/${{matrix.arch}}-threadsafe.csv - name: results-migrate run: | mkdir -p results make clean make lfs.csv \ CFLAGS+=" \ -DLFS_NO_ASSERT \ -DLFS_NO_DEBUG \ -DLFS_NO_WARN \ -DLFS_NO_ERROR \ -DLFS_MIGRATE" cp lfs.csv results/${{matrix.arch}}-migrate.csv ./scripts/summary.py results/${{matrix.arch}}-migrate.csv - name: results-error-asserts run: | mkdir -p results make clean make lfs.csv \ CFLAGS+=" \ -DLFS_NO_DEBUG \ -DLFS_NO_WARN \ -DLFS_NO_ERROR \ -D'LFS_ASSERT(test)=do {if(!(test)) {return -1;}} while(0)'" cp lfs.csv results/${{matrix.arch}}-error-asserts.csv ./scripts/summary.py results/${{matrix.arch}}-error-asserts.csv - name: upload-results uses: actions/upload-artifact@v2 with: name: results path: results # create statuses with results - name: collect-status run: | mkdir -p status for f in $(shopt -s nullglob ; echo results/*.csv) do export STEP="results$( echo $f | sed -n 's/[^-]*-\(.*\).csv/-\1/p')" for r in code stack structs do export CONTEXT="results (${{matrix.arch}}$( echo $f | sed -n 's/[^-]*-\(.*\).csv/, \1/p')) / $r" export PREV="$(curl -sS \ "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/status/master?per_page=100" \ | jq -re 'select(.sha != env.GITHUB_SHA) | .statuses[] | select(.context == env.CONTEXT).description | capture("(?[0-9∞]+)").result' \ || echo 0)" export DESCRIPTION="$(./scripts/summary.py $f -f $r -Y | awk ' NR==2 {printf "%s B",$2} NR==2 && ENVIRON["PREV"]+0 != 0 { printf " (%+.1f%%)",100*($2-ENVIRON["PREV"])/ENVIRON["PREV"]}')" jq -n '{ state: "success", context: env.CONTEXT, description: env.DESCRIPTION, target_job: "${{github.job}} (${{matrix.arch}})", target_step: env.STEP}' \ | tee status/$r-${{matrix.arch}}$( echo $f | sed -n 's/[^-]*-\(.*\).csv/-\1/p').json done done - name: upload-status uses: actions/upload-artifact@v2 with: name: status path: status retention-days: 1 # run under Valgrind to check for memory errors valgrind: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: install run: | # need toml, also pip3 isn't installed by default? sudo apt-get update -qq sudo apt-get install -qq python3 python3-pip sudo pip3 install toml - name: install-valgrind run: | sudo apt-get update -qq sudo apt-get install -qq valgrind valgrind --version # normal tests, we don't need to test all geometries - name: test-valgrind run: make test TESTFLAGS+="-k --valgrind" # self-host with littlefs-fuse for a fuzz-like test fuse: runs-on: ubuntu-20.04 if: ${{!endsWith(github.ref, '-prefix')}} steps: - uses: actions/checkout@v2 - name: install run: | # need toml, also pip3 isn't installed by default? sudo apt-get update -qq sudo apt-get install -qq python3 python3-pip libfuse-dev sudo pip3 install toml fusermount -V gcc --version - uses: actions/checkout@v2 with: repository: littlefs-project/littlefs-fuse ref: v2 path: littlefs-fuse - name: setup run: | # copy our new version into littlefs-fuse rm -rf littlefs-fuse/littlefs/* cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs # setup disk for littlefs-fuse mkdir mount LOOP=$(sudo losetup -f) sudo chmod a+rw $LOOP dd if=/dev/zero bs=512 count=128K of=disk losetup $LOOP disk echo "LOOP=$LOOP" >> $GITHUB_ENV - name: test run: | # self-host test make -C littlefs-fuse littlefs-fuse/lfs --format $LOOP littlefs-fuse/lfs $LOOP mount ls mount mkdir mount/littlefs cp -r $(git ls-tree --name-only HEAD) mount/littlefs cd mount/littlefs stat . ls -flh make -B test # test migration using littlefs-fuse migrate: runs-on: ubuntu-20.04 if: ${{!endsWith(github.ref, '-prefix')}} steps: - uses: actions/checkout@v2 - name: install run: | # need toml, also pip3 isn't installed by default? sudo apt-get update -qq sudo apt-get install -qq python3 python3-pip libfuse-dev sudo pip3 install toml fusermount -V gcc --version - uses: actions/checkout@v2 with: repository: littlefs-project/littlefs-fuse ref: v2 path: v2 - uses: actions/checkout@v2 with: repository: littlefs-project/littlefs-fuse ref: v1 path: v1 - name: setup run: | # copy our new version into littlefs-fuse rm -rf v2/littlefs/* cp -r $(git ls-tree --name-only HEAD) v2/littlefs # setup disk for littlefs-fuse mkdir mount LOOP=$(sudo losetup -f) sudo chmod a+rw $LOOP dd if=/dev/zero bs=512 count=128K of=disk losetup $LOOP disk echo "LOOP=$LOOP" >> $GITHUB_ENV - name: test run: | # compile v1 and v2 make -C v1 make -C v2 # run self-host test with v1 v1/lfs --format $LOOP v1/lfs $LOOP mount ls mount mkdir mount/littlefs cp -r $(git ls-tree --name-only HEAD) mount/littlefs cd mount/littlefs stat . ls -flh make -B test # attempt to migrate cd ../.. fusermount -u mount v2/lfs --migrate $LOOP v2/lfs $LOOP mount # run self-host test with v2 right where we left off ls mount cd mount/littlefs stat . ls -flh make -B test # collect coverage info coverage: runs-on: ubuntu-20.04 needs: [test] steps: - uses: actions/checkout@v2 - name: install run: | sudo apt-get update -qq sudo apt-get install -qq python3 python3-pip lcov sudo pip3 install toml # yes we continue-on-error nearly every step, continue-on-error # at job level apparently still marks a job as failed, which isn't # what we want - uses: actions/download-artifact@v2 continue-on-error: true with: name: coverage path: coverage - name: results-coverage continue-on-error: true run: | mkdir -p results lcov $(for f in coverage/*.info ; do echo "-a $f" ; done) \ -o results/coverage.info ./scripts/coverage.py results/coverage.info -o results/coverage.csv - name: upload-results uses: actions/upload-artifact@v2 with: name: results path: results - name: collect-status run: | mkdir -p status [ -e results/coverage.csv ] || exit 0 export STEP="results-coverage" export CONTEXT="results / coverage" export PREV="$(curl -sS \ "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/status/master?per_page=100" \ | jq -re 'select(.sha != env.GITHUB_SHA) | .statuses[] | select(.context == env.CONTEXT).description | capture("(?[0-9\\.]+)").result' \ || echo 0)" export DESCRIPTION="$( ./scripts/coverage.py -u results/coverage.csv -Y | awk -F '[ /%]+' ' NR==2 {printf "%.1f%% of %d lines",$4,$3} NR==2 && ENVIRON["PREV"]+0 != 0 { printf " (%+.1f%%)",$4-ENVIRON["PREV"]}')" jq -n '{ state: "success", context: env.CONTEXT, description: env.DESCRIPTION, target_job: "${{github.job}}", target_step: env.STEP}' \ | tee status/coverage.json - name: upload-status uses: actions/upload-artifact@v2 with: name: status path: status retention-days: 1