test.yml 15 KB

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