test.yml 14 KB

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