• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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