| name: test |
| on: [push, pull_request] |
| |
| defaults: |
| run: |
| shell: bash -euv -o pipefail {0} |
| |
| env: |
| CFLAGS: -Werror |
| MAKEFLAGS: -j |
| TESTFLAGS: -k |
| BENCHFLAGS: |
| |
| jobs: |
| # run tests |
| test: |
| runs-on: ubuntu-latest |
| strategy: |
| fail-fast: false |
| matrix: |
| arch: [x86_64, thumb, mips, powerpc] |
| |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip |
| pip3 install toml |
| gcc --version |
| python3 --version |
| |
| # cross-compile with ARM Thumb (32-bit, little-endian) |
| - name: install-thumb |
| if: ${{matrix.arch == 'thumb'}} |
| run: | |
| sudo apt-get install -qq \ |
| gcc-arm-linux-gnueabi \ |
| libc6-dev-armel-cross \ |
| qemu-user |
| echo "CC=arm-linux-gnueabi-gcc -mthumb --static" >> $GITHUB_ENV |
| echo "EXEC=qemu-arm" >> $GITHUB_ENV |
| arm-linux-gnueabi-gcc --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-mips-linux-gnu \ |
| libc6-dev-mips-cross \ |
| qemu-user |
| echo "CC=mips-linux-gnu-gcc --static" >> $GITHUB_ENV |
| echo "EXEC=qemu-mips" >> $GITHUB_ENV |
| mips-linux-gnu-gcc --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-powerpc-linux-gnu \ |
| libc6-dev-powerpc-cross \ |
| qemu-user |
| echo "CC=powerpc-linux-gnu-gcc --static" >> $GITHUB_ENV |
| echo "EXEC=qemu-ppc" >> $GITHUB_ENV |
| powerpc-linux-gnu-gcc --version |
| qemu-ppc -version |
| |
| # does littlefs compile? |
| - name: test-build |
| run: | |
| make clean |
| make build |
| |
| # make sure example can at least compile |
| - name: test-example |
| run: | |
| make clean |
| sed -n '/``` c/,/```/{/```/d; p}' README.md > test.c |
| CFLAGS="$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" \ |
| make all |
| rm test.c |
| |
| # run the tests! |
| - name: test |
| run: | |
| make clean |
| make test |
| |
| # collect coverage info |
| # |
| # Note the goal is to maximize coverage in the small, easy-to-run |
| # tests, so we intentionally exclude more aggressive powerloss testing |
| # from coverage results |
| - name: cov |
| if: ${{matrix.arch == 'x86_64'}} |
| run: | |
| make lfs.cov.csv |
| ./scripts/cov.py -u lfs.cov.csv |
| mkdir -p cov |
| cp lfs.cov.csv cov/cov.csv |
| |
| # find compile-time measurements |
| - name: sizes |
| run: | |
| make clean |
| CFLAGS="$CFLAGS \ |
| -DLFS_NO_ASSERT \ |
| -DLFS_NO_DEBUG \ |
| -DLFS_NO_WARN \ |
| -DLFS_NO_ERROR" \ |
| make lfs.code.csv lfs.data.csv lfs.stack.csv lfs.structs.csv |
| ./scripts/structs.py -u lfs.structs.csv |
| ./scripts/summary.py lfs.code.csv lfs.data.csv lfs.stack.csv \ |
| -bfunction \ |
| -fcode=code_size \ |
| -fdata=data_size \ |
| -fstack=stack_limit --max=stack_limit |
| mkdir -p sizes |
| cp lfs.code.csv sizes/${{matrix.arch}}.code.csv |
| cp lfs.data.csv sizes/${{matrix.arch}}.data.csv |
| cp lfs.stack.csv sizes/${{matrix.arch}}.stack.csv |
| cp lfs.structs.csv sizes/${{matrix.arch}}.structs.csv |
| - name: sizes-readonly |
| run: | |
| make clean |
| CFLAGS="$CFLAGS \ |
| -DLFS_NO_ASSERT \ |
| -DLFS_NO_DEBUG \ |
| -DLFS_NO_WARN \ |
| -DLFS_NO_ERROR \ |
| -DLFS_READONLY" \ |
| make lfs.code.csv lfs.data.csv lfs.stack.csv lfs.structs.csv |
| ./scripts/structs.py -u lfs.structs.csv |
| ./scripts/summary.py lfs.code.csv lfs.data.csv lfs.stack.csv \ |
| -bfunction \ |
| -fcode=code_size \ |
| -fdata=data_size \ |
| -fstack=stack_limit --max=stack_limit |
| mkdir -p sizes |
| cp lfs.code.csv sizes/${{matrix.arch}}-readonly.code.csv |
| cp lfs.data.csv sizes/${{matrix.arch}}-readonly.data.csv |
| cp lfs.stack.csv sizes/${{matrix.arch}}-readonly.stack.csv |
| cp lfs.structs.csv sizes/${{matrix.arch}}-readonly.structs.csv |
| - name: sizes-threadsafe |
| run: | |
| make clean |
| CFLAGS="$CFLAGS \ |
| -DLFS_NO_ASSERT \ |
| -DLFS_NO_DEBUG \ |
| -DLFS_NO_WARN \ |
| -DLFS_NO_ERROR \ |
| -DLFS_THREADSAFE" \ |
| make lfs.code.csv lfs.data.csv lfs.stack.csv lfs.structs.csv |
| ./scripts/structs.py -u lfs.structs.csv |
| ./scripts/summary.py lfs.code.csv lfs.data.csv lfs.stack.csv \ |
| -bfunction \ |
| -fcode=code_size \ |
| -fdata=data_size \ |
| -fstack=stack_limit --max=stack_limit |
| mkdir -p sizes |
| cp lfs.code.csv sizes/${{matrix.arch}}-threadsafe.code.csv |
| cp lfs.data.csv sizes/${{matrix.arch}}-threadsafe.data.csv |
| cp lfs.stack.csv sizes/${{matrix.arch}}-threadsafe.stack.csv |
| cp lfs.structs.csv sizes/${{matrix.arch}}-threadsafe.structs.csv |
| - name: sizes-multiversion |
| run: | |
| make clean |
| CFLAGS="$CFLAGS \ |
| -DLFS_NO_ASSERT \ |
| -DLFS_NO_DEBUG \ |
| -DLFS_NO_WARN \ |
| -DLFS_NO_ERROR \ |
| -DLFS_MULTIVERSION" \ |
| make lfs.code.csv lfs.data.csv lfs.stack.csv lfs.structs.csv |
| ./scripts/structs.py -u lfs.structs.csv |
| ./scripts/summary.py lfs.code.csv lfs.data.csv lfs.stack.csv \ |
| -bfunction \ |
| -fcode=code_size \ |
| -fdata=data_size \ |
| -fstack=stack_limit --max=stack_limit |
| mkdir -p sizes |
| cp lfs.code.csv sizes/${{matrix.arch}}-multiversion.code.csv |
| cp lfs.data.csv sizes/${{matrix.arch}}-multiversion.data.csv |
| cp lfs.stack.csv sizes/${{matrix.arch}}-multiversion.stack.csv |
| cp lfs.structs.csv sizes/${{matrix.arch}}-multiversion.structs.csv |
| - name: sizes-migrate |
| run: | |
| make clean |
| CFLAGS="$CFLAGS \ |
| -DLFS_NO_ASSERT \ |
| -DLFS_NO_DEBUG \ |
| -DLFS_NO_WARN \ |
| -DLFS_NO_ERROR \ |
| -DLFS_MIGRATE" \ |
| make lfs.code.csv lfs.data.csv lfs.stack.csv lfs.structs.csv |
| ./scripts/structs.py -u lfs.structs.csv |
| ./scripts/summary.py lfs.code.csv lfs.data.csv lfs.stack.csv \ |
| -bfunction \ |
| -fcode=code_size \ |
| -fdata=data_size \ |
| -fstack=stack_limit --max=stack_limit |
| mkdir -p sizes |
| cp lfs.code.csv sizes/${{matrix.arch}}-migrate.code.csv |
| cp lfs.data.csv sizes/${{matrix.arch}}-migrate.data.csv |
| cp lfs.stack.csv sizes/${{matrix.arch}}-migrate.stack.csv |
| cp lfs.structs.csv sizes/${{matrix.arch}}-migrate.structs.csv |
| - name: sizes-error-asserts |
| run: | |
| make clean |
| CFLAGS="$CFLAGS \ |
| -DLFS_NO_DEBUG \ |
| -DLFS_NO_WARN \ |
| -DLFS_NO_ERROR \ |
| -D'LFS_ASSERT(test)=do {if(!(test)) {return -1;}} while(0)'" \ |
| make lfs.code.csv lfs.data.csv lfs.stack.csv lfs.structs.csv |
| ./scripts/structs.py -u lfs.structs.csv |
| ./scripts/summary.py lfs.code.csv lfs.data.csv lfs.stack.csv \ |
| -bfunction \ |
| -fcode=code_size \ |
| -fdata=data_size \ |
| -fstack=stack_limit --max=stack_limit |
| mkdir -p sizes |
| cp lfs.code.csv sizes/${{matrix.arch}}-error-asserts.code.csv |
| cp lfs.data.csv sizes/${{matrix.arch}}-error-asserts.data.csv |
| cp lfs.stack.csv sizes/${{matrix.arch}}-error-asserts.stack.csv |
| cp lfs.structs.csv sizes/${{matrix.arch}}-error-asserts.structs.csv |
| |
| # create size statuses |
| - name: upload-sizes |
| uses: actions/upload-artifact@v4 |
| with: |
| name: sizes-${{matrix.arch}} |
| path: sizes |
| - name: status-sizes |
| run: | |
| mkdir -p status |
| for f in $(shopt -s nullglob ; echo sizes/*.csv) |
| do |
| # skip .data.csv as it should always be zero |
| [[ $f == *.data.csv ]] && continue |
| export STEP="sizes$(echo $f \ |
| | sed -n 's/[^-.]*-\([^.]*\)\..*csv/-\1/p')" |
| export CONTEXT="sizes (${{matrix.arch}}$(echo $f \ |
| | sed -n 's/[^-.]*-\([^.]*\)\..*csv/, \1/p')) / $(echo $f \ |
| | sed -n 's/[^.]*\.\(.*\)\.csv/\1/p')" |
| 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("(?<prev>[0-9∞]+)").prev' \ |
| || echo 0)" |
| export DESCRIPTION="$(./scripts/summary.py $f --max=stack_limit -Y \ |
| | awk ' |
| NR==2 {$1=0; printf "%s B",$NF} |
| NR==2 && ENVIRON["PREV"]+0 != 0 { |
| printf " (%+.1f%%)",100*($NF-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/$(basename $f .csv).json |
| done |
| - name: upload-status-sizes |
| uses: actions/upload-artifact@v4 |
| with: |
| name: status-sizes-${{matrix.arch}} |
| path: status |
| retention-days: 1 |
| |
| # create cov statuses |
| - name: upload-cov |
| if: ${{matrix.arch == 'x86_64'}} |
| uses: actions/upload-artifact@v4 |
| with: |
| name: cov |
| path: cov |
| - name: status-cov |
| if: ${{matrix.arch == 'x86_64'}} |
| run: | |
| mkdir -p status |
| f=cov/cov.csv |
| for s in lines branches |
| do |
| export STEP="cov" |
| export CONTEXT="cov / $s" |
| 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("(?<prev_a>[0-9]+)/(?<prev_b>[0-9]+)") |
| | 100*((.prev_a|tonumber) / (.prev_b|tonumber))' \ |
| || echo 0)" |
| export DESCRIPTION="$(./scripts/cov.py -u $f -f$s -Y \ |
| | awk -F '[ /%]+' -v s=$s ' |
| NR==2 {$1=0; printf "%d/%d %s",$2,$3,s} |
| 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}} (${{matrix.arch}})", |
| target_step: env.STEP, |
| }' | tee status/$(basename $f .csv)-$s.json |
| done |
| - name: upload-status-cov |
| if: ${{matrix.arch == 'x86_64'}} |
| uses: actions/upload-artifact@v4 |
| with: |
| name: status-cov |
| path: status |
| retention-days: 1 |
| |
| # run as many exhaustive tests as fits in GitHub's time limits |
| # |
| # this grows exponentially, so it doesn't turn out to be that many |
| test-pls: |
| runs-on: ubuntu-latest |
| strategy: |
| fail-fast: false |
| matrix: |
| pls: [1, 2] |
| |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip |
| pip3 install toml |
| gcc --version |
| python3 --version |
| - name: test-pls |
| if: ${{matrix.pls <= 1}} |
| run: | |
| TESTFLAGS="$TESTFLAGS -P${{matrix.pls}}" make test |
| # >=2pls takes multiple days to run fully, so we can only |
| # run a subset of tests, these are the most important |
| - name: test-limited-pls |
| if: ${{matrix.pls > 1}} |
| run: | |
| TESTFLAGS="$TESTFLAGS -P${{matrix.pls}} test_dirs test_relocations" \ |
| make test |
| |
| # run with LFS_NO_INTRINSICS to make sure that works |
| test-no-intrinsics: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip |
| pip3 install toml |
| gcc --version |
| python3 --version |
| - name: test-no-intrinsics |
| run: | |
| CFLAGS="$CFLAGS -DLFS_NO_INTRINSICS" make test |
| |
| test-shrink: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip |
| pip3 install toml |
| gcc --version |
| python3 --version |
| - name: test-no-intrinsics |
| run: | |
| CFLAGS="$CFLAGS -DLFS_SHRINKNONRELOCATING" make test |
| |
| # run with all trace options enabled to at least make sure these |
| # all compile |
| test-yes-trace: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip |
| pip3 install toml |
| gcc --version |
| python3 --version |
| - name: test-yes-trace |
| run: | |
| CFLAGS="$CFLAGS \ |
| -DLFS_YES_TRACE \ |
| -DLFS_RAMBD_YES_TRACE \ |
| -DLFS_FILEBD_YES_TRACE \ |
| -DLFS_RAMBD_YES_TRACE" \ |
| make test |
| |
| # run LFS_MULTIVERSION tests |
| test-multiversion: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip |
| pip3 install toml |
| gcc --version |
| python3 --version |
| - name: test-multiversion |
| run: | |
| CFLAGS="$CFLAGS -DLFS_MULTIVERSION" make test |
| |
| # run tests on the older version lfs2.0 |
| test-lfs2_0: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip |
| pip3 install toml |
| gcc --version |
| python3 --version |
| - name: test-lfs2_0 |
| run: | |
| CFLAGS="$CFLAGS -DLFS_MULTIVERSION" \ |
| TESTFLAGS="$TESTFLAGS -DDISK_VERSION=0x00020000" \ |
| make test |
| |
| # run under Valgrind to check for memory errors |
| test-valgrind: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip valgrind |
| pip3 install toml |
| gcc --version |
| python3 --version |
| valgrind --version |
| # Valgrind takes a while with diminishing value, so only test |
| # on one geometry |
| - name: test-valgrind |
| run: | |
| TESTFLAGS="$TESTFLAGS --valgrind --context=1024 -Gdefault -Pnone" \ |
| make test |
| |
| # compile/run with Clang, mostly to check for Clang-specific warnings |
| test-clang: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get install -qq clang python3 python3-pip |
| pip3 install toml |
| clang --version |
| python3 --version |
| - name: test-clang |
| run: | |
| CC=clang \ |
| make test |
| |
| # run benchmarks |
| # |
| # note there's no real benefit to running these on multiple archs |
| bench: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip valgrind |
| pip3 install toml |
| gcc --version |
| python3 --version |
| valgrind --version |
| - name: bench |
| run: | |
| make bench |
| |
| # find bench results |
| make lfs.bench.csv |
| ./scripts/summary.py lfs.bench.csv \ |
| -bsuite \ |
| -freaded=bench_readed \ |
| -fproged=bench_proged \ |
| -ferased=bench_erased |
| mkdir -p bench |
| cp lfs.bench.csv bench/bench.csv |
| |
| # find perfbd results |
| make lfs.perfbd.csv |
| ./scripts/perfbd.py -u lfs.perfbd.csv |
| mkdir -p bench |
| cp lfs.perfbd.csv bench/perfbd.csv |
| |
| # create bench statuses |
| - name: upload-bench |
| uses: actions/upload-artifact@v4 |
| with: |
| name: bench |
| path: bench |
| - name: status-bench |
| run: | |
| mkdir -p status |
| f=bench/bench.csv |
| for s in readed proged erased |
| do |
| export STEP="bench" |
| export CONTEXT="bench / $s" |
| 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("(?<prev>[0-9]+)").prev' \ |
| || echo 0)" |
| export DESCRIPTION="$(./scripts/summary.py $f -f$s=bench_$s -Y \ |
| | awk ' |
| NR==2 {$1=0; printf "%s B",$NF} |
| NR==2 && ENVIRON["PREV"]+0 != 0 { |
| printf " (%+.1f%%)",100*($NF-ENVIRON["PREV"])/ENVIRON["PREV"] |
| }')" |
| jq -n '{ |
| state: "success", |
| context: env.CONTEXT, |
| description: env.DESCRIPTION, |
| target_job: "${{github.job}}", |
| target_step: env.STEP, |
| }' | tee status/$(basename $f .csv)-$s.json |
| done |
| - name: upload-status-bench |
| uses: actions/upload-artifact@v4 |
| with: |
| name: status-bench |
| path: status |
| retention-days: 1 |
| |
| # run compatibility tests using the current master as the previous version |
| test-compat: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| if: ${{github.event_name == 'pull_request'}} |
| # checkout the current pr target into lfsp |
| - uses: actions/checkout@v4 |
| if: ${{github.event_name == 'pull_request'}} |
| with: |
| ref: ${{github.event.pull_request.base.ref}} |
| path: lfsp |
| - name: install |
| if: ${{github.event_name == 'pull_request'}} |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip |
| pip3 install toml |
| gcc --version |
| python3 --version |
| # adjust prefix of lfsp |
| - name: changeprefix |
| if: ${{github.event_name == 'pull_request'}} |
| run: | |
| ./scripts/changeprefix.py lfs lfsp lfsp/*.h lfsp/*.c |
| - name: test-compat |
| if: ${{github.event_name == 'pull_request'}} |
| run: | |
| TESTS=tests/test_compat.toml \ |
| SRC="$(find . lfsp -name '*.c' -maxdepth 1 \ |
| -and -not -name '*.t.*' \ |
| -and -not -name '*.b.*')" \ |
| CFLAGS="-DLFSP=lfsp/lfsp.h" \ |
| make test |
| |
| # self-host with littlefs-fuse for a fuzz-like test |
| fuse: |
| runs-on: ubuntu-latest |
| if: ${{!endsWith(github.ref, '-prefix')}} |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip libfuse-dev |
| sudo pip3 install toml |
| gcc --version |
| python3 --version |
| fusermount -V |
| - uses: actions/checkout@v4 |
| 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-runner |
| make -B test |
| |
| # test migration using littlefs-fuse |
| migrate: |
| runs-on: ubuntu-latest |
| if: ${{!endsWith(github.ref, '-prefix')}} |
| steps: |
| - uses: actions/checkout@v4 |
| - name: install |
| run: | |
| # need a few things |
| sudo apt-get update -qq |
| sudo apt-get install -qq gcc python3 python3-pip libfuse-dev |
| sudo pip3 install toml |
| gcc --version |
| python3 --version |
| fusermount -V |
| - uses: actions/checkout@v4 |
| with: |
| repository: littlefs-project/littlefs-fuse |
| ref: v2 |
| path: v2 |
| - uses: actions/checkout@v4 |
| 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-runner |
| 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-runner |
| make -B test |
| |
| # status related tasks that run after tests |
| status: |
| runs-on: ubuntu-latest |
| needs: [test, bench] |
| steps: |
| - uses: actions/checkout@v4 |
| if: ${{github.event_name == 'pull_request'}} |
| - name: install |
| if: ${{github.event_name == 'pull_request'}} |
| run: | |
| # need a few things |
| sudo apt-get install -qq gcc python3 python3-pip |
| pip3 install toml |
| gcc --version |
| python3 --version |
| - uses: actions/download-artifact@v4 |
| if: ${{github.event_name == 'pull_request'}} |
| continue-on-error: true |
| with: |
| pattern: '{sizes,sizes-*}' |
| merge-multiple: true |
| path: sizes |
| - uses: actions/download-artifact@v4 |
| if: ${{github.event_name == 'pull_request'}} |
| continue-on-error: true |
| with: |
| pattern: '{cov,cov-*}' |
| merge-multiple: true |
| path: cov |
| - uses: actions/download-artifact@v4 |
| if: ${{github.event_name == 'pull_request'}} |
| continue-on-error: true |
| with: |
| pattern: '{bench,bench-*}' |
| merge-multiple: true |
| path: bench |
| |
| # try to find results from tests |
| - name: create-table |
| if: ${{github.event_name == 'pull_request'}} |
| run: | |
| # compare against pull-request target |
| curl -sS \ |
| "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/status/` |
| `${{github.event.pull_request.base.ref}}` |
| `?per_page=100" \ |
| | jq -re 'select(.sha != env.GITHUB_SHA) | .statuses[]' \ |
| >> prev-status.json \ |
| || true |
| |
| # build table for GitHub |
| declare -A table |
| |
| # sizes table |
| i=0 |
| j=0 |
| for c in "" readonly threadsafe multiversion migrate error-asserts |
| do |
| # per-config results |
| c_or_default=${c:-default} |
| c_camel=${c_or_default^} |
| table[$i,$j]=$c_camel |
| ((j+=1)) |
| |
| for s in code stack structs |
| do |
| f=sizes/thumb${c:+-$c}.$s.csv |
| [ -e $f ] && table[$i,$j]=$( \ |
| export PREV="$(jq -re ' |
| select(.context == "'"sizes (thumb${c:+, $c}) / $s"'").description |
| | capture("(?<prev>[0-9∞]+)").prev' \ |
| prev-status.json || echo 0)" |
| ./scripts/summary.py $f --max=stack_limit -Y \ |
| | awk ' |
| NR==2 {$1=0; printf "%s B",$NF} |
| NR==2 && ENVIRON["PREV"]+0 != 0 { |
| printf " (%+.1f%%)",100*($NF-ENVIRON["PREV"])/ENVIRON["PREV"] |
| }' \ |
| | sed -e 's/ /\ /g') |
| ((j+=1)) |
| done |
| ((j=0, i+=1)) |
| done |
| |
| # coverage table |
| i=0 |
| j=4 |
| for s in lines branches |
| do |
| table[$i,$j]=${s^} |
| ((j+=1)) |
| |
| f=cov/cov.csv |
| [ -e $f ] && table[$i,$j]=$( \ |
| export PREV="$(jq -re ' |
| select(.context == "'"cov / $s"'").description |
| | capture("(?<prev_a>[0-9]+)/(?<prev_b>[0-9]+)") |
| | 100*((.prev_a|tonumber) / (.prev_b|tonumber))' \ |
| prev-status.json || echo 0)" |
| ./scripts/cov.py -u $f -f$s -Y \ |
| | awk -F '[ /%]+' -v s=$s ' |
| NR==2 {$1=0; printf "%d/%d %s",$2,$3,s} |
| NR==2 && ENVIRON["PREV"]+0 != 0 { |
| printf " (%+.1f%%)",$4-ENVIRON["PREV"] |
| }' \ |
| | sed -e 's/ /\ /g') |
| ((j=4, i+=1)) |
| done |
| |
| # benchmark table |
| i=3 |
| j=4 |
| for s in readed proged erased |
| do |
| table[$i,$j]=${s^} |
| ((j+=1)) |
| |
| f=bench/bench.csv |
| [ -e $f ] && table[$i,$j]=$( \ |
| export PREV="$(jq -re ' |
| select(.context == "'"bench / $s"'").description |
| | capture("(?<prev>[0-9]+)").prev' \ |
| prev-status.json || echo 0)" |
| ./scripts/summary.py $f -f$s=bench_$s -Y \ |
| | awk ' |
| NR==2 {$1=0; printf "%s B",$NF} |
| NR==2 && ENVIRON["PREV"]+0 != 0 { |
| printf " (%+.1f%%)",100*($NF-ENVIRON["PREV"])/ENVIRON["PREV"] |
| }' \ |
| | sed -e 's/ /\ /g') |
| ((j=4, i+=1)) |
| done |
| |
| # build the actual table |
| echo "| | Code | Stack | Structs | | Coverage |" >> table.txt |
| echo "|:--|-----:|------:|--------:|:--|---------:|" >> table.txt |
| for ((i=0; i<6; i++)) |
| do |
| echo -n "|" >> table.txt |
| for ((j=0; j<6; j++)) |
| do |
| echo -n " " >> table.txt |
| [[ i -eq 2 && j -eq 5 ]] && echo -n "**Benchmarks**" >> table.txt |
| echo -n "${table[$i,$j]:-}" >> table.txt |
| echo -n " |" >> table.txt |
| done |
| echo >> table.txt |
| done |
| |
| cat table.txt |
| |
| # create a bot comment for successful runs on pull requests |
| - name: create-comment |
| if: ${{github.event_name == 'pull_request'}} |
| run: | |
| touch comment.txt |
| echo "<details>" >> comment.txt |
| echo "<summary>" >> comment.txt |
| echo "Tests passed ✓, ` |
| `Code: $(awk 'NR==3 {print $4}' table.txt || true), ` |
| `Stack: $(awk 'NR==3 {print $6}' table.txt || true), ` |
| `Structs: $(awk 'NR==3 {print $8}' table.txt || true)" \ |
| >> comment.txt |
| echo "</summary>" >> comment.txt |
| echo >> comment.txt |
| [ -e table.txt ] && cat table.txt >> comment.txt |
| echo >> comment.txt |
| echo "</details>" >> comment.txt |
| cat comment.txt |
| |
| mkdir -p comment |
| jq -n --rawfile comment comment.txt '{ |
| number: ${{github.event.number}}, |
| body: $comment, |
| }' | tee comment/comment.json |
| - name: upload-comment |
| uses: actions/upload-artifact@v4 |
| with: |
| name: comment |
| path: comment |
| retention-days: 1 |
| |