test.yml 14 KB

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