mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Generated v2 prefixes
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -7,3 +7,6 @@ | |||||||
| blocks/ | blocks/ | ||||||
| lfs2 | lfs2 | ||||||
| test.c | test.c | ||||||
|  | tests/*.toml.* | ||||||
|  | scripts/__pycache__ | ||||||
|  | .gdb_history | ||||||
|   | |||||||
							
								
								
									
										275
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										275
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,49 +1,70 @@ | |||||||
| # Environment variables | # environment variables | ||||||
| env: | env: | ||||||
|   global: |   global: | ||||||
|     - CFLAGS=-Werror |     - CFLAGS=-Werror | ||||||
|  |     - MAKEFLAGS=-j | ||||||
|  |  | ||||||
| # Common test script | # cache installation dirs | ||||||
| script: | cache: | ||||||
|  |   pip: true | ||||||
|  |   directories: | ||||||
|  |     - $HOME/.cache/apt | ||||||
|  |  | ||||||
|  | # common installation | ||||||
|  | _: &install-common | ||||||
|  |   # need toml, also pip3 isn't installed by default? | ||||||
|  |   - sudo apt-get install python3 python3-pip | ||||||
|  |   - sudo pip3 install toml | ||||||
|  |   # setup a ram-backed disk to speed up reentrant tests | ||||||
|  |   - mkdir disks | ||||||
|  |   - sudo mount -t tmpfs -o size=100m tmpfs disks | ||||||
|  |   - export TFLAGS="$TFLAGS --disk=disks/disk" | ||||||
|  |  | ||||||
|  | # test cases | ||||||
|  | _: &test-example | ||||||
|   # make sure example can at least compile |   # make sure example can at least compile | ||||||
|   - sed -n '/``` c/,/```/{/```/d; p;}' README.md > test.c && |   - sed -n '/``` c/,/```/{/```/d; p}' README.md > test.c && | ||||||
|     make all CFLAGS+=" |     make all CFLAGS+=" | ||||||
|         -Duser_provided_block_device_read=NULL |         -Duser_provided_block_device_read=NULL | ||||||
|         -Duser_provided_block_device_prog=NULL |         -Duser_provided_block_device_prog=NULL | ||||||
|         -Duser_provided_block_device_erase=NULL |         -Duser_provided_block_device_erase=NULL | ||||||
|         -Duser_provided_block_device_sync=NULL |         -Duser_provided_block_device_sync=NULL | ||||||
|         -include stdio.h" |         -include stdio.h" | ||||||
|  | # default tests | ||||||
|  | _: &test-default | ||||||
|  |   # normal+reentrant tests | ||||||
|  |   - make test TFLAGS+="-nrk" | ||||||
|  | # common real-life geometries | ||||||
|  | _: &test-nor | ||||||
|  |   # NOR flash: read/prog = 1 block = 4KiB | ||||||
|  |   - make test TFLAGS+="-nrk -DLFS2_READ_SIZE=1 -DLFS2_BLOCK_SIZE=4096" | ||||||
|  | _: &test-emmc | ||||||
|  |   # eMMC: read/prog = 512 block = 512 | ||||||
|  |   - make test TFLAGS+="-nrk -DLFS2_READ_SIZE=512 -DLFS2_BLOCK_SIZE=512" | ||||||
|  | _: &test-nand | ||||||
|  |   # NAND flash: read/prog = 4KiB block = 32KiB | ||||||
|  |   - make test TFLAGS+="-nrk -DLFS2_READ_SIZE=4096 -DLFS2_BLOCK_SIZE=\(32*1024\)" | ||||||
|  | # other extreme geometries that are useful for testing various corner cases | ||||||
|  | _: &test-no-intrinsics | ||||||
|  |   - make test TFLAGS+="-nrk -DLFS2_NO_INTRINSICS" | ||||||
|  | _: &test-no-inline | ||||||
|  |   - make test TFLAGS+="-nrk -DLFS2_INLINE_MAX=0" | ||||||
|  | _: &test-byte-writes | ||||||
|  |   - make test TFLAGS+="-nrk -DLFS2_READ_SIZE=1 -DLFS2_CACHE_SIZE=1" | ||||||
|  | _: &test-block-cycles | ||||||
|  |   - make test TFLAGS+="-nrk -DLFS2_BLOCK_CYCLES=1" | ||||||
|  | _: &test-odd-block-count | ||||||
|  |   - make test TFLAGS+="-nrk -DLFS2_BLOCK_COUNT=1023 -DLFS2_LOOKAHEAD_SIZE=256" | ||||||
|  | _: &test-odd-block-size | ||||||
|  |   - make test TFLAGS+="-nrk -DLFS2_READ_SIZE=11 -DLFS2_BLOCK_SIZE=704" | ||||||
|  |  | ||||||
|   # run tests | # report size  | ||||||
|   - make test QUIET=1 | _: &report-size | ||||||
|  |  | ||||||
|   # run tests with a few different configurations |  | ||||||
|   - make test QUIET=1 CFLAGS+="-DLFS2_READ_SIZE=1      -DLFS2_CACHE_SIZE=4" |  | ||||||
|   - make test QUIET=1 CFLAGS+="-DLFS2_READ_SIZE=512    -DLFS2_CACHE_SIZE=512 -DLFS2_BLOCK_CYCLES=16" |  | ||||||
|   - make test QUIET=1 CFLAGS+="-DLFS2_READ_SIZE=8      -DLFS2_CACHE_SIZE=16  -DLFS2_BLOCK_CYCLES=2" |  | ||||||
|   - make test QUIET=1 CFLAGS+="-DLFS2_BLOCK_COUNT=1023 -DLFS2_LOOKAHEAD_SIZE=256" |  | ||||||
|  |  | ||||||
|   - make clean test QUIET=1 CFLAGS+="-DLFS2_INLINE_MAX=0" |  | ||||||
|   - make clean test QUIET=1 CFLAGS+="-DLFS2_EMUBD_ERASE_VALUE=0xff" |  | ||||||
|   - make clean test QUIET=1 CFLAGS+="-DLFS2_NO_INTRINSICS" |  | ||||||
|  |  | ||||||
|   # additional configurations that don't support all tests (this should be |  | ||||||
|   # fixed but at the moment it is what it is) |  | ||||||
|   - make test_files QUIET=1 |  | ||||||
|         CFLAGS+="-DLFS2_READ_SIZE=1 -DLFS2_BLOCK_SIZE=4096" |  | ||||||
|   - make test_files QUIET=1 |  | ||||||
|         CFLAGS+="-DLFS2_READ_SIZE=\(2*1024\) -DLFS2_BLOCK_SIZE=\(64*1024\)" |  | ||||||
|   - make test_files QUIET=1 |  | ||||||
|         CFLAGS+="-DLFS2_READ_SIZE=\(8*1024\) -DLFS2_BLOCK_SIZE=\(64*1024\)" |  | ||||||
|   - make test_files QUIET=1 |  | ||||||
|         CFLAGS+="-DLFS2_READ_SIZE=11 -DLFS2_BLOCK_SIZE=704" |  | ||||||
|  |  | ||||||
|   # compile and find the code size with the smallest configuration |   # compile and find the code size with the smallest configuration | ||||||
|   - make clean size |   - make -j1 clean size | ||||||
|         OBJ="$(ls lfs2*.o | tr '\n' ' ')" |         OBJ="$(ls lfs2*.c | sed 's/\.c/\.o/' | tr '\n' ' ')" | ||||||
|         CFLAGS+="-DLFS2_NO_ASSERT -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR" |         CFLAGS+="-DLFS2_NO_ASSERT -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR" | ||||||
|         | tee sizes |         | tee sizes | ||||||
|  |  | ||||||
|   # update status if we succeeded, compare with master if possible |   # update status if we succeeded, compare with master if possible | ||||||
|   - | |   - | | ||||||
|     if [ "$TRAVIS_TEST_RESULT" -eq 0 ] |     if [ "$TRAVIS_TEST_RESULT" -eq 0 ] | ||||||
| @@ -51,7 +72,7 @@ script: | |||||||
|         CURR=$(tail -n1 sizes | awk '{print $1}') |         CURR=$(tail -n1 sizes | awk '{print $1}') | ||||||
|         PREV=$(curl -u "$GEKY_BOT_STATUSES" https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \ |         PREV=$(curl -u "$GEKY_BOT_STATUSES" https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \ | ||||||
|             | jq -re "select(.sha != \"$TRAVIS_COMMIT\") |             | jq -re "select(.sha != \"$TRAVIS_COMMIT\") | ||||||
|                 | .statuses[] | select(.context == \"$STAGE/$NAME\").description |                 | .statuses[] | select(.context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\").description | ||||||
|                 | capture(\"code size is (?<size>[0-9]+)\").size" \ |                 | capture(\"code size is (?<size>[0-9]+)\").size" \ | ||||||
|             || echo 0) |             || echo 0) | ||||||
|    |    | ||||||
| @@ -62,79 +83,150 @@ script: | |||||||
|         fi |         fi | ||||||
|     fi |     fi | ||||||
|  |  | ||||||
| # CI matrix | # stage control | ||||||
|  | stages: | ||||||
|  |   - name: test | ||||||
|  |   - name: deploy | ||||||
|  |     if: branch = master AND type = push | ||||||
|  |  | ||||||
|  | # job control | ||||||
| jobs: | jobs: | ||||||
|   include: |  | ||||||
|   # native testing |   # native testing | ||||||
|     - stage: test |   - &x86 | ||||||
|  |     stage: test | ||||||
|     env: |     env: | ||||||
|         - STAGE=test |  | ||||||
|       - NAME=littlefs-x86 |       - NAME=littlefs-x86 | ||||||
|  |     install: *install-common | ||||||
|  |     script: [*test-example, *report-size] | ||||||
|  |   - {<<: *x86, script: [*test-default,          *report-size]} | ||||||
|  |   - {<<: *x86, script: [*test-nor,              *report-size]} | ||||||
|  |   - {<<: *x86, script: [*test-emmc,             *report-size]} | ||||||
|  |   - {<<: *x86, script: [*test-nand,             *report-size]} | ||||||
|  |   - {<<: *x86, script: [*test-no-intrinsics,    *report-size]} | ||||||
|  |   - {<<: *x86, script: [*test-no-inline,        *report-size]} | ||||||
|  |   - {<<: *x86, script: [*test-byte-writes,      *report-size]} | ||||||
|  |   - {<<: *x86, script: [*test-block-cycles,     *report-size]} | ||||||
|  |   - {<<: *x86, script: [*test-odd-block-count,  *report-size]} | ||||||
|  |   - {<<: *x86, script: [*test-odd-block-size,   *report-size]} | ||||||
|  |  | ||||||
|   # cross-compile with ARM (thumb mode) |   # cross-compile with ARM (thumb mode) | ||||||
|     - stage: test |   - &arm | ||||||
|  |     stage: test | ||||||
|     env: |     env: | ||||||
|         - STAGE=test |  | ||||||
|       - NAME=littlefs-arm |       - NAME=littlefs-arm | ||||||
|       - CC="arm-linux-gnueabi-gcc --static -mthumb" |       - CC="arm-linux-gnueabi-gcc --static -mthumb" | ||||||
|         - EXEC="qemu-arm" |       - TFLAGS="$TFLAGS --exec=qemu-arm" | ||||||
|     install: |     install: | ||||||
|  |       - *install-common | ||||||
|       - sudo apt-get install |       - sudo apt-get install | ||||||
|             gcc-arm-linux-gnueabi |             gcc-arm-linux-gnueabi | ||||||
|             libc6-dev-armel-cross |             libc6-dev-armel-cross | ||||||
|             qemu-user |             qemu-user | ||||||
|       - arm-linux-gnueabi-gcc --version |       - arm-linux-gnueabi-gcc --version | ||||||
|       - qemu-arm -version |       - qemu-arm -version | ||||||
|  |     script: [*test-example, *report-size] | ||||||
|     # cross-compile with PowerPC |   - {<<: *arm, script: [*test-default,          *report-size]} | ||||||
|     - stage: test |   - {<<: *arm, script: [*test-nor,              *report-size]} | ||||||
|       env: |   - {<<: *arm, script: [*test-emmc,             *report-size]} | ||||||
|         - STAGE=test |   - {<<: *arm, script: [*test-nand,             *report-size]} | ||||||
|         - NAME=littlefs-powerpc |   - {<<: *arm, script: [*test-no-intrinsics,    *report-size]} | ||||||
|         - CC="powerpc-linux-gnu-gcc --static" |   - {<<: *arm, script: [*test-no-inline,        *report-size]} | ||||||
|         - EXEC="qemu-ppc" |   # it just takes way to long to run byte-level writes in qemu, | ||||||
|       install: |   # note this is still tested in the native tests | ||||||
|         - sudo apt-get install |   #- {<<: *arm, script: [*test-byte-writes,      *report-size]} | ||||||
|               gcc-powerpc-linux-gnu |   - {<<: *arm, script: [*test-block-cycles,     *report-size]} | ||||||
|               libc6-dev-powerpc-cross |   - {<<: *arm, script: [*test-odd-block-count,  *report-size]} | ||||||
|               qemu-user |   - {<<: *arm, script: [*test-odd-block-size,   *report-size]} | ||||||
|         - powerpc-linux-gnu-gcc --version |  | ||||||
|         - qemu-ppc -version |  | ||||||
|  |  | ||||||
|   # cross-compile with MIPS |   # cross-compile with MIPS | ||||||
|     - stage: test |   - &mips | ||||||
|  |     stage: test | ||||||
|     env: |     env: | ||||||
|         - STAGE=test |  | ||||||
|       - NAME=littlefs-mips |       - NAME=littlefs-mips | ||||||
|       - CC="mips-linux-gnu-gcc --static" |       - CC="mips-linux-gnu-gcc --static" | ||||||
|         - EXEC="qemu-mips" |       - TFLAGS="$TFLAGS --exec=qemu-mips" | ||||||
|     install: |     install: | ||||||
|  |       - *install-common | ||||||
|       - sudo apt-get install |       - sudo apt-get install | ||||||
|             gcc-mips-linux-gnu |             gcc-mips-linux-gnu | ||||||
|             libc6-dev-mips-cross |             libc6-dev-mips-cross | ||||||
|             qemu-user |             qemu-user | ||||||
|       - mips-linux-gnu-gcc --version |       - mips-linux-gnu-gcc --version | ||||||
|       - qemu-mips -version |       - qemu-mips -version | ||||||
|  |     script: [*test-example, *report-size] | ||||||
|  |   - {<<: *mips, script: [*test-default,          *report-size]} | ||||||
|  |   - {<<: *mips, script: [*test-nor,              *report-size]} | ||||||
|  |   - {<<: *mips, script: [*test-emmc,             *report-size]} | ||||||
|  |   - {<<: *mips, script: [*test-nand,             *report-size]} | ||||||
|  |   - {<<: *mips, script: [*test-no-intrinsics,    *report-size]} | ||||||
|  |   - {<<: *mips, script: [*test-no-inline,        *report-size]} | ||||||
|  |   # it just takes way to long to run byte-level writes in qemu, | ||||||
|  |   # note this is still tested in the native tests | ||||||
|  |   #- {<<: *mips, script: [*test-byte-writes,      *report-size]} | ||||||
|  |   - {<<: *mips, script: [*test-block-cycles,     *report-size]} | ||||||
|  |   - {<<: *mips, script: [*test-odd-block-count,  *report-size]} | ||||||
|  |   - {<<: *mips, script: [*test-odd-block-size,   *report-size]} | ||||||
|  |  | ||||||
|  |   # cross-compile with PowerPC | ||||||
|  |   - &powerpc | ||||||
|  |     stage: test | ||||||
|  |     env: | ||||||
|  |       - NAME=littlefs-powerpc | ||||||
|  |       - CC="powerpc-linux-gnu-gcc --static" | ||||||
|  |       - TFLAGS="$TFLAGS --exec=qemu-ppc" | ||||||
|  |     install: | ||||||
|  |       - *install-common | ||||||
|  |       - sudo apt-get install | ||||||
|  |             gcc-powerpc-linux-gnu | ||||||
|  |             libc6-dev-powerpc-cross | ||||||
|  |             qemu-user | ||||||
|  |       - powerpc-linux-gnu-gcc --version | ||||||
|  |       - qemu-ppc -version | ||||||
|  |     script: [*test-example, *report-size] | ||||||
|  |   - {<<: *powerpc, script: [*test-default,          *report-size]} | ||||||
|  |   - {<<: *powerpc, script: [*test-nor,              *report-size]} | ||||||
|  |   - {<<: *powerpc, script: [*test-emmc,             *report-size]} | ||||||
|  |   - {<<: *powerpc, script: [*test-nand,             *report-size]} | ||||||
|  |   - {<<: *powerpc, script: [*test-no-intrinsics,    *report-size]} | ||||||
|  |   - {<<: *powerpc, script: [*test-no-inline,        *report-size]} | ||||||
|  |   # it just takes way to long to run byte-level writes in qemu, | ||||||
|  |   # note this is still tested in the native tests | ||||||
|  |   #- {<<: *powerpc, script: [*test-byte-writes,      *report-size]} | ||||||
|  |   - {<<: *powerpc, script: [*test-block-cycles,     *report-size]} | ||||||
|  |   - {<<: *powerpc, script: [*test-odd-block-count,  *report-size]} | ||||||
|  |   - {<<: *powerpc, script: [*test-odd-block-size,   *report-size]} | ||||||
|  |  | ||||||
|  |   # test under valgrind, checking for memory errors | ||||||
|  |   - &valgrind | ||||||
|  |     stage: test | ||||||
|  |     env: | ||||||
|  |       - NAME=littlefs-valgrind | ||||||
|  |     install: | ||||||
|  |       - *install-common | ||||||
|  |       - sudo apt-get install valgrind | ||||||
|  |       - valgrind --version | ||||||
|  |     script: | ||||||
|  |       - make test TFLAGS+="-k --valgrind" | ||||||
|  |  | ||||||
|   # self-host with littlefs-fuse for fuzz test |   # self-host with littlefs-fuse for fuzz test | ||||||
|   - stage: test |   - stage: test | ||||||
|     env: |     env: | ||||||
|         - STAGE=test |  | ||||||
|       - NAME=littlefs-fuse |       - NAME=littlefs-fuse | ||||||
|     if: branch !~ -prefix$ |     if: branch !~ -prefix$ | ||||||
|     install: |     install: | ||||||
|  |       - *install-common | ||||||
|       - sudo apt-get install libfuse-dev |       - sudo apt-get install libfuse-dev | ||||||
|       - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 |       - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 | ||||||
|       - fusermount -V |       - fusermount -V | ||||||
|       - gcc --version |       - gcc --version | ||||||
|       before_script: |  | ||||||
|       # setup disk for littlefs-fuse |       # setup disk for littlefs-fuse | ||||||
|       - rm -rf littlefs-fuse/littlefs/* |       - rm -rf littlefs-fuse/littlefs/* | ||||||
|       - cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs |       - cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs | ||||||
|  |  | ||||||
|       - mkdir mount |       - mkdir mount | ||||||
|       - sudo chmod a+rw /dev/loop0 |       - sudo chmod a+rw /dev/loop0 | ||||||
|         - dd if=/dev/zero bs=512 count=4096 of=disk |       - dd if=/dev/zero bs=512 count=128K of=disk | ||||||
|       - losetup /dev/loop0 disk |       - losetup /dev/loop0 disk | ||||||
|     script: |     script: | ||||||
|       # self-host test |       # self-host test | ||||||
| @@ -149,28 +241,28 @@ jobs: | |||||||
|       - cd mount/littlefs |       - cd mount/littlefs | ||||||
|       - stat . |       - stat . | ||||||
|       - ls -flh |       - ls -flh | ||||||
|         - make -B test_dirs test_files QUIET=1 |       - make -B test | ||||||
|  |  | ||||||
|     # self-host with littlefs-fuse for fuzz test |   # test migration using littlefs-fuse | ||||||
|   - stage: test |   - stage: test | ||||||
|     env: |     env: | ||||||
|         - STAGE=test |  | ||||||
|       - NAME=littlefs-migration |       - NAME=littlefs-migration | ||||||
|     if: branch !~ -prefix$ |     if: branch !~ -prefix$ | ||||||
|     install: |     install: | ||||||
|  |       - *install-common | ||||||
|       - sudo apt-get install libfuse-dev |       - sudo apt-get install libfuse-dev | ||||||
|       - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 v2 |       - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 v2 | ||||||
|       - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v1 v1 |       - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v1 v1 | ||||||
|       - fusermount -V |       - fusermount -V | ||||||
|       - gcc --version |       - gcc --version | ||||||
|       before_script: |  | ||||||
|       # setup disk for littlefs-fuse |       # setup disk for littlefs-fuse | ||||||
|       - rm -rf v2/littlefs/* |       - rm -rf v2/littlefs/* | ||||||
|       - cp -r $(git ls-tree --name-only HEAD) v2/littlefs |       - cp -r $(git ls-tree --name-only HEAD) v2/littlefs | ||||||
|  |  | ||||||
|       - mkdir mount |       - mkdir mount | ||||||
|       - sudo chmod a+rw /dev/loop0 |       - sudo chmod a+rw /dev/loop0 | ||||||
|         - dd if=/dev/zero bs=512 count=4096 of=disk |       - dd if=/dev/zero bs=512 count=128K of=disk | ||||||
|       - losetup /dev/loop0 disk |       - losetup /dev/loop0 disk | ||||||
|     script: |     script: | ||||||
|       # compile v1 and v2 |       # compile v1 and v2 | ||||||
| @@ -187,7 +279,7 @@ jobs: | |||||||
|       - cd mount/littlefs |       - cd mount/littlefs | ||||||
|       - stat . |       - stat . | ||||||
|       - ls -flh |       - ls -flh | ||||||
|         - make -B test_dirs test_files QUIET=1 |       - make -B test | ||||||
|  |  | ||||||
|       # attempt to migrate |       # attempt to migrate | ||||||
|       - cd ../.. |       - cd ../.. | ||||||
| @@ -201,12 +293,11 @@ jobs: | |||||||
|       - cd mount/littlefs |       - cd mount/littlefs | ||||||
|       - stat . |       - stat . | ||||||
|       - ls -flh |       - ls -flh | ||||||
|         - make -B test_dirs test_files QUIET=1 |       - make -B test | ||||||
|  |  | ||||||
|     # Automatically create releases |   # automatically create releases | ||||||
|   - stage: deploy |   - stage: deploy | ||||||
|     env: |     env: | ||||||
|         - STAGE=deploy |  | ||||||
|       - NAME=deploy |       - NAME=deploy | ||||||
|     script: |     script: | ||||||
|       - | |       - | | ||||||
| @@ -277,42 +368,62 @@ jobs: | |||||||
|             }" #" |             }" #" | ||||||
|         SCRIPT |         SCRIPT | ||||||
|  |  | ||||||
| # Manage statuses | # manage statuses | ||||||
| before_install: | before_install: | ||||||
|   - | |   - | | ||||||
|  |     # don't clobber other (not us) failures | ||||||
|  |     if ! curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||||
|  |         | jq -e ".statuses[] | select( | ||||||
|  |             .context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\" and | ||||||
|  |             .state == \"failure\" and | ||||||
|  |             (.target_url | endswith(\"$TRAVIS_JOB_NUMBER\") | not))" | ||||||
|  |     then | ||||||
|         curl -u "$GEKY_BOT_STATUSES" -X POST \ |         curl -u "$GEKY_BOT_STATUSES" -X POST \ | ||||||
|             https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ |             https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||||
|             -d "{ |             -d "{ | ||||||
|             \"context\": \"$STAGE/$NAME\", |                 \"context\": \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\", | ||||||
|                 \"state\": \"pending\", |                 \"state\": \"pending\", | ||||||
|                 \"description\": \"${STATUS:-In progress}\", |                 \"description\": \"${STATUS:-In progress}\", | ||||||
|             \"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\" |                 \"target_url\": \"$TRAVIS_JOB_WEB_URL#$TRAVIS_JOB_NUMBER\" | ||||||
|             }" |             }" | ||||||
|  |     fi | ||||||
|  |  | ||||||
| after_failure: | after_failure: | ||||||
|   - | |   - | | ||||||
|  |     # don't clobber other (not us) failures | ||||||
|  |     if ! curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||||
|  |         | jq -e ".statuses[] | select( | ||||||
|  |             .context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\" and | ||||||
|  |             .state == \"failure\" and | ||||||
|  |             (.target_url | endswith(\"$TRAVIS_JOB_NUMBER\") | not))" | ||||||
|  |     then | ||||||
|         curl -u "$GEKY_BOT_STATUSES" -X POST \ |         curl -u "$GEKY_BOT_STATUSES" -X POST \ | ||||||
|             https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ |             https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||||
|             -d "{ |             -d "{ | ||||||
|             \"context\": \"$STAGE/$NAME\", |                 \"context\": \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\", | ||||||
|                 \"state\": \"failure\", |                 \"state\": \"failure\", | ||||||
|                 \"description\": \"${STATUS:-Failed}\", |                 \"description\": \"${STATUS:-Failed}\", | ||||||
|             \"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\" |                 \"target_url\": \"$TRAVIS_JOB_WEB_URL#$TRAVIS_JOB_NUMBER\" | ||||||
|             }" |             }" | ||||||
|  |     fi | ||||||
|  |  | ||||||
| after_success: | after_success: | ||||||
|   - | |   - | | ||||||
|  |     # don't clobber other (not us) failures | ||||||
|  |     # only update if we were last job to mark in progress, | ||||||
|  |     # this isn't perfect but is probably good enough | ||||||
|  |     if ! curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||||
|  |         | jq -e ".statuses[] | select( | ||||||
|  |             .context == \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\" and | ||||||
|  |             (.state == \"failure\" or .state == \"pending\") and | ||||||
|  |             (.target_url | endswith(\"$TRAVIS_JOB_NUMBER\") | not))" | ||||||
|  |     then | ||||||
|         curl -u "$GEKY_BOT_STATUSES" -X POST \ |         curl -u "$GEKY_BOT_STATUSES" -X POST \ | ||||||
|             https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ |             https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \ | ||||||
|             -d "{ |             -d "{ | ||||||
|             \"context\": \"$STAGE/$NAME\", |                 \"context\": \"${TRAVIS_BUILD_STAGE_NAME,,}/$NAME\", | ||||||
|                 \"state\": \"success\", |                 \"state\": \"success\", | ||||||
|                 \"description\": \"${STATUS:-Passed}\", |                 \"description\": \"${STATUS:-Passed}\", | ||||||
|             \"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\" |                 \"target_url\": \"$TRAVIS_JOB_WEB_URL#$TRAVIS_JOB_NUMBER\" | ||||||
|             }" |             }" | ||||||
|  |     fi | ||||||
| # Job control |  | ||||||
| stages: |  | ||||||
|     - name: test |  | ||||||
|     - name: deploy |  | ||||||
|       if: branch = master AND type = push |  | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								Makefile
									
									
									
									
									
								
							| @@ -7,15 +7,11 @@ CC ?= gcc | |||||||
| AR ?= ar | AR ?= ar | ||||||
| SIZE ?= size | SIZE ?= size | ||||||
|  |  | ||||||
| SRC += $(wildcard *.c emubd/*.c) | SRC += $(wildcard *.c bd/*.c) | ||||||
| OBJ := $(SRC:.c=.o) | OBJ := $(SRC:.c=.o) | ||||||
| DEP := $(SRC:.c=.d) | DEP := $(SRC:.c=.d) | ||||||
| ASM := $(SRC:.c=.s) | ASM := $(SRC:.c=.s) | ||||||
|  |  | ||||||
| TEST := $(patsubst tests/%.sh,%,$(wildcard tests/test_*)) |  | ||||||
|  |  | ||||||
| SHELL = /bin/bash -o pipefail |  | ||||||
|  |  | ||||||
| ifdef DEBUG | ifdef DEBUG | ||||||
| override CFLAGS += -O0 -g3 | override CFLAGS += -O0 -g3 | ||||||
| else | else | ||||||
| @@ -33,6 +29,10 @@ override CFLAGS += -Wextra -Wshadow -Wjump-misses-init -Wundef | |||||||
| # Remove missing-field-initializers because of GCC bug | # Remove missing-field-initializers because of GCC bug | ||||||
| override CFLAGS += -Wno-missing-field-initializers | override CFLAGS += -Wno-missing-field-initializers | ||||||
|  |  | ||||||
|  | ifdef VERBOSE | ||||||
|  | override TFLAGS += -v | ||||||
|  | endif | ||||||
|  |  | ||||||
|  |  | ||||||
| all: $(TARGET) | all: $(TARGET) | ||||||
|  |  | ||||||
| @@ -41,30 +41,11 @@ asm: $(ASM) | |||||||
| size: $(OBJ) | size: $(OBJ) | ||||||
| 	$(SIZE) -t $^ | 	$(SIZE) -t $^ | ||||||
|  |  | ||||||
| .SUFFIXES: | test: | ||||||
| test: \ | 	./scripts/test.py $(TFLAGS) | ||||||
| 	test_format \ | .SECONDEXPANSION: | ||||||
| 	test_dirs \ | test%: tests/test$$(firstword $$(subst \#, ,%)).toml | ||||||
| 	test_files \ | 	./scripts/test.py $@ $(TFLAGS) | ||||||
| 	test_seek \ |  | ||||||
| 	test_truncate \ |  | ||||||
| 	test_entries \ |  | ||||||
| 	test_interspersed \ |  | ||||||
| 	test_alloc \ |  | ||||||
| 	test_paths \ |  | ||||||
| 	test_attrs \ |  | ||||||
| 	test_move \ |  | ||||||
| 	test_orphan \ |  | ||||||
| 	test_relocations \ |  | ||||||
| 	test_corrupt |  | ||||||
| 	@rm test.c |  | ||||||
| test_%: tests/test_%.sh |  | ||||||
|  |  | ||||||
| ifdef QUIET |  | ||||||
| 	@./$< | sed -nu '/^[-=]/p' |  | ||||||
| else |  | ||||||
| 	./$< |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| -include $(DEP) | -include $(DEP) | ||||||
|  |  | ||||||
| @@ -85,3 +66,4 @@ clean: | |||||||
| 	rm -f $(OBJ) | 	rm -f $(OBJ) | ||||||
| 	rm -f $(DEP) | 	rm -f $(DEP) | ||||||
| 	rm -f $(ASM) | 	rm -f $(ASM) | ||||||
|  | 	rm -f tests/*.toml.* | ||||||
|   | |||||||
| @@ -115,6 +115,9 @@ the filesystem until sync or close is called on the file. | |||||||
|  |  | ||||||
| ## Other notes | ## Other notes | ||||||
|  |  | ||||||
|  | Littlefs is written in C, and specifically should compile with any compiler | ||||||
|  | that conforms to the `C99` standard. | ||||||
|  |  | ||||||
| All littlefs calls have the potential to return a negative error code. The | All littlefs calls have the potential to return a negative error code. The | ||||||
| errors can be either one of those found in the `enum lfs2_error` in | errors can be either one of those found in the `enum lfs2_error` in | ||||||
| [lfs2.h](lfs2.h), or an error returned by the user's block device operations. | [lfs2.h](lfs2.h), or an error returned by the user's block device operations. | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								SPEC.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								SPEC.md
									
									
									
									
									
								
							| @@ -289,7 +289,7 @@ Layout of the name tag: | |||||||
| ``` | ``` | ||||||
|         tag                          data |         tag                          data | ||||||
| [--      32      --][---        variable length        ---] | [--      32      --][---        variable length        ---] | ||||||
| [1| 3| 8 | 10 | 10 ][---            (size)             ---] | [1| 3| 8 | 10 | 10 ][---          (size * 8)           ---] | ||||||
|  ^  ^  ^    ^    ^- size                   ^- file name |  ^  ^  ^    ^    ^- size                   ^- file name | ||||||
|  |  |  |    '------ id |  |  |  |    '------ id | ||||||
|  |  |  '----------- file type |  |  |  '----------- file type | ||||||
| @@ -470,7 +470,7 @@ Layout of the inline-struct tag: | |||||||
| ``` | ``` | ||||||
|         tag                          data |         tag                          data | ||||||
| [--      32      --][---        variable length        ---] | [--      32      --][---        variable length        ---] | ||||||
| [1|- 11 -| 10 | 10 ][---            (size)             ---] | [1|- 11 -| 10 | 10 ][---           (size * 8)          ---] | ||||||
|  ^    ^     ^    ^- size                    ^- inline data |  ^    ^     ^    ^- size                    ^- inline data | ||||||
|  |    |     '------ id |  |    |     '------ id | ||||||
|  |    '------------ type (0x201) |  |    '------------ type (0x201) | ||||||
| @@ -556,7 +556,7 @@ Layout of the user-attr tag: | |||||||
| ``` | ``` | ||||||
|         tag                          data |         tag                          data | ||||||
| [--      32      --][---        variable length        ---] | [--      32      --][---        variable length        ---] | ||||||
| [1| 3| 8 | 10 | 10 ][---            (size)             ---] | [1| 3| 8 | 10 | 10 ][---           (size * 8)          ---] | ||||||
|  ^  ^  ^    ^    ^- size                    ^- attr data |  ^  ^  ^    ^    ^- size                    ^- attr data | ||||||
|  |  |  |    '------ id |  |  |  |    '------ id | ||||||
|  |  |  '----------- attr type |  |  |  '----------- attr type | ||||||
| @@ -764,9 +764,9 @@ Layout of the CRC tag: | |||||||
| ``` | ``` | ||||||
|         tag                                    data |         tag                                    data | ||||||
| [--      32      --][--      32      --|---        variable length        ---] | [--      32      --][--      32      --|---        variable length        ---] | ||||||
| [1| 3| 8 | 10 | 10 ][--      32      --|---            (size)             ---] | [1| 3| 8 | 10 | 10 ][--      32      --|---        (size * 8 - 32)        ---] | ||||||
|  ^  ^  ^    ^    ^            ^- crc                             ^- padding |  ^  ^  ^    ^    ^            ^- crc                             ^- padding | ||||||
|  |  |  |    |    '- size (12) |  |  |  |    |    '- size | ||||||
|  |  |  |    '------ id (0x3ff) |  |  |  |    '------ id (0x3ff) | ||||||
|  |  |  '----------- valid state |  |  |  '----------- valid state | ||||||
|  |  '-------------- type1 (0x5) |  |  '-------------- type1 (0x5) | ||||||
|   | |||||||
							
								
								
									
										205
									
								
								bd/lfs2_filebd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								bd/lfs2_filebd.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | |||||||
|  | /* | ||||||
|  |  * Block device emulated in a file | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||||
|  |  * SPDX-License-Identifier: BSD-3-Clause | ||||||
|  |  */ | ||||||
|  | #include "bd/lfs2_filebd.h" | ||||||
|  |  | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <errno.h> | ||||||
|  |  | ||||||
|  | int lfs2_filebd_createcfg(const struct lfs2_config *cfg, const char *path, | ||||||
|  |         const struct lfs2_filebd_config *bdcfg) { | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_createcfg(%p {.context=%p, " | ||||||
|  |                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||||
|  |                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||||
|  |                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||||
|  |                 "\"%s\", " | ||||||
|  |                 "%p {.erase_value=%"PRId32"})", | ||||||
|  |             (void*)cfg, cfg->context, | ||||||
|  |             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||||
|  |             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||||
|  |             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, | ||||||
|  |             path, (void*)bdcfg, bdcfg->erase_value); | ||||||
|  |     lfs2_filebd_t *bd = cfg->context; | ||||||
|  |     bd->cfg = bdcfg; | ||||||
|  |  | ||||||
|  |     // open file | ||||||
|  |     bd->fd = open(path, O_RDWR | O_CREAT, 0666); | ||||||
|  |     if (bd->fd < 0) { | ||||||
|  |         int err = -errno; | ||||||
|  |         LFS2_FILEBD_TRACE("lfs2_filebd_createcfg -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_createcfg -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_filebd_create(const struct lfs2_config *cfg, const char *path) { | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_create(%p {.context=%p, " | ||||||
|  |                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||||
|  |                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||||
|  |                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||||
|  |                 "\"%s\")", | ||||||
|  |             (void*)cfg, cfg->context, | ||||||
|  |             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||||
|  |             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||||
|  |             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, | ||||||
|  |             path); | ||||||
|  |     static const struct lfs2_filebd_config defaults = {.erase_value=-1}; | ||||||
|  |     int err = lfs2_filebd_createcfg(cfg, path, &defaults); | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_create -> %d", err); | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_filebd_destroy(const struct lfs2_config *cfg) { | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_destroy(%p)", (void*)cfg); | ||||||
|  |     lfs2_filebd_t *bd = cfg->context; | ||||||
|  |     int err = close(bd->fd); | ||||||
|  |     if (err < 0) { | ||||||
|  |         err = -errno; | ||||||
|  |         LFS2_FILEBD_TRACE("lfs2_filebd_destroy -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_destroy -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_filebd_read(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, void *buffer, lfs2_size_t size) { | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_read(%p, " | ||||||
|  |                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||||
|  |             (void*)cfg, block, off, buffer, size); | ||||||
|  |     lfs2_filebd_t *bd = cfg->context; | ||||||
|  |  | ||||||
|  |     // check if read is valid | ||||||
|  |     LFS2_ASSERT(off  % cfg->read_size == 0); | ||||||
|  |     LFS2_ASSERT(size % cfg->read_size == 0); | ||||||
|  |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|  |     // zero for reproducability (in case file is truncated) | ||||||
|  |     if (bd->cfg->erase_value != -1) { | ||||||
|  |         memset(buffer, bd->cfg->erase_value, size); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // read | ||||||
|  |     off_t res1 = lseek(bd->fd, | ||||||
|  |             (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||||
|  |     if (res1 < 0) { | ||||||
|  |         int err = -errno; | ||||||
|  |         LFS2_FILEBD_TRACE("lfs2_filebd_read -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ssize_t res2 = read(bd->fd, buffer, size); | ||||||
|  |     if (res2 < 0) { | ||||||
|  |         int err = -errno; | ||||||
|  |         LFS2_FILEBD_TRACE("lfs2_filebd_read -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_read -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_filebd_prog(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, const void *buffer, lfs2_size_t size) { | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||||
|  |             (void*)cfg, block, off, buffer, size); | ||||||
|  |     lfs2_filebd_t *bd = cfg->context; | ||||||
|  |  | ||||||
|  |     // check if write is valid | ||||||
|  |     LFS2_ASSERT(off  % cfg->prog_size == 0); | ||||||
|  |     LFS2_ASSERT(size % cfg->prog_size == 0); | ||||||
|  |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|  |     // check that data was erased? only needed for testing | ||||||
|  |     if (bd->cfg->erase_value != -1) { | ||||||
|  |         off_t res1 = lseek(bd->fd, | ||||||
|  |                 (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||||
|  |         if (res1 < 0) { | ||||||
|  |             int err = -errno; | ||||||
|  |             LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err); | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (lfs2_off_t i = 0; i < size; i++) { | ||||||
|  |             uint8_t c; | ||||||
|  |             ssize_t res2 = read(bd->fd, &c, 1); | ||||||
|  |             if (res2 < 0) { | ||||||
|  |                 int err = -errno; | ||||||
|  |                 LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err); | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             LFS2_ASSERT(c == bd->cfg->erase_value); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // program data | ||||||
|  |     off_t res1 = lseek(bd->fd, | ||||||
|  |             (off_t)block*cfg->block_size + (off_t)off, SEEK_SET); | ||||||
|  |     if (res1 < 0) { | ||||||
|  |         int err = -errno; | ||||||
|  |         LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ssize_t res2 = write(bd->fd, buffer, size); | ||||||
|  |     if (res2 < 0) { | ||||||
|  |         int err = -errno; | ||||||
|  |         LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_prog -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_filebd_erase(const struct lfs2_config *cfg, lfs2_block_t block) { | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||||
|  |     lfs2_filebd_t *bd = cfg->context; | ||||||
|  |  | ||||||
|  |     // check if erase is valid | ||||||
|  |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|  |     // erase, only needed for testing | ||||||
|  |     if (bd->cfg->erase_value != -1) { | ||||||
|  |         off_t res1 = lseek(bd->fd, (off_t)block*cfg->block_size, SEEK_SET); | ||||||
|  |         if (res1 < 0) { | ||||||
|  |             int err = -errno; | ||||||
|  |             LFS2_FILEBD_TRACE("lfs2_filebd_erase -> %d", err); | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (lfs2_off_t i = 0; i < cfg->block_size; i++) { | ||||||
|  |             ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg->erase_value}, 1); | ||||||
|  |             if (res2 < 0) { | ||||||
|  |                 int err = -errno; | ||||||
|  |                 LFS2_FILEBD_TRACE("lfs2_filebd_erase -> %d", err); | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_erase -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_filebd_sync(const struct lfs2_config *cfg) { | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_sync(%p)", (void*)cfg); | ||||||
|  |     // file sync | ||||||
|  |     lfs2_filebd_t *bd = cfg->context; | ||||||
|  |     int err = fsync(bd->fd); | ||||||
|  |     if (err) { | ||||||
|  |         err = -errno; | ||||||
|  |         LFS2_FILEBD_TRACE("lfs2_filebd_sync -> %d", 0); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LFS2_FILEBD_TRACE("lfs2_filebd_sync -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										73
									
								
								bd/lfs2_filebd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								bd/lfs2_filebd.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | |||||||
|  | /* | ||||||
|  |  * Block device emulated in a file | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||||
|  |  * SPDX-License-Identifier: BSD-3-Clause | ||||||
|  |  */ | ||||||
|  | #ifndef LFS2_FILEBD_H | ||||||
|  | #define LFS2_FILEBD_H | ||||||
|  |  | ||||||
|  | #include "lfs2.h" | ||||||
|  | #include "lfs2_util.h" | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" | ||||||
|  | { | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Block device specific tracing | ||||||
|  | #ifdef LFS2_FILEBD_YES_TRACE | ||||||
|  | #define LFS2_FILEBD_TRACE(...) LFS2_TRACE(__VA_ARGS__) | ||||||
|  | #else | ||||||
|  | #define LFS2_FILEBD_TRACE(...) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | // filebd config (optional) | ||||||
|  | struct lfs2_filebd_config { | ||||||
|  |     // 8-bit erase value to use for simulating erases. -1 does not simulate | ||||||
|  |     // erases, which can speed up testing by avoiding all the extra block-device | ||||||
|  |     // operations to store the erase value. | ||||||
|  |     int32_t erase_value; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // filebd state | ||||||
|  | typedef struct lfs2_filebd { | ||||||
|  |     int fd; | ||||||
|  |     const struct lfs2_filebd_config *cfg; | ||||||
|  | } lfs2_filebd_t; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Create a file block device using the geometry in lfs2_config | ||||||
|  | int lfs2_filebd_create(const struct lfs2_config *cfg, const char *path); | ||||||
|  | int lfs2_filebd_createcfg(const struct lfs2_config *cfg, const char *path, | ||||||
|  |         const struct lfs2_filebd_config *bdcfg); | ||||||
|  |  | ||||||
|  | // Clean up memory associated with block device | ||||||
|  | int lfs2_filebd_destroy(const struct lfs2_config *cfg); | ||||||
|  |  | ||||||
|  | // Read a block | ||||||
|  | int lfs2_filebd_read(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, void *buffer, lfs2_size_t size); | ||||||
|  |  | ||||||
|  | // Program a block | ||||||
|  | // | ||||||
|  | // The block must have previously been erased. | ||||||
|  | int lfs2_filebd_prog(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, const void *buffer, lfs2_size_t size); | ||||||
|  |  | ||||||
|  | // Erase a block | ||||||
|  | // | ||||||
|  | // A block must be erased before being programmed. The | ||||||
|  | // state of an erased block is undefined. | ||||||
|  | int lfs2_filebd_erase(const struct lfs2_config *cfg, lfs2_block_t block); | ||||||
|  |  | ||||||
|  | // Sync the block device | ||||||
|  | int lfs2_filebd_sync(const struct lfs2_config *cfg); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } /* extern "C" */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										140
									
								
								bd/lfs2_rambd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								bd/lfs2_rambd.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | |||||||
|  | /* | ||||||
|  |  * Block device emulated in RAM | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||||
|  |  * SPDX-License-Identifier: BSD-3-Clause | ||||||
|  |  */ | ||||||
|  | #include "bd/lfs2_rambd.h" | ||||||
|  |  | ||||||
|  | int lfs2_rambd_createcfg(const struct lfs2_config *cfg, | ||||||
|  |         const struct lfs2_rambd_config *bdcfg) { | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_createcfg(%p {.context=%p, " | ||||||
|  |                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||||
|  |                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||||
|  |                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||||
|  |                 "%p {.erase_value=%"PRId32", .buffer=%p})", | ||||||
|  |             (void*)cfg, cfg->context, | ||||||
|  |             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||||
|  |             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||||
|  |             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, | ||||||
|  |             (void*)bdcfg, bdcfg->erase_value, bdcfg->buffer); | ||||||
|  |     lfs2_rambd_t *bd = cfg->context; | ||||||
|  |     bd->cfg = bdcfg; | ||||||
|  |  | ||||||
|  |     // allocate buffer? | ||||||
|  |     if (bd->cfg->buffer) { | ||||||
|  |         bd->buffer = bd->cfg->buffer; | ||||||
|  |     } else { | ||||||
|  |         bd->buffer = lfs2_malloc(cfg->block_size * cfg->block_count); | ||||||
|  |         if (!bd->buffer) { | ||||||
|  |             LFS2_RAMBD_TRACE("lfs2_rambd_createcfg -> %d", LFS2_ERR_NOMEM); | ||||||
|  |             return LFS2_ERR_NOMEM; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // zero for reproducability? | ||||||
|  |     if (bd->cfg->erase_value != -1) { | ||||||
|  |         memset(bd->buffer, bd->cfg->erase_value, | ||||||
|  |                 cfg->block_size * cfg->block_count); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_createcfg -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_rambd_create(const struct lfs2_config *cfg) { | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_create(%p {.context=%p, " | ||||||
|  |                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||||
|  |                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||||
|  |                 ".block_size=%"PRIu32", .block_count=%"PRIu32"})", | ||||||
|  |             (void*)cfg, cfg->context, | ||||||
|  |             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||||
|  |             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||||
|  |             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count); | ||||||
|  |     static const struct lfs2_rambd_config defaults = {.erase_value=-1}; | ||||||
|  |     int err = lfs2_rambd_createcfg(cfg, &defaults); | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_create -> %d", err); | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_rambd_destroy(const struct lfs2_config *cfg) { | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_destroy(%p)", (void*)cfg); | ||||||
|  |     // clean up memory | ||||||
|  |     lfs2_rambd_t *bd = cfg->context; | ||||||
|  |     if (!bd->cfg->buffer) { | ||||||
|  |         lfs2_free(bd->buffer); | ||||||
|  |     } | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_destroy -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_rambd_read(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, void *buffer, lfs2_size_t size) { | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_read(%p, " | ||||||
|  |                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||||
|  |             (void*)cfg, block, off, buffer, size); | ||||||
|  |     lfs2_rambd_t *bd = cfg->context; | ||||||
|  |  | ||||||
|  |     // check if read is valid | ||||||
|  |     LFS2_ASSERT(off  % cfg->read_size == 0); | ||||||
|  |     LFS2_ASSERT(size % cfg->read_size == 0); | ||||||
|  |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|  |     // read data | ||||||
|  |     memcpy(buffer, &bd->buffer[block*cfg->block_size + off], size); | ||||||
|  |  | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_read -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_rambd_prog(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, const void *buffer, lfs2_size_t size) { | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_prog(%p, " | ||||||
|  |                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||||
|  |             (void*)cfg, block, off, buffer, size); | ||||||
|  |     lfs2_rambd_t *bd = cfg->context; | ||||||
|  |  | ||||||
|  |     // check if write is valid | ||||||
|  |     LFS2_ASSERT(off  % cfg->prog_size == 0); | ||||||
|  |     LFS2_ASSERT(size % cfg->prog_size == 0); | ||||||
|  |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|  |     // check that data was erased? only needed for testing | ||||||
|  |     if (bd->cfg->erase_value != -1) { | ||||||
|  |         for (lfs2_off_t i = 0; i < size; i++) { | ||||||
|  |             LFS2_ASSERT(bd->buffer[block*cfg->block_size + off + i] == | ||||||
|  |                     bd->cfg->erase_value); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // program data | ||||||
|  |     memcpy(&bd->buffer[block*cfg->block_size + off], buffer, size); | ||||||
|  |  | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_prog -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_rambd_erase(const struct lfs2_config *cfg, lfs2_block_t block) { | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||||
|  |     lfs2_rambd_t *bd = cfg->context; | ||||||
|  |  | ||||||
|  |     // check if erase is valid | ||||||
|  |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|  |     // erase, only needed for testing | ||||||
|  |     if (bd->cfg->erase_value != -1) { | ||||||
|  |         memset(&bd->buffer[block*cfg->block_size], | ||||||
|  |                 bd->cfg->erase_value, cfg->block_size); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_erase -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_rambd_sync(const struct lfs2_config *cfg) { | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_sync(%p)", (void*)cfg); | ||||||
|  |     // sync does nothing because we aren't backed by anything real | ||||||
|  |     (void)cfg; | ||||||
|  |     LFS2_RAMBD_TRACE("lfs2_rambd_sync -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										75
									
								
								bd/lfs2_rambd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								bd/lfs2_rambd.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | |||||||
|  | /* | ||||||
|  |  * Block device emulated in RAM | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||||
|  |  * SPDX-License-Identifier: BSD-3-Clause | ||||||
|  |  */ | ||||||
|  | #ifndef LFS2_RAMBD_H | ||||||
|  | #define LFS2_RAMBD_H | ||||||
|  |  | ||||||
|  | #include "lfs2.h" | ||||||
|  | #include "lfs2_util.h" | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" | ||||||
|  | { | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Block device specific tracing | ||||||
|  | #ifdef LFS2_RAMBD_YES_TRACE | ||||||
|  | #define LFS2_RAMBD_TRACE(...) LFS2_TRACE(__VA_ARGS__) | ||||||
|  | #else | ||||||
|  | #define LFS2_RAMBD_TRACE(...) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | // rambd config (optional) | ||||||
|  | struct lfs2_rambd_config { | ||||||
|  |     // 8-bit erase value to simulate erasing with. -1 indicates no erase | ||||||
|  |     // occurs, which is still a valid block device | ||||||
|  |     int32_t erase_value; | ||||||
|  |  | ||||||
|  |     // Optional statically allocated buffer for the block device. | ||||||
|  |     void *buffer; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // rambd state | ||||||
|  | typedef struct lfs2_rambd { | ||||||
|  |     uint8_t *buffer; | ||||||
|  |     const struct lfs2_rambd_config *cfg; | ||||||
|  | } lfs2_rambd_t; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Create a RAM block device using the geometry in lfs2_config | ||||||
|  | int lfs2_rambd_create(const struct lfs2_config *cfg); | ||||||
|  | int lfs2_rambd_createcfg(const struct lfs2_config *cfg, | ||||||
|  |         const struct lfs2_rambd_config *bdcfg); | ||||||
|  |  | ||||||
|  | // Clean up memory associated with block device | ||||||
|  | int lfs2_rambd_destroy(const struct lfs2_config *cfg); | ||||||
|  |  | ||||||
|  | // Read a block | ||||||
|  | int lfs2_rambd_read(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, void *buffer, lfs2_size_t size); | ||||||
|  |  | ||||||
|  | // Program a block | ||||||
|  | // | ||||||
|  | // The block must have previously been erased. | ||||||
|  | int lfs2_rambd_prog(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, const void *buffer, lfs2_size_t size); | ||||||
|  |  | ||||||
|  | // Erase a block | ||||||
|  | // | ||||||
|  | // A block must be erased before being programmed. The | ||||||
|  | // state of an erased block is undefined. | ||||||
|  | int lfs2_rambd_erase(const struct lfs2_config *cfg, lfs2_block_t block); | ||||||
|  |  | ||||||
|  | // Sync the block device | ||||||
|  | int lfs2_rambd_sync(const struct lfs2_config *cfg); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } /* extern "C" */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										302
									
								
								bd/lfs2_testbd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								bd/lfs2_testbd.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,302 @@ | |||||||
|  | /* | ||||||
|  |  * Testing block device, wraps filebd and rambd while providing a bunch | ||||||
|  |  * of hooks for testing littlefs in various conditions. | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||||
|  |  * SPDX-License-Identifier: BSD-3-Clause | ||||||
|  |  */ | ||||||
|  | #include "bd/lfs2_testbd.h" | ||||||
|  |  | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int lfs2_testbd_createcfg(const struct lfs2_config *cfg, const char *path, | ||||||
|  |         const struct lfs2_testbd_config *bdcfg) { | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_createcfg(%p {.context=%p, " | ||||||
|  |                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||||
|  |                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||||
|  |                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||||
|  |                 "\"%s\", " | ||||||
|  |                 "%p {.erase_value=%"PRId32", .erase_cycles=%"PRIu32", " | ||||||
|  |                 ".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", " | ||||||
|  |                 ".buffer=%p, .wear_buffer=%p})", | ||||||
|  |             (void*)cfg, cfg->context, | ||||||
|  |             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||||
|  |             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||||
|  |             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, | ||||||
|  |             path, (void*)bdcfg, bdcfg->erase_value, bdcfg->erase_cycles, | ||||||
|  |             bdcfg->badblock_behavior, bdcfg->power_cycles, | ||||||
|  |             bdcfg->buffer, bdcfg->wear_buffer); | ||||||
|  |     lfs2_testbd_t *bd = cfg->context; | ||||||
|  |     bd->cfg = bdcfg; | ||||||
|  |  | ||||||
|  |     // setup testing things | ||||||
|  |     bd->persist = path; | ||||||
|  |     bd->power_cycles = bd->cfg->power_cycles; | ||||||
|  |  | ||||||
|  |     if (bd->cfg->erase_cycles) { | ||||||
|  |         if (bd->cfg->wear_buffer) { | ||||||
|  |             bd->wear = bd->cfg->wear_buffer; | ||||||
|  |         } else { | ||||||
|  |             bd->wear = lfs2_malloc(sizeof(lfs2_testbd_wear_t)*cfg->block_count); | ||||||
|  |             if (!bd->wear) { | ||||||
|  |                 LFS2_TESTBD_TRACE("lfs2_testbd_createcfg -> %d", LFS2_ERR_NOMEM); | ||||||
|  |                 return LFS2_ERR_NOMEM; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         memset(bd->wear, 0, sizeof(lfs2_testbd_wear_t) * cfg->block_count); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // create underlying block device | ||||||
|  |     if (bd->persist) { | ||||||
|  |         bd->u.file.cfg = (struct lfs2_filebd_config){ | ||||||
|  |             .erase_value = bd->cfg->erase_value, | ||||||
|  |         }; | ||||||
|  |         int err = lfs2_filebd_createcfg(cfg, path, &bd->u.file.cfg); | ||||||
|  |         LFS2_TESTBD_TRACE("lfs2_testbd_createcfg -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } else { | ||||||
|  |         bd->u.ram.cfg = (struct lfs2_rambd_config){ | ||||||
|  |             .erase_value = bd->cfg->erase_value, | ||||||
|  |             .buffer = bd->cfg->buffer, | ||||||
|  |         }; | ||||||
|  |         int err = lfs2_rambd_createcfg(cfg, &bd->u.ram.cfg); | ||||||
|  |         LFS2_TESTBD_TRACE("lfs2_testbd_createcfg -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_testbd_create(const struct lfs2_config *cfg, const char *path) { | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_create(%p {.context=%p, " | ||||||
|  |                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " | ||||||
|  |                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " | ||||||
|  |                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, " | ||||||
|  |                 "\"%s\")", | ||||||
|  |             (void*)cfg, cfg->context, | ||||||
|  |             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, | ||||||
|  |             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, | ||||||
|  |             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, | ||||||
|  |             path); | ||||||
|  |     static const struct lfs2_testbd_config defaults = {.erase_value=-1}; | ||||||
|  |     int err = lfs2_testbd_createcfg(cfg, path, &defaults); | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_create -> %d", err); | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_testbd_destroy(const struct lfs2_config *cfg) { | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_destroy(%p)", (void*)cfg); | ||||||
|  |     lfs2_testbd_t *bd = cfg->context; | ||||||
|  |     if (bd->cfg->erase_cycles && !bd->cfg->wear_buffer) { | ||||||
|  |         lfs2_free(bd->wear); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (bd->persist) { | ||||||
|  |         int err = lfs2_filebd_destroy(cfg); | ||||||
|  |         LFS2_TESTBD_TRACE("lfs2_testbd_destroy -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } else { | ||||||
|  |         int err = lfs2_rambd_destroy(cfg); | ||||||
|  |         LFS2_TESTBD_TRACE("lfs2_testbd_destroy -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Internal mapping to block devices /// | ||||||
|  | static int lfs2_testbd_rawread(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, void *buffer, lfs2_size_t size) { | ||||||
|  |     lfs2_testbd_t *bd = cfg->context; | ||||||
|  |     if (bd->persist) { | ||||||
|  |         return lfs2_filebd_read(cfg, block, off, buffer, size); | ||||||
|  |     } else { | ||||||
|  |         return lfs2_rambd_read(cfg, block, off, buffer, size); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int lfs2_testbd_rawprog(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, const void *buffer, lfs2_size_t size) { | ||||||
|  |     lfs2_testbd_t *bd = cfg->context; | ||||||
|  |     if (bd->persist) { | ||||||
|  |         return lfs2_filebd_prog(cfg, block, off, buffer, size); | ||||||
|  |     } else { | ||||||
|  |         return lfs2_rambd_prog(cfg, block, off, buffer, size); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int lfs2_testbd_rawerase(const struct lfs2_config *cfg, | ||||||
|  |         lfs2_block_t block) { | ||||||
|  |     lfs2_testbd_t *bd = cfg->context; | ||||||
|  |     if (bd->persist) { | ||||||
|  |         return lfs2_filebd_erase(cfg, block); | ||||||
|  |     } else { | ||||||
|  |         return lfs2_rambd_erase(cfg, block); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int lfs2_testbd_rawsync(const struct lfs2_config *cfg) { | ||||||
|  |     lfs2_testbd_t *bd = cfg->context; | ||||||
|  |     if (bd->persist) { | ||||||
|  |         return lfs2_filebd_sync(cfg); | ||||||
|  |     } else { | ||||||
|  |         return lfs2_rambd_sync(cfg); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// block device API /// | ||||||
|  | int lfs2_testbd_read(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, void *buffer, lfs2_size_t size) { | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_read(%p, " | ||||||
|  |                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||||
|  |             (void*)cfg, block, off, buffer, size); | ||||||
|  |     lfs2_testbd_t *bd = cfg->context; | ||||||
|  |  | ||||||
|  |     // check if read is valid | ||||||
|  |     LFS2_ASSERT(off  % cfg->read_size == 0); | ||||||
|  |     LFS2_ASSERT(size % cfg->read_size == 0); | ||||||
|  |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|  |     // block bad? | ||||||
|  |     if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles && | ||||||
|  |             bd->cfg->badblock_behavior == LFS2_TESTBD_BADBLOCK_READERROR) { | ||||||
|  |         LFS2_TESTBD_TRACE("lfs2_testbd_read -> %d", LFS2_ERR_CORRUPT); | ||||||
|  |         return LFS2_ERR_CORRUPT; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // read | ||||||
|  |     int err = lfs2_testbd_rawread(cfg, block, off, buffer, size); | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_read -> %d", err); | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_testbd_prog(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, const void *buffer, lfs2_size_t size) { | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_prog(%p, " | ||||||
|  |                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", | ||||||
|  |             (void*)cfg, block, off, buffer, size); | ||||||
|  |     lfs2_testbd_t *bd = cfg->context; | ||||||
|  |  | ||||||
|  |     // check if write is valid | ||||||
|  |     LFS2_ASSERT(off  % cfg->prog_size == 0); | ||||||
|  |     LFS2_ASSERT(size % cfg->prog_size == 0); | ||||||
|  |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|  |     // block bad? | ||||||
|  |     if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles) { | ||||||
|  |         if (bd->cfg->badblock_behavior == | ||||||
|  |                 LFS2_TESTBD_BADBLOCK_PROGERROR) { | ||||||
|  |             LFS2_TESTBD_TRACE("lfs2_testbd_prog -> %d", LFS2_ERR_CORRUPT); | ||||||
|  |             return LFS2_ERR_CORRUPT; | ||||||
|  |         } else if (bd->cfg->badblock_behavior == | ||||||
|  |                 LFS2_TESTBD_BADBLOCK_PROGNOOP || | ||||||
|  |                 bd->cfg->badblock_behavior == | ||||||
|  |                 LFS2_TESTBD_BADBLOCK_ERASENOOP) { | ||||||
|  |             LFS2_TESTBD_TRACE("lfs2_testbd_prog -> %d", 0); | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // prog | ||||||
|  |     int err = lfs2_testbd_rawprog(cfg, block, off, buffer, size); | ||||||
|  |     if (err) { | ||||||
|  |         LFS2_TESTBD_TRACE("lfs2_testbd_prog -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // lose power? | ||||||
|  |     if (bd->power_cycles > 0) { | ||||||
|  |         bd->power_cycles -= 1; | ||||||
|  |         if (bd->power_cycles == 0) { | ||||||
|  |             // sync to make sure we persist the last changes | ||||||
|  |             assert(lfs2_testbd_rawsync(cfg) == 0); | ||||||
|  |             // simulate power loss | ||||||
|  |             exit(33); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_prog -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_testbd_erase(const struct lfs2_config *cfg, lfs2_block_t block) { | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); | ||||||
|  |     lfs2_testbd_t *bd = cfg->context; | ||||||
|  |  | ||||||
|  |     // check if erase is valid | ||||||
|  |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|  |     // block bad? | ||||||
|  |     if (bd->cfg->erase_cycles) { | ||||||
|  |         if (bd->wear[block] >= bd->cfg->erase_cycles) { | ||||||
|  |             if (bd->cfg->badblock_behavior == | ||||||
|  |                     LFS2_TESTBD_BADBLOCK_ERASEERROR) { | ||||||
|  |                 LFS2_TESTBD_TRACE("lfs2_testbd_erase -> %d", LFS2_ERR_CORRUPT); | ||||||
|  |                 return LFS2_ERR_CORRUPT; | ||||||
|  |             } else if (bd->cfg->badblock_behavior == | ||||||
|  |                     LFS2_TESTBD_BADBLOCK_ERASENOOP) { | ||||||
|  |                 LFS2_TESTBD_TRACE("lfs2_testbd_erase -> %d", 0); | ||||||
|  |                 return 0; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             // mark wear | ||||||
|  |             bd->wear[block] += 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // erase | ||||||
|  |     int err = lfs2_testbd_rawerase(cfg, block); | ||||||
|  |     if (err) { | ||||||
|  |         LFS2_TESTBD_TRACE("lfs2_testbd_erase -> %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // lose power? | ||||||
|  |     if (bd->power_cycles > 0) { | ||||||
|  |         bd->power_cycles -= 1; | ||||||
|  |         if (bd->power_cycles == 0) { | ||||||
|  |             // sync to make sure we persist the last changes | ||||||
|  |             assert(lfs2_testbd_rawsync(cfg) == 0); | ||||||
|  |             // simulate power loss | ||||||
|  |             exit(33); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_prog -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_testbd_sync(const struct lfs2_config *cfg) { | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_sync(%p)", (void*)cfg); | ||||||
|  |     int err = lfs2_testbd_rawsync(cfg); | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_sync -> %d", err); | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// simulated wear operations /// | ||||||
|  | lfs2_testbd_swear_t lfs2_testbd_getwear(const struct lfs2_config *cfg, | ||||||
|  |         lfs2_block_t block) { | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block); | ||||||
|  |     lfs2_testbd_t *bd = cfg->context; | ||||||
|  |  | ||||||
|  |     // check if block is valid | ||||||
|  |     LFS2_ASSERT(bd->cfg->erase_cycles); | ||||||
|  |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_getwear -> %"PRIu32, bd->wear[block]); | ||||||
|  |     return bd->wear[block]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs2_testbd_setwear(const struct lfs2_config *cfg, | ||||||
|  |         lfs2_block_t block, lfs2_testbd_wear_t wear) { | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block); | ||||||
|  |     lfs2_testbd_t *bd = cfg->context; | ||||||
|  |  | ||||||
|  |     // check if block is valid | ||||||
|  |     LFS2_ASSERT(bd->cfg->erase_cycles); | ||||||
|  |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|  |     bd->wear[block] = wear; | ||||||
|  |  | ||||||
|  |     LFS2_TESTBD_TRACE("lfs2_testbd_setwear -> %d", 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										141
									
								
								bd/lfs2_testbd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								bd/lfs2_testbd.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | |||||||
|  | /* | ||||||
|  |  * Testing block device, wraps filebd and rambd while providing a bunch | ||||||
|  |  * of hooks for testing littlefs in various conditions. | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2017, Arm Limited. All rights reserved. | ||||||
|  |  * SPDX-License-Identifier: BSD-3-Clause | ||||||
|  |  */ | ||||||
|  | #ifndef LFS2_TESTBD_H | ||||||
|  | #define LFS2_TESTBD_H | ||||||
|  |  | ||||||
|  | #include "lfs2.h" | ||||||
|  | #include "lfs2_util.h" | ||||||
|  | #include "bd/lfs2_rambd.h" | ||||||
|  | #include "bd/lfs2_filebd.h" | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" | ||||||
|  | { | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Block device specific tracing | ||||||
|  | #ifdef LFS2_TESTBD_YES_TRACE | ||||||
|  | #define LFS2_TESTBD_TRACE(...) LFS2_TRACE(__VA_ARGS__) | ||||||
|  | #else | ||||||
|  | #define LFS2_TESTBD_TRACE(...) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | // Mode determining how "bad blocks" behave during testing. This simulates | ||||||
|  | // some real-world circumstances such as progs not sticking (prog-noop), | ||||||
|  | // a readonly disk (erase-noop), and ECC failures (read-error). | ||||||
|  | // | ||||||
|  | // Not that read-noop is not allowed. Read _must_ return a consistent (but | ||||||
|  | // may be arbitrary) value on every read. | ||||||
|  | enum lfs2_testbd_badblock_behavior { | ||||||
|  |     LFS2_TESTBD_BADBLOCK_PROGERROR, | ||||||
|  |     LFS2_TESTBD_BADBLOCK_ERASEERROR, | ||||||
|  |     LFS2_TESTBD_BADBLOCK_READERROR, | ||||||
|  |     LFS2_TESTBD_BADBLOCK_PROGNOOP, | ||||||
|  |     LFS2_TESTBD_BADBLOCK_ERASENOOP, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Type for measuring wear | ||||||
|  | typedef uint32_t lfs2_testbd_wear_t; | ||||||
|  | typedef int32_t  lfs2_testbd_swear_t; | ||||||
|  |  | ||||||
|  | // testbd config, this is required for testing | ||||||
|  | struct lfs2_testbd_config { | ||||||
|  |     // 8-bit erase value to use for simulating erases. -1 does not simulate | ||||||
|  |     // erases, which can speed up testing by avoiding all the extra block-device | ||||||
|  |     // operations to store the erase value. | ||||||
|  |     int32_t erase_value; | ||||||
|  |  | ||||||
|  |     // Number of erase cycles before a block becomes "bad". The exact behavior | ||||||
|  |     // of bad blocks is controlled by the badblock_mode. | ||||||
|  |     uint32_t erase_cycles; | ||||||
|  |  | ||||||
|  |     // The mode determining how bad blocks fail | ||||||
|  |     uint8_t badblock_behavior; | ||||||
|  |  | ||||||
|  |     // Number of write operations (erase/prog) before forcefully killing | ||||||
|  |     // the program with exit. Simulates power-loss. 0 disables. | ||||||
|  |     uint32_t power_cycles; | ||||||
|  |  | ||||||
|  |     // Optional buffer for RAM block device. | ||||||
|  |     void *buffer; | ||||||
|  |  | ||||||
|  |     // Optional buffer for wear | ||||||
|  |     void *wear_buffer; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // testbd state | ||||||
|  | typedef struct lfs2_testbd { | ||||||
|  |     union { | ||||||
|  |         struct { | ||||||
|  |             lfs2_filebd_t bd; | ||||||
|  |             struct lfs2_filebd_config cfg; | ||||||
|  |         } file; | ||||||
|  |         struct { | ||||||
|  |             lfs2_rambd_t bd; | ||||||
|  |             struct lfs2_rambd_config cfg; | ||||||
|  |         } ram; | ||||||
|  |     } u; | ||||||
|  |  | ||||||
|  |     bool persist; | ||||||
|  |     uint32_t power_cycles; | ||||||
|  |     lfs2_testbd_wear_t *wear; | ||||||
|  |  | ||||||
|  |     const struct lfs2_testbd_config *cfg; | ||||||
|  | } lfs2_testbd_t; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// Block device API /// | ||||||
|  |  | ||||||
|  | // Create a test block device using the geometry in lfs2_config | ||||||
|  | // | ||||||
|  | // Note that filebd is used if a path is provided, if path is NULL | ||||||
|  | // testbd will use rambd which can be much faster. | ||||||
|  | int lfs2_testbd_create(const struct lfs2_config *cfg, const char *path); | ||||||
|  | int lfs2_testbd_createcfg(const struct lfs2_config *cfg, const char *path, | ||||||
|  |         const struct lfs2_testbd_config *bdcfg); | ||||||
|  |  | ||||||
|  | // Clean up memory associated with block device | ||||||
|  | int lfs2_testbd_destroy(const struct lfs2_config *cfg); | ||||||
|  |  | ||||||
|  | // Read a block | ||||||
|  | int lfs2_testbd_read(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, void *buffer, lfs2_size_t size); | ||||||
|  |  | ||||||
|  | // Program a block | ||||||
|  | // | ||||||
|  | // The block must have previously been erased. | ||||||
|  | int lfs2_testbd_prog(const struct lfs2_config *cfg, lfs2_block_t block, | ||||||
|  |         lfs2_off_t off, const void *buffer, lfs2_size_t size); | ||||||
|  |  | ||||||
|  | // Erase a block | ||||||
|  | // | ||||||
|  | // A block must be erased before being programmed. The | ||||||
|  | // state of an erased block is undefined. | ||||||
|  | int lfs2_testbd_erase(const struct lfs2_config *cfg, lfs2_block_t block); | ||||||
|  |  | ||||||
|  | // Sync the block device | ||||||
|  | int lfs2_testbd_sync(const struct lfs2_config *cfg); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// Additional extended API for driving test features /// | ||||||
|  |  | ||||||
|  | // Get simulated wear on a given block | ||||||
|  | lfs2_testbd_swear_t lfs2_testbd_getwear(const struct lfs2_config *cfg, | ||||||
|  |         lfs2_block_t block); | ||||||
|  |  | ||||||
|  | // Manually set simulated wear on a given block | ||||||
|  | int lfs2_testbd_setwear(const struct lfs2_config *cfg, | ||||||
|  |         lfs2_block_t block, lfs2_testbd_wear_t wear); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } /* extern "C" */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif | ||||||
| @@ -1,414 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Block device emulated on standard files |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. |  | ||||||
|  * SPDX-License-Identifier: BSD-3-Clause |  | ||||||
|  */ |  | ||||||
| #include "emubd/lfs2_emubd.h" |  | ||||||
|  |  | ||||||
| #include <errno.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <limits.h> |  | ||||||
| #include <sys/stat.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <assert.h> |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <inttypes.h> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // Emulated block device utils |  | ||||||
| static inline void lfs2_emubd_tole32(lfs2_emubd_t *emu) { |  | ||||||
|     emu->cfg.read_size     = lfs2_tole32(emu->cfg.read_size); |  | ||||||
|     emu->cfg.prog_size     = lfs2_tole32(emu->cfg.prog_size); |  | ||||||
|     emu->cfg.block_size    = lfs2_tole32(emu->cfg.block_size); |  | ||||||
|     emu->cfg.block_count   = lfs2_tole32(emu->cfg.block_count); |  | ||||||
|  |  | ||||||
|     emu->stats.read_count  = lfs2_tole32(emu->stats.read_count); |  | ||||||
|     emu->stats.prog_count  = lfs2_tole32(emu->stats.prog_count); |  | ||||||
|     emu->stats.erase_count = lfs2_tole32(emu->stats.erase_count); |  | ||||||
|  |  | ||||||
|     for (unsigned i = 0; i < sizeof(emu->history.blocks) / |  | ||||||
|             sizeof(emu->history.blocks[0]); i++) { |  | ||||||
|         emu->history.blocks[i] = lfs2_tole32(emu->history.blocks[i]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void lfs2_emubd_fromle32(lfs2_emubd_t *emu) { |  | ||||||
|     emu->cfg.read_size     = lfs2_fromle32(emu->cfg.read_size); |  | ||||||
|     emu->cfg.prog_size     = lfs2_fromle32(emu->cfg.prog_size); |  | ||||||
|     emu->cfg.block_size    = lfs2_fromle32(emu->cfg.block_size); |  | ||||||
|     emu->cfg.block_count   = lfs2_fromle32(emu->cfg.block_count); |  | ||||||
|  |  | ||||||
|     emu->stats.read_count  = lfs2_fromle32(emu->stats.read_count); |  | ||||||
|     emu->stats.prog_count  = lfs2_fromle32(emu->stats.prog_count); |  | ||||||
|     emu->stats.erase_count = lfs2_fromle32(emu->stats.erase_count); |  | ||||||
|  |  | ||||||
|     for (unsigned i = 0; i < sizeof(emu->history.blocks) / |  | ||||||
|             sizeof(emu->history.blocks[0]); i++) { |  | ||||||
|         emu->history.blocks[i] = lfs2_fromle32(emu->history.blocks[i]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // Block device emulated on existing filesystem |  | ||||||
| int lfs2_emubd_create(const struct lfs2_config *cfg, const char *path) { |  | ||||||
|     LFS2_TRACE("lfs2_emubd_create(%p {.context=%p, " |  | ||||||
|                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " |  | ||||||
|                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " |  | ||||||
|                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, \"%s\")", |  | ||||||
|             (void*)cfg, cfg->context, |  | ||||||
|             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, |  | ||||||
|             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, |  | ||||||
|             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, |  | ||||||
|             path); |  | ||||||
|     lfs2_emubd_t *emu = cfg->context; |  | ||||||
|     emu->cfg.read_size   = cfg->read_size; |  | ||||||
|     emu->cfg.prog_size   = cfg->prog_size; |  | ||||||
|     emu->cfg.block_size  = cfg->block_size; |  | ||||||
|     emu->cfg.block_count = cfg->block_count; |  | ||||||
|  |  | ||||||
|     // Allocate buffer for creating children files |  | ||||||
|     size_t pathlen = strlen(path); |  | ||||||
|     emu->path = malloc(pathlen + 1 + LFS2_NAME_MAX + 1); |  | ||||||
|     if (!emu->path) { |  | ||||||
|         int err = -ENOMEM; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_create -> %"PRId32, err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     strcpy(emu->path, path); |  | ||||||
|     emu->path[pathlen] = '/'; |  | ||||||
|     emu->child = &emu->path[pathlen+1]; |  | ||||||
|     memset(emu->child, '\0', LFS2_NAME_MAX+1); |  | ||||||
|  |  | ||||||
|     // Create directory if it doesn't exist |  | ||||||
|     int err = mkdir(path, 0777); |  | ||||||
|     if (err && errno != EEXIST) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_create -> %"PRId32, err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Load stats to continue incrementing |  | ||||||
|     snprintf(emu->child, LFS2_NAME_MAX, ".stats"); |  | ||||||
|     FILE *f = fopen(emu->path, "r"); |  | ||||||
|     if (!f) { |  | ||||||
|         memset(&emu->stats, LFS2_EMUBD_ERASE_VALUE, sizeof(emu->stats)); |  | ||||||
|     } else { |  | ||||||
|         size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); |  | ||||||
|         lfs2_emubd_fromle32(emu); |  | ||||||
|         if (res < 1) { |  | ||||||
|             err = -errno; |  | ||||||
|             LFS2_TRACE("lfs2_emubd_create -> %"PRId32, err); |  | ||||||
|             fclose(f); |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         err = fclose(f); |  | ||||||
|         if (err) { |  | ||||||
|             err = -errno; |  | ||||||
|             LFS2_TRACE("lfs2_emubd_create -> %"PRId32, err); |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Load history |  | ||||||
|     snprintf(emu->child, LFS2_NAME_MAX, ".history"); |  | ||||||
|     f = fopen(emu->path, "r"); |  | ||||||
|     if (!f) { |  | ||||||
|         memset(&emu->history, 0, sizeof(emu->history)); |  | ||||||
|     } else { |  | ||||||
|         size_t res = fread(&emu->history, sizeof(emu->history), 1, f); |  | ||||||
|         lfs2_emubd_fromle32(emu); |  | ||||||
|         if (res < 1) { |  | ||||||
|             err = -errno; |  | ||||||
|             LFS2_TRACE("lfs2_emubd_create -> %"PRId32, err); |  | ||||||
|             fclose(f); |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         err = fclose(f); |  | ||||||
|         if (err) { |  | ||||||
|             err = -errno; |  | ||||||
|             LFS2_TRACE("lfs2_emubd_create -> %"PRId32, err); |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     LFS2_TRACE("lfs2_emubd_create -> %"PRId32, 0); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void lfs2_emubd_destroy(const struct lfs2_config *cfg) { |  | ||||||
|     LFS2_TRACE("lfs2_emubd_destroy(%p)", (void*)cfg); |  | ||||||
|     lfs2_emubd_sync(cfg); |  | ||||||
|  |  | ||||||
|     lfs2_emubd_t *emu = cfg->context; |  | ||||||
|     free(emu->path); |  | ||||||
|     LFS2_TRACE("lfs2_emubd_destroy -> %s", "void"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int lfs2_emubd_read(const struct lfs2_config *cfg, lfs2_block_t block, |  | ||||||
|         lfs2_off_t off, void *buffer, lfs2_size_t size) { |  | ||||||
|     LFS2_TRACE("lfs2_emubd_read(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", |  | ||||||
|             (void*)cfg, block, off, buffer, size); |  | ||||||
|     lfs2_emubd_t *emu = cfg->context; |  | ||||||
|     uint8_t *data = buffer; |  | ||||||
|  |  | ||||||
|     // Check if read is valid |  | ||||||
|     assert(off  % cfg->read_size == 0); |  | ||||||
|     assert(size % cfg->read_size == 0); |  | ||||||
|     assert(block < cfg->block_count); |  | ||||||
|  |  | ||||||
|     // Zero out buffer for debugging |  | ||||||
|     memset(data, 0, size); |  | ||||||
|  |  | ||||||
|     // Read data |  | ||||||
|     snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block); |  | ||||||
|  |  | ||||||
|     FILE *f = fopen(emu->path, "rb"); |  | ||||||
|     if (!f && errno != ENOENT) { |  | ||||||
|         int err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_read -> %d", err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (f) { |  | ||||||
|         int err = fseek(f, off, SEEK_SET); |  | ||||||
|         if (err) { |  | ||||||
|             err = -errno; |  | ||||||
|             LFS2_TRACE("lfs2_emubd_read -> %d", err); |  | ||||||
|             fclose(f); |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         size_t res = fread(data, 1, size, f); |  | ||||||
|         if (res < size && !feof(f)) { |  | ||||||
|             err = -errno; |  | ||||||
|             LFS2_TRACE("lfs2_emubd_read -> %d", err); |  | ||||||
|             fclose(f); |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         err = fclose(f); |  | ||||||
|         if (err) { |  | ||||||
|             err = -errno; |  | ||||||
|             LFS2_TRACE("lfs2_emubd_read -> %d", err); |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     emu->stats.read_count += size; |  | ||||||
|     LFS2_TRACE("lfs2_emubd_read -> %d", 0); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int lfs2_emubd_prog(const struct lfs2_config *cfg, lfs2_block_t block, |  | ||||||
|         lfs2_off_t off, const void *buffer, lfs2_size_t size) { |  | ||||||
|     LFS2_TRACE("lfs2_emubd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", |  | ||||||
|             (void*)cfg, block, off, buffer, size); |  | ||||||
|     lfs2_emubd_t *emu = cfg->context; |  | ||||||
|     const uint8_t *data = buffer; |  | ||||||
|  |  | ||||||
|     // Check if write is valid |  | ||||||
|     assert(off  % cfg->prog_size == 0); |  | ||||||
|     assert(size % cfg->prog_size == 0); |  | ||||||
|     assert(block < cfg->block_count); |  | ||||||
|  |  | ||||||
|     // Program data |  | ||||||
|     snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block); |  | ||||||
|  |  | ||||||
|     FILE *f = fopen(emu->path, "r+b"); |  | ||||||
|     if (!f) { |  | ||||||
|         int err = (errno == EACCES) ? 0 : -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_prog -> %d", err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Check that file was erased |  | ||||||
|     assert(f); |  | ||||||
|  |  | ||||||
|     int err = fseek(f, off, SEEK_SET); |  | ||||||
|     if (err) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_prog -> %d", err); |  | ||||||
|         fclose(f); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     size_t res = fwrite(data, 1, size, f); |  | ||||||
|     if (res < size) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_prog -> %d", err); |  | ||||||
|         fclose(f); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     err = fseek(f, off, SEEK_SET); |  | ||||||
|     if (err) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_prog -> %d", err); |  | ||||||
|         fclose(f); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     uint8_t dat; |  | ||||||
|     res = fread(&dat, 1, 1, f); |  | ||||||
|     if (res < 1) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_prog -> %d", err); |  | ||||||
|         fclose(f); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     err = fclose(f); |  | ||||||
|     if (err) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_prog -> %d", err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // update history and stats |  | ||||||
|     if (block != emu->history.blocks[0]) { |  | ||||||
|         memmove(&emu->history.blocks[1], &emu->history.blocks[0], |  | ||||||
|                 sizeof(emu->history) - sizeof(emu->history.blocks[0])); |  | ||||||
|         emu->history.blocks[0] = block; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     emu->stats.prog_count += size; |  | ||||||
|     LFS2_TRACE("lfs2_emubd_prog -> %d", 0); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int lfs2_emubd_erase(const struct lfs2_config *cfg, lfs2_block_t block) { |  | ||||||
|     LFS2_TRACE("lfs2_emubd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); |  | ||||||
|     lfs2_emubd_t *emu = cfg->context; |  | ||||||
|  |  | ||||||
|     // Check if erase is valid |  | ||||||
|     assert(block < cfg->block_count); |  | ||||||
|  |  | ||||||
|     // Erase the block |  | ||||||
|     snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block); |  | ||||||
|     struct stat st; |  | ||||||
|     int err = stat(emu->path, &st); |  | ||||||
|     if (err && errno != ENOENT) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_erase -> %d", err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!err && S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode)) { |  | ||||||
|         err = unlink(emu->path); |  | ||||||
|         if (err) { |  | ||||||
|             err = -errno; |  | ||||||
|             LFS2_TRACE("lfs2_emubd_erase -> %d", err); |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (err || (S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode))) { |  | ||||||
|         FILE *f = fopen(emu->path, "w"); |  | ||||||
|         if (!f) { |  | ||||||
|             err = -errno; |  | ||||||
|             LFS2_TRACE("lfs2_emubd_erase -> %d", err); |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         err = fclose(f); |  | ||||||
|         if (err) { |  | ||||||
|             err = -errno; |  | ||||||
|             LFS2_TRACE("lfs2_emubd_erase -> %d", err); |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     emu->stats.erase_count += cfg->block_size; |  | ||||||
|     LFS2_TRACE("lfs2_emubd_erase -> %d", 0); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int lfs2_emubd_sync(const struct lfs2_config *cfg) { |  | ||||||
|     LFS2_TRACE("lfs2_emubd_sync(%p)", (void*)cfg); |  | ||||||
|     lfs2_emubd_t *emu = cfg->context; |  | ||||||
|  |  | ||||||
|     // Just write out info/stats for later lookup |  | ||||||
|     snprintf(emu->child, LFS2_NAME_MAX, ".config"); |  | ||||||
|     FILE *f = fopen(emu->path, "w"); |  | ||||||
|     if (!f) { |  | ||||||
|         int err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_sync -> %d", err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_emubd_tole32(emu); |  | ||||||
|     size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f); |  | ||||||
|     lfs2_emubd_fromle32(emu); |  | ||||||
|     if (res < 1) { |  | ||||||
|         int err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_sync -> %d", err); |  | ||||||
|         fclose(f); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     int err = fclose(f); |  | ||||||
|     if (err) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_sync -> %d", err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     snprintf(emu->child, LFS2_NAME_MAX, ".stats"); |  | ||||||
|     f = fopen(emu->path, "w"); |  | ||||||
|     if (!f) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_sync -> %d", err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_emubd_tole32(emu); |  | ||||||
|     res = fwrite(&emu->stats, sizeof(emu->stats), 1, f); |  | ||||||
|     lfs2_emubd_fromle32(emu); |  | ||||||
|     if (res < 1) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_sync -> %d", err); |  | ||||||
|         fclose(f); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     err = fclose(f); |  | ||||||
|     if (err) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_sync -> %d", err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     snprintf(emu->child, LFS2_NAME_MAX, ".history"); |  | ||||||
|     f = fopen(emu->path, "w"); |  | ||||||
|     if (!f) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_sync -> %d", err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_emubd_tole32(emu); |  | ||||||
|     res = fwrite(&emu->history, sizeof(emu->history), 1, f); |  | ||||||
|     lfs2_emubd_fromle32(emu); |  | ||||||
|     if (res < 1) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_sync -> %d", err); |  | ||||||
|         fclose(f); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     err = fclose(f); |  | ||||||
|     if (err) { |  | ||||||
|         err = -errno; |  | ||||||
|         LFS2_TRACE("lfs2_emubd_sync -> %d", err); |  | ||||||
|         return err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     LFS2_TRACE("lfs2_emubd_sync -> %d", 0); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| @@ -1,79 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Block device emulated on standard files |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2017, Arm Limited. All rights reserved. |  | ||||||
|  * SPDX-License-Identifier: BSD-3-Clause |  | ||||||
|  */ |  | ||||||
| #ifndef LFS2_EMUBD_H |  | ||||||
| #define LFS2_EMUBD_H |  | ||||||
|  |  | ||||||
| #include "lfs2.h" |  | ||||||
| #include "lfs2_util.h" |  | ||||||
|  |  | ||||||
| #ifdef __cplusplus |  | ||||||
| extern "C" |  | ||||||
| { |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // Config options |  | ||||||
| #ifndef LFS2_EMUBD_ERASE_VALUE |  | ||||||
| #define LFS2_EMUBD_ERASE_VALUE 0x00 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // The emu bd state |  | ||||||
| typedef struct lfs2_emubd { |  | ||||||
|     char *path; |  | ||||||
|     char *child; |  | ||||||
|  |  | ||||||
|     struct { |  | ||||||
|         uint64_t read_count; |  | ||||||
|         uint64_t prog_count; |  | ||||||
|         uint64_t erase_count; |  | ||||||
|     } stats; |  | ||||||
|  |  | ||||||
|     struct { |  | ||||||
|         lfs2_block_t blocks[4]; |  | ||||||
|     } history; |  | ||||||
|  |  | ||||||
|     struct { |  | ||||||
|         uint32_t read_size; |  | ||||||
|         uint32_t prog_size; |  | ||||||
|         uint32_t block_size; |  | ||||||
|         uint32_t block_count; |  | ||||||
|     } cfg; |  | ||||||
| } lfs2_emubd_t; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // Create a block device using path for the directory to store blocks |  | ||||||
| int lfs2_emubd_create(const struct lfs2_config *cfg, const char *path); |  | ||||||
|  |  | ||||||
| // Clean up memory associated with emu block device |  | ||||||
| void lfs2_emubd_destroy(const struct lfs2_config *cfg); |  | ||||||
|  |  | ||||||
| // Read a block |  | ||||||
| int lfs2_emubd_read(const struct lfs2_config *cfg, lfs2_block_t block, |  | ||||||
|         lfs2_off_t off, void *buffer, lfs2_size_t size); |  | ||||||
|  |  | ||||||
| // Program a block |  | ||||||
| // |  | ||||||
| // The block must have previously been erased. |  | ||||||
| int lfs2_emubd_prog(const struct lfs2_config *cfg, lfs2_block_t block, |  | ||||||
|         lfs2_off_t off, const void *buffer, lfs2_size_t size); |  | ||||||
|  |  | ||||||
| // Erase a block |  | ||||||
| // |  | ||||||
| // A block must be erased before being programmed. The |  | ||||||
| // state of an erased block is undefined. |  | ||||||
| int lfs2_emubd_erase(const struct lfs2_config *cfg, lfs2_block_t block); |  | ||||||
|  |  | ||||||
| // Sync the block device |  | ||||||
| int lfs2_emubd_sync(const struct lfs2_config *cfg); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #ifdef __cplusplus |  | ||||||
| } /* extern "C" */ |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
							
								
								
									
										14
									
								
								lfs2.h
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								lfs2.h
									
									
									
									
									
								
							| @@ -21,7 +21,7 @@ extern "C" | |||||||
| // Software library version | // Software library version | ||||||
| // Major (top-nibble), incremented on backwards incompatible changes | // Major (top-nibble), incremented on backwards incompatible changes | ||||||
| // Minor (bottom-nibble), incremented on feature additions | // Minor (bottom-nibble), incremented on feature additions | ||||||
| #define LFS2_VERSION 0x00020001 | #define LFS2_VERSION 0x00020002 | ||||||
| #define LFS2_VERSION_MAJOR (0xffff & (LFS2_VERSION >> 16)) | #define LFS2_VERSION_MAJOR (0xffff & (LFS2_VERSION >> 16)) | ||||||
| #define LFS2_VERSION_MINOR (0xffff & (LFS2_VERSION >>  0)) | #define LFS2_VERSION_MINOR (0xffff & (LFS2_VERSION >>  0)) | ||||||
|  |  | ||||||
| @@ -355,6 +355,11 @@ typedef struct lfs2_superblock { | |||||||
|     lfs2_size_t attr_max; |     lfs2_size_t attr_max; | ||||||
| } lfs2_superblock_t; | } lfs2_superblock_t; | ||||||
|  |  | ||||||
|  | typedef struct lfs2_gstate { | ||||||
|  |     uint32_t tag; | ||||||
|  |     lfs2_block_t pair[2]; | ||||||
|  | } lfs2_gstate_t; | ||||||
|  |  | ||||||
| // The littlefs filesystem type | // The littlefs filesystem type | ||||||
| typedef struct lfs2 { | typedef struct lfs2 { | ||||||
|     lfs2_cache_t rcache; |     lfs2_cache_t rcache; | ||||||
| @@ -369,10 +374,9 @@ typedef struct lfs2 { | |||||||
|     } *mlist; |     } *mlist; | ||||||
|     uint32_t seed; |     uint32_t seed; | ||||||
|  |  | ||||||
|     struct lfs2_gstate { |     lfs2_gstate_t gstate; | ||||||
|         uint32_t tag; |     lfs2_gstate_t gdisk; | ||||||
|         lfs2_block_t pair[2]; |     lfs2_gstate_t gdelta; | ||||||
|     } gstate, gpending, gdelta; |  | ||||||
|  |  | ||||||
|     struct lfs2_free { |     struct lfs2_free { | ||||||
|         lfs2_block_t off; |         lfs2_block_t off; | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								lfs2_util.h
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								lfs2_util.h
									
									
									
									
									
								
							| @@ -50,31 +50,35 @@ extern "C" | |||||||
|  |  | ||||||
| // Logging functions | // Logging functions | ||||||
| #ifdef LFS2_YES_TRACE | #ifdef LFS2_YES_TRACE | ||||||
| #define LFS2_TRACE(fmt, ...) \ | #define LFS2_TRACE_(fmt, ...) \ | ||||||
|     printf("lfs2_trace:%d: " fmt "\n", __LINE__, __VA_ARGS__) |     printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||||
|  | #define LFS2_TRACE(...) LFS2_TRACE_(__VA_ARGS__, "") | ||||||
| #else | #else | ||||||
| #define LFS2_TRACE(fmt, ...) | #define LFS2_TRACE(...) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifndef LFS2_NO_DEBUG | #ifndef LFS2_NO_DEBUG | ||||||
| #define LFS2_DEBUG(fmt, ...) \ | #define LFS2_DEBUG_(fmt, ...) \ | ||||||
|     printf("lfs2_debug:%d: " fmt "\n", __LINE__, __VA_ARGS__) |     printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||||
|  | #define LFS2_DEBUG(...) LFS2_DEBUG_(__VA_ARGS__, "") | ||||||
| #else | #else | ||||||
| #define LFS2_DEBUG(fmt, ...) | #define LFS2_DEBUG(...) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifndef LFS2_NO_WARN | #ifndef LFS2_NO_WARN | ||||||
| #define LFS2_WARN(fmt, ...) \ | #define LFS2_WARN_(fmt, ...) \ | ||||||
|     printf("lfs2_warn:%d: " fmt "\n", __LINE__, __VA_ARGS__) |     printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||||
|  | #define LFS2_WARN(...) LFS2_WARN_(__VA_ARGS__, "") | ||||||
| #else | #else | ||||||
| #define LFS2_WARN(fmt, ...) | #define LFS2_WARN(...) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifndef LFS2_NO_ERROR | #ifndef LFS2_NO_ERROR | ||||||
| #define LFS2_ERROR(fmt, ...) \ | #define LFS2_ERROR_(fmt, ...) \ | ||||||
|     printf("lfs2_error:%d: " fmt "\n", __LINE__, __VA_ARGS__) |     printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) | ||||||
|  | #define LFS2_ERROR(...) LFS2_ERROR_(__VA_ARGS__, "") | ||||||
| #else | #else | ||||||
| #define LFS2_ERROR(fmt, ...) | #define LFS2_ERROR(...) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| // Runtime assertions | // Runtime assertions | ||||||
| @@ -107,7 +111,7 @@ static inline uint32_t lfs2_alignup(uint32_t a, uint32_t alignment) { | |||||||
|     return lfs2_aligndown(a + alignment-1, alignment); |     return lfs2_aligndown(a + alignment-1, alignment); | ||||||
| } | } | ||||||
|  |  | ||||||
| // Find the next smallest power of 2 less than or equal to a | // Find the smallest power of 2 greater than or equal to a | ||||||
| static inline uint32_t lfs2_npw2(uint32_t a) { | static inline uint32_t lfs2_npw2(uint32_t a) { | ||||||
| #if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) | #if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) | ||||||
|     return 32 - __builtin_clz(a-1); |     return 32 - __builtin_clz(a-1); | ||||||
|   | |||||||
| @@ -1,44 +0,0 @@ | |||||||
| #!/usr/bin/env python2 |  | ||||||
|  |  | ||||||
| import struct |  | ||||||
| import sys |  | ||||||
| import os |  | ||||||
| import argparse |  | ||||||
|  |  | ||||||
| def corrupt(block): |  | ||||||
|     with open(block, 'r+b') as file: |  | ||||||
|         # skip rev |  | ||||||
|         file.read(4) |  | ||||||
|  |  | ||||||
|         # go to last commit |  | ||||||
|         tag = 0xffffffff |  | ||||||
|         while True: |  | ||||||
|             try: |  | ||||||
|                 ntag, = struct.unpack('>I', file.read(4)) |  | ||||||
|             except struct.error: |  | ||||||
|                 break |  | ||||||
|  |  | ||||||
|             tag ^= ntag |  | ||||||
|             size = (tag & 0x3ff) if (tag & 0x3ff) != 0x3ff else 0 |  | ||||||
|             file.seek(size, os.SEEK_CUR) |  | ||||||
|  |  | ||||||
|         # lob off last 3 bytes |  | ||||||
|         file.seek(-(size + 3), os.SEEK_CUR) |  | ||||||
|         file.truncate() |  | ||||||
|  |  | ||||||
| def main(args): |  | ||||||
|     if args.n or not args.blocks: |  | ||||||
|         with open('blocks/.history', 'rb') as file: |  | ||||||
|             for i in range(int(args.n or 1)): |  | ||||||
|                 last, = struct.unpack('<I', file.read(4)) |  | ||||||
|                 args.blocks.append('blocks/%x' % last) |  | ||||||
|  |  | ||||||
|     for block in args.blocks: |  | ||||||
|         print 'corrupting %s' % block |  | ||||||
|         corrupt(block) |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": |  | ||||||
|     parser = argparse.ArgumentParser() |  | ||||||
|     parser.add_argument('-n') |  | ||||||
|     parser.add_argument('blocks', nargs='*') |  | ||||||
|     main(parser.parse_args()) |  | ||||||
							
								
								
									
										112
									
								
								scripts/debug.py
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								scripts/debug.py
									
									
									
									
									
								
							| @@ -1,112 +0,0 @@ | |||||||
| #!/usr/bin/env python2 |  | ||||||
|  |  | ||||||
| import struct |  | ||||||
| import binascii |  | ||||||
|  |  | ||||||
| TYPES = { |  | ||||||
|     (0x700, 0x400): 'splice', |  | ||||||
|     (0x7ff, 0x401): 'create', |  | ||||||
|     (0x7ff, 0x4ff): 'delete', |  | ||||||
|     (0x700, 0x000): 'name', |  | ||||||
|     (0x7ff, 0x001): 'name reg', |  | ||||||
|     (0x7ff, 0x002): 'name dir', |  | ||||||
|     (0x7ff, 0x0ff): 'name superblock', |  | ||||||
|     (0x700, 0x200): 'struct', |  | ||||||
|     (0x7ff, 0x200): 'struct dir', |  | ||||||
|     (0x7ff, 0x202): 'struct ctz', |  | ||||||
|     (0x7ff, 0x201): 'struct inline', |  | ||||||
|     (0x700, 0x300): 'userattr', |  | ||||||
|     (0x700, 0x600): 'tail', |  | ||||||
|     (0x7ff, 0x600): 'tail soft', |  | ||||||
|     (0x7ff, 0x601): 'tail hard', |  | ||||||
|     (0x700, 0x700): 'gstate', |  | ||||||
|     (0x7ff, 0x7ff): 'gstate move', |  | ||||||
|     (0x700, 0x500): 'crc', |  | ||||||
| } |  | ||||||
|  |  | ||||||
| def typeof(type): |  | ||||||
|     for prefix in range(12): |  | ||||||
|         mask = 0x7ff & ~((1 << prefix)-1) |  | ||||||
|         if (mask, type & mask) in TYPES: |  | ||||||
|             return TYPES[mask, type & mask] + ( |  | ||||||
|                 ' %0*x' % (prefix/4, type & ((1 << prefix)-1)) |  | ||||||
|                 if prefix else '') |  | ||||||
|     else: |  | ||||||
|         return '%02x' % type |  | ||||||
|  |  | ||||||
| def main(*blocks): |  | ||||||
|     # find most recent block |  | ||||||
|     file = None |  | ||||||
|     rev = None |  | ||||||
|     crc = None |  | ||||||
|     versions = [] |  | ||||||
|  |  | ||||||
|     for block in blocks: |  | ||||||
|         try: |  | ||||||
|             nfile = open(block, 'rb') |  | ||||||
|             ndata = nfile.read(4) |  | ||||||
|             ncrc = binascii.crc32(ndata) |  | ||||||
|             nrev, = struct.unpack('<I', ndata) |  | ||||||
|  |  | ||||||
|             assert rev != nrev |  | ||||||
|             if not file or ((rev - nrev) & 0x80000000): |  | ||||||
|                 file = nfile |  | ||||||
|                 rev = nrev |  | ||||||
|                 crc = ncrc |  | ||||||
|  |  | ||||||
|             versions.append((nrev, '%s (rev %d)' % (block, nrev))) |  | ||||||
|         except (IOError, struct.error): |  | ||||||
|             pass |  | ||||||
|  |  | ||||||
|     if not file: |  | ||||||
|         print 'Bad metadata pair {%s}' % ', '.join(blocks) |  | ||||||
|         return 1 |  | ||||||
|  |  | ||||||
|     print "--- %s ---" % ', '.join(v for _,v in sorted(versions, reverse=True)) |  | ||||||
|  |  | ||||||
|     # go through each tag, print useful information |  | ||||||
|     print "%-4s  %-8s  %-14s  %3s %4s  %s" % ( |  | ||||||
|         'off', 'tag', 'type', 'id', 'len', 'dump') |  | ||||||
|  |  | ||||||
|     tag = 0xffffffff |  | ||||||
|     off = 4 |  | ||||||
|     while True: |  | ||||||
|         try: |  | ||||||
|             data = file.read(4) |  | ||||||
|             crc = binascii.crc32(data, crc) |  | ||||||
|             ntag, = struct.unpack('>I', data) |  | ||||||
|         except struct.error: |  | ||||||
|             break |  | ||||||
|  |  | ||||||
|         tag ^= ntag |  | ||||||
|         off += 4 |  | ||||||
|  |  | ||||||
|         type = (tag & 0x7ff00000) >> 20 |  | ||||||
|         id   = (tag & 0x000ffc00) >> 10 |  | ||||||
|         size = (tag & 0x000003ff) >> 0 |  | ||||||
|         iscrc = (type & 0x700) == 0x500 |  | ||||||
|  |  | ||||||
|         data = file.read(size if size != 0x3ff else 0) |  | ||||||
|         if iscrc: |  | ||||||
|             crc = binascii.crc32(data[:4], crc) |  | ||||||
|         else: |  | ||||||
|             crc = binascii.crc32(data, crc) |  | ||||||
|  |  | ||||||
|         print '%04x: %08x  %-15s %3s %4s  %-23s  %-8s' % ( |  | ||||||
|             off, tag, |  | ||||||
|             typeof(type) + (' bad!' if iscrc and ~crc else ''), |  | ||||||
|             hex(id)[2:] if id != 0x3ff else '.', |  | ||||||
|             size if size != 0x3ff else 'x', |  | ||||||
|             ' '.join('%02x' % ord(c) for c in data[:8]), |  | ||||||
|             ''.join(c if c >= ' ' and c <= '~' else '.' for c in data[:8])) |  | ||||||
|  |  | ||||||
|         off += size if size != 0x3ff else 0 |  | ||||||
|         if iscrc: |  | ||||||
|             crc = 0 |  | ||||||
|             tag ^= (type & 1) << 31 |  | ||||||
|  |  | ||||||
|     return 0 |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": |  | ||||||
|     import sys |  | ||||||
|     sys.exit(main(*sys.argv[1:])) |  | ||||||
							
								
								
									
										383
									
								
								scripts/explode_asserts.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										383
									
								
								scripts/explode_asserts.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,383 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  |  | ||||||
|  | import re | ||||||
|  | import sys | ||||||
|  |  | ||||||
|  | PATTERN = ['LFS2_ASSERT', 'assert'] | ||||||
|  | PREFIX = 'LFS2' | ||||||
|  | MAXWIDTH = 16 | ||||||
|  |  | ||||||
|  | ASSERT = "__{PREFIX}_ASSERT_{TYPE}_{COMP}" | ||||||
|  | FAIL = """ | ||||||
|  | __attribute__((unused)) | ||||||
|  | static void __{prefix}_assert_fail_{type}( | ||||||
|  |         const char *file, int line, const char *comp, | ||||||
|  |         {ctype} lh, size_t lsize, | ||||||
|  |         {ctype} rh, size_t rsize) {{ | ||||||
|  |     printf("%s:%d:assert: assert failed with ", file, line); | ||||||
|  |     __{prefix}_assert_print_{type}(lh, lsize); | ||||||
|  |     printf(", expected %s ", comp); | ||||||
|  |     __{prefix}_assert_print_{type}(rh, rsize); | ||||||
|  |     printf("\\n"); | ||||||
|  |     fflush(NULL); | ||||||
|  |     raise(SIGABRT); | ||||||
|  | }} | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | COMP = { | ||||||
|  |     '==': 'eq', | ||||||
|  |     '!=': 'ne', | ||||||
|  |     '<=': 'le', | ||||||
|  |     '>=': 'ge', | ||||||
|  |     '<':  'lt', | ||||||
|  |     '>':  'gt', | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TYPE = { | ||||||
|  |     'int': { | ||||||
|  |         'ctype': 'intmax_t', | ||||||
|  |         'fail': FAIL, | ||||||
|  |         'print': """ | ||||||
|  |         __attribute__((unused)) | ||||||
|  |         static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{ | ||||||
|  |             (void)size; | ||||||
|  |             printf("%"PRIiMAX, v); | ||||||
|  |         }} | ||||||
|  |         """, | ||||||
|  |         'assert': """ | ||||||
|  |         #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh) | ||||||
|  |         do {{ | ||||||
|  |             __typeof__(lh) _lh = lh; | ||||||
|  |             __typeof__(lh) _rh = (__typeof__(lh))rh; | ||||||
|  |             if (!(_lh {op} _rh)) {{ | ||||||
|  |                 __{prefix}_assert_fail_{type}(file, line, "{comp}", | ||||||
|  |                         (intmax_t)_lh, 0, (intmax_t)_rh, 0); | ||||||
|  |             }} | ||||||
|  |         }} while (0) | ||||||
|  |         """ | ||||||
|  |     }, | ||||||
|  |     'bool': { | ||||||
|  |         'ctype': 'bool', | ||||||
|  |         'fail': FAIL, | ||||||
|  |         'print': """ | ||||||
|  |         __attribute__((unused)) | ||||||
|  |         static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{ | ||||||
|  |             (void)size; | ||||||
|  |             printf("%s", v ? "true" : "false"); | ||||||
|  |         }} | ||||||
|  |         """, | ||||||
|  |         'assert': """ | ||||||
|  |         #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh) | ||||||
|  |         do {{ | ||||||
|  |             bool _lh = !!(lh); | ||||||
|  |             bool _rh = !!(rh); | ||||||
|  |             if (!(_lh {op} _rh)) {{ | ||||||
|  |                 __{prefix}_assert_fail_{type}(file, line, "{comp}", | ||||||
|  |                         _lh, 0, _rh, 0); | ||||||
|  |             }} | ||||||
|  |         }} while (0) | ||||||
|  |         """ | ||||||
|  |     }, | ||||||
|  |     'mem': { | ||||||
|  |         'ctype': 'const void *', | ||||||
|  |         'fail': FAIL, | ||||||
|  |         'print': """ | ||||||
|  |         __attribute__((unused)) | ||||||
|  |         static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{ | ||||||
|  |             const uint8_t *s = v; | ||||||
|  |             printf("\\\""); | ||||||
|  |             for (size_t i = 0; i < size && i < {maxwidth}; i++) {{ | ||||||
|  |                 if (s[i] >= ' ' && s[i] <= '~') {{ | ||||||
|  |                     printf("%c", s[i]); | ||||||
|  |                 }} else {{ | ||||||
|  |                     printf("\\\\x%02x", s[i]); | ||||||
|  |                 }} | ||||||
|  |             }} | ||||||
|  |             if (size > {maxwidth}) {{ | ||||||
|  |                 printf("..."); | ||||||
|  |             }} | ||||||
|  |             printf("\\\""); | ||||||
|  |         }} | ||||||
|  |         """, | ||||||
|  |         'assert': """ | ||||||
|  |         #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh, size) | ||||||
|  |         do {{ | ||||||
|  |             const void *_lh = lh; | ||||||
|  |             const void *_rh = rh; | ||||||
|  |             if (!(memcmp(_lh, _rh, size) {op} 0)) {{ | ||||||
|  |                 __{prefix}_assert_fail_{type}(file, line, "{comp}", | ||||||
|  |                         _lh, size, _rh, size); | ||||||
|  |             }} | ||||||
|  |         }} while (0) | ||||||
|  |         """ | ||||||
|  |     }, | ||||||
|  |     'str': { | ||||||
|  |         'ctype': 'const char *', | ||||||
|  |         'fail': FAIL, | ||||||
|  |         'print': """ | ||||||
|  |         __attribute__((unused)) | ||||||
|  |         static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{ | ||||||
|  |             __{prefix}_assert_print_mem(v, size); | ||||||
|  |         }} | ||||||
|  |         """, | ||||||
|  |         'assert': """ | ||||||
|  |         #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh) | ||||||
|  |         do {{ | ||||||
|  |             const char *_lh = lh; | ||||||
|  |             const char *_rh = rh; | ||||||
|  |             if (!(strcmp(_lh, _rh) {op} 0)) {{ | ||||||
|  |                 __{prefix}_assert_fail_{type}(file, line, "{comp}", | ||||||
|  |                         _lh, strlen(_lh), _rh, strlen(_rh)); | ||||||
|  |             }} | ||||||
|  |         }} while (0) | ||||||
|  |         """ | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | def mkdecls(outf, maxwidth=16): | ||||||
|  |     outf.write("#include <stdio.h>\n") | ||||||
|  |     outf.write("#include <stdbool.h>\n") | ||||||
|  |     outf.write("#include <stdint.h>\n") | ||||||
|  |     outf.write("#include <inttypes.h>\n") | ||||||
|  |     outf.write("#include <signal.h>\n") | ||||||
|  |  | ||||||
|  |     for type, desc in sorted(TYPE.items()): | ||||||
|  |         format = { | ||||||
|  |             'type': type.lower(), 'TYPE': type.upper(), | ||||||
|  |             'ctype': desc['ctype'], | ||||||
|  |             'prefix': PREFIX.lower(), 'PREFIX': PREFIX.upper(), | ||||||
|  |             'maxwidth': maxwidth, | ||||||
|  |         } | ||||||
|  |         outf.write(re.sub('\s+', ' ', | ||||||
|  |             desc['print'].strip().format(**format))+'\n') | ||||||
|  |         outf.write(re.sub('\s+', ' ', | ||||||
|  |             desc['fail'].strip().format(**format))+'\n') | ||||||
|  |  | ||||||
|  |         for op, comp in sorted(COMP.items()): | ||||||
|  |             format.update({ | ||||||
|  |                 'comp': comp.lower(), 'COMP': comp.upper(), | ||||||
|  |                 'op': op, | ||||||
|  |             }) | ||||||
|  |             outf.write(re.sub('\s+', ' ', | ||||||
|  |                 desc['assert'].strip().format(**format))+'\n') | ||||||
|  |  | ||||||
|  | def mkassert(type, comp, lh, rh, size=None): | ||||||
|  |     format = { | ||||||
|  |         'type': type.lower(), 'TYPE': type.upper(), | ||||||
|  |         'comp': comp.lower(), 'COMP': comp.upper(), | ||||||
|  |         'prefix': PREFIX.lower(), 'PREFIX': PREFIX.upper(), | ||||||
|  |         'lh': lh.strip(' '), | ||||||
|  |         'rh': rh.strip(' '), | ||||||
|  |         'size': size, | ||||||
|  |     } | ||||||
|  |     if size: | ||||||
|  |         return ((ASSERT + '(__FILE__, __LINE__, {lh}, {rh}, {size})') | ||||||
|  |             .format(**format)) | ||||||
|  |     else: | ||||||
|  |         return ((ASSERT + '(__FILE__, __LINE__, {lh}, {rh})') | ||||||
|  |             .format(**format)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # simple recursive descent parser | ||||||
|  | LEX = { | ||||||
|  |     'ws':       [r'(?:\s|\n|#.*?\n|//.*?\n|/\*.*?\*/)+'], | ||||||
|  |     'assert':   PATTERN, | ||||||
|  |     'string':   [r'"(?:\\.|[^"])*"', r"'(?:\\.|[^'])\'"], | ||||||
|  |     'arrow':    ['=>'], | ||||||
|  |     'paren':    ['\(', '\)'], | ||||||
|  |     'op':       ['strcmp', 'memcmp', '->'], | ||||||
|  |     'comp':     ['==', '!=', '<=', '>=', '<', '>'], | ||||||
|  |     'logic':    ['\&\&', '\|\|'], | ||||||
|  |     'sep':      [':', ';', '\{', '\}', ','], | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class ParseFailure(Exception): | ||||||
|  |     def __init__(self, expected, found): | ||||||
|  |         self.expected = expected | ||||||
|  |         self.found = found | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         return "expected %r, found %s..." % ( | ||||||
|  |             self.expected, repr(self.found)[:70]) | ||||||
|  |  | ||||||
|  | class Parse: | ||||||
|  |     def __init__(self, inf, lexemes): | ||||||
|  |         p = '|'.join('(?P<%s>%s)' % (n, '|'.join(l)) | ||||||
|  |             for n, l in lexemes.items()) | ||||||
|  |         p = re.compile(p, re.DOTALL) | ||||||
|  |         data = inf.read() | ||||||
|  |         tokens = [] | ||||||
|  |         while True: | ||||||
|  |             m = p.search(data) | ||||||
|  |             if m: | ||||||
|  |                 if m.start() > 0: | ||||||
|  |                     tokens.append((None, data[:m.start()])) | ||||||
|  |                 tokens.append((m.lastgroup, m.group())) | ||||||
|  |                 data = data[m.end():] | ||||||
|  |             else: | ||||||
|  |                 tokens.append((None, data)) | ||||||
|  |                 break | ||||||
|  |         self.tokens = tokens | ||||||
|  |         self.off = 0 | ||||||
|  |  | ||||||
|  |     def lookahead(self, *pattern): | ||||||
|  |         if self.off < len(self.tokens): | ||||||
|  |             token = self.tokens[self.off] | ||||||
|  |             if token[0] in pattern or token[1] in pattern: | ||||||
|  |                 self.m = token[1] | ||||||
|  |                 return self.m | ||||||
|  |         self.m = None | ||||||
|  |         return self.m | ||||||
|  |  | ||||||
|  |     def accept(self, *patterns): | ||||||
|  |         m = self.lookahead(*patterns) | ||||||
|  |         if m is not None: | ||||||
|  |             self.off += 1 | ||||||
|  |         return m | ||||||
|  |  | ||||||
|  |     def expect(self, *patterns): | ||||||
|  |         m = self.accept(*patterns) | ||||||
|  |         if not m: | ||||||
|  |             raise ParseFailure(patterns, self.tokens[self.off:]) | ||||||
|  |         return m | ||||||
|  |  | ||||||
|  |     def push(self): | ||||||
|  |         return self.off | ||||||
|  |  | ||||||
|  |     def pop(self, state): | ||||||
|  |         self.off = state | ||||||
|  |  | ||||||
|  | def passert(p): | ||||||
|  |     def pastr(p): | ||||||
|  |         p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws') | ||||||
|  |         p.expect('strcmp') ; p.accept('ws') ; p.expect('(') ; p.accept('ws') | ||||||
|  |         lh = pexpr(p) ; p.accept('ws') | ||||||
|  |         p.expect(',') ; p.accept('ws') | ||||||
|  |         rh = pexpr(p) ; p.accept('ws') | ||||||
|  |         p.expect(')') ; p.accept('ws') | ||||||
|  |         comp = p.expect('comp') ; p.accept('ws') | ||||||
|  |         p.expect('0') ; p.accept('ws') | ||||||
|  |         p.expect(')') | ||||||
|  |         return mkassert('str', COMP[comp], lh, rh) | ||||||
|  |  | ||||||
|  |     def pamem(p): | ||||||
|  |         p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws') | ||||||
|  |         p.expect('memcmp') ; p.accept('ws') ; p.expect('(') ; p.accept('ws') | ||||||
|  |         lh = pexpr(p) ; p.accept('ws') | ||||||
|  |         p.expect(',') ; p.accept('ws') | ||||||
|  |         rh = pexpr(p) ; p.accept('ws') | ||||||
|  |         p.expect(',') ; p.accept('ws') | ||||||
|  |         size = pexpr(p) ; p.accept('ws') | ||||||
|  |         p.expect(')') ; p.accept('ws') | ||||||
|  |         comp = p.expect('comp') ; p.accept('ws') | ||||||
|  |         p.expect('0') ; p.accept('ws') | ||||||
|  |         p.expect(')') | ||||||
|  |         return mkassert('mem', COMP[comp], lh, rh, size) | ||||||
|  |  | ||||||
|  |     def paint(p): | ||||||
|  |         p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws') | ||||||
|  |         lh = pexpr(p) ; p.accept('ws') | ||||||
|  |         comp = p.expect('comp') ; p.accept('ws') | ||||||
|  |         rh = pexpr(p) ; p.accept('ws') | ||||||
|  |         p.expect(')') | ||||||
|  |         return mkassert('int', COMP[comp], lh, rh) | ||||||
|  |  | ||||||
|  |     def pabool(p): | ||||||
|  |         p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws') | ||||||
|  |         lh = pexprs(p) ; p.accept('ws') | ||||||
|  |         p.expect(')') | ||||||
|  |         return mkassert('bool', 'eq', lh, 'true') | ||||||
|  |  | ||||||
|  |     def pa(p): | ||||||
|  |         return p.expect('assert') | ||||||
|  |  | ||||||
|  |     state = p.push() | ||||||
|  |     lastf = None | ||||||
|  |     for pa in [pastr, pamem, paint, pabool, pa]: | ||||||
|  |         try: | ||||||
|  |             return pa(p) | ||||||
|  |         except ParseFailure as f: | ||||||
|  |             p.pop(state) | ||||||
|  |             lastf = f | ||||||
|  |     else: | ||||||
|  |         raise lastf | ||||||
|  |  | ||||||
|  | def pexpr(p): | ||||||
|  |     res = [] | ||||||
|  |     while True: | ||||||
|  |         if p.accept('('): | ||||||
|  |             res.append(p.m) | ||||||
|  |             while True: | ||||||
|  |                 res.append(pexprs(p)) | ||||||
|  |                 if p.accept('sep'): | ||||||
|  |                     res.append(p.m) | ||||||
|  |                 else: | ||||||
|  |                     break | ||||||
|  |             res.append(p.expect(')')) | ||||||
|  |         elif p.lookahead('assert'): | ||||||
|  |             res.append(passert(p)) | ||||||
|  |         elif p.accept('assert', 'ws', 'string', 'op', None): | ||||||
|  |             res.append(p.m) | ||||||
|  |         else: | ||||||
|  |             return ''.join(res) | ||||||
|  |  | ||||||
|  | def pexprs(p): | ||||||
|  |     res = [] | ||||||
|  |     while True: | ||||||
|  |         res.append(pexpr(p)) | ||||||
|  |         if p.accept('comp', 'logic', ','): | ||||||
|  |             res.append(p.m) | ||||||
|  |         else: | ||||||
|  |             return ''.join(res) | ||||||
|  |  | ||||||
|  | def pstmt(p): | ||||||
|  |     ws = p.accept('ws') or '' | ||||||
|  |     lh = pexprs(p) | ||||||
|  |     if p.accept('=>'): | ||||||
|  |         rh = pexprs(p) | ||||||
|  |         return ws + mkassert('int', 'eq', lh, rh) | ||||||
|  |     else: | ||||||
|  |         return ws + lh | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def main(args): | ||||||
|  |     inf = open(args.input, 'r') if args.input else sys.stdin | ||||||
|  |     outf = open(args.output, 'w') if args.output else sys.stdout | ||||||
|  |  | ||||||
|  |     lexemes = LEX.copy() | ||||||
|  |     if args.pattern: | ||||||
|  |         lexemes['assert'] = args.pattern | ||||||
|  |     p = Parse(inf, lexemes) | ||||||
|  |  | ||||||
|  |     # write extra verbose asserts | ||||||
|  |     mkdecls(outf, maxwidth=args.maxwidth) | ||||||
|  |     if args.input: | ||||||
|  |         outf.write("#line %d \"%s\"\n" % (1, args.input)) | ||||||
|  |  | ||||||
|  |     # parse and write out stmt at a time | ||||||
|  |     try: | ||||||
|  |         while True: | ||||||
|  |             outf.write(pstmt(p)) | ||||||
|  |             if p.accept('sep'): | ||||||
|  |                 outf.write(p.m) | ||||||
|  |             else: | ||||||
|  |                 break | ||||||
|  |     except ParseFailure as f: | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  |     for i in range(p.off, len(p.tokens)): | ||||||
|  |         outf.write(p.tokens[i][1]) | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     import argparse | ||||||
|  |     parser = argparse.ArgumentParser( | ||||||
|  |         description="Cpp step that increases assert verbosity") | ||||||
|  |     parser.add_argument('input', nargs='?', | ||||||
|  |         help="Input C file after cpp.") | ||||||
|  |     parser.add_argument('-o', '--output', required=True, | ||||||
|  |         help="Output C file.") | ||||||
|  |     parser.add_argument('-p', '--pattern', action='append', | ||||||
|  |         help="Patterns to search for starting an assert statement.") | ||||||
|  |     parser.add_argument('--maxwidth', default=MAXWIDTH, type=int, | ||||||
|  |         help="Maximum number of characters to display for strcmp and memcmp.") | ||||||
|  |     main(parser.parse_args()) | ||||||
							
								
								
									
										26
									
								
								scripts/readblock.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										26
									
								
								scripts/readblock.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  |  | ||||||
|  | import subprocess as sp | ||||||
|  |  | ||||||
|  | def main(args): | ||||||
|  |     with open(args.disk, 'rb') as f: | ||||||
|  |         f.seek(args.block * args.block_size) | ||||||
|  |         block = (f.read(args.block_size) | ||||||
|  |             .ljust(args.block_size, b'\xff')) | ||||||
|  |  | ||||||
|  |     # what did you expect? | ||||||
|  |     print("%-8s  %-s" % ('off', 'data')) | ||||||
|  |     return sp.run(['xxd', '-g1', '-'], input=block).returncode | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     import argparse | ||||||
|  |     import sys | ||||||
|  |     parser = argparse.ArgumentParser( | ||||||
|  |         description="Hex dump a specific block in a disk.") | ||||||
|  |     parser.add_argument('disk', | ||||||
|  |         help="File representing the block device.") | ||||||
|  |     parser.add_argument('block_size', type=lambda x: int(x, 0), | ||||||
|  |         help="Size of a block in bytes.") | ||||||
|  |     parser.add_argument('block', type=lambda x: int(x, 0), | ||||||
|  |         help="Address of block to dump.") | ||||||
|  |     sys.exit(main(parser.parse_args())) | ||||||
							
								
								
									
										367
									
								
								scripts/readmdir.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										367
									
								
								scripts/readmdir.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,367 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  |  | ||||||
|  | import struct | ||||||
|  | import binascii | ||||||
|  | import sys | ||||||
|  | import itertools as it | ||||||
|  |  | ||||||
|  | TAG_TYPES = { | ||||||
|  |     'splice':       (0x700, 0x400), | ||||||
|  |     'create':       (0x7ff, 0x401), | ||||||
|  |     'delete':       (0x7ff, 0x4ff), | ||||||
|  |     'name':         (0x700, 0x000), | ||||||
|  |     'reg':          (0x7ff, 0x001), | ||||||
|  |     'dir':          (0x7ff, 0x002), | ||||||
|  |     'superblock':   (0x7ff, 0x0ff), | ||||||
|  |     'struct':       (0x700, 0x200), | ||||||
|  |     'dirstruct':    (0x7ff, 0x200), | ||||||
|  |     'ctzstruct':    (0x7ff, 0x202), | ||||||
|  |     'inlinestruct': (0x7ff, 0x201), | ||||||
|  |     'userattr':     (0x700, 0x300), | ||||||
|  |     'tail':         (0x700, 0x600), | ||||||
|  |     'softtail':     (0x7ff, 0x600), | ||||||
|  |     'hardtail':     (0x7ff, 0x601), | ||||||
|  |     'gstate':       (0x700, 0x700), | ||||||
|  |     'movestate':    (0x7ff, 0x7ff), | ||||||
|  |     'crc':          (0x700, 0x500), | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class Tag: | ||||||
|  |     def __init__(self, *args): | ||||||
|  |         if len(args) == 1: | ||||||
|  |             self.tag = args[0] | ||||||
|  |         elif len(args) == 3: | ||||||
|  |             if isinstance(args[0], str): | ||||||
|  |                 type = TAG_TYPES[args[0]][1] | ||||||
|  |             else: | ||||||
|  |                 type = args[0] | ||||||
|  |  | ||||||
|  |             if isinstance(args[1], str): | ||||||
|  |                 id = int(args[1], 0) if args[1] not in 'x.' else 0x3ff | ||||||
|  |             else: | ||||||
|  |                 id = args[1] | ||||||
|  |  | ||||||
|  |             if isinstance(args[2], str): | ||||||
|  |                 size = int(args[2], str) if args[2] not in 'x.' else 0x3ff | ||||||
|  |             else: | ||||||
|  |                 size = args[2] | ||||||
|  |  | ||||||
|  |             self.tag = (type << 20) | (id << 10) | size | ||||||
|  |         else: | ||||||
|  |             assert False | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def isvalid(self): | ||||||
|  |         return not bool(self.tag & 0x80000000) | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def isattr(self): | ||||||
|  |         return not bool(self.tag & 0x40000000) | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def iscompactable(self): | ||||||
|  |         return bool(self.tag & 0x20000000) | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def isunique(self): | ||||||
|  |         return not bool(self.tag & 0x10000000) | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def type(self): | ||||||
|  |         return (self.tag & 0x7ff00000) >> 20 | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def type1(self): | ||||||
|  |         return (self.tag & 0x70000000) >> 20 | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def type3(self): | ||||||
|  |         return (self.tag & 0x7ff00000) >> 20 | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def id(self): | ||||||
|  |         return (self.tag & 0x000ffc00) >> 10 | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def size(self): | ||||||
|  |         return (self.tag & 0x000003ff) >> 0 | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def dsize(self): | ||||||
|  |         return 4 + (self.size if self.size != 0x3ff else 0) | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def chunk(self): | ||||||
|  |         return self.type & 0xff | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def schunk(self): | ||||||
|  |         return struct.unpack('b', struct.pack('B', self.chunk))[0] | ||||||
|  |  | ||||||
|  |     def is_(self, type): | ||||||
|  |         return (self.type & TAG_TYPES[type][0]) == TAG_TYPES[type][1] | ||||||
|  |  | ||||||
|  |     def mkmask(self): | ||||||
|  |         return Tag( | ||||||
|  |             0x700 if self.isunique else 0x7ff, | ||||||
|  |             0x3ff if self.isattr else 0, | ||||||
|  |             0) | ||||||
|  |  | ||||||
|  |     def chid(self, nid): | ||||||
|  |         ntag = Tag(self.type, nid, self.size) | ||||||
|  |         if hasattr(self, 'off'):  ntag.off  = self.off | ||||||
|  |         if hasattr(self, 'data'): ntag.data = self.data | ||||||
|  |         if hasattr(self, 'crc'):  ntag.crc  = self.crc | ||||||
|  |         return ntag | ||||||
|  |  | ||||||
|  |     def typerepr(self): | ||||||
|  |         if self.is_('crc') and getattr(self, 'crc', 0xffffffff) != 0xffffffff: | ||||||
|  |             return 'crc (bad)' | ||||||
|  |  | ||||||
|  |         reverse_types = {v: k for k, v in TAG_TYPES.items()} | ||||||
|  |         for prefix in range(12): | ||||||
|  |             mask = 0x7ff & ~((1 << prefix)-1) | ||||||
|  |             if (mask, self.type & mask) in reverse_types: | ||||||
|  |                 type = reverse_types[mask, self.type & mask] | ||||||
|  |                 if prefix > 0: | ||||||
|  |                     return '%s %#0*x' % ( | ||||||
|  |                         type, prefix//4, self.type & ((1 << prefix)-1)) | ||||||
|  |                 else: | ||||||
|  |                     return type | ||||||
|  |         else: | ||||||
|  |             return '%02x' % self.type | ||||||
|  |  | ||||||
|  |     def idrepr(self): | ||||||
|  |         return repr(self.id) if self.id != 0x3ff else '.' | ||||||
|  |  | ||||||
|  |     def sizerepr(self): | ||||||
|  |         return repr(self.size) if self.size != 0x3ff else 'x' | ||||||
|  |  | ||||||
|  |     def __repr__(self): | ||||||
|  |         return 'Tag(%r, %d, %d)' % (self.typerepr(), self.id, self.size) | ||||||
|  |  | ||||||
|  |     def __lt__(self, other): | ||||||
|  |         return (self.id, self.type) < (other.id, other.type) | ||||||
|  |  | ||||||
|  |     def __bool__(self): | ||||||
|  |         return self.isvalid | ||||||
|  |  | ||||||
|  |     def __int__(self): | ||||||
|  |         return self.tag | ||||||
|  |  | ||||||
|  |     def __index__(self): | ||||||
|  |         return self.tag | ||||||
|  |  | ||||||
|  | class MetadataPair: | ||||||
|  |     def __init__(self, blocks): | ||||||
|  |         if len(blocks) > 1: | ||||||
|  |             self.pair = [MetadataPair([block]) for block in blocks] | ||||||
|  |             self.pair = sorted(self.pair, reverse=True) | ||||||
|  |  | ||||||
|  |             self.data = self.pair[0].data | ||||||
|  |             self.rev  = self.pair[0].rev | ||||||
|  |             self.tags = self.pair[0].tags | ||||||
|  |             self.ids  = self.pair[0].ids | ||||||
|  |             self.log  = self.pair[0].log | ||||||
|  |             self.all_ = self.pair[0].all_ | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         self.pair = [self] | ||||||
|  |         self.data = blocks[0] | ||||||
|  |         block = self.data | ||||||
|  |  | ||||||
|  |         self.rev, = struct.unpack('<I', block[0:4]) | ||||||
|  |         crc = binascii.crc32(block[0:4]) | ||||||
|  |  | ||||||
|  |         # parse tags | ||||||
|  |         corrupt = False | ||||||
|  |         tag = Tag(0xffffffff) | ||||||
|  |         off = 4 | ||||||
|  |         self.log = [] | ||||||
|  |         self.all_ = [] | ||||||
|  |         while len(block) - off >= 4: | ||||||
|  |             ntag, = struct.unpack('>I', block[off:off+4]) | ||||||
|  |  | ||||||
|  |             tag = Tag(int(tag) ^ ntag) | ||||||
|  |             tag.off = off + 4 | ||||||
|  |             tag.data = block[off+4:off+tag.dsize] | ||||||
|  |             if tag.is_('crc'): | ||||||
|  |                 crc = binascii.crc32(block[off:off+4+4], crc) | ||||||
|  |             else: | ||||||
|  |                 crc = binascii.crc32(block[off:off+tag.dsize], crc) | ||||||
|  |             tag.crc = crc | ||||||
|  |             off += tag.dsize | ||||||
|  |  | ||||||
|  |             self.all_.append(tag) | ||||||
|  |  | ||||||
|  |             if tag.is_('crc'): | ||||||
|  |                 # is valid commit? | ||||||
|  |                 if crc != 0xffffffff: | ||||||
|  |                     corrupt = True | ||||||
|  |                 if not corrupt: | ||||||
|  |                     self.log = self.all_.copy() | ||||||
|  |  | ||||||
|  |                 # reset tag parsing | ||||||
|  |                 crc = 0 | ||||||
|  |                 tag = Tag(int(tag) ^ ((tag.type & 1) << 31)) | ||||||
|  |  | ||||||
|  |         # find active ids | ||||||
|  |         self.ids = list(it.takewhile( | ||||||
|  |             lambda id: Tag('name', id, 0) in self, | ||||||
|  |             it.count())) | ||||||
|  |  | ||||||
|  |         # find most recent tags | ||||||
|  |         self.tags = [] | ||||||
|  |         for tag in self.log: | ||||||
|  |             if tag.is_('crc') or tag.is_('splice'): | ||||||
|  |                 continue | ||||||
|  |             elif tag.id == 0x3ff: | ||||||
|  |                 if tag in self and self[tag] is tag: | ||||||
|  |                     self.tags.append(tag) | ||||||
|  |             else: | ||||||
|  |                 # id could have change, I know this is messy and slow | ||||||
|  |                 # but it works | ||||||
|  |                 for id in self.ids: | ||||||
|  |                     ntag = tag.chid(id) | ||||||
|  |                     if ntag in self and self[ntag] is tag: | ||||||
|  |                         self.tags.append(ntag) | ||||||
|  |  | ||||||
|  |         self.tags = sorted(self.tags) | ||||||
|  |  | ||||||
|  |     def __bool__(self): | ||||||
|  |         return bool(self.log) | ||||||
|  |  | ||||||
|  |     def __lt__(self, other): | ||||||
|  |         # corrupt blocks don't count | ||||||
|  |         if not self or not other: | ||||||
|  |             return bool(other) | ||||||
|  |  | ||||||
|  |         # use sequence arithmetic to avoid overflow | ||||||
|  |         return not ((other.rev - self.rev) & 0x80000000) | ||||||
|  |  | ||||||
|  |     def __contains__(self, args): | ||||||
|  |         try: | ||||||
|  |             self[args] | ||||||
|  |             return True | ||||||
|  |         except KeyError: | ||||||
|  |             return False | ||||||
|  |  | ||||||
|  |     def __getitem__(self, args): | ||||||
|  |         if isinstance(args, tuple): | ||||||
|  |             gmask, gtag = args | ||||||
|  |         else: | ||||||
|  |             gmask, gtag = args.mkmask(), args | ||||||
|  |  | ||||||
|  |         gdiff = 0 | ||||||
|  |         for tag in reversed(self.log): | ||||||
|  |             if (gmask.id != 0 and tag.is_('splice') and | ||||||
|  |                     tag.id <= gtag.id - gdiff): | ||||||
|  |                 if tag.is_('create') and tag.id == gtag.id - gdiff: | ||||||
|  |                     # creation point | ||||||
|  |                     break | ||||||
|  |  | ||||||
|  |                 gdiff += tag.schunk | ||||||
|  |  | ||||||
|  |             if ((int(gmask) & int(tag)) == | ||||||
|  |                     (int(gmask) & int(gtag.chid(gtag.id - gdiff)))): | ||||||
|  |                 if tag.size == 0x3ff: | ||||||
|  |                     # deleted | ||||||
|  |                     break | ||||||
|  |  | ||||||
|  |                 return tag | ||||||
|  |  | ||||||
|  |         raise KeyError(gmask, gtag) | ||||||
|  |  | ||||||
|  |     def _dump_tags(self, tags, f=sys.stdout, truncate=True): | ||||||
|  |         f.write("%-8s  %-8s  %-13s %4s %4s" % ( | ||||||
|  |             'off', 'tag', 'type', 'id', 'len')) | ||||||
|  |         if truncate: | ||||||
|  |             f.write('  data (truncated)') | ||||||
|  |         f.write('\n') | ||||||
|  |  | ||||||
|  |         for tag in tags: | ||||||
|  |             f.write("%08x: %08x  %-13s %4s %4s" % ( | ||||||
|  |                 tag.off, tag, | ||||||
|  |                 tag.typerepr(), tag.idrepr(), tag.sizerepr())) | ||||||
|  |             if truncate: | ||||||
|  |                 f.write("  %-23s  %-8s\n" % ( | ||||||
|  |                     ' '.join('%02x' % c for c in tag.data[:8]), | ||||||
|  |                     ''.join(c if c >= ' ' and c <= '~' else '.' | ||||||
|  |                         for c in map(chr, tag.data[:8])))) | ||||||
|  |             else: | ||||||
|  |                 f.write("\n") | ||||||
|  |                 for i in range(0, len(tag.data), 16): | ||||||
|  |                     f.write("  %08x: %-47s  %-16s\n" % ( | ||||||
|  |                         tag.off+i, | ||||||
|  |                         ' '.join('%02x' % c for c in tag.data[i:i+16]), | ||||||
|  |                         ''.join(c if c >= ' ' and c <= '~' else '.' | ||||||
|  |                             for c in map(chr, tag.data[i:i+16])))) | ||||||
|  |  | ||||||
|  |     def dump_tags(self, f=sys.stdout, truncate=True): | ||||||
|  |         self._dump_tags(self.tags, f=f, truncate=truncate) | ||||||
|  |  | ||||||
|  |     def dump_log(self, f=sys.stdout, truncate=True): | ||||||
|  |         self._dump_tags(self.log, f=f, truncate=truncate) | ||||||
|  |  | ||||||
|  |     def dump_all(self, f=sys.stdout, truncate=True): | ||||||
|  |         self._dump_tags(self.all_, f=f, truncate=truncate) | ||||||
|  |  | ||||||
|  | def main(args): | ||||||
|  |     blocks = [] | ||||||
|  |     with open(args.disk, 'rb') as f: | ||||||
|  |         for block in [args.block1, args.block2]: | ||||||
|  |             if block is None: | ||||||
|  |                 continue | ||||||
|  |             f.seek(block * args.block_size) | ||||||
|  |             blocks.append(f.read(args.block_size) | ||||||
|  |                 .ljust(args.block_size, b'\xff')) | ||||||
|  |  | ||||||
|  |     # find most recent pair | ||||||
|  |     mdir = MetadataPair(blocks) | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         mdir.tail = mdir[Tag('tail', 0, 0)] | ||||||
|  |         if mdir.tail.size != 8 or mdir.tail.data == 8*b'\xff': | ||||||
|  |             mdir.tail = None | ||||||
|  |     except KeyError: | ||||||
|  |         mdir.tail = None | ||||||
|  |  | ||||||
|  |     print("mdir {%s} rev %d%s%s%s" % ( | ||||||
|  |         ', '.join('%#x' % b | ||||||
|  |             for b in [args.block1, args.block2] | ||||||
|  |             if b is not None), | ||||||
|  |         mdir.rev, | ||||||
|  |         ' (was %s)' % ', '.join('%d' % m.rev for m in mdir.pair[1:]) | ||||||
|  |         if len(mdir.pair) > 1 else '', | ||||||
|  |         ' (corrupted!)' if not mdir else '', | ||||||
|  |         ' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data) | ||||||
|  |         if mdir.tail else '')) | ||||||
|  |     if args.all: | ||||||
|  |         mdir.dump_all(truncate=not args.no_truncate) | ||||||
|  |     elif args.log: | ||||||
|  |         mdir.dump_log(truncate=not args.no_truncate) | ||||||
|  |     else: | ||||||
|  |         mdir.dump_tags(truncate=not args.no_truncate) | ||||||
|  |  | ||||||
|  |     return 0 if mdir else 1 | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     import argparse | ||||||
|  |     import sys | ||||||
|  |     parser = argparse.ArgumentParser( | ||||||
|  |         description="Dump useful info about metadata pairs in littlefs.") | ||||||
|  |     parser.add_argument('disk', | ||||||
|  |         help="File representing the block device.") | ||||||
|  |     parser.add_argument('block_size', type=lambda x: int(x, 0), | ||||||
|  |         help="Size of a block in bytes.") | ||||||
|  |     parser.add_argument('block1', type=lambda x: int(x, 0), | ||||||
|  |         help="First block address for finding the metadata pair.") | ||||||
|  |     parser.add_argument('block2', nargs='?', type=lambda x: int(x, 0), | ||||||
|  |         help="Second block address for finding the metadata pair.") | ||||||
|  |     parser.add_argument('-l', '--log', action='store_true', | ||||||
|  |         help="Show tags in log.") | ||||||
|  |     parser.add_argument('-a', '--all', action='store_true', | ||||||
|  |         help="Show all tags in log, included tags in corrupted commits.") | ||||||
|  |     parser.add_argument('-T', '--no-truncate', action='store_true', | ||||||
|  |         help="Don't truncate large amounts of data.") | ||||||
|  |     sys.exit(main(parser.parse_args())) | ||||||
							
								
								
									
										183
									
								
								scripts/readtree.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										183
									
								
								scripts/readtree.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,183 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  |  | ||||||
|  | import struct | ||||||
|  | import sys | ||||||
|  | import json | ||||||
|  | import io | ||||||
|  | import itertools as it | ||||||
|  | from readmdir import Tag, MetadataPair | ||||||
|  |  | ||||||
|  | def main(args): | ||||||
|  |     superblock = None | ||||||
|  |     gstate = b'\0\0\0\0\0\0\0\0\0\0\0\0' | ||||||
|  |     dirs = [] | ||||||
|  |     mdirs = [] | ||||||
|  |     corrupted = [] | ||||||
|  |     cycle = False | ||||||
|  |     with open(args.disk, 'rb') as f: | ||||||
|  |         tail = (args.block1, args.block2) | ||||||
|  |         hard = False | ||||||
|  |         while True: | ||||||
|  |             for m in it.chain((m for d in dirs for m in d), mdirs): | ||||||
|  |                 if set(m.blocks) == set(tail): | ||||||
|  |                     # cycle detected | ||||||
|  |                     cycle = m.blocks | ||||||
|  |             if cycle: | ||||||
|  |                 break | ||||||
|  |  | ||||||
|  |             # load mdir | ||||||
|  |             data = [] | ||||||
|  |             blocks = {} | ||||||
|  |             for block in tail: | ||||||
|  |                 f.seek(block * args.block_size) | ||||||
|  |                 data.append(f.read(args.block_size) | ||||||
|  |                     .ljust(args.block_size, b'\xff')) | ||||||
|  |                 blocks[id(data[-1])] = block | ||||||
|  |  | ||||||
|  |             mdir = MetadataPair(data) | ||||||
|  |             mdir.blocks = tuple(blocks[id(p.data)] for p in mdir.pair) | ||||||
|  |  | ||||||
|  |             # fetch some key metadata as a we scan | ||||||
|  |             try: | ||||||
|  |                 mdir.tail = mdir[Tag('tail', 0, 0)] | ||||||
|  |                 if mdir.tail.size != 8 or mdir.tail.data == 8*b'\xff': | ||||||
|  |                     mdir.tail = None | ||||||
|  |             except KeyError: | ||||||
|  |                 mdir.tail = None | ||||||
|  |  | ||||||
|  |             # have superblock? | ||||||
|  |             try: | ||||||
|  |                 nsuperblock = mdir[ | ||||||
|  |                     Tag(0x7ff, 0x3ff, 0), Tag('superblock', 0, 0)] | ||||||
|  |                 superblock = nsuperblock, mdir[Tag('inlinestruct', 0, 0)] | ||||||
|  |             except KeyError: | ||||||
|  |                 pass | ||||||
|  |  | ||||||
|  |             # have gstate? | ||||||
|  |             try: | ||||||
|  |                 ngstate = mdir[Tag('movestate', 0, 0)] | ||||||
|  |                 gstate = bytes((a or 0) ^ (b or 0) | ||||||
|  |                     for a,b in it.zip_longest(gstate, ngstate.data)) | ||||||
|  |             except KeyError: | ||||||
|  |                 pass | ||||||
|  |  | ||||||
|  |             # corrupted? | ||||||
|  |             if not mdir: | ||||||
|  |                 corrupted.append(mdir) | ||||||
|  |  | ||||||
|  |             # add to directories | ||||||
|  |             mdirs.append(mdir) | ||||||
|  |             if mdir.tail is None or not mdir.tail.is_('hardtail'): | ||||||
|  |                 dirs.append(mdirs) | ||||||
|  |                 mdirs = [] | ||||||
|  |  | ||||||
|  |             if mdir.tail is None: | ||||||
|  |                 break | ||||||
|  |  | ||||||
|  |             tail = struct.unpack('<II', mdir.tail.data) | ||||||
|  |             hard = mdir.tail.is_('hardtail') | ||||||
|  |  | ||||||
|  |     # find paths | ||||||
|  |     dirtable = {} | ||||||
|  |     for dir in dirs: | ||||||
|  |         dirtable[frozenset(dir[0].blocks)] = dir | ||||||
|  |  | ||||||
|  |     pending = [("/", dirs[0])] | ||||||
|  |     while pending: | ||||||
|  |         path, dir = pending.pop(0) | ||||||
|  |         for mdir in dir: | ||||||
|  |             for tag in mdir.tags: | ||||||
|  |                 if tag.is_('dir'): | ||||||
|  |                     try: | ||||||
|  |                         npath = tag.data.decode('utf8') | ||||||
|  |                         dirstruct = mdir[Tag('dirstruct', tag.id, 0)] | ||||||
|  |                         nblocks = struct.unpack('<II', dirstruct.data) | ||||||
|  |                         nmdir = dirtable[frozenset(nblocks)] | ||||||
|  |                         pending.append(((path + '/' + npath), nmdir)) | ||||||
|  |                     except KeyError: | ||||||
|  |                         pass | ||||||
|  |  | ||||||
|  |         dir[0].path = path.replace('//', '/') | ||||||
|  |  | ||||||
|  |     # print littlefs + version info | ||||||
|  |     version = ('?', '?') | ||||||
|  |     if superblock: | ||||||
|  |         version = tuple(reversed( | ||||||
|  |             struct.unpack('<HH', superblock[1].data[0:4].ljust(4, b'\xff')))) | ||||||
|  |     print("%-47s%s" % ("littlefs v%s.%s" % version, | ||||||
|  |         "data (truncated, if it fits)" | ||||||
|  |         if not any([args.no_truncate, args.tags, args.log, args.all]) else "")) | ||||||
|  |  | ||||||
|  |     # print gstate | ||||||
|  |     print("gstate 0x%s" % ''.join('%02x' % c for c in gstate)) | ||||||
|  |     tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0]) | ||||||
|  |     blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff')) | ||||||
|  |     if tag.size or not tag.isvalid: | ||||||
|  |         print("  orphans >=%d" % max(tag.size, 1)) | ||||||
|  |     if tag.type: | ||||||
|  |         print("  move dir {%#x, %#x} id %d" % ( | ||||||
|  |             blocks[0], blocks[1], tag.id)) | ||||||
|  |  | ||||||
|  |     # print mdir info | ||||||
|  |     for i, dir in enumerate(dirs): | ||||||
|  |         print("dir %s" % (json.dumps(dir[0].path) | ||||||
|  |             if hasattr(dir[0], 'path') else '(orphan)')) | ||||||
|  |  | ||||||
|  |         for j, mdir in enumerate(dir): | ||||||
|  |             print("mdir {%#x, %#x} rev %d (was %d)%s%s" % ( | ||||||
|  |                 mdir.blocks[0], mdir.blocks[1], mdir.rev, mdir.pair[1].rev, | ||||||
|  |                 ' (corrupted!)' if not mdir else '', | ||||||
|  |                 ' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data) | ||||||
|  |                 if mdir.tail else '')) | ||||||
|  |  | ||||||
|  |             f = io.StringIO() | ||||||
|  |             if args.log: | ||||||
|  |                 mdir.dump_log(f, truncate=not args.no_truncate) | ||||||
|  |             elif args.all: | ||||||
|  |                 mdir.dump_all(f, truncate=not args.no_truncate) | ||||||
|  |             else: | ||||||
|  |                 mdir.dump_tags(f, truncate=not args.no_truncate) | ||||||
|  |  | ||||||
|  |             lines = list(filter(None, f.getvalue().split('\n'))) | ||||||
|  |             for k, line in enumerate(lines): | ||||||
|  |                 print("%s %s" % ( | ||||||
|  |                     ' ' if j == len(dir)-1 else | ||||||
|  |                     'v' if k == len(lines)-1 else | ||||||
|  |                     '|', | ||||||
|  |                     line)) | ||||||
|  |  | ||||||
|  |     errcode = 0 | ||||||
|  |     for mdir in corrupted: | ||||||
|  |         errcode = errcode or 1 | ||||||
|  |         print("*** corrupted mdir {%#x, %#x}! ***" % ( | ||||||
|  |             mdir.blocks[0], mdir.blocks[1])) | ||||||
|  |  | ||||||
|  |     if cycle: | ||||||
|  |         errcode = errcode or 2 | ||||||
|  |         print("*** cycle detected {%#x, %#x}! ***" % ( | ||||||
|  |             cycle[0], cycle[1])) | ||||||
|  |  | ||||||
|  |     return errcode | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     import argparse | ||||||
|  |     import sys | ||||||
|  |     parser = argparse.ArgumentParser( | ||||||
|  |         description="Dump semantic info about the metadata tree in littlefs") | ||||||
|  |     parser.add_argument('disk', | ||||||
|  |         help="File representing the block device.") | ||||||
|  |     parser.add_argument('block_size', type=lambda x: int(x, 0), | ||||||
|  |         help="Size of a block in bytes.") | ||||||
|  |     parser.add_argument('block1', nargs='?', default=0, | ||||||
|  |         type=lambda x: int(x, 0), | ||||||
|  |         help="Optional first block address for finding the superblock.") | ||||||
|  |     parser.add_argument('block2', nargs='?', default=1, | ||||||
|  |         type=lambda x: int(x, 0), | ||||||
|  |         help="Optional second block address for finding the superblock.") | ||||||
|  |     parser.add_argument('-l', '--log', action='store_true', | ||||||
|  |         help="Show tags in log.") | ||||||
|  |     parser.add_argument('-a', '--all', action='store_true', | ||||||
|  |         help="Show all tags in log, included tags in corrupted commits.") | ||||||
|  |     parser.add_argument('-T', '--no-truncate', action='store_true', | ||||||
|  |         help="Show the full contents of files/attrs/tags.") | ||||||
|  |     sys.exit(main(parser.parse_args())) | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| #!/usr/bin/env python2 |  | ||||||
|  |  | ||||||
| import struct |  | ||||||
| import sys |  | ||||||
| import time |  | ||||||
| import os |  | ||||||
| import re |  | ||||||
|  |  | ||||||
| def main(): |  | ||||||
|     with open('blocks/.config') as file: |  | ||||||
|         read_size, prog_size, block_size, block_count = ( |  | ||||||
|             struct.unpack('<LLLL', file.read())) |  | ||||||
|  |  | ||||||
|     real_size = sum( |  | ||||||
|         os.path.getsize(os.path.join('blocks', f)) |  | ||||||
|         for f in os.listdir('blocks') if re.match('\d+', f)) |  | ||||||
|  |  | ||||||
|     with open('blocks/.stats') as file: |  | ||||||
|         read_count, prog_count, erase_count = ( |  | ||||||
|             struct.unpack('<QQQ', file.read())) |  | ||||||
|  |  | ||||||
|     runtime = time.time() - os.stat('blocks').st_ctime |  | ||||||
|  |  | ||||||
|     print 'results: %dB %dB %dB %.3fs' % ( |  | ||||||
|         read_count, prog_count, erase_count, runtime) |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": |  | ||||||
|     main(*sys.argv[1:]) |  | ||||||
| @@ -1,96 +0,0 @@ | |||||||
| /// AUTOGENERATED TEST /// |  | ||||||
| #include "lfs2.h" |  | ||||||
| #include "emubd/lfs2_emubd.h" |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // test stuff |  | ||||||
| static void test_assert(const char *file, unsigned line, |  | ||||||
|         const char *s, uintmax_t v, uintmax_t e) {{ |  | ||||||
|     if (v != e) {{ |  | ||||||
|         fprintf(stderr, "\033[97m%s:%u: \033[91m" |  | ||||||
|                 "assert failed with %jd, expected %jd\033[0m\n" |  | ||||||
|                 "    %s\n\n", file, line, v, e, s); |  | ||||||
|         exit(-2); |  | ||||||
|     }} |  | ||||||
| }} |  | ||||||
|  |  | ||||||
| #define test_assert(v, e) \ |  | ||||||
|         test_assert(__FILE__, __LINE__, #v " => " #e, v, e) |  | ||||||
|  |  | ||||||
| // implicit variable for asserts |  | ||||||
| uintmax_t test; |  | ||||||
|  |  | ||||||
| // utility functions for traversals |  | ||||||
| static int __attribute__((used)) test_count(void *p, lfs2_block_t b) {{ |  | ||||||
|     (void)b; |  | ||||||
|     unsigned *u = (unsigned*)p; |  | ||||||
|     *u += 1; |  | ||||||
|     return 0; |  | ||||||
| }} |  | ||||||
|  |  | ||||||
| // lfs2 declarations |  | ||||||
| lfs2_t lfs2; |  | ||||||
| lfs2_emubd_t bd; |  | ||||||
| // other declarations for convenience |  | ||||||
| lfs2_file_t file; |  | ||||||
| lfs2_dir_t dir; |  | ||||||
| struct lfs2_info info; |  | ||||||
| uint8_t buffer[1024]; |  | ||||||
| char path[1024]; |  | ||||||
|  |  | ||||||
| // test configuration options |  | ||||||
| #ifndef LFS2_READ_SIZE |  | ||||||
| #define LFS2_READ_SIZE 16 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifndef LFS2_PROG_SIZE |  | ||||||
| #define LFS2_PROG_SIZE LFS2_READ_SIZE |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifndef LFS2_BLOCK_SIZE |  | ||||||
| #define LFS2_BLOCK_SIZE 512 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifndef LFS2_BLOCK_COUNT |  | ||||||
| #define LFS2_BLOCK_COUNT 1024 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifndef LFS2_BLOCK_CYCLES |  | ||||||
| #define LFS2_BLOCK_CYCLES 1024 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifndef LFS2_CACHE_SIZE |  | ||||||
| #define LFS2_CACHE_SIZE (64 % LFS2_PROG_SIZE == 0 ? 64 : LFS2_PROG_SIZE) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifndef LFS2_LOOKAHEAD_SIZE |  | ||||||
| #define LFS2_LOOKAHEAD_SIZE 16 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| const struct lfs2_config cfg = {{ |  | ||||||
|     .context = &bd, |  | ||||||
|     .read  = &lfs2_emubd_read, |  | ||||||
|     .prog  = &lfs2_emubd_prog, |  | ||||||
|     .erase = &lfs2_emubd_erase, |  | ||||||
|     .sync  = &lfs2_emubd_sync, |  | ||||||
|  |  | ||||||
|     .read_size      = LFS2_READ_SIZE, |  | ||||||
|     .prog_size      = LFS2_PROG_SIZE, |  | ||||||
|     .block_size     = LFS2_BLOCK_SIZE, |  | ||||||
|     .block_count    = LFS2_BLOCK_COUNT, |  | ||||||
|     .block_cycles   = LFS2_BLOCK_CYCLES, |  | ||||||
|     .cache_size     = LFS2_CACHE_SIZE, |  | ||||||
|     .lookahead_size = LFS2_LOOKAHEAD_SIZE, |  | ||||||
| }}; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // Entry point |  | ||||||
| int main(void) {{ |  | ||||||
|     lfs2_emubd_create(&cfg, "blocks"); |  | ||||||
|  |  | ||||||
| {tests} |  | ||||||
|     lfs2_emubd_destroy(&cfg); |  | ||||||
| }} |  | ||||||
							
								
								
									
										819
									
								
								scripts/test.py
									
									
									
									
									
								
							
							
						
						
									
										819
									
								
								scripts/test.py
									
									
									
									
									
								
							| @@ -1,81 +1,778 @@ | |||||||
| #!/usr/bin/env python2 | #!/usr/bin/env python3 | ||||||
|  |  | ||||||
|  | # This script manages littlefs tests, which are configured with | ||||||
|  | # .toml files stored in the tests directory. | ||||||
|  | # | ||||||
|  |  | ||||||
|  | import toml | ||||||
|  | import glob | ||||||
| import re | import re | ||||||
| import sys |  | ||||||
| import subprocess |  | ||||||
| import os | import os | ||||||
|  | import io | ||||||
|  | import itertools as it | ||||||
|  | import collections.abc as abc | ||||||
|  | import subprocess as sp | ||||||
|  | import base64 | ||||||
|  | import sys | ||||||
|  | import copy | ||||||
|  | import shlex | ||||||
|  | import pty | ||||||
|  | import errno | ||||||
|  | import signal | ||||||
|  |  | ||||||
|  | TESTDIR = 'tests' | ||||||
|  | RULES = """ | ||||||
|  | define FLATTEN | ||||||
|  | tests/%$(subst /,.,$(target)): $(target) | ||||||
|  |     ./scripts/explode_asserts.py $$< -o $$@ | ||||||
|  | endef | ||||||
|  | $(foreach target,$(SRC),$(eval $(FLATTEN))) | ||||||
|  |  | ||||||
| def generate(test): | -include tests/*.d | ||||||
|     with open("scripts/template.fmt") as file: |  | ||||||
|         template = file.read() |  | ||||||
|  |  | ||||||
|     haslines = 'TEST_LINE' in os.environ and 'TEST_FILE' in os.environ | .SECONDARY: | ||||||
|  | %.test: %.test.o $(foreach f,$(subst /,.,$(SRC:.c=.o)),%.$f) | ||||||
|  |     $(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ | ||||||
|  | """ | ||||||
|  | GLOBALS = """ | ||||||
|  | //////////////// AUTOGENERATED TEST //////////////// | ||||||
|  | #include "lfs2.h" | ||||||
|  | #include "bd/lfs2_testbd.h" | ||||||
|  | #include <stdio.h> | ||||||
|  | extern const char *lfs2_testbd_path; | ||||||
|  | extern uint32_t lfs2_testbd_cycles; | ||||||
|  | """ | ||||||
|  | DEFINES = { | ||||||
|  |     'LFS2_READ_SIZE': 16, | ||||||
|  |     'LFS2_PROG_SIZE': 'LFS2_READ_SIZE', | ||||||
|  |     'LFS2_BLOCK_SIZE': 512, | ||||||
|  |     'LFS2_BLOCK_COUNT': 1024, | ||||||
|  |     'LFS2_BLOCK_CYCLES': -1, | ||||||
|  |     'LFS2_CACHE_SIZE': '(64 % LFS2_PROG_SIZE == 0 ? 64 : LFS2_PROG_SIZE)', | ||||||
|  |     'LFS2_LOOKAHEAD_SIZE': 16, | ||||||
|  |     'LFS2_ERASE_VALUE': 0xff, | ||||||
|  |     'LFS2_ERASE_CYCLES': 0, | ||||||
|  |     'LFS2_BADBLOCK_BEHAVIOR': 'LFS2_TESTBD_BADBLOCK_PROGERROR', | ||||||
|  | } | ||||||
|  | PROLOGUE = """ | ||||||
|  |     // prologue | ||||||
|  |     __attribute__((unused)) lfs2_t lfs2; | ||||||
|  |     __attribute__((unused)) lfs2_testbd_t bd; | ||||||
|  |     __attribute__((unused)) lfs2_file_t file; | ||||||
|  |     __attribute__((unused)) lfs2_dir_t dir; | ||||||
|  |     __attribute__((unused)) struct lfs2_info info; | ||||||
|  |     __attribute__((unused)) char path[1024]; | ||||||
|  |     __attribute__((unused)) uint8_t buffer[1024]; | ||||||
|  |     __attribute__((unused)) lfs2_size_t size; | ||||||
|  |     __attribute__((unused)) int err; | ||||||
|      |      | ||||||
|     lines = [] |     __attribute__((unused)) const struct lfs2_config cfg = { | ||||||
|     for offset, line in enumerate( |         .context        = &bd, | ||||||
|             re.split('(?<=(?:.;| [{}]))\n', test.read())): |         .read           = lfs2_testbd_read, | ||||||
|         match = re.match('((?: *\n)*)( *)(.*)=>(.*);', |         .prog           = lfs2_testbd_prog, | ||||||
|                 line, re.DOTALL | re.MULTILINE) |         .erase          = lfs2_testbd_erase, | ||||||
|         if match: |         .sync           = lfs2_testbd_sync, | ||||||
|             preface, tab, test, expect = match.groups() |         .read_size      = LFS2_READ_SIZE, | ||||||
|             lines.extend(['']*preface.count('\n')) |         .prog_size      = LFS2_PROG_SIZE, | ||||||
|             lines.append(tab+'test_assert({test}, {expect});'.format( |         .block_size     = LFS2_BLOCK_SIZE, | ||||||
|                 test=test.strip(), expect=expect.strip())) |         .block_count    = LFS2_BLOCK_COUNT, | ||||||
|  |         .block_cycles   = LFS2_BLOCK_CYCLES, | ||||||
|  |         .cache_size     = LFS2_CACHE_SIZE, | ||||||
|  |         .lookahead_size = LFS2_LOOKAHEAD_SIZE, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     __attribute__((unused)) const struct lfs2_testbd_config bdcfg = { | ||||||
|  |         .erase_value        = LFS2_ERASE_VALUE, | ||||||
|  |         .erase_cycles       = LFS2_ERASE_CYCLES, | ||||||
|  |         .badblock_behavior  = LFS2_BADBLOCK_BEHAVIOR, | ||||||
|  |         .power_cycles       = lfs2_testbd_cycles, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     lfs2_testbd_createcfg(&cfg, lfs2_testbd_path, &bdcfg) => 0; | ||||||
|  | """ | ||||||
|  | EPILOGUE = """ | ||||||
|  |     // epilogue | ||||||
|  |     lfs2_testbd_destroy(&cfg) => 0; | ||||||
|  | """ | ||||||
|  | PASS = '\033[32m✓\033[0m' | ||||||
|  | FAIL = '\033[31m✗\033[0m' | ||||||
|  |  | ||||||
|  | class TestFailure(Exception): | ||||||
|  |     def __init__(self, case, returncode=None, stdout=None, assert_=None): | ||||||
|  |         self.case = case | ||||||
|  |         self.returncode = returncode | ||||||
|  |         self.stdout = stdout | ||||||
|  |         self.assert_ = assert_ | ||||||
|  |  | ||||||
|  | class TestCase: | ||||||
|  |     def __init__(self, config, filter=filter, | ||||||
|  |             suite=None, caseno=None, lineno=None, **_): | ||||||
|  |         self.config = config | ||||||
|  |         self.filter = filter | ||||||
|  |         self.suite = suite | ||||||
|  |         self.caseno = caseno | ||||||
|  |         self.lineno = lineno | ||||||
|  |  | ||||||
|  |         self.code = config['code'] | ||||||
|  |         self.code_lineno = config['code_lineno'] | ||||||
|  |         self.defines = config.get('define', {}) | ||||||
|  |         self.if_ = config.get('if', None) | ||||||
|  |         self.in_ = config.get('in', None) | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         if hasattr(self, 'permno'): | ||||||
|  |             if any(k not in self.case.defines for k in self.defines): | ||||||
|  |                 return '%s#%d#%d (%s)' % ( | ||||||
|  |                     self.suite.name, self.caseno, self.permno, ', '.join( | ||||||
|  |                         '%s=%s' % (k, v) for k, v in self.defines.items() | ||||||
|  |                         if k not in self.case.defines)) | ||||||
|             else: |             else: | ||||||
|             lines.append(line) |                 return '%s#%d#%d' % ( | ||||||
|  |                     self.suite.name, self.caseno, self.permno) | ||||||
|  |         else: | ||||||
|  |             return '%s#%d' % ( | ||||||
|  |                 self.suite.name, self.caseno) | ||||||
|  |  | ||||||
|     # Create test file |     def permute(self, class_=None, defines={}, permno=None, **_): | ||||||
|     with open('test.c', 'w') as file: |         ncase = (class_ or type(self))(self.config) | ||||||
|         if 'TEST_LINE' in os.environ and 'TEST_FILE' in os.environ: |         for k, v in self.__dict__.items(): | ||||||
|             lines.insert(0, '#line %d "%s"' % ( |             setattr(ncase, k, v) | ||||||
|                     int(os.environ['TEST_LINE']) + 1, |         ncase.case = self | ||||||
|                     os.environ['TEST_FILE'])) |         ncase.perms = [ncase] | ||||||
|             lines.append('#line %d "test.c"' % ( |         ncase.permno = permno | ||||||
|                     template[:template.find('{tests}')].count('\n') |         ncase.defines = defines | ||||||
|                     + len(lines) + 2)) |         return ncase | ||||||
|  |  | ||||||
|         file.write(template.format(tests='\n'.join(lines))) |     def build(self, f, **_): | ||||||
|  |         # prologue | ||||||
|  |         for k, v in sorted(self.defines.items()): | ||||||
|  |             if k not in self.suite.defines: | ||||||
|  |                 f.write('#define %s %s\n' % (k, v)) | ||||||
|  |  | ||||||
|     # Remove build artifacts to force rebuild |         f.write('void test_case%d(%s) {' % (self.caseno, ','.join( | ||||||
|  |             '\n'+8*' '+'__attribute__((unused)) intmax_t %s' % k | ||||||
|  |             for k in sorted(self.perms[0].defines) | ||||||
|  |             if k not in self.defines))) | ||||||
|  |  | ||||||
|  |         f.write(PROLOGUE) | ||||||
|  |         f.write('\n') | ||||||
|  |         f.write(4*' '+'// test case %d\n' % self.caseno) | ||||||
|  |         f.write(4*' '+'#line %d "%s"\n' % (self.code_lineno, self.suite.path)) | ||||||
|  |  | ||||||
|  |         # test case goes here | ||||||
|  |         f.write(self.code) | ||||||
|  |  | ||||||
|  |         # epilogue | ||||||
|  |         f.write(EPILOGUE) | ||||||
|  |         f.write('}\n') | ||||||
|  |  | ||||||
|  |         for k, v in sorted(self.defines.items()): | ||||||
|  |             if k not in self.suite.defines: | ||||||
|  |                 f.write('#undef %s\n' % k) | ||||||
|  |  | ||||||
|  |     def shouldtest(self, **args): | ||||||
|  |         if (self.filter is not None and | ||||||
|  |                 len(self.filter) >= 1 and | ||||||
|  |                 self.filter[0] != self.caseno): | ||||||
|  |             return False | ||||||
|  |         elif (self.filter is not None and | ||||||
|  |                 len(self.filter) >= 2 and | ||||||
|  |                 self.filter[1] != self.permno): | ||||||
|  |             return False | ||||||
|  |         elif args.get('no_internal', False) and self.in_ is not None: | ||||||
|  |             return False | ||||||
|  |         elif self.if_ is not None: | ||||||
|  |             if_ = self.if_ | ||||||
|  |             while True: | ||||||
|  |                 for k, v in sorted(self.defines.items(), | ||||||
|  |                         key=lambda x: len(x[0]), reverse=True): | ||||||
|  |                     if k in if_: | ||||||
|  |                         if_ = if_.replace(k, '(%s)' % v) | ||||||
|  |                         break | ||||||
|  |                 else: | ||||||
|  |                     break | ||||||
|  |             if_ = ( | ||||||
|  |                 re.sub('(\&\&|\?)', ' and ', | ||||||
|  |                 re.sub('(\|\||:)', ' or ', | ||||||
|  |                 re.sub('!(?!=)', ' not ', if_)))) | ||||||
|  |             return eval(if_) | ||||||
|  |         else: | ||||||
|  |             return True | ||||||
|  |  | ||||||
|  |     def test(self, exec=[], persist=False, cycles=None, | ||||||
|  |             gdb=False, failure=None, disk=None, **args): | ||||||
|  |         # build command | ||||||
|  |         cmd = exec + ['./%s.test' % self.suite.path, | ||||||
|  |             repr(self.caseno), repr(self.permno)] | ||||||
|  |  | ||||||
|  |         # persist disk or keep in RAM for speed? | ||||||
|  |         if persist: | ||||||
|  |             if not disk: | ||||||
|  |                 disk = self.suite.path + '.disk' | ||||||
|  |             if persist != 'noerase': | ||||||
|                 try: |                 try: | ||||||
|         os.remove('test.o') |                     with open(disk, 'w') as f: | ||||||
|         os.remove('lfs2') |                         f.truncate(0) | ||||||
|     except OSError: |                     if args.get('verbose', False): | ||||||
|  |                         print('truncate --size=0', disk) | ||||||
|  |                 except FileNotFoundError: | ||||||
|                     pass |                     pass | ||||||
|  |  | ||||||
| def compile(): |             cmd.append(disk) | ||||||
|     subprocess.check_call([ |  | ||||||
|             os.environ.get('MAKE', 'make'), |  | ||||||
|             '--no-print-directory', '-s']) |  | ||||||
|  |  | ||||||
| def execute(): |         # simulate power-loss after n cycles? | ||||||
|     if 'EXEC' in os.environ: |         if cycles: | ||||||
|         subprocess.check_call([os.environ['EXEC'], "./lfs2"]) |             cmd.append(str(cycles)) | ||||||
|     else: |  | ||||||
|         subprocess.check_call(["./lfs2"]) |  | ||||||
|  |  | ||||||
| def main(test=None): |         # failed? drop into debugger? | ||||||
|  |         if gdb and failure: | ||||||
|  |             ncmd = ['gdb'] | ||||||
|  |             if gdb == 'assert': | ||||||
|  |                 ncmd.extend(['-ex', 'r']) | ||||||
|  |                 if failure.assert_: | ||||||
|  |                     ncmd.extend(['-ex', 'up 2']) | ||||||
|  |             elif gdb == 'main': | ||||||
|  |                 ncmd.extend([ | ||||||
|  |                     '-ex', 'b %s:%d' % (self.suite.path, self.code_lineno), | ||||||
|  |                     '-ex', 'r']) | ||||||
|  |             ncmd.extend(['--args'] + cmd) | ||||||
|  |  | ||||||
|  |             if args.get('verbose', False): | ||||||
|  |                 print(' '.join(shlex.quote(c) for c in ncmd)) | ||||||
|  |             signal.signal(signal.SIGINT, signal.SIG_IGN) | ||||||
|  |             sys.exit(sp.call(ncmd)) | ||||||
|  |  | ||||||
|  |         # run test case! | ||||||
|  |         mpty, spty = pty.openpty() | ||||||
|  |         if args.get('verbose', False): | ||||||
|  |             print(' '.join(shlex.quote(c) for c in cmd)) | ||||||
|  |         proc = sp.Popen(cmd, stdout=spty, stderr=spty) | ||||||
|  |         os.close(spty) | ||||||
|  |         mpty = os.fdopen(mpty, 'r', 1) | ||||||
|  |         stdout = [] | ||||||
|  |         assert_ = None | ||||||
|         try: |         try: | ||||||
|         if test and not test.startswith('-'): |             while True: | ||||||
|             with open(test) as file: |                 try: | ||||||
|                 generate(file) |                     line = mpty.readline() | ||||||
|         else: |                 except OSError as e: | ||||||
|             generate(sys.stdin) |                     if e.errno == errno.EIO: | ||||||
|  |                         break | ||||||
|         compile() |                     raise | ||||||
|  |                 stdout.append(line) | ||||||
|         if test == '-s': |                 if args.get('verbose', False): | ||||||
|             sys.exit(1) |                     sys.stdout.write(line) | ||||||
|  |                 # intercept asserts | ||||||
|         execute() |                 m = re.match( | ||||||
|  |                     '^{0}([^:]+):(\d+):(?:\d+:)?{0}{1}:{0}(.*)$' | ||||||
|     except subprocess.CalledProcessError: |                     .format('(?:\033\[[\d;]*.| )*', 'assert'), | ||||||
|         # Python stack trace is counterproductive, just exit |                     line) | ||||||
|         sys.exit(2) |                 if m and assert_ is None: | ||||||
|  |                     try: | ||||||
|  |                         with open(m.group(1)) as f: | ||||||
|  |                             lineno = int(m.group(2)) | ||||||
|  |                             line = (next(it.islice(f, lineno-1, None)) | ||||||
|  |                                 .strip('\n')) | ||||||
|  |                         assert_ = { | ||||||
|  |                             'path': m.group(1), | ||||||
|  |                             'line': line, | ||||||
|  |                             'lineno': lineno, | ||||||
|  |                             'message': m.group(3)} | ||||||
|  |                     except: | ||||||
|  |                         pass | ||||||
|         except KeyboardInterrupt: |         except KeyboardInterrupt: | ||||||
|         # Python stack trace is counterproductive, just exit |             raise TestFailure(self, 1, stdout, None) | ||||||
|         sys.exit(3) |         proc.wait() | ||||||
|  |  | ||||||
|  |         # did we pass? | ||||||
|  |         if proc.returncode != 0: | ||||||
|  |             raise TestFailure(self, proc.returncode, stdout, assert_) | ||||||
|  |         else: | ||||||
|  |             return PASS | ||||||
|  |  | ||||||
|  | class ValgrindTestCase(TestCase): | ||||||
|  |     def __init__(self, config, **args): | ||||||
|  |         self.leaky = config.get('leaky', False) | ||||||
|  |         super().__init__(config, **args) | ||||||
|  |  | ||||||
|  |     def shouldtest(self, **args): | ||||||
|  |         return not self.leaky and super().shouldtest(**args) | ||||||
|  |  | ||||||
|  |     def test(self, exec=[], **args): | ||||||
|  |         verbose = args.get('verbose', False) | ||||||
|  |         uninit = (self.defines.get('LFS2_ERASE_VALUE', None) == -1) | ||||||
|  |         exec = [ | ||||||
|  |             'valgrind', | ||||||
|  |             '--leak-check=full', | ||||||
|  |             ] + (['--undef-value-errors=no'] if uninit else []) + [ | ||||||
|  |             ] + (['--track-origins=yes'] if not uninit else []) + [ | ||||||
|  |             '--error-exitcode=4', | ||||||
|  |             '--error-limit=no', | ||||||
|  |             ] + (['--num-callers=1'] if not verbose else []) + [ | ||||||
|  |             '-q'] + exec | ||||||
|  |         return super().test(exec=exec, **args) | ||||||
|  |  | ||||||
|  | class ReentrantTestCase(TestCase): | ||||||
|  |     def __init__(self, config, **args): | ||||||
|  |         self.reentrant = config.get('reentrant', False) | ||||||
|  |         super().__init__(config, **args) | ||||||
|  |  | ||||||
|  |     def shouldtest(self, **args): | ||||||
|  |         return self.reentrant and super().shouldtest(**args) | ||||||
|  |  | ||||||
|  |     def test(self, persist=False, gdb=False, failure=None, **args): | ||||||
|  |         for cycles in it.count(1): | ||||||
|  |             # clear disk first? | ||||||
|  |             if cycles == 1 and persist != 'noerase': | ||||||
|  |                 persist = 'erase' | ||||||
|  |             else: | ||||||
|  |                 persist = 'noerase' | ||||||
|  |  | ||||||
|  |             # exact cycle we should drop into debugger? | ||||||
|  |             if gdb and failure and failure.cycleno == cycles: | ||||||
|  |                 return super().test(gdb=gdb, persist=persist, cycles=cycles, | ||||||
|  |                     failure=failure, **args) | ||||||
|  |  | ||||||
|  |             # run tests, but kill the program after prog/erase has | ||||||
|  |             # been hit n cycles. We exit with a special return code if the | ||||||
|  |             # program has not finished, since this isn't a test failure. | ||||||
|  |             try: | ||||||
|  |                 return super().test(persist=persist, cycles=cycles, **args) | ||||||
|  |             except TestFailure as nfailure: | ||||||
|  |                 if nfailure.returncode == 33: | ||||||
|  |                     continue | ||||||
|  |                 else: | ||||||
|  |                     nfailure.cycleno = cycles | ||||||
|  |                     raise | ||||||
|  |  | ||||||
|  | class TestSuite: | ||||||
|  |     def __init__(self, path, classes=[TestCase], defines={}, | ||||||
|  |             filter=None, **args): | ||||||
|  |         self.name = os.path.basename(path) | ||||||
|  |         if self.name.endswith('.toml'): | ||||||
|  |             self.name = self.name[:-len('.toml')] | ||||||
|  |         self.path = path | ||||||
|  |         self.classes = classes | ||||||
|  |         self.defines = defines.copy() | ||||||
|  |         self.filter = filter | ||||||
|  |  | ||||||
|  |         with open(path) as f: | ||||||
|  |             # load tests | ||||||
|  |             config = toml.load(f) | ||||||
|  |  | ||||||
|  |             # find line numbers | ||||||
|  |             f.seek(0) | ||||||
|  |             linenos = [] | ||||||
|  |             code_linenos = [] | ||||||
|  |             for i, line in enumerate(f): | ||||||
|  |                 if re.match(r'\[\[\s*case\s*\]\]', line): | ||||||
|  |                     linenos.append(i+1) | ||||||
|  |                 if re.match(r'code\s*=\s*(\'\'\'|""")', line): | ||||||
|  |                     code_linenos.append(i+2) | ||||||
|  |  | ||||||
|  |             code_linenos.reverse() | ||||||
|  |  | ||||||
|  |         # grab global config | ||||||
|  |         for k, v in config.get('define', {}).items(): | ||||||
|  |             if k not in self.defines: | ||||||
|  |                 self.defines[k] = v | ||||||
|  |         self.code = config.get('code', None) | ||||||
|  |         if self.code is not None: | ||||||
|  |             self.code_lineno = code_linenos.pop() | ||||||
|  |  | ||||||
|  |         # create initial test cases | ||||||
|  |         self.cases = [] | ||||||
|  |         for i, (case, lineno) in enumerate(zip(config['case'], linenos)): | ||||||
|  |             # code lineno? | ||||||
|  |             if 'code' in case: | ||||||
|  |                 case['code_lineno'] = code_linenos.pop() | ||||||
|  |             # merge conditions if necessary | ||||||
|  |             if 'if' in config and 'if' in case: | ||||||
|  |                 case['if'] = '(%s) && (%s)' % (config['if'], case['if']) | ||||||
|  |             elif 'if' in config: | ||||||
|  |                 case['if'] = config['if'] | ||||||
|  |             # initialize test case | ||||||
|  |             self.cases.append(TestCase(case, filter=filter, | ||||||
|  |                 suite=self, caseno=i+1, lineno=lineno, **args)) | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.name | ||||||
|  |  | ||||||
|  |     def __lt__(self, other): | ||||||
|  |         return self.name < other.name | ||||||
|  |  | ||||||
|  |     def permute(self, **args): | ||||||
|  |         for case in self.cases: | ||||||
|  |             # lets find all parameterized definitions, in one of [args.D, | ||||||
|  |             # suite.defines, case.defines, DEFINES]. Note that each of these | ||||||
|  |             # can be either a dict of defines, or a list of dicts, expressing | ||||||
|  |             # an initial set of permutations. | ||||||
|  |             pending = [{}] | ||||||
|  |             for inits in [self.defines, case.defines, DEFINES]: | ||||||
|  |                 if not isinstance(inits, list): | ||||||
|  |                     inits = [inits] | ||||||
|  |  | ||||||
|  |                 npending = [] | ||||||
|  |                 for init, pinit in it.product(inits, pending): | ||||||
|  |                     ninit = pinit.copy() | ||||||
|  |                     for k, v in init.items(): | ||||||
|  |                         if k not in ninit: | ||||||
|  |                             try: | ||||||
|  |                                 ninit[k] = eval(v) | ||||||
|  |                             except: | ||||||
|  |                                 ninit[k] = v | ||||||
|  |                     npending.append(ninit) | ||||||
|  |  | ||||||
|  |                 pending = npending | ||||||
|  |  | ||||||
|  |             # expand permutations | ||||||
|  |             pending = list(reversed(pending)) | ||||||
|  |             expanded = [] | ||||||
|  |             while pending: | ||||||
|  |                 perm = pending.pop() | ||||||
|  |                 for k, v in sorted(perm.items()): | ||||||
|  |                     if not isinstance(v, str) and isinstance(v, abc.Iterable): | ||||||
|  |                         for nv in reversed(v): | ||||||
|  |                             nperm = perm.copy() | ||||||
|  |                             nperm[k] = nv | ||||||
|  |                             pending.append(nperm) | ||||||
|  |                         break | ||||||
|  |                 else: | ||||||
|  |                     expanded.append(perm) | ||||||
|  |  | ||||||
|  |             # generate permutations | ||||||
|  |             case.perms = [] | ||||||
|  |             for i, (class_, defines) in enumerate( | ||||||
|  |                     it.product(self.classes, expanded)): | ||||||
|  |                 case.perms.append(case.permute( | ||||||
|  |                     class_, defines, permno=i+1, **args)) | ||||||
|  |  | ||||||
|  |             # also track non-unique defines | ||||||
|  |             case.defines = {} | ||||||
|  |             for k, v in case.perms[0].defines.items(): | ||||||
|  |                 if all(perm.defines[k] == v for perm in case.perms): | ||||||
|  |                     case.defines[k] = v | ||||||
|  |  | ||||||
|  |         # track all perms and non-unique defines | ||||||
|  |         self.perms = [] | ||||||
|  |         for case in self.cases: | ||||||
|  |             self.perms.extend(case.perms) | ||||||
|  |  | ||||||
|  |         self.defines = {} | ||||||
|  |         for k, v in self.perms[0].defines.items(): | ||||||
|  |             if all(perm.defines.get(k, None) == v for perm in self.perms): | ||||||
|  |                 self.defines[k] = v | ||||||
|  |  | ||||||
|  |         return self.perms | ||||||
|  |  | ||||||
|  |     def build(self, **args): | ||||||
|  |         # build test files | ||||||
|  |         tf = open(self.path + '.test.c.t', 'w') | ||||||
|  |         tf.write(GLOBALS) | ||||||
|  |         if self.code is not None: | ||||||
|  |             tf.write('#line %d "%s"\n' % (self.code_lineno, self.path)) | ||||||
|  |             tf.write(self.code) | ||||||
|  |  | ||||||
|  |         tfs = {None: tf} | ||||||
|  |         for case in self.cases: | ||||||
|  |             if case.in_ not in tfs: | ||||||
|  |                 tfs[case.in_] = open(self.path+'.'+ | ||||||
|  |                     case.in_.replace('/', '.')+'.t', 'w') | ||||||
|  |                 tfs[case.in_].write('#line 1 "%s"\n' % case.in_) | ||||||
|  |                 with open(case.in_) as f: | ||||||
|  |                     for line in f: | ||||||
|  |                         tfs[case.in_].write(line) | ||||||
|  |                 tfs[case.in_].write('\n') | ||||||
|  |                 tfs[case.in_].write(GLOBALS) | ||||||
|  |  | ||||||
|  |             tfs[case.in_].write('\n') | ||||||
|  |             case.build(tfs[case.in_], **args) | ||||||
|  |  | ||||||
|  |         tf.write('\n') | ||||||
|  |         tf.write('const char *lfs2_testbd_path;\n') | ||||||
|  |         tf.write('uint32_t lfs2_testbd_cycles;\n') | ||||||
|  |         tf.write('int main(int argc, char **argv) {\n') | ||||||
|  |         tf.write(4*' '+'int case_         = (argc > 1) ? atoi(argv[1]) : 0;\n') | ||||||
|  |         tf.write(4*' '+'int perm          = (argc > 2) ? atoi(argv[2]) : 0;\n') | ||||||
|  |         tf.write(4*' '+'lfs2_testbd_path   = (argc > 3) ? argv[3] : NULL;\n') | ||||||
|  |         tf.write(4*' '+'lfs2_testbd_cycles = (argc > 4) ? atoi(argv[4]) : 0;\n') | ||||||
|  |         for perm in self.perms: | ||||||
|  |             # test declaration | ||||||
|  |             tf.write(4*' '+'extern void test_case%d(%s);\n' % ( | ||||||
|  |                 perm.caseno, ', '.join( | ||||||
|  |                     'intmax_t %s' % k for k in sorted(perm.defines) | ||||||
|  |                     if k not in perm.case.defines))) | ||||||
|  |             # test call | ||||||
|  |             tf.write(4*' '+ | ||||||
|  |                 'if (argc < 3 || (case_ == %d && perm == %d)) {' | ||||||
|  |                 ' test_case%d(%s); ' | ||||||
|  |                 '}\n' % (perm.caseno, perm.permno, perm.caseno, ', '.join( | ||||||
|  |                     str(v) for k, v in sorted(perm.defines.items()) | ||||||
|  |                     if k not in perm.case.defines))) | ||||||
|  |         tf.write('}\n') | ||||||
|  |  | ||||||
|  |         for tf in tfs.values(): | ||||||
|  |             tf.close() | ||||||
|  |  | ||||||
|  |         # write makefiles | ||||||
|  |         with open(self.path + '.mk', 'w') as mk: | ||||||
|  |             mk.write(RULES.replace(4*' ', '\t')) | ||||||
|  |             mk.write('\n') | ||||||
|  |  | ||||||
|  |             # add truely global defines globally | ||||||
|  |             for k, v in sorted(self.defines.items()): | ||||||
|  |                 mk.write('%s: override CFLAGS += -D%s=%r\n' % ( | ||||||
|  |                     self.path+'.test', k, v)) | ||||||
|  |  | ||||||
|  |             for path in tfs: | ||||||
|  |                 if path is None: | ||||||
|  |                     mk.write('%s: %s | %s\n' % ( | ||||||
|  |                         self.path+'.test.c', | ||||||
|  |                         self.path, | ||||||
|  |                         self.path+'.test.c.t')) | ||||||
|  |                 else: | ||||||
|  |                     mk.write('%s: %s %s | %s\n' % ( | ||||||
|  |                         self.path+'.'+path.replace('/', '.'), | ||||||
|  |                         self.path, path, | ||||||
|  |                         self.path+'.'+path.replace('/', '.')+'.t')) | ||||||
|  |                 mk.write('\t./scripts/explode_asserts.py $| -o $@\n') | ||||||
|  |  | ||||||
|  |         self.makefile = self.path + '.mk' | ||||||
|  |         self.target = self.path + '.test' | ||||||
|  |         return self.makefile, self.target | ||||||
|  |  | ||||||
|  |     def test(self, **args): | ||||||
|  |         # run test suite! | ||||||
|  |         if not args.get('verbose', True): | ||||||
|  |             sys.stdout.write(self.name + ' ') | ||||||
|  |             sys.stdout.flush() | ||||||
|  |         for perm in self.perms: | ||||||
|  |             if not perm.shouldtest(**args): | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             try: | ||||||
|  |                 result = perm.test(**args) | ||||||
|  |             except TestFailure as failure: | ||||||
|  |                 perm.result = failure | ||||||
|  |                 if not args.get('verbose', True): | ||||||
|  |                     sys.stdout.write(FAIL) | ||||||
|  |                     sys.stdout.flush() | ||||||
|  |                 if not args.get('keep_going', False): | ||||||
|  |                     if not args.get('verbose', True): | ||||||
|  |                         sys.stdout.write('\n') | ||||||
|  |                     raise | ||||||
|  |             else: | ||||||
|  |                 perm.result = PASS | ||||||
|  |                 if not args.get('verbose', True): | ||||||
|  |                     sys.stdout.write(PASS) | ||||||
|  |                     sys.stdout.flush() | ||||||
|  |  | ||||||
|  |         if not args.get('verbose', True): | ||||||
|  |             sys.stdout.write('\n') | ||||||
|  |  | ||||||
|  | def main(**args): | ||||||
|  |     # figure out explicit defines | ||||||
|  |     defines = {} | ||||||
|  |     for define in args['D']: | ||||||
|  |         k, v, *_ = define.split('=', 2) + [''] | ||||||
|  |         defines[k] = v | ||||||
|  |  | ||||||
|  |     # and what class of TestCase to run | ||||||
|  |     classes = [] | ||||||
|  |     if args.get('normal', False): | ||||||
|  |         classes.append(TestCase) | ||||||
|  |     if args.get('reentrant', False): | ||||||
|  |         classes.append(ReentrantTestCase) | ||||||
|  |     if args.get('valgrind', False): | ||||||
|  |         classes.append(ValgrindTestCase) | ||||||
|  |     if not classes: | ||||||
|  |         classes = [TestCase] | ||||||
|  |  | ||||||
|  |     suites = [] | ||||||
|  |     for testpath in args['testpaths']: | ||||||
|  |         # optionally specified test case/perm | ||||||
|  |         testpath, *filter = testpath.split('#') | ||||||
|  |         filter = [int(f) for f in filter] | ||||||
|  |  | ||||||
|  |         # figure out the suite's toml file | ||||||
|  |         if os.path.isdir(testpath): | ||||||
|  |             testpath = testpath + '/test_*.toml' | ||||||
|  |         elif os.path.isfile(testpath): | ||||||
|  |             testpath = testpath | ||||||
|  |         elif testpath.endswith('.toml'): | ||||||
|  |             testpath = TESTDIR + '/' + testpath | ||||||
|  |         else: | ||||||
|  |             testpath = TESTDIR + '/' + testpath + '.toml' | ||||||
|  |  | ||||||
|  |         # find tests | ||||||
|  |         for path in glob.glob(testpath): | ||||||
|  |             suites.append(TestSuite(path, classes, defines, filter, **args)) | ||||||
|  |  | ||||||
|  |     # sort for reproducability | ||||||
|  |     suites = sorted(suites) | ||||||
|  |  | ||||||
|  |     # generate permutations | ||||||
|  |     for suite in suites: | ||||||
|  |         suite.permute(**args) | ||||||
|  |  | ||||||
|  |     # build tests in parallel | ||||||
|  |     print('====== building ======') | ||||||
|  |     makefiles = [] | ||||||
|  |     targets = [] | ||||||
|  |     for suite in suites: | ||||||
|  |         makefile, target = suite.build(**args) | ||||||
|  |         makefiles.append(makefile) | ||||||
|  |         targets.append(target) | ||||||
|  |  | ||||||
|  |     cmd = (['make', '-f', 'Makefile'] + | ||||||
|  |         list(it.chain.from_iterable(['-f', m] for m in makefiles)) + | ||||||
|  |         [target for target in targets]) | ||||||
|  |     mpty, spty = pty.openpty() | ||||||
|  |     if args.get('verbose', False): | ||||||
|  |         print(' '.join(shlex.quote(c) for c in cmd)) | ||||||
|  |     proc = sp.Popen(cmd, stdout=spty, stderr=spty) | ||||||
|  |     os.close(spty) | ||||||
|  |     mpty = os.fdopen(mpty, 'r', 1) | ||||||
|  |     stdout = [] | ||||||
|  |     while True: | ||||||
|  |         try: | ||||||
|  |             line = mpty.readline() | ||||||
|  |         except OSError as e: | ||||||
|  |             if e.errno == errno.EIO: | ||||||
|  |                 break | ||||||
|  |             raise | ||||||
|  |         stdout.append(line) | ||||||
|  |         if args.get('verbose', False): | ||||||
|  |             sys.stdout.write(line) | ||||||
|  |         # intercept warnings | ||||||
|  |         m = re.match( | ||||||
|  |             '^{0}([^:]+):(\d+):(?:\d+:)?{0}{1}:{0}(.*)$' | ||||||
|  |             .format('(?:\033\[[\d;]*.| )*', 'warning'), | ||||||
|  |             line) | ||||||
|  |         if m and not args.get('verbose', False): | ||||||
|  |             try: | ||||||
|  |                 with open(m.group(1)) as f: | ||||||
|  |                     lineno = int(m.group(2)) | ||||||
|  |                     line = next(it.islice(f, lineno-1, None)).strip('\n') | ||||||
|  |                 sys.stdout.write( | ||||||
|  |                     "\033[01m{path}:{lineno}:\033[01;35mwarning:\033[m " | ||||||
|  |                     "{message}\n{line}\n\n".format( | ||||||
|  |                         path=m.group(1), line=line, lineno=lineno, | ||||||
|  |                         message=m.group(3))) | ||||||
|  |             except: | ||||||
|  |                 pass | ||||||
|  |     proc.wait() | ||||||
|  |  | ||||||
|  |     if proc.returncode != 0: | ||||||
|  |         if not args.get('verbose', False): | ||||||
|  |             for line in stdout: | ||||||
|  |                 sys.stdout.write(line) | ||||||
|  |         sys.exit(-3) | ||||||
|  |  | ||||||
|  |     print('built %d test suites, %d test cases, %d permutations' % ( | ||||||
|  |         len(suites), | ||||||
|  |         sum(len(suite.cases) for suite in suites), | ||||||
|  |         sum(len(suite.perms) for suite in suites))) | ||||||
|  |  | ||||||
|  |     filtered = 0 | ||||||
|  |     for suite in suites: | ||||||
|  |         for perm in suite.perms: | ||||||
|  |             filtered += perm.shouldtest(**args) | ||||||
|  |     if filtered != sum(len(suite.perms) for suite in suites): | ||||||
|  |         print('filtered down to %d permutations' % filtered) | ||||||
|  |  | ||||||
|  |     # only requested to build? | ||||||
|  |     if args.get('build', False): | ||||||
|  |         return 0 | ||||||
|  |  | ||||||
|  |     print('====== testing ======') | ||||||
|  |     try: | ||||||
|  |         for suite in suites: | ||||||
|  |             suite.test(**args) | ||||||
|  |     except TestFailure: | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  |     print('====== results ======') | ||||||
|  |     passed = 0 | ||||||
|  |     failed = 0 | ||||||
|  |     for suite in suites: | ||||||
|  |         for perm in suite.perms: | ||||||
|  |             if not hasattr(perm, 'result'): | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             if perm.result == PASS: | ||||||
|  |                 passed += 1 | ||||||
|  |             else: | ||||||
|  |                 sys.stdout.write( | ||||||
|  |                     "\033[01m{path}:{lineno}:\033[01;31mfailure:\033[m " | ||||||
|  |                     "{perm} failed with {returncode}\n".format( | ||||||
|  |                         perm=perm, path=perm.suite.path, lineno=perm.lineno, | ||||||
|  |                         returncode=perm.result.returncode or 0)) | ||||||
|  |                 if perm.result.stdout: | ||||||
|  |                     if perm.result.assert_: | ||||||
|  |                         stdout = perm.result.stdout[:-1] | ||||||
|  |                     else: | ||||||
|  |                         stdout = perm.result.stdout | ||||||
|  |                     for line in stdout[-5:]: | ||||||
|  |                         sys.stdout.write(line) | ||||||
|  |                 if perm.result.assert_: | ||||||
|  |                     sys.stdout.write( | ||||||
|  |                         "\033[01m{path}:{lineno}:\033[01;31massert:\033[m " | ||||||
|  |                         "{message}\n{line}\n".format( | ||||||
|  |                             **perm.result.assert_)) | ||||||
|  |                 sys.stdout.write('\n') | ||||||
|  |                 failed += 1 | ||||||
|  |  | ||||||
|  |     if args.get('gdb', False): | ||||||
|  |         failure = None | ||||||
|  |         for suite in suites: | ||||||
|  |             for perm in suite.perms: | ||||||
|  |                 if getattr(perm, 'result', PASS) != PASS: | ||||||
|  |                     failure = perm.result | ||||||
|  |         if failure is not None: | ||||||
|  |             print('======= gdb ======') | ||||||
|  |             # drop into gdb | ||||||
|  |             failure.case.test(failure=failure, **args) | ||||||
|  |             sys.exit(0) | ||||||
|  |  | ||||||
|  |     print('tests passed: %d' % passed) | ||||||
|  |     print('tests failed: %d' % failed) | ||||||
|  |     return 1 if failed > 0 else 0 | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     main(*sys.argv[1:]) |     import argparse | ||||||
|  |     parser = argparse.ArgumentParser( | ||||||
|  |         description="Run parameterized tests in various configurations.") | ||||||
|  |     parser.add_argument('testpaths', nargs='*', default=[TESTDIR], | ||||||
|  |         help="Description of test(s) to run. By default, this is all tests \ | ||||||
|  |             found in the \"{0}\" directory. Here, you can specify a different \ | ||||||
|  |             directory of tests, a specific file, a suite by name, and even a \ | ||||||
|  |             specific test case by adding brackets. For example \ | ||||||
|  |             \"test_dirs[0]\" or \"{0}/test_dirs.toml[0]\".".format(TESTDIR)) | ||||||
|  |     parser.add_argument('-D', action='append', default=[], | ||||||
|  |         help="Overriding parameter definitions.") | ||||||
|  |     parser.add_argument('-v', '--verbose', action='store_true', | ||||||
|  |         help="Output everything that is happening.") | ||||||
|  |     parser.add_argument('-k', '--keep-going', action='store_true', | ||||||
|  |         help="Run all tests instead of stopping on first error. Useful for CI.") | ||||||
|  |     parser.add_argument('-p', '--persist', choices=['erase', 'noerase'], | ||||||
|  |         nargs='?', const='erase', | ||||||
|  |         help="Store disk image in a file.") | ||||||
|  |     parser.add_argument('-b', '--build', action='store_true', | ||||||
|  |         help="Only build the tests, do not execute.") | ||||||
|  |     parser.add_argument('-g', '--gdb', choices=['init', 'main', 'assert'], | ||||||
|  |         nargs='?', const='assert', | ||||||
|  |         help="Drop into gdb on test failure.") | ||||||
|  |     parser.add_argument('--no-internal', action='store_true', | ||||||
|  |         help="Don't run tests that require internal knowledge.") | ||||||
|  |     parser.add_argument('-n', '--normal', action='store_true', | ||||||
|  |         help="Run tests normally.") | ||||||
|  |     parser.add_argument('-r', '--reentrant', action='store_true', | ||||||
|  |         help="Run reentrant tests with simulated power-loss.") | ||||||
|  |     parser.add_argument('-V', '--valgrind', action='store_true', | ||||||
|  |         help="Run non-leaky tests under valgrind to check for memory leaks.") | ||||||
|  |     parser.add_argument('-e', '--exec', default=[], type=lambda e: e.split(' '), | ||||||
|  |         help="Run tests with another executable prefixed on the command line.") | ||||||
|  |     parser.add_argument('-d', '--disk', | ||||||
|  |         help="Specify a file to use for persistent/reentrant tests.") | ||||||
|  |     sys.exit(main(**vars(parser.parse_args()))) | ||||||
|   | |||||||
							
								
								
									
										435
									
								
								tests/test_alloc.sh → tests/test_alloc.toml
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										435
									
								
								tests/test_alloc.sh → tests/test_alloc.toml
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @@ -1,127 +1,201 @@ | |||||||
| #!/bin/bash | # allocator tests | ||||||
| set -euE | # note for these to work there are a number constraints on the device geometry | ||||||
| export TEST_FILE=$0 | if = 'LFS2_BLOCK_CYCLES == -1' | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG | 
 | ||||||
|  | [[case]] # parallel allocation test | ||||||
|  | define.FILES = 3 | ||||||
|  | define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)' | ||||||
|  | code = ''' | ||||||
|  |     const char *names[FILES] = {"bacon", "eggs", "pancakes"}; | ||||||
|  |     lfs2_file_t files[FILES]; | ||||||
| 
 | 
 | ||||||
| echo "=== Allocator tests ===" |  | ||||||
| rm -rf blocks |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
| TEST |  | ||||||
| 
 |  | ||||||
| SIZE=15000 |  | ||||||
| 
 |  | ||||||
| lfs2_mkdir() { |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     lfs2_mkdir(&lfs2, "$1") => 0; |     lfs2_mkdir(&lfs2, "breakfast") => 0; | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| lfs2_remove() { |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     lfs2_remove(&lfs2, "$1/eggs") => 0; |     for (int n = 0; n < FILES; n++) { | ||||||
|     lfs2_remove(&lfs2, "$1/bacon") => 0; |         sprintf(path, "breakfast/%s", names[n]); | ||||||
|     lfs2_remove(&lfs2, "$1/pancakes") => 0; |  | ||||||
|     lfs2_remove(&lfs2, "$1") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| lfs2_alloc_singleproc() { |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     const char *names[] = {"bacon", "eggs", "pancakes"}; |  | ||||||
|     lfs2_file_t files[sizeof(names)/sizeof(names[0])]; |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { |  | ||||||
|         sprintf(path, "$1/%s", names[n]); |  | ||||||
|         lfs2_file_open(&lfs2, &files[n], path, |         lfs2_file_open(&lfs2, &files[n], path, | ||||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||||
|     } |     } | ||||||
|     for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { |     for (int n = 0; n < FILES; n++) { | ||||||
|         lfs2_size_t size = strlen(names[n]); |         size = strlen(names[n]); | ||||||
|         for (int i = 0; i < $SIZE; i++) { |         for (lfs2_size_t i = 0; i < SIZE; i += size) { | ||||||
|             lfs2_file_write(&lfs2, &files[n], names[n], size) => size; |             lfs2_file_write(&lfs2, &files[n], names[n], size) => size; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) { |     for (int n = 0; n < FILES; n++) { | ||||||
|         lfs2_file_close(&lfs2, &files[n]) => 0; |         lfs2_file_close(&lfs2, &files[n]) => 0; | ||||||
|     } |     } | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| lfs2_alloc_multiproc() { |  | ||||||
| for name in bacon eggs pancakes |  | ||||||
| do |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     lfs2_file_open(&lfs2, &file, "$1/$name", |     for (int n = 0; n < FILES; n++) { | ||||||
|  |         sprintf(path, "breakfast/%s", names[n]); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |         size = strlen(names[n]); | ||||||
|  |         for (lfs2_size_t i = 0; i < SIZE; i += size) { | ||||||
|  |             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |             assert(memcmp(buffer, names[n], size) == 0); | ||||||
|  |         } | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | [[case]] # serial allocation test | ||||||
|  | define.FILES = 3 | ||||||
|  | define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)' | ||||||
|  | code = ''' | ||||||
|  |     const char *names[FILES] = {"bacon", "eggs", "pancakes"}; | ||||||
|  | 
 | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "breakfast") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | 
 | ||||||
|  |     for (int n = 0; n < FILES; n++) { | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         sprintf(path, "breakfast/%s", names[n]); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, | ||||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||||
|     lfs2_size_t size = strlen("$name"); |         size = strlen(names[n]); | ||||||
|     memcpy(buffer, "$name", size); |         memcpy(buffer, names[n], size); | ||||||
|     for (int i = 0; i < $SIZE; i++) { |         for (int i = 0; i < SIZE; i += size) { | ||||||
|             lfs2_file_write(&lfs2, &file, buffer, size) => size; |             lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|         } |         } | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|         lfs2_unmount(&lfs2) => 0; |         lfs2_unmount(&lfs2) => 0; | ||||||
| TEST |  | ||||||
| done |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| lfs2_verify() { |  | ||||||
| for name in bacon eggs pancakes |  | ||||||
| do |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     lfs2_file_open(&lfs2, &file, "$1/$name", LFS2_O_RDONLY) => 0; |     for (int n = 0; n < FILES; n++) { | ||||||
|     lfs2_size_t size = strlen("$name"); |         sprintf(path, "breakfast/%s", names[n]); | ||||||
|     for (int i = 0; i < $SIZE; i++) { |         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |         size = strlen(names[n]); | ||||||
|  |         for (int i = 0; i < SIZE; i += size) { | ||||||
|             lfs2_file_read(&lfs2, &file, buffer, size) => size; |             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|         memcmp(buffer, "$name", size) => 0; |             assert(memcmp(buffer, names[n], size) == 0); | ||||||
|  |         } | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | [[case]] # parallel allocation reuse test | ||||||
|  | define.FILES = 3 | ||||||
|  | define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)' | ||||||
|  | define.CYCLES = [1, 10] | ||||||
|  | code = ''' | ||||||
|  |     const char *names[FILES] = {"bacon", "eggs", "pancakes"}; | ||||||
|  |     lfs2_file_t files[FILES]; | ||||||
|  | 
 | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  | 
 | ||||||
|  |     for (int c = 0; c < CYCLES; c++) { | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mkdir(&lfs2, "breakfast") => 0; | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  | 
 | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         for (int n = 0; n < FILES; n++) { | ||||||
|  |             sprintf(path, "breakfast/%s", names[n]); | ||||||
|  |             lfs2_file_open(&lfs2, &files[n], path, | ||||||
|  |                     LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||||
|  |         } | ||||||
|  |         for (int n = 0; n < FILES; n++) { | ||||||
|  |             size = strlen(names[n]); | ||||||
|  |             for (int i = 0; i < SIZE; i += size) { | ||||||
|  |                 lfs2_file_write(&lfs2, &files[n], names[n], size) => size; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         for (int n = 0; n < FILES; n++) { | ||||||
|  |             lfs2_file_close(&lfs2, &files[n]) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  | 
 | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         for (int n = 0; n < FILES; n++) { | ||||||
|  |             sprintf(path, "breakfast/%s", names[n]); | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |             size = strlen(names[n]); | ||||||
|  |             for (int i = 0; i < SIZE; i += size) { | ||||||
|  |                 lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |                 assert(memcmp(buffer, names[n], size) == 0); | ||||||
|  |             } | ||||||
|  |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  | 
 | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         for (int n = 0; n < FILES; n++) { | ||||||
|  |             sprintf(path, "breakfast/%s", names[n]); | ||||||
|  |             lfs2_remove(&lfs2, path) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_remove(&lfs2, "breakfast") => 0; | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |     } | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | [[case]] # serial allocation reuse test | ||||||
|  | define.FILES = 3 | ||||||
|  | define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-6)) / FILES)' | ||||||
|  | define.CYCLES = [1, 10] | ||||||
|  | code = ''' | ||||||
|  |     const char *names[FILES] = {"bacon", "eggs", "pancakes"}; | ||||||
|  | 
 | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  | 
 | ||||||
|  |     for (int c = 0; c < CYCLES; c++) { | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mkdir(&lfs2, "breakfast") => 0; | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  | 
 | ||||||
|  |         for (int n = 0; n < FILES; n++) { | ||||||
|  |             lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |             sprintf(path, "breakfast/%s", names[n]); | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                     LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||||
|  |             size = strlen(names[n]); | ||||||
|  |             memcpy(buffer, names[n], size); | ||||||
|  |             for (int i = 0; i < SIZE; i += size) { | ||||||
|  |                 lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|             } |             } | ||||||
|             lfs2_file_close(&lfs2, &file) => 0; |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|             lfs2_unmount(&lfs2) => 0; |             lfs2_unmount(&lfs2) => 0; | ||||||
| TEST |  | ||||||
| done |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| echo "--- Single-process allocation test ---" |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
| lfs2_mkdir singleproc |         for (int n = 0; n < FILES; n++) { | ||||||
| lfs2_alloc_singleproc singleproc |             sprintf(path, "breakfast/%s", names[n]); | ||||||
| lfs2_verify singleproc |             lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |             size = strlen(names[n]); | ||||||
|  |             for (int i = 0; i < SIZE; i += size) { | ||||||
|  |                 lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |                 assert(memcmp(buffer, names[n], size) == 0); | ||||||
|  |             } | ||||||
|  |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
| 
 | 
 | ||||||
| echo "--- Multi-process allocation test ---" |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
| lfs2_mkdir multiproc |         for (int n = 0; n < FILES; n++) { | ||||||
| lfs2_alloc_multiproc multiproc |             sprintf(path, "breakfast/%s", names[n]); | ||||||
| lfs2_verify multiproc |             lfs2_remove(&lfs2, path) => 0; | ||||||
| lfs2_verify singleproc |         } | ||||||
|  |         lfs2_remove(&lfs2, "breakfast") => 0; | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |     } | ||||||
|  | ''' | ||||||
| 
 | 
 | ||||||
| echo "--- Single-process reuse test ---" | [[case]] # exhaustion test | ||||||
| lfs2_remove singleproc | code = ''' | ||||||
| lfs2_mkdir singleprocreuse |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
| lfs2_alloc_singleproc singleprocreuse |  | ||||||
| lfs2_verify singleprocreuse |  | ||||||
| lfs2_verify multiproc |  | ||||||
| 
 |  | ||||||
| echo "--- Multi-process reuse test ---" |  | ||||||
| lfs2_remove multiproc |  | ||||||
| lfs2_mkdir multiprocreuse |  | ||||||
| lfs2_alloc_singleproc multiprocreuse |  | ||||||
| lfs2_verify multiprocreuse |  | ||||||
| lfs2_verify singleprocreuse |  | ||||||
| 
 |  | ||||||
| echo "--- Cleanup ---" |  | ||||||
| lfs2_remove multiprocreuse |  | ||||||
| lfs2_remove singleprocreuse |  | ||||||
| 
 |  | ||||||
| echo "--- Exhaustion test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); |     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||||
|     lfs2_size_t size = strlen("exhaustion"); |     size = strlen("exhaustion"); | ||||||
|     memcpy(buffer, "exhaustion", size); |     memcpy(buffer, "exhaustion", size); | ||||||
|     lfs2_file_write(&lfs2, &file, buffer, size) => size; |     lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|     lfs2_file_sync(&lfs2, &file) => 0; |     lfs2_file_sync(&lfs2, &file) => 0; | ||||||
| @@ -141,27 +215,27 @@ scripts/test.py << TEST | |||||||
| 
 | 
 | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | 
 | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_RDONLY); |     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_RDONLY); | ||||||
|     lfs2_size_t size = strlen("exhaustion"); |     size = strlen("exhaustion"); | ||||||
|     lfs2_file_size(&lfs2, &file) => size; |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|     memcmp(buffer, "exhaustion", size) => 0; |     memcmp(buffer, "exhaustion", size) => 0; | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | ''' | ||||||
| 
 | 
 | ||||||
| echo "--- Exhaustion wraparound test ---" | [[case]] # exhaustion wraparound test | ||||||
| scripts/test.py << TEST | define.SIZE = '(((LFS2_BLOCK_SIZE-8)*(LFS2_BLOCK_COUNT-4)) / 3)' | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     lfs2_remove(&lfs2, "exhaustion") => 0; |  | ||||||
| 
 | 
 | ||||||
|     lfs2_file_open(&lfs2, &file, "padding", LFS2_O_WRONLY | LFS2_O_CREAT); |     lfs2_file_open(&lfs2, &file, "padding", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||||
|     lfs2_size_t size = strlen("buffering"); |     size = strlen("buffering"); | ||||||
|     memcpy(buffer, "buffering", size); |     memcpy(buffer, "buffering", size); | ||||||
|     for (int i = 0; i < $SIZE; i++) { |     for (int i = 0; i < SIZE; i += size) { | ||||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; |         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|     } |     } | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
| @@ -188,30 +262,29 @@ scripts/test.py << TEST | |||||||
| 
 | 
 | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | 
 | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_RDONLY); |     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_RDONLY); | ||||||
|     lfs2_size_t size = strlen("exhaustion"); |     size = strlen("exhaustion"); | ||||||
|     lfs2_file_size(&lfs2, &file) => size; |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|     memcmp(buffer, "exhaustion", size) => 0; |     memcmp(buffer, "exhaustion", size) => 0; | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|     lfs2_remove(&lfs2, "exhaustion") => 0; |     lfs2_remove(&lfs2, "exhaustion") => 0; | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | ''' | ||||||
| 
 | 
 | ||||||
| echo "--- Dir exhaustion test ---" | [[case]] # dir exhaustion test | ||||||
| scripts/test.py << TEST | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
| 
 | 
 | ||||||
|     // find out max file size |     // find out max file size | ||||||
|     lfs2_mkdir(&lfs2, "exhaustiondir") => 0; |     lfs2_mkdir(&lfs2, "exhaustiondir") => 0; | ||||||
|     lfs2_size_t size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); |     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||||
|     int count = 0; |     int count = 0; | ||||||
|     int err; |  | ||||||
|     while (true) { |     while (true) { | ||||||
|         err = lfs2_file_write(&lfs2, &file, buffer, size); |         err = lfs2_file_write(&lfs2, &file, buffer, size); | ||||||
|         if (err < 0) { |         if (err < 0) { | ||||||
| @@ -248,18 +321,102 @@ scripts/test.py << TEST | |||||||
| 
 | 
 | ||||||
|     lfs2_remove(&lfs2, "exhaustion") => 0; |     lfs2_remove(&lfs2, "exhaustion") => 0; | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | ''' | ||||||
| 
 | 
 | ||||||
| ## Below, these tests depend _very_ heavily on the geometry of the | [[case]] # what if we have a bad block during an allocation scan? | ||||||
| ## block device being tested, they should be removed and replaced | in = "lfs2.c" | ||||||
| ## by generalized tests. For now we'll just skip if the geometry | define.LFS2_ERASE_CYCLES = 0xffffffff | ||||||
| ## is customized. | define.LFS2_BADBLOCK_BEHAVIOR = 'LFS2_TESTBD_BADBLOCK_READERROR' | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     // first fill to exhaustion to find available space | ||||||
|  |     lfs2_file_open(&lfs2, &file, "pacman", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     strcpy((char*)buffer, "waka"); | ||||||
|  |     size = strlen("waka"); | ||||||
|  |     lfs2_size_t filesize = 0; | ||||||
|  |     while (true) { | ||||||
|  |         lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, buffer, size); | ||||||
|  |         assert(res == (lfs2_ssize_t)size || res == LFS2_ERR_NOSPC); | ||||||
|  |         if (res == LFS2_ERR_NOSPC) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         filesize += size; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // now fill all but a couple of blocks of the filesystem with data | ||||||
|  |     filesize -= 3*LFS2_BLOCK_SIZE; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "pacman", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     strcpy((char*)buffer, "waka"); | ||||||
|  |     size = strlen("waka"); | ||||||
|  |     for (lfs2_size_t i = 0; i < filesize/size; i++) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // also save head of file so we can error during lookahead scan | ||||||
|  |     lfs2_block_t fileblock = file.ctz.head; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
| 
 | 
 | ||||||
| if [[ ! $MAKEFLAGS =~ "LFS2_BLOCK_CYCLES" ]] |     // remount to force an alloc scan | ||||||
| then |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
| 
 | 
 | ||||||
| echo "--- Chained dir exhaustion test ---" |     // but mark the head of our file as a "bad block", this is force our | ||||||
| scripts/test.py << TEST |     // scan to bail early | ||||||
|  |     lfs2_testbd_setwear(&cfg, fileblock, 0xffffffff) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "ghost", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     strcpy((char*)buffer, "chomp"); | ||||||
|  |     size = strlen("chomp"); | ||||||
|  |     while (true) { | ||||||
|  |         lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, buffer, size); | ||||||
|  |         assert(res == (lfs2_ssize_t)size || res == LFS2_ERR_CORRUPT); | ||||||
|  |         if (res == LFS2_ERR_CORRUPT) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  | 
 | ||||||
|  |     // now reverse the "bad block" and try to write the file again until we | ||||||
|  |     // run out of space | ||||||
|  |     lfs2_testbd_setwear(&cfg, fileblock, 0) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "ghost", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     strcpy((char*)buffer, "chomp"); | ||||||
|  |     size = strlen("chomp"); | ||||||
|  |     while (true) { | ||||||
|  |         lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, buffer, size); | ||||||
|  |         assert(res == (lfs2_ssize_t)size || res == LFS2_ERR_NOSPC); | ||||||
|  |         if (res == LFS2_ERR_NOSPC) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  | 
 | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | 
 | ||||||
|  |     // check that the disk isn't hurt | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "pacman", LFS2_O_RDONLY) => 0; | ||||||
|  |     strcpy((char*)buffer, "waka"); | ||||||
|  |     size = strlen("waka"); | ||||||
|  |     for (lfs2_size_t i = 0; i < filesize/size; i++) { | ||||||
|  |         uint8_t rbuffer[4]; | ||||||
|  |         lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |         assert(memcmp(rbuffer, buffer, size) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Below, I don't like these tests. They're fragile and depend _heavily_ | ||||||
|  | # on the geometry of the block device. But they are valuable. Eventually they | ||||||
|  | # should be removed and replaced with generalized tests. | ||||||
|  | 
 | ||||||
|  | [[case]] # chained dir exhaustion test | ||||||
|  | define.LFS2_BLOCK_SIZE = 512 | ||||||
|  | define.LFS2_BLOCK_COUNT = 1024 | ||||||
|  | if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024' | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
| 
 | 
 | ||||||
|     // find out max file size |     // find out max file size | ||||||
| @@ -268,11 +425,10 @@ scripts/test.py << TEST | |||||||
|         sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); |         sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); | ||||||
|         lfs2_mkdir(&lfs2, path) => 0; |         lfs2_mkdir(&lfs2, path) => 0; | ||||||
|     } |     } | ||||||
|     lfs2_size_t size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); |     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||||
|     int count = 0; |     int count = 0; | ||||||
|     int err; |  | ||||||
|     while (true) { |     while (true) { | ||||||
|         err = lfs2_file_write(&lfs2, &file, buffer, size); |         err = lfs2_file_write(&lfs2, &file, buffer, size); | ||||||
|         if (err < 0) { |         if (err < 0) { | ||||||
| @@ -324,13 +480,14 @@ scripts/test.py << TEST | |||||||
| 
 | 
 | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | ''' | ||||||
| 
 | 
 | ||||||
| echo "--- Split dir test ---" | [[case]] # split dir test | ||||||
| scripts/test.py << TEST | define.LFS2_BLOCK_SIZE = 512 | ||||||
|  | define.LFS2_BLOCK_COUNT = 1024 | ||||||
|  | if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024' | ||||||
|  | code = ''' | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
| 
 | 
 | ||||||
|     // create one block hole for half a directory |     // create one block hole for half a directory | ||||||
| @@ -342,7 +499,7 @@ scripts/test.py << TEST | |||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
| 
 | 
 | ||||||
|     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); |     lfs2_file_open(&lfs2, &file, "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT); | ||||||
|     lfs2_size_t size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs2_size_t i = 0; |     for (lfs2_size_t i = 0; | ||||||
|             i < (cfg.block_count-4)*(cfg.block_size-8); |             i < (cfg.block_count-4)*(cfg.block_size-8); | ||||||
| @@ -368,18 +525,20 @@ scripts/test.py << TEST | |||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
| 
 | 
 | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | ''' | ||||||
| 
 | 
 | ||||||
| echo "--- Outdated lookahead test ---" | [[case]] # outdated lookahead test | ||||||
| scripts/test.py << TEST | define.LFS2_BLOCK_SIZE = 512 | ||||||
|  | define.LFS2_BLOCK_COUNT = 1024 | ||||||
|  | if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024' | ||||||
|  | code = ''' | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
| 
 |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
| 
 | 
 | ||||||
|     // fill completely with two files |     // fill completely with two files | ||||||
|     lfs2_file_open(&lfs2, &file, "exhaustion1", |     lfs2_file_open(&lfs2, &file, "exhaustion1", | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|     lfs2_size_t size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs2_size_t i = 0; |     for (lfs2_size_t i = 0; | ||||||
|             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||||
| @@ -429,18 +588,22 @@ scripts/test.py << TEST | |||||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; |         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|     } |     } | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
| TEST |  | ||||||
| 
 | 
 | ||||||
| echo "--- Outdated lookahead and split dir test ---" |     lfs2_unmount(&lfs2) => 0; | ||||||
| scripts/test.py << TEST | ''' | ||||||
|  | 
 | ||||||
|  | [[case]] # outdated lookahead and split dir test | ||||||
|  | define.LFS2_BLOCK_SIZE = 512 | ||||||
|  | define.LFS2_BLOCK_COUNT = 1024 | ||||||
|  | if = 'LFS2_BLOCK_SIZE == 512 && LFS2_BLOCK_COUNT == 1024' | ||||||
|  | code = ''' | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
| 
 |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
| 
 | 
 | ||||||
|     // fill completely with two files |     // fill completely with two files | ||||||
|     lfs2_file_open(&lfs2, &file, "exhaustion1", |     lfs2_file_open(&lfs2, &file, "exhaustion1", | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|     lfs2_size_t size = strlen("blahblahblahblah"); |     size = strlen("blahblahblahblah"); | ||||||
|     memcpy(buffer, "blahblahblahblah", size); |     memcpy(buffer, "blahblahblahblah", size); | ||||||
|     for (lfs2_size_t i = 0; |     for (lfs2_size_t i = 0; | ||||||
|             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); |             i < ((cfg.block_count-2)/2)*(cfg.block_size-8); | ||||||
| @@ -487,8 +650,4 @@ scripts/test.py << TEST | |||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
| 
 | 
 | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | ''' | ||||||
| 
 |  | ||||||
| fi |  | ||||||
| 
 |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										84
									
								
								tests/test_attrs.sh → tests/test_attrs.toml
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										84
									
								
								tests/test_attrs.sh → tests/test_attrs.toml
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @@ -1,25 +1,13 @@ | |||||||
| #!/bin/bash | [[case]] # set/get attribute | ||||||
| set -eu | code = ''' | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
| 
 |  | ||||||
| echo "=== Attr tests ===" |  | ||||||
| rm -rf blocks |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
| 
 |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     lfs2_mkdir(&lfs2, "hello") => 0; |     lfs2_mkdir(&lfs2, "hello") => 0; | ||||||
|     lfs2_file_open(&lfs2, &file, "hello/hello", |     lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |     lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello"); | ||||||
|     lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) |  | ||||||
|             => strlen("hello"); |  | ||||||
|     lfs2_file_close(&lfs2, &file); |     lfs2_file_close(&lfs2, &file); | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST |  | ||||||
| 
 | 
 | ||||||
| echo "--- Set/get attribute ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     memset(buffer, 0, sizeof(buffer)); |     memset(buffer, 0, sizeof(buffer)); | ||||||
|     lfs2_setattr(&lfs2, "hello", 'A', "aaaa",   4) => 0; |     lfs2_setattr(&lfs2, "hello", 'A', "aaaa",   4) => 0; | ||||||
| @@ -71,8 +59,7 @@ scripts/test.py << TEST | |||||||
|     lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; |     lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5; | ||||||
| 
 | 
 | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | 
 | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     memset(buffer, 0, sizeof(buffer)); |     memset(buffer, 0, sizeof(buffer)); | ||||||
|     lfs2_getattr(&lfs2, "hello", 'A', buffer,    4) => 4; |     lfs2_getattr(&lfs2, "hello", 'A', buffer,    4) => 4; | ||||||
| @@ -87,10 +74,18 @@ scripts/test.py << TEST | |||||||
|     memcmp(buffer, "hello", strlen("hello")) => 0; |     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||||
|     lfs2_file_close(&lfs2, &file); |     lfs2_file_close(&lfs2, &file); | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | ''' | ||||||
|  | 
 | ||||||
|  | [[case]] # set/get root attribute | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "hello") => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello"); | ||||||
|  |     lfs2_file_close(&lfs2, &file); | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
| 
 | 
 | ||||||
| echo "--- Set/get root attribute ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     memset(buffer, 0, sizeof(buffer)); |     memset(buffer, 0, sizeof(buffer)); | ||||||
|     lfs2_setattr(&lfs2, "/", 'A', "aaaa",   4) => 0; |     lfs2_setattr(&lfs2, "/", 'A', "aaaa",   4) => 0; | ||||||
| @@ -141,8 +136,7 @@ scripts/test.py << TEST | |||||||
|     lfs2_getattr(&lfs2, "/", 'B', buffer+4,  6) => 9; |     lfs2_getattr(&lfs2, "/", 'B', buffer+4,  6) => 9; | ||||||
|     lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; |     lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5; | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | 
 | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     memset(buffer, 0, sizeof(buffer)); |     memset(buffer, 0, sizeof(buffer)); | ||||||
|     lfs2_getattr(&lfs2, "/", 'A', buffer,    4) => 4; |     lfs2_getattr(&lfs2, "/", 'A', buffer,    4) => 4; | ||||||
| @@ -157,10 +151,18 @@ scripts/test.py << TEST | |||||||
|     memcmp(buffer, "hello", strlen("hello")) => 0; |     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||||
|     lfs2_file_close(&lfs2, &file); |     lfs2_file_close(&lfs2, &file); | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | ''' | ||||||
|  | 
 | ||||||
|  | [[case]] # set/get file attribute | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "hello") => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello"); | ||||||
|  |     lfs2_file_close(&lfs2, &file); | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
| 
 | 
 | ||||||
| echo "--- Set/get file attribute ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     memset(buffer, 0, sizeof(buffer)); |     memset(buffer, 0, sizeof(buffer)); | ||||||
|     struct lfs2_attr attrs1[] = { |     struct lfs2_attr attrs1[] = { | ||||||
| @@ -235,18 +237,17 @@ scripts/test.py << TEST | |||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
| 
 | 
 | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | 
 | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|     memset(buffer, 0, sizeof(buffer)); |     memset(buffer, 0, sizeof(buffer)); | ||||||
|     struct lfs2_attr attrs2[] = { |     struct lfs2_attr attrs3[] = { | ||||||
|         {'A', buffer,    4}, |         {'A', buffer,    4}, | ||||||
|         {'B', buffer+4,  9}, |         {'B', buffer+4,  9}, | ||||||
|         {'C', buffer+13, 5}, |         {'C', buffer+13, 5}, | ||||||
|     }; |     }; | ||||||
|     struct lfs2_file_config cfg2 = {.attrs=attrs2, .attr_count=3}; |     struct lfs2_file_config cfg3 = {.attrs=attrs3, .attr_count=3}; | ||||||
| 
 | 
 | ||||||
|     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg2) => 0; |     lfs2_file_opencfg(&lfs2, &file, "hello/hello", LFS2_O_RDONLY, &cfg3) => 0; | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|     memcmp(buffer,    "aaaa",      4) => 0; |     memcmp(buffer,    "aaaa",      4) => 0; | ||||||
|     memcmp(buffer+4,  "fffffffff", 9) => 0; |     memcmp(buffer+4,  "fffffffff", 9) => 0; | ||||||
| @@ -257,11 +258,22 @@ scripts/test.py << TEST | |||||||
|     memcmp(buffer, "hello", strlen("hello")) => 0; |     memcmp(buffer, "hello", strlen("hello")) => 0; | ||||||
|     lfs2_file_close(&lfs2, &file); |     lfs2_file_close(&lfs2, &file); | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | ''' | ||||||
| 
 | 
 | ||||||
| echo "--- Deferred file attributes ---" | [[case]] # deferred file attributes | ||||||
| scripts/test.py << TEST | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "hello") => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "hello/hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     lfs2_file_write(&lfs2, &file, "hello", strlen("hello")) => strlen("hello"); | ||||||
|  |     lfs2_file_close(&lfs2, &file); | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | 
 | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_setattr(&lfs2, "hello/hello", 'B', "fffffffff",  9) => 0; | ||||||
|  |     lfs2_setattr(&lfs2, "hello/hello", 'C', "ccccc",      5) => 0; | ||||||
|  | 
 | ||||||
|     memset(buffer, 0, sizeof(buffer)); |     memset(buffer, 0, sizeof(buffer)); | ||||||
|     struct lfs2_attr attrs1[] = { |     struct lfs2_attr attrs1[] = { | ||||||
|         {'B', "gggg", 4}, |         {'B', "gggg", 4}, | ||||||
| @@ -289,6 +301,4 @@ scripts/test.py << TEST | |||||||
| 
 | 
 | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|     lfs2_unmount(&lfs2) => 0; |     lfs2_unmount(&lfs2) => 0; | ||||||
| TEST | ''' | ||||||
| 
 |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										241
									
								
								tests/test_badblocks.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								tests/test_badblocks.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | |||||||
|  | # bad blocks with block cycles should be tested in test_relocations | ||||||
|  | if = 'LFS2_BLOCK_CYCLES == -1' | ||||||
|  |  | ||||||
|  | [[case]] # single bad blocks | ||||||
|  | define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster | ||||||
|  | define.LFS2_ERASE_CYCLES = 0xffffffff | ||||||
|  | define.LFS2_ERASE_VALUE = [0x00, 0xff, -1] | ||||||
|  | define.LFS2_BADBLOCK_BEHAVIOR = [ | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASEERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_READERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGNOOP', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASENOOP', | ||||||
|  | ] | ||||||
|  | define.NAMEMULT = 64 | ||||||
|  | define.FILEMULT = 1 | ||||||
|  | code = ''' | ||||||
|  |     for (lfs2_block_t badblock = 2; badblock < LFS2_BLOCK_COUNT; badblock++) { | ||||||
|  |         lfs2_testbd_setwear(&cfg, badblock-1, 0) => 0; | ||||||
|  |         lfs2_testbd_setwear(&cfg, badblock, 0xffffffff) => 0; | ||||||
|  |          | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         for (int i = 1; i < 10; i++) { | ||||||
|  |             for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |                 buffer[j] = '0'+i; | ||||||
|  |             } | ||||||
|  |             buffer[NAMEMULT] = '\0'; | ||||||
|  |             lfs2_mkdir(&lfs2, (char*)buffer) => 0; | ||||||
|  |  | ||||||
|  |             buffer[NAMEMULT] = '/'; | ||||||
|  |             for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |                 buffer[j+NAMEMULT+1] = '0'+i; | ||||||
|  |             } | ||||||
|  |             buffer[2*NAMEMULT+1] = '\0'; | ||||||
|  |             lfs2_file_open(&lfs2, &file, (char*)buffer, | ||||||
|  |                     LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |              | ||||||
|  |             size = NAMEMULT; | ||||||
|  |             for (int j = 0; j < i*FILEMULT; j++) { | ||||||
|  |                 lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         for (int i = 1; i < 10; i++) { | ||||||
|  |             for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |                 buffer[j] = '0'+i; | ||||||
|  |             } | ||||||
|  |             buffer[NAMEMULT] = '\0'; | ||||||
|  |             lfs2_stat(&lfs2, (char*)buffer, &info) => 0; | ||||||
|  |             info.type => LFS2_TYPE_DIR; | ||||||
|  |  | ||||||
|  |             buffer[NAMEMULT] = '/'; | ||||||
|  |             for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |                 buffer[j+NAMEMULT+1] = '0'+i; | ||||||
|  |             } | ||||||
|  |             buffer[2*NAMEMULT+1] = '\0'; | ||||||
|  |             lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0; | ||||||
|  |              | ||||||
|  |             size = NAMEMULT; | ||||||
|  |             for (int j = 0; j < i*FILEMULT; j++) { | ||||||
|  |                 uint8_t rbuffer[1024]; | ||||||
|  |                 lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |                 memcmp(buffer, rbuffer, size) => 0; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |     } | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # region corruption (causes cascading failures) | ||||||
|  | define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster | ||||||
|  | define.LFS2_ERASE_CYCLES = 0xffffffff | ||||||
|  | define.LFS2_ERASE_VALUE = [0x00, 0xff, -1] | ||||||
|  | define.LFS2_BADBLOCK_BEHAVIOR = [ | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASEERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_READERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGNOOP', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASENOOP', | ||||||
|  | ] | ||||||
|  | define.NAMEMULT = 64 | ||||||
|  | define.FILEMULT = 1 | ||||||
|  | code = ''' | ||||||
|  |     for (lfs2_block_t i = 0; i < (LFS2_BLOCK_COUNT-2)/2; i++) { | ||||||
|  |         lfs2_testbd_setwear(&cfg, i+2, 0xffffffff) => 0; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 1; i < 10; i++) { | ||||||
|  |         for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |             buffer[j] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[NAMEMULT] = '\0'; | ||||||
|  |         lfs2_mkdir(&lfs2, (char*)buffer) => 0; | ||||||
|  |  | ||||||
|  |         buffer[NAMEMULT] = '/'; | ||||||
|  |         for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |             buffer[j+NAMEMULT+1] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[2*NAMEMULT+1] = '\0'; | ||||||
|  |         lfs2_file_open(&lfs2, &file, (char*)buffer, | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |          | ||||||
|  |         size = NAMEMULT; | ||||||
|  |         for (int j = 0; j < i*FILEMULT; j++) { | ||||||
|  |             lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 1; i < 10; i++) { | ||||||
|  |         for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |             buffer[j] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[NAMEMULT] = '\0'; | ||||||
|  |         lfs2_stat(&lfs2, (char*)buffer, &info) => 0; | ||||||
|  |         info.type => LFS2_TYPE_DIR; | ||||||
|  |  | ||||||
|  |         buffer[NAMEMULT] = '/'; | ||||||
|  |         for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |             buffer[j+NAMEMULT+1] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[2*NAMEMULT+1] = '\0'; | ||||||
|  |         lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0; | ||||||
|  |          | ||||||
|  |         size = NAMEMULT; | ||||||
|  |         for (int j = 0; j < i*FILEMULT; j++) { | ||||||
|  |             uint8_t rbuffer[1024]; | ||||||
|  |             lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |             memcmp(buffer, rbuffer, size) => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # alternating corruption (causes cascading failures) | ||||||
|  | define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster | ||||||
|  | define.LFS2_ERASE_CYCLES = 0xffffffff | ||||||
|  | define.LFS2_ERASE_VALUE = [0x00, 0xff, -1] | ||||||
|  | define.LFS2_BADBLOCK_BEHAVIOR = [ | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASEERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_READERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGNOOP', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASENOOP', | ||||||
|  | ] | ||||||
|  | define.NAMEMULT = 64 | ||||||
|  | define.FILEMULT = 1 | ||||||
|  | code = ''' | ||||||
|  |     for (lfs2_block_t i = 0; i < (LFS2_BLOCK_COUNT-2)/2; i++) { | ||||||
|  |         lfs2_testbd_setwear(&cfg, (2*i) + 2, 0xffffffff) => 0; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 1; i < 10; i++) { | ||||||
|  |         for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |             buffer[j] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[NAMEMULT] = '\0'; | ||||||
|  |         lfs2_mkdir(&lfs2, (char*)buffer) => 0; | ||||||
|  |  | ||||||
|  |         buffer[NAMEMULT] = '/'; | ||||||
|  |         for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |             buffer[j+NAMEMULT+1] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[2*NAMEMULT+1] = '\0'; | ||||||
|  |         lfs2_file_open(&lfs2, &file, (char*)buffer, | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |          | ||||||
|  |         size = NAMEMULT; | ||||||
|  |         for (int j = 0; j < i*FILEMULT; j++) { | ||||||
|  |             lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 1; i < 10; i++) { | ||||||
|  |         for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |             buffer[j] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[NAMEMULT] = '\0'; | ||||||
|  |         lfs2_stat(&lfs2, (char*)buffer, &info) => 0; | ||||||
|  |         info.type => LFS2_TYPE_DIR; | ||||||
|  |  | ||||||
|  |         buffer[NAMEMULT] = '/'; | ||||||
|  |         for (int j = 0; j < NAMEMULT; j++) { | ||||||
|  |             buffer[j+NAMEMULT+1] = '0'+i; | ||||||
|  |         } | ||||||
|  |         buffer[2*NAMEMULT+1] = '\0'; | ||||||
|  |         lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0; | ||||||
|  |          | ||||||
|  |         size = NAMEMULT; | ||||||
|  |         for (int j = 0; j < i*FILEMULT; j++) { | ||||||
|  |             uint8_t rbuffer[1024]; | ||||||
|  |             lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |             memcmp(buffer, rbuffer, size) => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | # other corner cases | ||||||
|  | [[case]] # bad superblocks (corrupt 1 or 0) | ||||||
|  | define.LFS2_ERASE_CYCLES = 0xffffffff | ||||||
|  | define.LFS2_ERASE_VALUE = [0x00, 0xff, -1] | ||||||
|  | define.LFS2_BADBLOCK_BEHAVIOR = [ | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASEERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_READERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGNOOP', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASENOOP', | ||||||
|  | ] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_testbd_setwear(&cfg, 0, 0xffffffff) => 0; | ||||||
|  |     lfs2_testbd_setwear(&cfg, 1, 0xffffffff) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_format(&lfs2, &cfg) => LFS2_ERR_NOSPC; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||||
|  | ''' | ||||||
| @@ -1,120 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| echo "=== Corrupt tests ===" |  | ||||||
|  |  | ||||||
| NAMEMULT=64 |  | ||||||
| FILEMULT=1 |  | ||||||
|  |  | ||||||
| lfs2_mktree() { |  | ||||||
| scripts/test.py ${1:-} << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (int i = 1; i < 10; i++) { |  | ||||||
|         for (int j = 0; j < $NAMEMULT; j++) { |  | ||||||
|             buffer[j] = '0'+i; |  | ||||||
|         } |  | ||||||
|         buffer[$NAMEMULT] = '\0'; |  | ||||||
|         lfs2_mkdir(&lfs2, (char*)buffer) => 0; |  | ||||||
|  |  | ||||||
|         buffer[$NAMEMULT] = '/'; |  | ||||||
|         for (int j = 0; j < $NAMEMULT; j++) { |  | ||||||
|             buffer[j+$NAMEMULT+1] = '0'+i; |  | ||||||
|         } |  | ||||||
|         buffer[2*$NAMEMULT+1] = '\0'; |  | ||||||
|         lfs2_file_open(&lfs2, &file, (char*)buffer, |  | ||||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|          |  | ||||||
|         lfs2_size_t size = $NAMEMULT; |  | ||||||
|         for (int j = 0; j < i*$FILEMULT; j++) { |  | ||||||
|             lfs2_file_write(&lfs2, &file, buffer, size) => size; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| } |  | ||||||
|  |  | ||||||
| lfs2_chktree() { |  | ||||||
| scripts/test.py ${1:-} << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (int i = 1; i < 10; i++) { |  | ||||||
|         for (int j = 0; j < $NAMEMULT; j++) { |  | ||||||
|             buffer[j] = '0'+i; |  | ||||||
|         } |  | ||||||
|         buffer[$NAMEMULT] = '\0'; |  | ||||||
|         lfs2_stat(&lfs2, (char*)buffer, &info) => 0; |  | ||||||
|         info.type => LFS2_TYPE_DIR; |  | ||||||
|  |  | ||||||
|         buffer[$NAMEMULT] = '/'; |  | ||||||
|         for (int j = 0; j < $NAMEMULT; j++) { |  | ||||||
|             buffer[j+$NAMEMULT+1] = '0'+i; |  | ||||||
|         } |  | ||||||
|         buffer[2*$NAMEMULT+1] = '\0'; |  | ||||||
|         lfs2_file_open(&lfs2, &file, (char*)buffer, LFS2_O_RDONLY) => 0; |  | ||||||
|          |  | ||||||
|         lfs2_size_t size = $NAMEMULT; |  | ||||||
|         for (int j = 0; j < i*$FILEMULT; j++) { |  | ||||||
|             uint8_t rbuffer[1024]; |  | ||||||
|             lfs2_file_read(&lfs2, &file, rbuffer, size) => size; |  | ||||||
|             memcmp(buffer, rbuffer, size) => 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| } |  | ||||||
|  |  | ||||||
| echo "--- Sanity check ---" |  | ||||||
| rm -rf blocks |  | ||||||
| lfs2_mktree |  | ||||||
| lfs2_chktree |  | ||||||
| BLOCKS="$(ls blocks | grep -vw '[01]')" |  | ||||||
|  |  | ||||||
| echo "--- Block corruption ---" |  | ||||||
| for b in $BLOCKS |  | ||||||
| do  |  | ||||||
|     rm -rf blocks |  | ||||||
|     mkdir blocks |  | ||||||
|     ln -s /dev/zero blocks/$b |  | ||||||
|     lfs2_mktree |  | ||||||
|     lfs2_chktree |  | ||||||
| done |  | ||||||
|  |  | ||||||
| echo "--- Block persistance ---" |  | ||||||
| for b in $BLOCKS |  | ||||||
| do  |  | ||||||
|     rm -rf blocks |  | ||||||
|     mkdir blocks |  | ||||||
|     lfs2_mktree |  | ||||||
|     chmod a-w blocks/$b || true |  | ||||||
|     lfs2_mktree |  | ||||||
|     lfs2_chktree |  | ||||||
| done |  | ||||||
|  |  | ||||||
| echo "--- Big region corruption ---" |  | ||||||
| rm -rf blocks |  | ||||||
| mkdir blocks |  | ||||||
| for i in {2..512} |  | ||||||
| do |  | ||||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) |  | ||||||
| done |  | ||||||
| lfs2_mktree |  | ||||||
| lfs2_chktree |  | ||||||
|  |  | ||||||
| echo "--- Alternating corruption ---" |  | ||||||
| rm -rf blocks |  | ||||||
| mkdir blocks |  | ||||||
| for i in {2..1024..2} |  | ||||||
| do |  | ||||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) |  | ||||||
| done |  | ||||||
| lfs2_mktree |  | ||||||
| lfs2_chktree |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
| @@ -1,489 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| echo "=== Directory tests ===" |  | ||||||
|  |  | ||||||
| LARGESIZE=128 |  | ||||||
|  |  | ||||||
| rm -rf blocks |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Root directory ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Directory creation ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "potato") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- File creation ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "burito", LFS2_O_CREAT | LFS2_O_WRONLY) => 0; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Directory iteration ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "burito") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "potato") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Directory failures ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "potato") => LFS2_ERR_EXIST; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "tomato") => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "burito") => LFS2_ERR_NOTDIR; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "tomato", LFS2_O_RDONLY) => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "potato", LFS2_O_RDONLY) => LFS2_ERR_ISDIR; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Nested directories ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "potato/baked") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "potato/sweet") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "potato/fried") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "potato") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "baked") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "fried") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "sweet") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Multi-block directory ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "cactus") => 0; |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |  | ||||||
|         sprintf(path, "cactus/test%03d", i); |  | ||||||
|         lfs2_mkdir(&lfs2, path) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "cactus") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |  | ||||||
|         sprintf(path, "test%03d", i); |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         strcmp(info.name, path) => 0; |  | ||||||
|         info.type => LFS2_TYPE_DIR; |  | ||||||
|     } |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Directory remove ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_remove(&lfs2, "potato") => LFS2_ERR_NOTEMPTY; |  | ||||||
|     lfs2_remove(&lfs2, "potato/sweet") => 0; |  | ||||||
|     lfs2_remove(&lfs2, "potato/baked") => 0; |  | ||||||
|     lfs2_remove(&lfs2, "potato/fried") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "potato") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "potato") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "burito") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "cactus") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "burito") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "cactus") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Directory rename ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "coldpotato") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "coldpotato/baked") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "coldpotato/sweet") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "coldpotato/fried") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_rename(&lfs2, "coldpotato", "hotpotato") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "hotpotato") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "baked") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "fried") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "sweet") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "warmpotato") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "warmpotato/mushy") => 0; |  | ||||||
|     lfs2_rename(&lfs2, "hotpotato", "warmpotato") => LFS2_ERR_NOTEMPTY; |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "warmpotato/mushy") => 0; |  | ||||||
|     lfs2_rename(&lfs2, "hotpotato", "warmpotato") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "warmpotato") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "baked") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "fried") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "sweet") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "coldpotato") => 0; |  | ||||||
|     lfs2_rename(&lfs2, "warmpotato/baked", "coldpotato/baked") => 0; |  | ||||||
|     lfs2_rename(&lfs2, "warmpotato/sweet", "coldpotato/sweet") => 0; |  | ||||||
|     lfs2_rename(&lfs2, "warmpotato/fried", "coldpotato/fried") => 0; |  | ||||||
|     lfs2_remove(&lfs2, "coldpotato") => LFS2_ERR_NOTEMPTY; |  | ||||||
|     lfs2_remove(&lfs2, "warmpotato") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "coldpotato") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "baked") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "fried") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "sweet") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Recursive remove ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_remove(&lfs2, "coldpotato") => LFS2_ERR_NOTEMPTY; |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "coldpotato") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|  |  | ||||||
|     while (true) { |  | ||||||
|         int err = lfs2_dir_read(&lfs2, &dir, &info); |  | ||||||
|         err >= 0 => 1; |  | ||||||
|         if (err == 0) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         strcpy(path, "coldpotato/"); |  | ||||||
|         strcat(path, info.name); |  | ||||||
|         lfs2_remove(&lfs2, path) => 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "coldpotato") => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "burito") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "cactus") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Multi-block rename ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |  | ||||||
|         char oldpath[1024]; |  | ||||||
|         char newpath[1024]; |  | ||||||
|         sprintf(oldpath, "cactus/test%03d", i); |  | ||||||
|         sprintf(newpath, "cactus/tedd%03d", i); |  | ||||||
|         lfs2_rename(&lfs2, oldpath, newpath) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "cactus") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |  | ||||||
|         sprintf(path, "tedd%03d", i); |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         strcmp(info.name, path) => 0; |  | ||||||
|         info.type => LFS2_TYPE_DIR; |  | ||||||
|     } |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Multi-block remove ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_remove(&lfs2, "cactus") => LFS2_ERR_NOTEMPTY; |  | ||||||
|  |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |  | ||||||
|         sprintf(path, "cactus/tedd%03d", i); |  | ||||||
|         lfs2_remove(&lfs2, path) => 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "cactus") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "burito") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Multi-block directory with files ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "prickly-pear") => 0; |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |  | ||||||
|         sprintf(path, "prickly-pear/test%03d", i); |  | ||||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|         lfs2_size_t size = 6; |  | ||||||
|         memcpy(buffer, "Hello", size); |  | ||||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "prickly-pear") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |  | ||||||
|         sprintf(path, "test%03d", i); |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         strcmp(info.name, path) => 0; |  | ||||||
|         info.type => LFS2_TYPE_REG; |  | ||||||
|         info.size => 6; |  | ||||||
|     } |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Multi-block rename with files ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |  | ||||||
|         char oldpath[1024]; |  | ||||||
|         char newpath[1024]; |  | ||||||
|         sprintf(oldpath, "prickly-pear/test%03d", i); |  | ||||||
|         sprintf(newpath, "prickly-pear/tedd%03d", i); |  | ||||||
|         lfs2_rename(&lfs2, oldpath, newpath) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "prickly-pear") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |  | ||||||
|         sprintf(path, "tedd%03d", i); |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         strcmp(info.name, path) => 0; |  | ||||||
|         info.type => LFS2_TYPE_REG; |  | ||||||
|         info.size => 6; |  | ||||||
|     } |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Multi-block remove with files ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_remove(&lfs2, "prickly-pear") => LFS2_ERR_NOTEMPTY; |  | ||||||
|  |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |  | ||||||
|         sprintf(path, "prickly-pear/tedd%03d", i); |  | ||||||
|         lfs2_remove(&lfs2, path) => 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "prickly-pear") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "burito") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										838
									
								
								tests/test_dirs.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										838
									
								
								tests/test_dirs.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,838 @@ | |||||||
|  | [[case]] # root | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # many directory creation | ||||||
|  | define.N = 'range(0, 100, 3)' | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "dir%03d", i); | ||||||
|  |         lfs2_mkdir(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "dir%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # many directory removal | ||||||
|  | define.N = 'range(3, 100, 11)' | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "removeme%03d", i); | ||||||
|  |         lfs2_mkdir(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "removeme%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "removeme%03d", i); | ||||||
|  |         lfs2_remove(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # many directory rename | ||||||
|  | define.N = 'range(3, 100, 11)' | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "test%03d", i); | ||||||
|  |         lfs2_mkdir(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "test%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         char oldpath[128]; | ||||||
|  |         char newpath[128]; | ||||||
|  |         sprintf(oldpath, "test%03d", i); | ||||||
|  |         sprintf(newpath, "tedd%03d", i); | ||||||
|  |         lfs2_rename(&lfs2, oldpath, newpath) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "tedd%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # reentrant many directory creation/rename/removal | ||||||
|  | define.N = [5, 11] | ||||||
|  | reentrant = true | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "hi%03d", i); | ||||||
|  |         err = lfs2_mkdir(&lfs2, path); | ||||||
|  |         assert(err == 0 || err == LFS2_ERR_EXIST); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "hello%03d", i); | ||||||
|  |         err = lfs2_remove(&lfs2, path); | ||||||
|  |         assert(err == 0 || err == LFS2_ERR_NOENT); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "hi%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         char oldpath[128]; | ||||||
|  |         char newpath[128]; | ||||||
|  |         sprintf(oldpath, "hi%03d", i); | ||||||
|  |         sprintf(newpath, "hello%03d", i); | ||||||
|  |         // YES this can overwrite an existing newpath | ||||||
|  |         lfs2_rename(&lfs2, oldpath, newpath) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "hello%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "hello%03d", i); | ||||||
|  |         lfs2_remove(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # file creation | ||||||
|  | define.N = 'range(3, 100, 11)' | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "file%03d", i); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "file%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_REG); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # file removal | ||||||
|  | define.N = 'range(0, 100, 3)' | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "removeme%03d", i); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "removeme%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_REG); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "removeme%03d", i); | ||||||
|  |         lfs2_remove(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # file rename | ||||||
|  | define.N = 'range(0, 100, 3)' | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "test%03d", i); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "test%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_REG); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         char oldpath[128]; | ||||||
|  |         char newpath[128]; | ||||||
|  |         sprintf(oldpath, "test%03d", i); | ||||||
|  |         sprintf(newpath, "tedd%03d", i); | ||||||
|  |         lfs2_rename(&lfs2, oldpath, newpath) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "tedd%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_REG); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # reentrant file creation/rename/removal | ||||||
|  | define.N = [5, 25] | ||||||
|  | reentrant = true | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "hi%03d", i); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_WRONLY) => 0; | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "hello%03d", i); | ||||||
|  |         err = lfs2_remove(&lfs2, path); | ||||||
|  |         assert(err == 0 || err == LFS2_ERR_NOENT); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "hi%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_REG); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         char oldpath[128]; | ||||||
|  |         char newpath[128]; | ||||||
|  |         sprintf(oldpath, "hi%03d", i); | ||||||
|  |         sprintf(newpath, "hello%03d", i); | ||||||
|  |         // YES this can overwrite an existing newpath | ||||||
|  |         lfs2_rename(&lfs2, oldpath, newpath) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "hello%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_REG); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "hello%03d", i); | ||||||
|  |         lfs2_remove(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # nested directories | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "potato") => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "burito", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "potato/baked") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "potato/sweet") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "potato/fried") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "potato") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "baked") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "fried") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "sweet") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // try removing? | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_remove(&lfs2, "potato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // try renaming? | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_rename(&lfs2, "potato", "coldpotato") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_rename(&lfs2, "coldpotato", "warmpotato") => 0; | ||||||
|  |     lfs2_rename(&lfs2, "warmpotato", "hotpotato") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_remove(&lfs2, "potato") => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_remove(&lfs2, "coldpotato") => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_remove(&lfs2, "warmpotato") => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_remove(&lfs2, "hotpotato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // try cross-directory renaming | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coldpotato") => 0; | ||||||
|  |     lfs2_rename(&lfs2, "hotpotato/baked", "coldpotato/baked") => 0; | ||||||
|  |     lfs2_rename(&lfs2, "coldpotato", "hotpotato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_remove(&lfs2, "coldpotato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_remove(&lfs2, "hotpotato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_rename(&lfs2, "hotpotato/fried", "coldpotato/fried") => 0; | ||||||
|  |     lfs2_rename(&lfs2, "coldpotato", "hotpotato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_remove(&lfs2, "coldpotato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_remove(&lfs2, "hotpotato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_rename(&lfs2, "hotpotato/sweet", "coldpotato/sweet") => 0; | ||||||
|  |     lfs2_rename(&lfs2, "coldpotato", "hotpotato") => 0; | ||||||
|  |     lfs2_remove(&lfs2, "coldpotato") => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_remove(&lfs2, "hotpotato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "hotpotato") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "baked") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "fried") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "sweet") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |      | ||||||
|  |     // final remove | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_remove(&lfs2, "hotpotato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_remove(&lfs2, "hotpotato/baked") => 0; | ||||||
|  |     lfs2_remove(&lfs2, "hotpotato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_remove(&lfs2, "hotpotato/fried") => 0; | ||||||
|  |     lfs2_remove(&lfs2, "hotpotato") => LFS2_ERR_NOTEMPTY; | ||||||
|  |     lfs2_remove(&lfs2, "hotpotato/sweet") => 0; | ||||||
|  |     lfs2_remove(&lfs2, "hotpotato") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     info.type => LFS2_TYPE_DIR; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "burito") == 0); | ||||||
|  |     info.type => LFS2_TYPE_REG; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # recursive remove | ||||||
|  | define.N = [10, 100] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "prickly-pear") => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "prickly-pear/cactus%03d", i); | ||||||
|  |         lfs2_mkdir(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "prickly-pear") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "cactus%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2); | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_remove(&lfs2, "prickly-pear") => LFS2_ERR_NOTEMPTY; | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "prickly-pear") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "cactus%03d", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |         sprintf(path, "prickly-pear/%s", info.name); | ||||||
|  |         lfs2_remove(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_remove(&lfs2, "prickly-pear") => 0; | ||||||
|  |     lfs2_remove(&lfs2, "prickly-pear") => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_remove(&lfs2, "prickly-pear") => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # other error cases | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "potato") => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "burito", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mkdir(&lfs2, "potato") => LFS2_ERR_EXIST; | ||||||
|  |     lfs2_mkdir(&lfs2, "burito") => LFS2_ERR_EXIST; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "burito", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => LFS2_ERR_EXIST; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "potato", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => LFS2_ERR_EXIST; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "tomato") => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "burito") => LFS2_ERR_NOTDIR; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "tomato", LFS2_O_RDONLY) => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "potato", LFS2_O_RDONLY) => LFS2_ERR_ISDIR; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "tomato", LFS2_O_WRONLY) => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "potato", LFS2_O_WRONLY) => LFS2_ERR_ISDIR; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "potato", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT) => LFS2_ERR_ISDIR; | ||||||
|  |  | ||||||
|  |     lfs2_mkdir(&lfs2, "/") => LFS2_ERR_EXIST; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "/", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => LFS2_ERR_EXIST; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "/", LFS2_O_RDONLY) => LFS2_ERR_ISDIR; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "/", LFS2_O_WRONLY) => LFS2_ERR_ISDIR; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "/", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT) => LFS2_ERR_ISDIR; | ||||||
|  |  | ||||||
|  |     // check that errors did not corrupt directory | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_REG); | ||||||
|  |     assert(strcmp(info.name, "burito") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "potato") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // or on disk | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_REG); | ||||||
|  |     assert(strcmp(info.name, "burito") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     assert(strcmp(info.name, "potato") == 0); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # directory seek | ||||||
|  | define.COUNT = [4, 128, 132] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "hello") => 0; | ||||||
|  |     for (int i = 0; i < COUNT; i++) { | ||||||
|  |         sprintf(path, "hello/kitty%03d", i); | ||||||
|  |         lfs2_mkdir(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     for (int j = 2; j < COUNT; j++) { | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_dir_open(&lfs2, &dir, "hello") => 0; | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, ".") == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, "..") == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |         lfs2_soff_t pos; | ||||||
|  |         for (int i = 0; i < j; i++) { | ||||||
|  |             sprintf(path, "kitty%03d", i); | ||||||
|  |             lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |             assert(strcmp(info.name, path) == 0); | ||||||
|  |             assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |             pos = lfs2_dir_tell(&lfs2, &dir); | ||||||
|  |             assert(pos >= 0); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_dir_seek(&lfs2, &dir, pos) => 0; | ||||||
|  |         sprintf(path, "kitty%03d", j); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |         lfs2_dir_rewind(&lfs2, &dir) => 0; | ||||||
|  |         sprintf(path, "kitty%03d", 0); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, ".") == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, "..") == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |         lfs2_dir_seek(&lfs2, &dir, pos) => 0; | ||||||
|  |         sprintf(path, "kitty%03d", j); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |         lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |     } | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # root seek | ||||||
|  | define.COUNT = [4, 128, 132] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < COUNT; i++) { | ||||||
|  |         sprintf(path, "hi%03d", i); | ||||||
|  |         lfs2_mkdir(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     for (int j = 2; j < COUNT; j++) { | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, ".") == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, "..") == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |         lfs2_soff_t pos; | ||||||
|  |         for (int i = 0; i < j; i++) { | ||||||
|  |             sprintf(path, "hi%03d", i); | ||||||
|  |             lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |             assert(strcmp(info.name, path) == 0); | ||||||
|  |             assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |             pos = lfs2_dir_tell(&lfs2, &dir); | ||||||
|  |             assert(pos >= 0); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_dir_seek(&lfs2, &dir, pos) => 0; | ||||||
|  |         sprintf(path, "hi%03d", j); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |         lfs2_dir_rewind(&lfs2, &dir) => 0; | ||||||
|  |         sprintf(path, "hi%03d", 0); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, ".") == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, "..") == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |         lfs2_dir_seek(&lfs2, &dir, pos) => 0; | ||||||
|  |         sprintf(path, "hi%03d", j); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |         lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |     } | ||||||
|  | ''' | ||||||
|  |  | ||||||
| @@ -1,251 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| echo "=== Entry tests ===" |  | ||||||
|  |  | ||||||
| # Note: These tests are intended for 512 byte inline size at different |  | ||||||
| # inline sizes they should still pass, but won't be testing anything |  | ||||||
|  |  | ||||||
| rm -rf blocks |  | ||||||
| function read_file { |  | ||||||
| cat << TEST |  | ||||||
|  |  | ||||||
|     size = $2; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "$1", LFS2_O_RDONLY) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; |  | ||||||
|     memcmp(rbuffer, wbuffer, size) => 0; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
| TEST |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function write_file { |  | ||||||
| cat << TEST |  | ||||||
|  |  | ||||||
|     size = $2; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "$1", |  | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; |  | ||||||
|     memset(wbuffer, 'c', size); |  | ||||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
| TEST |  | ||||||
| } |  | ||||||
|  |  | ||||||
| echo "--- Entry grow test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     uint8_t wbuffer[1024]; |  | ||||||
|     uint8_t rbuffer[1024]; |  | ||||||
|     lfs2_size_t size; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     $(write_file "hi0" 20) |  | ||||||
|     $(write_file "hi1" 20) |  | ||||||
|     $(write_file "hi2" 20) |  | ||||||
|     $(write_file "hi3" 20) |  | ||||||
|  |  | ||||||
|     $(read_file "hi1" 20) |  | ||||||
|     $(write_file "hi1" 200) |  | ||||||
|  |  | ||||||
|     $(read_file "hi0" 20) |  | ||||||
|     $(read_file "hi1" 200) |  | ||||||
|     $(read_file "hi2" 20) |  | ||||||
|     $(read_file "hi3" 20) |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Entry shrink test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     uint8_t wbuffer[1024]; |  | ||||||
|     uint8_t rbuffer[1024]; |  | ||||||
|     lfs2_size_t size; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     $(write_file "hi0" 20) |  | ||||||
|     $(write_file "hi1" 200) |  | ||||||
|     $(write_file "hi2" 20) |  | ||||||
|     $(write_file "hi3" 20) |  | ||||||
|  |  | ||||||
|     $(read_file "hi1" 200) |  | ||||||
|     $(write_file "hi1" 20) |  | ||||||
|  |  | ||||||
|     $(read_file "hi0" 20) |  | ||||||
|     $(read_file "hi1" 20) |  | ||||||
|     $(read_file "hi2" 20) |  | ||||||
|     $(read_file "hi3" 20) |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Entry spill test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     uint8_t wbuffer[1024]; |  | ||||||
|     uint8_t rbuffer[1024]; |  | ||||||
|     lfs2_size_t size; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     $(write_file "hi0" 200) |  | ||||||
|     $(write_file "hi1" 200) |  | ||||||
|     $(write_file "hi2" 200) |  | ||||||
|     $(write_file "hi3" 200) |  | ||||||
|  |  | ||||||
|     $(read_file "hi0" 200) |  | ||||||
|     $(read_file "hi1" 200) |  | ||||||
|     $(read_file "hi2" 200) |  | ||||||
|     $(read_file "hi3" 200) |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Entry push spill test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     uint8_t wbuffer[1024]; |  | ||||||
|     uint8_t rbuffer[1024]; |  | ||||||
|     lfs2_size_t size; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     $(write_file "hi0" 200) |  | ||||||
|     $(write_file "hi1" 20) |  | ||||||
|     $(write_file "hi2" 200) |  | ||||||
|     $(write_file "hi3" 200) |  | ||||||
|  |  | ||||||
|     $(read_file "hi1" 20) |  | ||||||
|     $(write_file "hi1" 200) |  | ||||||
|  |  | ||||||
|     $(read_file "hi0" 200) |  | ||||||
|     $(read_file "hi1" 200) |  | ||||||
|     $(read_file "hi2" 200) |  | ||||||
|     $(read_file "hi3" 200) |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Entry push spill two test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     uint8_t wbuffer[1024]; |  | ||||||
|     uint8_t rbuffer[1024]; |  | ||||||
|     lfs2_size_t size; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     $(write_file "hi0" 200) |  | ||||||
|     $(write_file "hi1" 20) |  | ||||||
|     $(write_file "hi2" 200) |  | ||||||
|     $(write_file "hi3" 200) |  | ||||||
|     $(write_file "hi4" 200) |  | ||||||
|  |  | ||||||
|     $(read_file "hi1" 20) |  | ||||||
|     $(write_file "hi1" 200) |  | ||||||
|  |  | ||||||
|     $(read_file "hi0" 200) |  | ||||||
|     $(read_file "hi1" 200) |  | ||||||
|     $(read_file "hi2" 200) |  | ||||||
|     $(read_file "hi3" 200) |  | ||||||
|     $(read_file "hi4" 200) |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Entry drop test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     uint8_t wbuffer[1024]; |  | ||||||
|     uint8_t rbuffer[1024]; |  | ||||||
|     lfs2_size_t size; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     $(write_file "hi0" 200) |  | ||||||
|     $(write_file "hi1" 200) |  | ||||||
|     $(write_file "hi2" 200) |  | ||||||
|     $(write_file "hi3" 200) |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "hi1") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "hi1", &info) => LFS2_ERR_NOENT; |  | ||||||
|     $(read_file "hi0" 200) |  | ||||||
|     $(read_file "hi2" 200) |  | ||||||
|     $(read_file "hi3" 200) |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "hi2") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "hi2", &info) => LFS2_ERR_NOENT; |  | ||||||
|     $(read_file "hi0" 200) |  | ||||||
|     $(read_file "hi3" 200) |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "hi3") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "hi3", &info) => LFS2_ERR_NOENT; |  | ||||||
|     $(read_file "hi0" 200) |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "hi0") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "hi0", &info) => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Create too big ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     memset(path, 'm', 200); |  | ||||||
|     path[200] = '\0'; |  | ||||||
|  |  | ||||||
|     lfs2_size_t size = 400; |  | ||||||
|     lfs2_file_open(&lfs2, &file, path, |  | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; |  | ||||||
|     uint8_t wbuffer[1024]; |  | ||||||
|     memset(wbuffer, 'c', size); |  | ||||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|  |  | ||||||
|     size = 400; |  | ||||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; |  | ||||||
|     uint8_t rbuffer[1024]; |  | ||||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; |  | ||||||
|     memcmp(rbuffer, wbuffer, size) => 0; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Resize too big ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     memset(path, 'm', 200); |  | ||||||
|     path[200] = '\0'; |  | ||||||
|  |  | ||||||
|     lfs2_size_t size = 40; |  | ||||||
|     lfs2_file_open(&lfs2, &file, path, |  | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; |  | ||||||
|     uint8_t wbuffer[1024]; |  | ||||||
|     memset(wbuffer, 'c', size); |  | ||||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|  |  | ||||||
|     size = 40; |  | ||||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; |  | ||||||
|     uint8_t rbuffer[1024]; |  | ||||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; |  | ||||||
|     memcmp(rbuffer, wbuffer, size) => 0; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|  |  | ||||||
|     size = 400; |  | ||||||
|     lfs2_file_open(&lfs2, &file, path, |  | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; |  | ||||||
|     memset(wbuffer, 'c', size); |  | ||||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|  |  | ||||||
|     size = 400; |  | ||||||
|     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; |  | ||||||
|     memcmp(rbuffer, wbuffer, size) => 0; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										611
									
								
								tests/test_entries.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										611
									
								
								tests/test_entries.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,611 @@ | |||||||
|  | # These tests are for some specific corner cases with neighboring inline files. | ||||||
|  | # Note that these tests are intended for 512 byte inline sizes. They should | ||||||
|  | # still pass with other inline sizes but wouldn't be testing anything. | ||||||
|  |  | ||||||
|  | define.LFS2_CACHE_SIZE = 512 | ||||||
|  | if = 'LFS2_CACHE_SIZE % LFS2_PROG_SIZE == 0 && LFS2_CACHE_SIZE == 512' | ||||||
|  |  | ||||||
|  | [[case]] # entry grow test | ||||||
|  | code = ''' | ||||||
|  |     uint8_t wbuffer[1024]; | ||||||
|  |     uint8_t rbuffer[1024]; | ||||||
|  |  | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // write hi0 20 | ||||||
|  |     sprintf(path, "hi0"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi1 20 | ||||||
|  |     sprintf(path, "hi1"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi2 20 | ||||||
|  |     sprintf(path, "hi2"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi3 20 | ||||||
|  |     sprintf(path, "hi3"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // read hi1 20 | ||||||
|  |     sprintf(path, "hi1"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi1 200 | ||||||
|  |     sprintf(path, "hi1"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // read hi0 20 | ||||||
|  |     sprintf(path, "hi0"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi1 200 | ||||||
|  |     sprintf(path, "hi1"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi2 20 | ||||||
|  |     sprintf(path, "hi2"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi3 20 | ||||||
|  |     sprintf(path, "hi3"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # entry shrink test | ||||||
|  | code = ''' | ||||||
|  |     uint8_t wbuffer[1024]; | ||||||
|  |     uint8_t rbuffer[1024]; | ||||||
|  |  | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // write hi0 20 | ||||||
|  |     sprintf(path, "hi0"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi1 200 | ||||||
|  |     sprintf(path, "hi1"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi2 20 | ||||||
|  |     sprintf(path, "hi2"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi3 20 | ||||||
|  |     sprintf(path, "hi3"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // read hi1 200 | ||||||
|  |     sprintf(path, "hi1"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi1 20 | ||||||
|  |     sprintf(path, "hi1"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // read hi0 20 | ||||||
|  |     sprintf(path, "hi0"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi1 20 | ||||||
|  |     sprintf(path, "hi1"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi2 20 | ||||||
|  |     sprintf(path, "hi2"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi3 20 | ||||||
|  |     sprintf(path, "hi3"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # entry spill test | ||||||
|  | code = ''' | ||||||
|  |     uint8_t wbuffer[1024]; | ||||||
|  |     uint8_t rbuffer[1024]; | ||||||
|  |  | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // write hi0 200 | ||||||
|  |     sprintf(path, "hi0"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi1 200 | ||||||
|  |     sprintf(path, "hi1"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi2 200 | ||||||
|  |     sprintf(path, "hi2"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi3 200 | ||||||
|  |     sprintf(path, "hi3"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // read hi0 200 | ||||||
|  |     sprintf(path, "hi0"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi1 200 | ||||||
|  |     sprintf(path, "hi1"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi2 200 | ||||||
|  |     sprintf(path, "hi2"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi3 200 | ||||||
|  |     sprintf(path, "hi3"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # entry push spill test | ||||||
|  | code = ''' | ||||||
|  |     uint8_t wbuffer[1024]; | ||||||
|  |     uint8_t rbuffer[1024]; | ||||||
|  |  | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // write hi0 200 | ||||||
|  |     sprintf(path, "hi0"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi1 20 | ||||||
|  |     sprintf(path, "hi1"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi2 200 | ||||||
|  |     sprintf(path, "hi2"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi3 200 | ||||||
|  |     sprintf(path, "hi3"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // read hi1 20 | ||||||
|  |     sprintf(path, "hi1"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi1 200 | ||||||
|  |     sprintf(path, "hi1"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // read hi0 200 | ||||||
|  |     sprintf(path, "hi0"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi1 200 | ||||||
|  |     sprintf(path, "hi1"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi2 200 | ||||||
|  |     sprintf(path, "hi2"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi3 200 | ||||||
|  |     sprintf(path, "hi3"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # entry push spill two test | ||||||
|  | code = ''' | ||||||
|  |     uint8_t wbuffer[1024]; | ||||||
|  |     uint8_t rbuffer[1024]; | ||||||
|  |  | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // write hi0 200 | ||||||
|  |     sprintf(path, "hi0"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi1 20 | ||||||
|  |     sprintf(path, "hi1"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi2 200 | ||||||
|  |     sprintf(path, "hi2"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi3 200 | ||||||
|  |     sprintf(path, "hi3"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi4 200 | ||||||
|  |     sprintf(path, "hi4"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // read hi1 20 | ||||||
|  |     sprintf(path, "hi1"); size = 20; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi1 200 | ||||||
|  |     sprintf(path, "hi1"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // read hi0 200 | ||||||
|  |     sprintf(path, "hi0"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi1 200 | ||||||
|  |     sprintf(path, "hi1"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi2 200 | ||||||
|  |     sprintf(path, "hi2"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi3 200 | ||||||
|  |     sprintf(path, "hi3"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi4 200 | ||||||
|  |     sprintf(path, "hi4"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # entry drop test | ||||||
|  | code = ''' | ||||||
|  |     uint8_t wbuffer[1024]; | ||||||
|  |     uint8_t rbuffer[1024]; | ||||||
|  |  | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // write hi0 200 | ||||||
|  |     sprintf(path, "hi0"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi1 200 | ||||||
|  |     sprintf(path, "hi1"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi2 200 | ||||||
|  |     sprintf(path, "hi2"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // write hi3 200 | ||||||
|  |     sprintf(path, "hi3"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_remove(&lfs2, "hi1") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "hi1", &info) => LFS2_ERR_NOENT; | ||||||
|  |     // read hi0 200 | ||||||
|  |     sprintf(path, "hi0"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi2 200 | ||||||
|  |     sprintf(path, "hi2"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi3 200 | ||||||
|  |     sprintf(path, "hi3"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_remove(&lfs2, "hi2") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "hi2", &info) => LFS2_ERR_NOENT; | ||||||
|  |     // read hi0 200 | ||||||
|  |     sprintf(path, "hi0"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // read hi3 200 | ||||||
|  |     sprintf(path, "hi3"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_remove(&lfs2, "hi3") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "hi3", &info) => LFS2_ERR_NOENT; | ||||||
|  |     // read hi0 200 | ||||||
|  |     sprintf(path, "hi0"); size = 200; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_remove(&lfs2, "hi0") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "hi0", &info) => LFS2_ERR_NOENT; | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # create too big | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     memset(path, 'm', 200); | ||||||
|  |     path[200] = '\0'; | ||||||
|  |  | ||||||
|  |     size = 400; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     uint8_t wbuffer[1024]; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     size = 400; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     uint8_t rbuffer[1024]; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # resize too big | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     memset(path, 'm', 200); | ||||||
|  |     path[200] = '\0'; | ||||||
|  |  | ||||||
|  |     size = 40; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     uint8_t wbuffer[1024]; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     size = 40; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     uint8_t rbuffer[1024]; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     size = 400; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     memset(wbuffer, 'c', size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     size = 400; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |     memcmp(rbuffer, wbuffer, size) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
							
								
								
									
										288
									
								
								tests/test_evil.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								tests/test_evil.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,288 @@ | |||||||
|  | # Tests for recovering from conditions which shouldn't normally | ||||||
|  | # happen during normal operation of littlefs | ||||||
|  |  | ||||||
|  | # invalid pointer tests (outside of block_count) | ||||||
|  |  | ||||||
|  | [[case]] # invalid tail-pointer test | ||||||
|  | define.TAIL_TYPE = ['LFS2_TYPE_HARDTAIL', 'LFS2_TYPE_SOFTTAIL'] | ||||||
|  | define.INVALSET = [0x3, 0x1, 0x2] | ||||||
|  | in = "lfs2.c" | ||||||
|  | code = ''' | ||||||
|  |     // create littlefs | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // change tail-pointer to invalid pointers | ||||||
|  |     lfs2_init(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mdir_t mdir; | ||||||
|  |     lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; | ||||||
|  |     lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS( | ||||||
|  |             {LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8), | ||||||
|  |                 (lfs2_block_t[2]){ | ||||||
|  |                     (INVALSET & 0x1) ? 0xcccccccc : 0, | ||||||
|  |                     (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0; | ||||||
|  |     lfs2_deinit(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // test that mount fails gracefully | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # invalid dir pointer test | ||||||
|  | define.INVALSET = [0x3, 0x1, 0x2] | ||||||
|  | in = "lfs2.c" | ||||||
|  | code = ''' | ||||||
|  |     // create littlefs | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     // make a dir | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "dir_here") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // change the dir pointer to be invalid | ||||||
|  |     lfs2_init(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mdir_t mdir; | ||||||
|  |     lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; | ||||||
|  |     // make sure id 1 == our directory | ||||||
|  |     lfs2_dir_get(&lfs2, &mdir, | ||||||
|  |             LFS2_MKTAG(0x700, 0x3ff, 0), | ||||||
|  |             LFS2_MKTAG(LFS2_TYPE_NAME, 1, strlen("dir_here")), buffer) | ||||||
|  |                 => LFS2_MKTAG(LFS2_TYPE_DIR, 1, strlen("dir_here")); | ||||||
|  |     assert(memcmp((char*)buffer, "dir_here", strlen("dir_here")) == 0); | ||||||
|  |     // change dir pointer | ||||||
|  |     lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS( | ||||||
|  |             {LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, 8), | ||||||
|  |                 (lfs2_block_t[2]){ | ||||||
|  |                     (INVALSET & 0x1) ? 0xcccccccc : 0, | ||||||
|  |                     (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0; | ||||||
|  |     lfs2_deinit(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // test that accessing our bad dir fails, note there's a number | ||||||
|  |     // of ways to access the dir, some can fail, but some don't | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "dir_here", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "dir_here") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "dir_here") => LFS2_ERR_CORRUPT; | ||||||
|  |     lfs2_stat(&lfs2, "dir_here/file_here", &info) => LFS2_ERR_CORRUPT; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "dir_here/dir_here") => LFS2_ERR_CORRUPT; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "dir_here/file_here", | ||||||
|  |             LFS2_O_RDONLY) => LFS2_ERR_CORRUPT; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "dir_here/file_here", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT) => LFS2_ERR_CORRUPT; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # invalid file pointer test | ||||||
|  | in = "lfs2.c" | ||||||
|  | define.SIZE = [10, 1000, 100000] # faked file size | ||||||
|  | code = ''' | ||||||
|  |     // create littlefs | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     // make a file | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "file_here", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // change the file pointer to be invalid | ||||||
|  |     lfs2_init(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mdir_t mdir; | ||||||
|  |     lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; | ||||||
|  |     // make sure id 1 == our file | ||||||
|  |     lfs2_dir_get(&lfs2, &mdir, | ||||||
|  |             LFS2_MKTAG(0x700, 0x3ff, 0), | ||||||
|  |             LFS2_MKTAG(LFS2_TYPE_NAME, 1, strlen("file_here")), buffer) | ||||||
|  |                 => LFS2_MKTAG(LFS2_TYPE_REG, 1, strlen("file_here")); | ||||||
|  |     assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0); | ||||||
|  |     // change file pointer | ||||||
|  |     lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS( | ||||||
|  |             {LFS2_MKTAG(LFS2_TYPE_CTZSTRUCT, 1, sizeof(struct lfs2_ctz)), | ||||||
|  |                 &(struct lfs2_ctz){0xcccccccc, lfs2_tole32(SIZE)}})) => 0; | ||||||
|  |     lfs2_deinit(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // test that accessing our bad file fails, note there's a number | ||||||
|  |     // of ways to access the dir, some can fail, but some don't | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "file_here", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "file_here") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_REG); | ||||||
|  |     assert(info.size == SIZE); | ||||||
|  |  | ||||||
|  |     lfs2_file_open(&lfs2, &file, "file_here", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, SIZE) => LFS2_ERR_CORRUPT; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // any allocs that traverse CTZ must unfortunately must fail | ||||||
|  |     if (SIZE > 2*LFS2_BLOCK_SIZE) { | ||||||
|  |         lfs2_mkdir(&lfs2, "dir_here") => LFS2_ERR_CORRUPT; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # invalid pointer in CTZ skip-list test | ||||||
|  | define.SIZE = ['2*LFS2_BLOCK_SIZE', '3*LFS2_BLOCK_SIZE', '4*LFS2_BLOCK_SIZE'] | ||||||
|  | in = "lfs2.c" | ||||||
|  | code = ''' | ||||||
|  |     // create littlefs | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     // make a file | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "file_here", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     for (int i = 0; i < SIZE; i++) { | ||||||
|  |         char c = 'c'; | ||||||
|  |         lfs2_file_write(&lfs2, &file, &c, 1) => 1; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |     // change pointer in CTZ skip-list to be invalid | ||||||
|  |     lfs2_init(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mdir_t mdir; | ||||||
|  |     lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; | ||||||
|  |     // make sure id 1 == our file and get our CTZ structure | ||||||
|  |     lfs2_dir_get(&lfs2, &mdir, | ||||||
|  |             LFS2_MKTAG(0x700, 0x3ff, 0), | ||||||
|  |             LFS2_MKTAG(LFS2_TYPE_NAME, 1, strlen("file_here")), buffer) | ||||||
|  |                 => LFS2_MKTAG(LFS2_TYPE_REG, 1, strlen("file_here")); | ||||||
|  |     assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0); | ||||||
|  |     struct lfs2_ctz ctz; | ||||||
|  |     lfs2_dir_get(&lfs2, &mdir, | ||||||
|  |             LFS2_MKTAG(0x700, 0x3ff, 0), | ||||||
|  |             LFS2_MKTAG(LFS2_TYPE_STRUCT, 1, sizeof(struct lfs2_ctz)), &ctz) | ||||||
|  |                 => LFS2_MKTAG(LFS2_TYPE_CTZSTRUCT, 1, sizeof(struct lfs2_ctz)); | ||||||
|  |     lfs2_ctz_fromle32(&ctz); | ||||||
|  |     // rewrite block to contain bad pointer | ||||||
|  |     uint8_t bbuffer[LFS2_BLOCK_SIZE]; | ||||||
|  |     cfg.read(&cfg, ctz.head, 0, bbuffer, LFS2_BLOCK_SIZE) => 0; | ||||||
|  |     uint32_t bad = lfs2_tole32(0xcccccccc); | ||||||
|  |     memcpy(&bbuffer[0], &bad, sizeof(bad)); | ||||||
|  |     memcpy(&bbuffer[4], &bad, sizeof(bad)); | ||||||
|  |     cfg.erase(&cfg, ctz.head) => 0; | ||||||
|  |     cfg.prog(&cfg, ctz.head, 0, bbuffer, LFS2_BLOCK_SIZE) => 0; | ||||||
|  |     lfs2_deinit(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // test that accessing our bad file fails, note there's a number | ||||||
|  |     // of ways to access the dir, some can fail, but some don't | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "file_here", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "file_here") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_REG); | ||||||
|  |     assert(info.size == SIZE); | ||||||
|  |  | ||||||
|  |     lfs2_file_open(&lfs2, &file, "file_here", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, SIZE) => LFS2_ERR_CORRUPT; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // any allocs that traverse CTZ must unfortunately must fail | ||||||
|  |     if (SIZE > 2*LFS2_BLOCK_SIZE) { | ||||||
|  |         lfs2_mkdir(&lfs2, "dir_here") => LFS2_ERR_CORRUPT; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | [[case]] # invalid gstate pointer | ||||||
|  | define.INVALSET = [0x3, 0x1, 0x2] | ||||||
|  | in = "lfs2.c" | ||||||
|  | code = ''' | ||||||
|  |     // create littlefs | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // create an invalid gstate | ||||||
|  |     lfs2_init(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mdir_t mdir; | ||||||
|  |     lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; | ||||||
|  |     lfs2_fs_prepmove(&lfs2, 1, (lfs2_block_t [2]){ | ||||||
|  |             (INVALSET & 0x1) ? 0xcccccccc : 0, | ||||||
|  |             (INVALSET & 0x2) ? 0xcccccccc : 0}); | ||||||
|  |     lfs2_dir_commit(&lfs2, &mdir, NULL, 0) => 0; | ||||||
|  |     lfs2_deinit(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // test that mount fails gracefully | ||||||
|  |     // mount may not fail, but our first alloc should fail when | ||||||
|  |     // we try to fix the gstate | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "should_fail") => LFS2_ERR_CORRUPT; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | # cycle detection/recovery tests | ||||||
|  |  | ||||||
|  | [[case]] # metadata-pair threaded-list loop test | ||||||
|  | in = "lfs2.c" | ||||||
|  | code = ''' | ||||||
|  |     // create littlefs | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // change tail-pointer to point to ourself | ||||||
|  |     lfs2_init(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mdir_t mdir; | ||||||
|  |     lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; | ||||||
|  |     lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS( | ||||||
|  |             {LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8), | ||||||
|  |                 (lfs2_block_t[2]){0, 1}})) => 0; | ||||||
|  |     lfs2_deinit(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // test that mount fails gracefully | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # metadata-pair threaded-list 2-length loop test | ||||||
|  | in = "lfs2.c" | ||||||
|  | code = ''' | ||||||
|  |     // create littlefs with child dir | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "child") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // find child | ||||||
|  |     lfs2_init(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mdir_t mdir; | ||||||
|  |     lfs2_block_t pair[2]; | ||||||
|  |     lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; | ||||||
|  |     lfs2_dir_get(&lfs2, &mdir, | ||||||
|  |             LFS2_MKTAG(0x7ff, 0x3ff, 0), | ||||||
|  |             LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair) | ||||||
|  |                 => LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair)); | ||||||
|  |     lfs2_pair_fromle32(pair); | ||||||
|  |     // change tail-pointer to point to root | ||||||
|  |     lfs2_dir_fetch(&lfs2, &mdir, pair) => 0; | ||||||
|  |     lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS( | ||||||
|  |             {LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8), | ||||||
|  |                 (lfs2_block_t[2]){0, 1}})) => 0; | ||||||
|  |     lfs2_deinit(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // test that mount fails gracefully | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # metadata-pair threaded-list 1-length child loop test | ||||||
|  | in = "lfs2.c" | ||||||
|  | code = ''' | ||||||
|  |     // create littlefs with child dir | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "child") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // find child | ||||||
|  |     lfs2_init(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mdir_t mdir; | ||||||
|  |     lfs2_block_t pair[2]; | ||||||
|  |     lfs2_dir_fetch(&lfs2, &mdir, (lfs2_block_t[2]){0, 1}) => 0; | ||||||
|  |     lfs2_dir_get(&lfs2, &mdir, | ||||||
|  |             LFS2_MKTAG(0x7ff, 0x3ff, 0), | ||||||
|  |             LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair) | ||||||
|  |                 => LFS2_MKTAG(LFS2_TYPE_DIRSTRUCT, 1, sizeof(pair)); | ||||||
|  |     lfs2_pair_fromle32(pair); | ||||||
|  |     // change tail-pointer to point to ourself | ||||||
|  |     lfs2_dir_fetch(&lfs2, &mdir, pair) => 0; | ||||||
|  |     lfs2_dir_commit(&lfs2, &mdir, LFS2_MKATTRS( | ||||||
|  |             {LFS2_MKTAG(LFS2_TYPE_HARDTAIL, 0x3ff, 8), pair})) => 0; | ||||||
|  |     lfs2_deinit(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // test that mount fails gracefully | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||||
|  | ''' | ||||||
							
								
								
									
										465
									
								
								tests/test_exhaustion.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								tests/test_exhaustion.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,465 @@ | |||||||
|  | [[case]] # test running a filesystem to exhaustion | ||||||
|  | define.LFS2_ERASE_CYCLES = 10 | ||||||
|  | define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster | ||||||
|  | define.LFS2_BLOCK_CYCLES = 'LFS2_ERASE_CYCLES / 2' | ||||||
|  | define.LFS2_BADBLOCK_BEHAVIOR = [ | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASEERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_READERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGNOOP', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASENOOP', | ||||||
|  | ] | ||||||
|  | define.FILES = 10 | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "roadrunner") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     uint32_t cycle = 0; | ||||||
|  |     while (true) { | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |             // chose name, roughly random seed, and random 2^n size | ||||||
|  |             sprintf(path, "roadrunner/test%d", i); | ||||||
|  |             srand(cycle * i); | ||||||
|  |             size = 1 << ((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                     LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |  | ||||||
|  |             for (lfs2_size_t j = 0; j < size; j++) { | ||||||
|  |                 char c = 'a' + (rand() % 26); | ||||||
|  |                 lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1); | ||||||
|  |                 assert(res == 1 || res == LFS2_ERR_NOSPC); | ||||||
|  |                 if (res == LFS2_ERR_NOSPC) { | ||||||
|  |                     err = lfs2_file_close(&lfs2, &file); | ||||||
|  |                     assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||||
|  |                     lfs2_unmount(&lfs2) => 0; | ||||||
|  |                     goto exhausted; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             err = lfs2_file_close(&lfs2, &file); | ||||||
|  |             assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||||
|  |             if (err == LFS2_ERR_NOSPC) { | ||||||
|  |                 lfs2_unmount(&lfs2) => 0; | ||||||
|  |                 goto exhausted; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |             // check for errors | ||||||
|  |             sprintf(path, "roadrunner/test%d", i); | ||||||
|  |             srand(cycle * i); | ||||||
|  |             size = 1 << ((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |             for (lfs2_size_t j = 0; j < size; j++) { | ||||||
|  |                 char c = 'a' + (rand() % 26); | ||||||
|  |                 char r; | ||||||
|  |                 lfs2_file_read(&lfs2, &file, &r, 1) => 1; | ||||||
|  |                 assert(r == c); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |         cycle += 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | exhausted: | ||||||
|  |     // should still be readable | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |         // check for errors | ||||||
|  |         sprintf(path, "roadrunner/test%d", i); | ||||||
|  |         lfs2_stat(&lfs2, path, &info) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     LFS2_WARN("completed %d cycles", cycle); | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # test running a filesystem to exhaustion | ||||||
|  |          # which also requires expanding superblocks | ||||||
|  | define.LFS2_ERASE_CYCLES = 10 | ||||||
|  | define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster | ||||||
|  | define.LFS2_BLOCK_CYCLES = 'LFS2_ERASE_CYCLES / 2' | ||||||
|  | define.LFS2_BADBLOCK_BEHAVIOR = [ | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASEERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_READERROR', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_PROGNOOP', | ||||||
|  |     'LFS2_TESTBD_BADBLOCK_ERASENOOP', | ||||||
|  | ] | ||||||
|  | define.FILES = 10 | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     uint32_t cycle = 0; | ||||||
|  |     while (true) { | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |             // chose name, roughly random seed, and random 2^n size | ||||||
|  |             sprintf(path, "test%d", i); | ||||||
|  |             srand(cycle * i); | ||||||
|  |             size = 1 << ((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                     LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |  | ||||||
|  |             for (lfs2_size_t j = 0; j < size; j++) { | ||||||
|  |                 char c = 'a' + (rand() % 26); | ||||||
|  |                 lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1); | ||||||
|  |                 assert(res == 1 || res == LFS2_ERR_NOSPC); | ||||||
|  |                 if (res == LFS2_ERR_NOSPC) { | ||||||
|  |                     err = lfs2_file_close(&lfs2, &file); | ||||||
|  |                     assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||||
|  |                     lfs2_unmount(&lfs2) => 0; | ||||||
|  |                     goto exhausted; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             err = lfs2_file_close(&lfs2, &file); | ||||||
|  |             assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||||
|  |             if (err == LFS2_ERR_NOSPC) { | ||||||
|  |                 lfs2_unmount(&lfs2) => 0; | ||||||
|  |                 goto exhausted; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |             // check for errors | ||||||
|  |             sprintf(path, "test%d", i); | ||||||
|  |             srand(cycle * i); | ||||||
|  |             size = 1 << ((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |             for (lfs2_size_t j = 0; j < size; j++) { | ||||||
|  |                 char c = 'a' + (rand() % 26); | ||||||
|  |                 char r; | ||||||
|  |                 lfs2_file_read(&lfs2, &file, &r, 1) => 1; | ||||||
|  |                 assert(r == c); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |         cycle += 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | exhausted: | ||||||
|  |     // should still be readable | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |         // check for errors | ||||||
|  |         sprintf(path, "test%d", i); | ||||||
|  |         lfs2_stat(&lfs2, path, &info) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     LFS2_WARN("completed %d cycles", cycle); | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | # These are a sort of high-level litmus test for wear-leveling. One definition | ||||||
|  | # of wear-leveling is that increasing a block device's space translates directly | ||||||
|  | # into increasing the block devices lifetime. This is something we can actually | ||||||
|  | # check for. | ||||||
|  |  | ||||||
|  | [[case]] # wear-level test running a filesystem to exhaustion | ||||||
|  | define.LFS2_ERASE_CYCLES = 20 | ||||||
|  | define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster | ||||||
|  | define.LFS2_BLOCK_CYCLES = 'LFS2_ERASE_CYCLES / 2' | ||||||
|  | define.FILES = 10 | ||||||
|  | code = ''' | ||||||
|  |     uint32_t run_cycles[2]; | ||||||
|  |     const uint32_t run_block_count[2] = {LFS2_BLOCK_COUNT/2, LFS2_BLOCK_COUNT}; | ||||||
|  |  | ||||||
|  |     for (int run = 0; run < 2; run++) { | ||||||
|  |         for (lfs2_block_t b = 0; b < LFS2_BLOCK_COUNT; b++) { | ||||||
|  |             lfs2_testbd_setwear(&cfg, b, | ||||||
|  |                     (b < run_block_count[run]) ? 0 : LFS2_ERASE_CYCLES) => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mkdir(&lfs2, "roadrunner") => 0; | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |         uint32_t cycle = 0; | ||||||
|  |         while (true) { | ||||||
|  |             lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |             for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |                 // chose name, roughly random seed, and random 2^n size | ||||||
|  |                 sprintf(path, "roadrunner/test%d", i); | ||||||
|  |                 srand(cycle * i); | ||||||
|  |                 size = 1 << ((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |                 lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                         LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |  | ||||||
|  |                 for (lfs2_size_t j = 0; j < size; j++) { | ||||||
|  |                     char c = 'a' + (rand() % 26); | ||||||
|  |                     lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1); | ||||||
|  |                     assert(res == 1 || res == LFS2_ERR_NOSPC); | ||||||
|  |                     if (res == LFS2_ERR_NOSPC) { | ||||||
|  |                         err = lfs2_file_close(&lfs2, &file); | ||||||
|  |                         assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||||
|  |                         lfs2_unmount(&lfs2) => 0; | ||||||
|  |                         goto exhausted; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 err = lfs2_file_close(&lfs2, &file); | ||||||
|  |                 assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||||
|  |                 if (err == LFS2_ERR_NOSPC) { | ||||||
|  |                     lfs2_unmount(&lfs2) => 0; | ||||||
|  |                     goto exhausted; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |                 // check for errors | ||||||
|  |                 sprintf(path, "roadrunner/test%d", i); | ||||||
|  |                 srand(cycle * i); | ||||||
|  |                 size = 1 << ((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |                 lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |                 for (lfs2_size_t j = 0; j < size; j++) { | ||||||
|  |                     char c = 'a' + (rand() % 26); | ||||||
|  |                     char r; | ||||||
|  |                     lfs2_file_read(&lfs2, &file, &r, 1) => 1; | ||||||
|  |                     assert(r == c); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |             } | ||||||
|  |             lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |             cycle += 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  | exhausted: | ||||||
|  |         // should still be readable | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |             // check for errors | ||||||
|  |             sprintf(path, "roadrunner/test%d", i); | ||||||
|  |             lfs2_stat(&lfs2, path, &info) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |         run_cycles[run] = cycle; | ||||||
|  |         LFS2_WARN("completed %d blocks %d cycles", | ||||||
|  |                 run_block_count[run], run_cycles[run]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // check we increased the lifetime by 2x with ~10% error | ||||||
|  |     LFS2_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # wear-level test + expanding superblock | ||||||
|  | define.LFS2_ERASE_CYCLES = 20 | ||||||
|  | define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster | ||||||
|  | define.LFS2_BLOCK_CYCLES = 'LFS2_ERASE_CYCLES / 2' | ||||||
|  | define.FILES = 10 | ||||||
|  | code = ''' | ||||||
|  |     uint32_t run_cycles[2]; | ||||||
|  |     const uint32_t run_block_count[2] = {LFS2_BLOCK_COUNT/2, LFS2_BLOCK_COUNT}; | ||||||
|  |  | ||||||
|  |     for (int run = 0; run < 2; run++) { | ||||||
|  |         for (lfs2_block_t b = 0; b < LFS2_BLOCK_COUNT; b++) { | ||||||
|  |             lfs2_testbd_setwear(&cfg, b, | ||||||
|  |                     (b < run_block_count[run]) ? 0 : LFS2_ERASE_CYCLES) => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |         uint32_t cycle = 0; | ||||||
|  |         while (true) { | ||||||
|  |             lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |             for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |                 // chose name, roughly random seed, and random 2^n size | ||||||
|  |                 sprintf(path, "test%d", i); | ||||||
|  |                 srand(cycle * i); | ||||||
|  |                 size = 1 << ((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |                 lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                         LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |  | ||||||
|  |                 for (lfs2_size_t j = 0; j < size; j++) { | ||||||
|  |                     char c = 'a' + (rand() % 26); | ||||||
|  |                     lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1); | ||||||
|  |                     assert(res == 1 || res == LFS2_ERR_NOSPC); | ||||||
|  |                     if (res == LFS2_ERR_NOSPC) { | ||||||
|  |                         err = lfs2_file_close(&lfs2, &file); | ||||||
|  |                         assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||||
|  |                         lfs2_unmount(&lfs2) => 0; | ||||||
|  |                         goto exhausted; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 err = lfs2_file_close(&lfs2, &file); | ||||||
|  |                 assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||||
|  |                 if (err == LFS2_ERR_NOSPC) { | ||||||
|  |                     lfs2_unmount(&lfs2) => 0; | ||||||
|  |                     goto exhausted; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |                 // check for errors | ||||||
|  |                 sprintf(path, "test%d", i); | ||||||
|  |                 srand(cycle * i); | ||||||
|  |                 size = 1 << ((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |                 lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |                 for (lfs2_size_t j = 0; j < size; j++) { | ||||||
|  |                     char c = 'a' + (rand() % 26); | ||||||
|  |                     char r; | ||||||
|  |                     lfs2_file_read(&lfs2, &file, &r, 1) => 1; | ||||||
|  |                     assert(r == c); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |             } | ||||||
|  |             lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |             cycle += 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  | exhausted: | ||||||
|  |         // should still be readable | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |             // check for errors | ||||||
|  |             sprintf(path, "test%d", i); | ||||||
|  |             lfs2_stat(&lfs2, path, &info) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |         run_cycles[run] = cycle; | ||||||
|  |         LFS2_WARN("completed %d blocks %d cycles", | ||||||
|  |                 run_block_count[run], run_cycles[run]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // check we increased the lifetime by 2x with ~10% error | ||||||
|  |     LFS2_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # test that we wear blocks roughly evenly | ||||||
|  | define.LFS2_ERASE_CYCLES = 0xffffffff | ||||||
|  | define.LFS2_BLOCK_COUNT = 256 # small bd so test runs faster | ||||||
|  | define.LFS2_BLOCK_CYCLES = [5, 4, 3, 2, 1] | ||||||
|  | define.CYCLES = 100 | ||||||
|  | define.FILES = 10 | ||||||
|  | if = 'LFS2_BLOCK_CYCLES < CYCLES/10' | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "roadrunner") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     uint32_t cycle = 0; | ||||||
|  |     while (cycle < CYCLES) { | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |             // chose name, roughly random seed, and random 2^n size | ||||||
|  |             sprintf(path, "roadrunner/test%d", i); | ||||||
|  |             srand(cycle * i); | ||||||
|  |             size = 1 << 4; //((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                     LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |  | ||||||
|  |             for (lfs2_size_t j = 0; j < size; j++) { | ||||||
|  |                 char c = 'a' + (rand() % 26); | ||||||
|  |                 lfs2_ssize_t res = lfs2_file_write(&lfs2, &file, &c, 1); | ||||||
|  |                 assert(res == 1 || res == LFS2_ERR_NOSPC); | ||||||
|  |                 if (res == LFS2_ERR_NOSPC) { | ||||||
|  |                     err = lfs2_file_close(&lfs2, &file); | ||||||
|  |                     assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||||
|  |                     lfs2_unmount(&lfs2) => 0; | ||||||
|  |                     goto exhausted; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             err = lfs2_file_close(&lfs2, &file); | ||||||
|  |             assert(err == 0 || err == LFS2_ERR_NOSPC); | ||||||
|  |             if (err == LFS2_ERR_NOSPC) { | ||||||
|  |                 lfs2_unmount(&lfs2) => 0; | ||||||
|  |                 goto exhausted; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |             // check for errors | ||||||
|  |             sprintf(path, "roadrunner/test%d", i); | ||||||
|  |             srand(cycle * i); | ||||||
|  |             size = 1 << 4; //((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |             for (lfs2_size_t j = 0; j < size; j++) { | ||||||
|  |                 char c = 'a' + (rand() % 26); | ||||||
|  |                 char r; | ||||||
|  |                 lfs2_file_read(&lfs2, &file, &r, 1) => 1; | ||||||
|  |                 assert(r == c); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |         cycle += 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | exhausted: | ||||||
|  |     // should still be readable | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |         // check for errors | ||||||
|  |         sprintf(path, "roadrunner/test%d", i); | ||||||
|  |         lfs2_stat(&lfs2, path, &info) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     LFS2_WARN("completed %d cycles", cycle); | ||||||
|  |  | ||||||
|  |     // check the wear on our block device | ||||||
|  |     lfs2_testbd_wear_t minwear = -1; | ||||||
|  |     lfs2_testbd_wear_t totalwear = 0; | ||||||
|  |     lfs2_testbd_wear_t maxwear = 0; | ||||||
|  |     // skip 0 and 1 as superblock movement is intentionally avoided | ||||||
|  |     for (lfs2_block_t b = 2; b < LFS2_BLOCK_COUNT; b++) { | ||||||
|  |         lfs2_testbd_wear_t wear = lfs2_testbd_getwear(&cfg, b); | ||||||
|  |         printf("%08x: wear %d\n", b, wear); | ||||||
|  |         assert(wear >= 0); | ||||||
|  |         if (wear < minwear) { | ||||||
|  |             minwear = wear; | ||||||
|  |         } | ||||||
|  |         if (wear > maxwear) { | ||||||
|  |             maxwear = wear; | ||||||
|  |         } | ||||||
|  |         totalwear += wear; | ||||||
|  |     } | ||||||
|  |     lfs2_testbd_wear_t avgwear = totalwear / LFS2_BLOCK_COUNT; | ||||||
|  |     LFS2_WARN("max wear: %d cycles", maxwear); | ||||||
|  |     LFS2_WARN("avg wear: %d cycles", totalwear / LFS2_BLOCK_COUNT); | ||||||
|  |     LFS2_WARN("min wear: %d cycles", minwear); | ||||||
|  |  | ||||||
|  |     // find standard deviation^2 | ||||||
|  |     lfs2_testbd_wear_t dev2 = 0; | ||||||
|  |     for (lfs2_block_t b = 2; b < LFS2_BLOCK_COUNT; b++) { | ||||||
|  |         lfs2_testbd_wear_t wear = lfs2_testbd_getwear(&cfg, b); | ||||||
|  |         assert(wear >= 0); | ||||||
|  |         lfs2_testbd_swear_t diff = wear - avgwear; | ||||||
|  |         dev2 += diff*diff; | ||||||
|  |     } | ||||||
|  |     dev2 /= totalwear; | ||||||
|  |     LFS2_WARN("std dev^2: %d", dev2); | ||||||
|  |     assert(dev2 < 8); | ||||||
|  | ''' | ||||||
|  |  | ||||||
| @@ -1,221 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| echo "=== File tests ===" |  | ||||||
|  |  | ||||||
| SMALLSIZE=32 |  | ||||||
| MEDIUMSIZE=8192 |  | ||||||
| LARGESIZE=262144 |  | ||||||
|  |  | ||||||
| rm -rf blocks |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Simple file test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|     lfs2_size_t size = strlen("Hello World!\n"); |  | ||||||
|     uint8_t wbuffer[1024]; |  | ||||||
|     memcpy(wbuffer, "Hello World!\n", size); |  | ||||||
|     lfs2_file_write(&lfs2, &file, wbuffer, size) => size; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_open(&lfs2, &file, "hello", LFS2_O_RDONLY) => 0; |  | ||||||
|     size = strlen("Hello World!\n"); |  | ||||||
|     uint8_t rbuffer[1024]; |  | ||||||
|     lfs2_file_read(&lfs2, &file, rbuffer, size) => size; |  | ||||||
|     memcmp(rbuffer, wbuffer, size) => 0; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| w_test() { |  | ||||||
| scripts/test.py ${4:-} << TEST |  | ||||||
|     lfs2_size_t size = $1; |  | ||||||
|     lfs2_size_t chunk = 31; |  | ||||||
|     srand(0); |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "$2", |  | ||||||
|         ${3:-LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC}) => 0; |  | ||||||
|     for (lfs2_size_t i = 0; i < size; i += chunk) { |  | ||||||
|         chunk = (chunk < size - i) ? chunk : size - i; |  | ||||||
|         for (lfs2_size_t b = 0; b < chunk; b++) { |  | ||||||
|             buffer[b] = rand() & 0xff; |  | ||||||
|         } |  | ||||||
|         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; |  | ||||||
|     } |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| } |  | ||||||
|  |  | ||||||
| r_test() { |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_size_t size = $1; |  | ||||||
|     lfs2_size_t chunk = 29; |  | ||||||
|     srand(0); |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_stat(&lfs2, "$2", &info) => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => size; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "$2", ${3:-LFS2_O_RDONLY}) => 0; |  | ||||||
|     for (lfs2_size_t i = 0; i < size; i += chunk) { |  | ||||||
|         chunk = (chunk < size - i) ? chunk : size - i; |  | ||||||
|         lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; |  | ||||||
|         for (lfs2_size_t b = 0; b < chunk && i+b < size; b++) { |  | ||||||
|             buffer[b] => rand() & 0xff; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| } |  | ||||||
|  |  | ||||||
| echo "--- Small file test ---" |  | ||||||
| w_test $SMALLSIZE smallavacado |  | ||||||
| r_test $SMALLSIZE smallavacado |  | ||||||
|  |  | ||||||
| echo "--- Medium file test ---" |  | ||||||
| w_test $MEDIUMSIZE mediumavacado |  | ||||||
| r_test $MEDIUMSIZE mediumavacado |  | ||||||
|  |  | ||||||
| echo "--- Large file test ---" |  | ||||||
| w_test $LARGESIZE largeavacado |  | ||||||
| r_test $LARGESIZE largeavacado |  | ||||||
|  |  | ||||||
| echo "--- Zero file test ---" |  | ||||||
| w_test 0 noavacado |  | ||||||
| r_test 0 noavacado |  | ||||||
|  |  | ||||||
| echo "--- Truncate small test ---" |  | ||||||
| w_test $SMALLSIZE mediumavacado |  | ||||||
| r_test $SMALLSIZE mediumavacado |  | ||||||
| w_test $MEDIUMSIZE mediumavacado |  | ||||||
| r_test $MEDIUMSIZE mediumavacado |  | ||||||
|  |  | ||||||
| echo "--- Truncate zero test ---" |  | ||||||
| w_test $SMALLSIZE noavacado |  | ||||||
| r_test $SMALLSIZE noavacado |  | ||||||
| w_test 0 noavacado |  | ||||||
| r_test 0 noavacado |  | ||||||
|  |  | ||||||
| echo "--- Non-overlap check ---" |  | ||||||
| r_test $SMALLSIZE smallavacado |  | ||||||
| r_test $MEDIUMSIZE mediumavacado |  | ||||||
| r_test $LARGESIZE largeavacado |  | ||||||
| r_test 0 noavacado |  | ||||||
|  |  | ||||||
| echo "--- Dir check ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hello") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => strlen("Hello World!\n"); |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "largeavacado") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => $LARGESIZE; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "mediumavacado") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => $MEDIUMSIZE; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "noavacado") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "smallavacado") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => $SMALLSIZE; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Many files test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     // Create 300 files of 7 bytes |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (unsigned i = 0; i < 300; i++) { |  | ||||||
|         sprintf(path, "file_%03d", i); |  | ||||||
|         lfs2_file_open(&lfs2, &file, path, |  | ||||||
|                 LFS2_O_RDWR | LFS2_O_CREAT | LFS2_O_EXCL) => 0; |  | ||||||
|         lfs2_size_t size = 7; |  | ||||||
|         uint8_t wbuffer[1024]; |  | ||||||
|         uint8_t rbuffer[1024]; |  | ||||||
|         snprintf((char*)wbuffer, size, "Hi %03d", i); |  | ||||||
|         lfs2_file_write(&lfs2, &file, wbuffer, size) => size; |  | ||||||
|         lfs2_file_rewind(&lfs2, &file) => 0; |  | ||||||
|         lfs2_file_read(&lfs2, &file, rbuffer, size) => size; |  | ||||||
|         memcmp(wbuffer, rbuffer, size) => 0; |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Many files with flush test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     // Create 300 files of 7 bytes |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (unsigned i = 0; i < 300; i++) { |  | ||||||
|         sprintf(path, "file_%03d", i); |  | ||||||
|         lfs2_file_open(&lfs2, &file, path, |  | ||||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; |  | ||||||
|         lfs2_size_t size = 7; |  | ||||||
|         uint8_t wbuffer[1024]; |  | ||||||
|         uint8_t rbuffer[1024]; |  | ||||||
|         snprintf((char*)wbuffer, size, "Hi %03d", i); |  | ||||||
|         lfs2_file_write(&lfs2, &file, wbuffer, size) => size; |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|  |  | ||||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; |  | ||||||
|         lfs2_file_read(&lfs2, &file, rbuffer, size) => size; |  | ||||||
|         memcmp(wbuffer, rbuffer, size) => 0; |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Many files with power cycle test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     // Create 300 files of 7 bytes |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (unsigned i = 0; i < 300; i++) { |  | ||||||
|         sprintf(path, "file_%03d", i); |  | ||||||
|         lfs2_file_open(&lfs2, &file, path, |  | ||||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; |  | ||||||
|         lfs2_size_t size = 7; |  | ||||||
|         uint8_t wbuffer[1024]; |  | ||||||
|         uint8_t rbuffer[1024]; |  | ||||||
|         snprintf((char*)wbuffer, size, "Hi %03d", i); |  | ||||||
|         lfs2_file_write(&lfs2, &file, wbuffer, size) => size; |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|         lfs2_unmount(&lfs2) => 0; |  | ||||||
|  |  | ||||||
|         lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; |  | ||||||
|         lfs2_file_read(&lfs2, &file, rbuffer, size) => size; |  | ||||||
|         memcmp(wbuffer, rbuffer, size) => 0; |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										486
									
								
								tests/test_files.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										486
									
								
								tests/test_files.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,486 @@ | |||||||
|  |  | ||||||
|  | [[case]] # simple file test | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "hello", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |     size = strlen("Hello World!")+1; | ||||||
|  |     strcpy((char*)buffer, "Hello World!"); | ||||||
|  |     lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "hello", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     assert(strcmp((char*)buffer, "Hello World!") == 0); | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # larger files | ||||||
|  | define.SIZE = [32, 8192, 262144, 0, 7, 8193] | ||||||
|  | define.CHUNKSIZE = [31, 16, 33, 1, 1023] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // write | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i); | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             buffer[b] = rand() & 0xff; | ||||||
|  |         } | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // read | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i); | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # rewriting files | ||||||
|  | define.SIZE1 = [32, 8192, 131072, 0, 7, 8193] | ||||||
|  | define.SIZE2 = [32, 8192, 131072, 0, 7, 8193] | ||||||
|  | define.CHUNKSIZE = [31, 16, 1] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // write | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i); | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             buffer[b] = rand() & 0xff; | ||||||
|  |         } | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // read | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE1; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i); | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // rewrite | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY) => 0; | ||||||
|  |     srand(2); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i); | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             buffer[b] = rand() & 0xff; | ||||||
|  |         } | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // read | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => lfs2_max(SIZE1, SIZE2); | ||||||
|  |     srand(2); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i); | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (SIZE1 > SIZE2) { | ||||||
|  |         srand(1); | ||||||
|  |         for (lfs2_size_t b = 0; b < SIZE2; b++) { | ||||||
|  |             rand(); | ||||||
|  |         } | ||||||
|  |         for (lfs2_size_t i = SIZE2; i < SIZE1; i += CHUNKSIZE) { | ||||||
|  |             lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i); | ||||||
|  |             lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |             for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |                 assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # appending files | ||||||
|  | define.SIZE1 = [32, 8192, 131072, 0, 7, 8193] | ||||||
|  | define.SIZE2 = [32, 8192, 131072, 0, 7, 8193] | ||||||
|  | define.CHUNKSIZE = [31, 16, 1] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // write | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i); | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             buffer[b] = rand() & 0xff; | ||||||
|  |         } | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // read | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE1; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i); | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // append | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_APPEND) => 0; | ||||||
|  |     srand(2); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i); | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             buffer[b] = rand() & 0xff; | ||||||
|  |         } | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // read | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE1 + SIZE2; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i); | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     srand(2); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i); | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # truncating files | ||||||
|  | define.SIZE1 = [32, 8192, 131072, 0, 7, 8193] | ||||||
|  | define.SIZE2 = [32, 8192, 131072, 0, 7, 8193] | ||||||
|  | define.CHUNKSIZE = [31, 16, 1] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     // write | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i); | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             buffer[b] = rand() & 0xff; | ||||||
|  |         } | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // read | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE1; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE1-i); | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // truncate | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_TRUNC) => 0; | ||||||
|  |     srand(2); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i); | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             buffer[b] = rand() & 0xff; | ||||||
|  |         } | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // read | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE2; | ||||||
|  |     srand(2); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE2-i); | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # reentrant file writing | ||||||
|  | define.SIZE = [32, 0, 7, 2049] | ||||||
|  | define.CHUNKSIZE = [31, 16, 65] | ||||||
|  | reentrant = true | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     err = lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY); | ||||||
|  |     assert(err == LFS2_ERR_NOENT || err == 0); | ||||||
|  |     if (err == 0) { | ||||||
|  |         // can only be 0 (new file) or full size | ||||||
|  |         size = lfs2_file_size(&lfs2, &file); | ||||||
|  |         assert(size == 0 || size == SIZE); | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // write | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i); | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             buffer[b] = rand() & 0xff; | ||||||
|  |         } | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // read | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i); | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # reentrant file writing with syncs | ||||||
|  | define = [ | ||||||
|  |     # append (O(n)) | ||||||
|  |     {MODE='LFS2_O_APPEND',   SIZE=[32, 0, 7, 2049],  CHUNKSIZE=[31, 16, 65]}, | ||||||
|  |     # truncate (O(n^2)) | ||||||
|  |     {MODE='LFS2_O_TRUNC',    SIZE=[32, 0, 7, 200],   CHUNKSIZE=[31, 16, 65]}, | ||||||
|  |     # rewrite (O(n^2)) | ||||||
|  |     {MODE=0,                SIZE=[32, 0, 7, 200],   CHUNKSIZE=[31, 16, 65]}, | ||||||
|  | ] | ||||||
|  | reentrant = true | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     err = lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY); | ||||||
|  |     assert(err == LFS2_ERR_NOENT || err == 0); | ||||||
|  |     if (err == 0) { | ||||||
|  |         // with syncs we could be any size, but it at least must be valid data | ||||||
|  |         size = lfs2_file_size(&lfs2, &file); | ||||||
|  |         assert(size <= SIZE); | ||||||
|  |         srand(1); | ||||||
|  |         for (lfs2_size_t i = 0; i < size; i += CHUNKSIZE) { | ||||||
|  |             lfs2_size_t chunk = lfs2_min(CHUNKSIZE, size-i); | ||||||
|  |             lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |             for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |                 assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // write | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", | ||||||
|  |         LFS2_O_WRONLY | LFS2_O_CREAT | MODE) => 0; | ||||||
|  |     size = lfs2_file_size(&lfs2, &file); | ||||||
|  |     assert(size <= SIZE); | ||||||
|  |     srand(1); | ||||||
|  |     lfs2_size_t skip = (MODE == LFS2_O_APPEND) ? size : 0; | ||||||
|  |     for (lfs2_size_t b = 0; b < skip; b++) { | ||||||
|  |         rand(); | ||||||
|  |     } | ||||||
|  |     for (lfs2_size_t i = skip; i < SIZE; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i); | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             buffer[b] = rand() & 0xff; | ||||||
|  |         } | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |         lfs2_file_sync(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     // read | ||||||
|  |     lfs2_file_open(&lfs2, &file, "avacado", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE; | ||||||
|  |     srand(1); | ||||||
|  |     for (lfs2_size_t i = 0; i < SIZE; i += CHUNKSIZE) { | ||||||
|  |         lfs2_size_t chunk = lfs2_min(CHUNKSIZE, SIZE-i); | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, chunk) => chunk; | ||||||
|  |         for (lfs2_size_t b = 0; b < chunk; b++) { | ||||||
|  |             assert(buffer[b] == (rand() & 0xff)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, CHUNKSIZE) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # many files | ||||||
|  | define.N = 300 | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     // create N files of 7 bytes | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "file_%03d", i); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |         char wbuffer[1024]; | ||||||
|  |         size = 7; | ||||||
|  |         snprintf(wbuffer, size, "Hi %03d", i); | ||||||
|  |         lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |         char rbuffer[1024]; | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |         lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |         assert(strcmp(rbuffer, wbuffer) == 0); | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # many files with power cycle | ||||||
|  | define.N = 300 | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     // create N files of 7 bytes | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "file_%03d", i); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |         char wbuffer[1024]; | ||||||
|  |         size = 7; | ||||||
|  |         snprintf(wbuffer, size, "Hi %03d", i); | ||||||
|  |         lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |         char rbuffer[1024]; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |         lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |         assert(strcmp(rbuffer, wbuffer) == 0); | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # many files with power loss | ||||||
|  | define.N = 300 | ||||||
|  | reentrant = true | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |     // create N files of 7 bytes | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         sprintf(path, "file_%03d", i); | ||||||
|  |         err = lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY | LFS2_O_CREAT); | ||||||
|  |         char wbuffer[1024]; | ||||||
|  |         size = 7; | ||||||
|  |         snprintf(wbuffer, size, "Hi %03d", i); | ||||||
|  |         if ((lfs2_size_t)lfs2_file_size(&lfs2, &file) != size) { | ||||||
|  |             lfs2_file_write(&lfs2, &file, wbuffer, size) => size; | ||||||
|  |         } | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |         char rbuffer[1024]; | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |         lfs2_file_read(&lfs2, &file, rbuffer, size) => size; | ||||||
|  |         assert(strcmp(rbuffer, wbuffer) == 0); | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
| @@ -1,51 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| echo "=== Formatting tests ===" |  | ||||||
| rm -rf blocks |  | ||||||
|  |  | ||||||
| echo "--- Basic formatting ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Basic mounting ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Invalid superblocks ---" |  | ||||||
| ln -f -s /dev/zero blocks/0 |  | ||||||
| ln -f -s /dev/zero blocks/1 |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => LFS2_ERR_NOSPC; |  | ||||||
| TEST |  | ||||||
| rm blocks/0 blocks/1 |  | ||||||
|  |  | ||||||
| echo "--- Invalid mount ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Expanding superblock ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (int i = 0; i < 100; i++) { |  | ||||||
|         lfs2_mkdir(&lfs2, "dummy") => 0; |  | ||||||
|         lfs2_remove(&lfs2, "dummy") => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "dummy") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
| @@ -1,190 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| echo "=== Interspersed tests ===" |  | ||||||
| rm -rf blocks |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Interspersed file test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_t files[4]; |  | ||||||
|     lfs2_file_open(&lfs2, &files[0], "a", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &files[1], "b", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &files[2], "c", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &files[3], "d", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|  |  | ||||||
|     for (int i = 0; i < 10; i++) { |  | ||||||
|         lfs2_file_write(&lfs2, &files[0], (const void*)"a", 1) => 1; |  | ||||||
|         lfs2_file_write(&lfs2, &files[1], (const void*)"b", 1) => 1; |  | ||||||
|         lfs2_file_write(&lfs2, &files[2], (const void*)"c", 1) => 1; |  | ||||||
|         lfs2_file_write(&lfs2, &files[3], (const void*)"d", 1) => 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &files[0]); |  | ||||||
|     lfs2_file_close(&lfs2, &files[1]); |  | ||||||
|     lfs2_file_close(&lfs2, &files[2]); |  | ||||||
|     lfs2_file_close(&lfs2, &files[3]); |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "a") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => 10; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "b") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => 10; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "c") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => 10; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "d") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => 10; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_open(&lfs2, &files[0], "a", LFS2_O_RDONLY) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &files[1], "b", LFS2_O_RDONLY) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &files[2], "c", LFS2_O_RDONLY) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &files[3], "d", LFS2_O_RDONLY) => 0; |  | ||||||
|  |  | ||||||
|     for (int i = 0; i < 10; i++) { |  | ||||||
|         lfs2_file_read(&lfs2, &files[0], buffer, 1) => 1; |  | ||||||
|         buffer[0] => 'a'; |  | ||||||
|         lfs2_file_read(&lfs2, &files[1], buffer, 1) => 1; |  | ||||||
|         buffer[0] => 'b'; |  | ||||||
|         lfs2_file_read(&lfs2, &files[2], buffer, 1) => 1; |  | ||||||
|         buffer[0] => 'c'; |  | ||||||
|         lfs2_file_read(&lfs2, &files[3], buffer, 1) => 1; |  | ||||||
|         buffer[0] => 'd'; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &files[0]); |  | ||||||
|     lfs2_file_close(&lfs2, &files[1]); |  | ||||||
|     lfs2_file_close(&lfs2, &files[2]); |  | ||||||
|     lfs2_file_close(&lfs2, &files[3]); |  | ||||||
|      |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Interspersed remove file test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_t files[4]; |  | ||||||
|     lfs2_file_open(&lfs2, &files[0], "e", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|  |  | ||||||
|     for (int i = 0; i < 5; i++) { |  | ||||||
|         lfs2_file_write(&lfs2, &files[0], (const void*)"e", 1) => 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "a") => 0; |  | ||||||
|     lfs2_remove(&lfs2, "b") => 0; |  | ||||||
|     lfs2_remove(&lfs2, "c") => 0; |  | ||||||
|     lfs2_remove(&lfs2, "d") => 0; |  | ||||||
|  |  | ||||||
|     for (int i = 0; i < 5; i++) { |  | ||||||
|         lfs2_file_write(&lfs2, &files[0], (const void*)"e", 1) => 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &files[0]); |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "e") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => 10; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_open(&lfs2, &files[0], "e", LFS2_O_RDONLY) => 0; |  | ||||||
|  |  | ||||||
|     for (int i = 0; i < 10; i++) { |  | ||||||
|         lfs2_file_read(&lfs2, &files[0], buffer, 1) => 1; |  | ||||||
|         buffer[0] => 'e'; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &files[0]); |  | ||||||
|      |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Remove inconveniently test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_t files[4]; |  | ||||||
|     lfs2_file_open(&lfs2, &files[0], "e", LFS2_O_WRONLY | LFS2_O_TRUNC) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &files[1], "f", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &files[2], "g", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|  |  | ||||||
|     for (int i = 0; i < 5; i++) { |  | ||||||
|         lfs2_file_write(&lfs2, &files[0], (const void*)"e", 1) => 1; |  | ||||||
|         lfs2_file_write(&lfs2, &files[1], (const void*)"f", 1) => 1; |  | ||||||
|         lfs2_file_write(&lfs2, &files[2], (const void*)"g", 1) => 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "f") => 0; |  | ||||||
|  |  | ||||||
|     for (int i = 0; i < 5; i++) { |  | ||||||
|         lfs2_file_write(&lfs2, &files[0], (const void*)"e", 1) => 1; |  | ||||||
|         lfs2_file_write(&lfs2, &files[1], (const void*)"f", 1) => 1; |  | ||||||
|         lfs2_file_write(&lfs2, &files[2], (const void*)"g", 1) => 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &files[0]); |  | ||||||
|     lfs2_file_close(&lfs2, &files[1]); |  | ||||||
|     lfs2_file_close(&lfs2, &files[2]); |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "e") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => 10; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "g") => 0; |  | ||||||
|     info.type => LFS2_TYPE_REG; |  | ||||||
|     info.size => 10; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_open(&lfs2, &files[0], "e", LFS2_O_RDONLY) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &files[1], "g", LFS2_O_RDONLY) => 0; |  | ||||||
|  |  | ||||||
|     for (int i = 0; i < 10; i++) { |  | ||||||
|         lfs2_file_read(&lfs2, &files[0], buffer, 1) => 1; |  | ||||||
|         buffer[0] => 'e'; |  | ||||||
|         lfs2_file_read(&lfs2, &files[1], buffer, 1) => 1; |  | ||||||
|         buffer[0] => 'g'; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &files[0]); |  | ||||||
|     lfs2_file_close(&lfs2, &files[1]); |  | ||||||
|      |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										244
									
								
								tests/test_interspersed.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								tests/test_interspersed.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,244 @@ | |||||||
|  |  | ||||||
|  | [[case]] # interspersed file test | ||||||
|  | define.SIZE = [10, 100] | ||||||
|  | define.FILES = [4, 10, 26]  | ||||||
|  | code = ''' | ||||||
|  |     lfs2_file_t files[FILES]; | ||||||
|  |     const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         sprintf(path, "%c", alphas[j]); | ||||||
|  |         lfs2_file_open(&lfs2, &files[j], path, | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < SIZE; i++) { | ||||||
|  |         for (int j = 0; j < FILES; j++) { | ||||||
|  |             lfs2_file_write(&lfs2, &files[j], &alphas[j], 1) => 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         lfs2_file_close(&lfs2, &files[j]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         sprintf(path, "%c", alphas[j]); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_REG); | ||||||
|  |         assert(info.size == SIZE); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         sprintf(path, "%c", alphas[j]); | ||||||
|  |         lfs2_file_open(&lfs2, &files[j], path, LFS2_O_RDONLY) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < 10; i++) { | ||||||
|  |         for (int j = 0; j < FILES; j++) { | ||||||
|  |             lfs2_file_read(&lfs2, &files[j], buffer, 1) => 1; | ||||||
|  |             assert(buffer[0] == alphas[j]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         lfs2_file_close(&lfs2, &files[j]); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # interspersed remove file test | ||||||
|  | define.SIZE = [10, 100] | ||||||
|  | define.FILES = [4, 10, 26] | ||||||
|  | code = ''' | ||||||
|  |     const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         sprintf(path, "%c", alphas[j]); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |         for (int i = 0; i < SIZE; i++) { | ||||||
|  |             lfs2_file_write(&lfs2, &file, &alphas[j], 1) => 1; | ||||||
|  |         } | ||||||
|  |         lfs2_file_close(&lfs2, &file); | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "zzz", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, (const void*)"~", 1) => 1; | ||||||
|  |         lfs2_file_sync(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |         sprintf(path, "%c", alphas[j]); | ||||||
|  |         lfs2_remove(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file); | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "zzz") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_REG); | ||||||
|  |     assert(info.size == FILES); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_open(&lfs2, &file, "zzz", LFS2_O_RDONLY) => 0; | ||||||
|  |     for (int i = 0; i < FILES; i++) { | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, 1) => 1; | ||||||
|  |         assert(buffer[0] == '~'); | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file); | ||||||
|  |      | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # remove inconveniently test | ||||||
|  | define.SIZE = [10, 100] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_t files[3]; | ||||||
|  |     lfs2_file_open(&lfs2, &files[0], "e", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &files[1], "f", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &files[2], "g", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < SIZE/2; i++) { | ||||||
|  |         lfs2_file_write(&lfs2, &files[0], (const void*)"e", 1) => 1; | ||||||
|  |         lfs2_file_write(&lfs2, &files[1], (const void*)"f", 1) => 1; | ||||||
|  |         lfs2_file_write(&lfs2, &files[2], (const void*)"g", 1) => 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_remove(&lfs2, "f") => 0; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < SIZE/2; i++) { | ||||||
|  |         lfs2_file_write(&lfs2, &files[0], (const void*)"e", 1) => 1; | ||||||
|  |         lfs2_file_write(&lfs2, &files[1], (const void*)"f", 1) => 1; | ||||||
|  |         lfs2_file_write(&lfs2, &files[2], (const void*)"g", 1) => 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &files[0]); | ||||||
|  |     lfs2_file_close(&lfs2, &files[1]); | ||||||
|  |     lfs2_file_close(&lfs2, &files[2]); | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "e") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_REG); | ||||||
|  |     assert(info.size == SIZE); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "g") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_REG); | ||||||
|  |     assert(info.size == SIZE); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_open(&lfs2, &files[0], "e", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &files[1], "g", LFS2_O_RDONLY) => 0; | ||||||
|  |     for (int i = 0; i < SIZE; i++) { | ||||||
|  |         lfs2_file_read(&lfs2, &files[0], buffer, 1) => 1; | ||||||
|  |         assert(buffer[0] == 'e'); | ||||||
|  |         lfs2_file_read(&lfs2, &files[1], buffer, 1) => 1; | ||||||
|  |         assert(buffer[0] == 'g'); | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &files[0]); | ||||||
|  |     lfs2_file_close(&lfs2, &files[1]); | ||||||
|  |      | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # reentrant interspersed file test | ||||||
|  | define.SIZE = [10, 100] | ||||||
|  | define.FILES = [4, 10, 26]  | ||||||
|  | reentrant = true | ||||||
|  | code = ''' | ||||||
|  |     lfs2_file_t files[FILES]; | ||||||
|  |     const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; | ||||||
|  |  | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         sprintf(path, "%c", alphas[j]); | ||||||
|  |         lfs2_file_open(&lfs2, &files[j], path, | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < SIZE; i++) { | ||||||
|  |         for (int j = 0; j < FILES; j++) { | ||||||
|  |             size = lfs2_file_size(&lfs2, &files[j]); | ||||||
|  |             assert((int)size >= 0); | ||||||
|  |             if ((int)size <= i) { | ||||||
|  |                 lfs2_file_write(&lfs2, &files[j], &alphas[j], 1) => 1; | ||||||
|  |                 lfs2_file_sync(&lfs2, &files[j]) => 0; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         lfs2_file_close(&lfs2, &files[j]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "/") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, ".") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     assert(strcmp(info.name, "..") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         sprintf(path, "%c", alphas[j]); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         assert(strcmp(info.name, path) == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_REG); | ||||||
|  |         assert(info.size == SIZE); | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         sprintf(path, "%c", alphas[j]); | ||||||
|  |         lfs2_file_open(&lfs2, &files[j], path, LFS2_O_RDONLY) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < 10; i++) { | ||||||
|  |         for (int j = 0; j < FILES; j++) { | ||||||
|  |             lfs2_file_read(&lfs2, &files[j], buffer, 1) => 1; | ||||||
|  |             assert(buffer[0] == alphas[j]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int j = 0; j < FILES; j++) { | ||||||
|  |         lfs2_file_close(&lfs2, &files[j]); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
| @@ -1,333 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| echo "=== Move tests ===" |  | ||||||
| rm -rf blocks |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "a") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "b") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "c") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "d") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mkdir(&lfs2, "a/hi") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "a/hi/hola") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "a/hi/bonjour") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "a/hi/ohayo") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_open(&lfs2, &file, "a/hello", LFS2_O_CREAT | LFS2_O_WRONLY) => 0; |  | ||||||
|     lfs2_file_write(&lfs2, &file, "hola\n", 5) => 5; |  | ||||||
|     lfs2_file_write(&lfs2, &file, "bonjour\n", 8) => 8; |  | ||||||
|     lfs2_file_write(&lfs2, &file, "ohayo\n", 6) => 6; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Move file ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_rename(&lfs2, "a/hello", "b/hello") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "a") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hi") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "b") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hello") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Move file corrupt source ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_rename(&lfs2, "b/hello", "c/hello") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/corrupt.py -n 1 |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "b") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "c") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hello") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Move file corrupt source and dest ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_rename(&lfs2, "c/hello", "d/hello") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/corrupt.py -n 2 |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "c") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hello") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "d") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Move file after corrupt ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_rename(&lfs2, "c/hello", "d/hello") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "c") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "d") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hello") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Move dir ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_rename(&lfs2, "a/hi", "b/hi") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "a") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "b") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hi") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Move dir corrupt source ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_rename(&lfs2, "b/hi", "c/hi") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/corrupt.py -n 1 |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "b") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "c") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hi") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Move dir corrupt source and dest ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_rename(&lfs2, "c/hi", "d/hi") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/corrupt.py -n 2 |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "c") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hi") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "d") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hello") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Move dir after corrupt ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_rename(&lfs2, "c/hi", "d/hi") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "c") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "d") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hello") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hi") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Move check ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "a/hi") => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "b/hi") => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "c/hi") => LFS2_ERR_NOENT; |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "d/hi") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "bonjour") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hola") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "ohayo") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "a/hello") => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "b/hello") => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "c/hello") => LFS2_ERR_NOENT; |  | ||||||
|  |  | ||||||
|     lfs2_file_open(&lfs2, &file, "d/hello", LFS2_O_RDONLY) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, 5) => 5; |  | ||||||
|     memcmp(buffer, "hola\n", 5) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, 8) => 8; |  | ||||||
|     memcmp(buffer, "bonjour\n", 8) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, 6) => 6; |  | ||||||
|     memcmp(buffer, "ohayo\n", 6) => 0; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Move state stealing ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_remove(&lfs2, "b") => 0; |  | ||||||
|     lfs2_remove(&lfs2, "c") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "a/hi") => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "b") => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "c") => LFS2_ERR_NOENT; |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "d/hi") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "bonjour") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "hola") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "ohayo") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "a/hello") => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "b") => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "c") => LFS2_ERR_NOENT; |  | ||||||
|  |  | ||||||
|     lfs2_file_open(&lfs2, &file, "d/hello", LFS2_O_RDONLY) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, 5) => 5; |  | ||||||
|     memcmp(buffer, "hola\n", 5) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, 8) => 8; |  | ||||||
|     memcmp(buffer, "bonjour\n", 8) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, 6) => 6; |  | ||||||
|     memcmp(buffer, "ohayo\n", 6) => 0; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										1815
									
								
								tests/test_move.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1815
									
								
								tests/test_move.toml
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,46 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| echo "=== Orphan tests ===" |  | ||||||
| rm -rf blocks |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Orphan test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "parent") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "parent/orphan") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "parent/child") => 0; |  | ||||||
|     lfs2_remove(&lfs2, "parent/orphan") => 0; |  | ||||||
| TEST |  | ||||||
| # corrupt most recent commit, this should be the update to the previous |  | ||||||
| # linked-list entry and should orphan the child |  | ||||||
| scripts/corrupt.py |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_ssize_t before = lfs2_fs_size(&lfs2); |  | ||||||
|     before => 8; |  | ||||||
|  |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_ssize_t orphaned = lfs2_fs_size(&lfs2); |  | ||||||
|     orphaned => 8; |  | ||||||
|  |  | ||||||
|     lfs2_mkdir(&lfs2, "parent/otherchild") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_ssize_t deorphaned = lfs2_fs_size(&lfs2); |  | ||||||
|     deorphaned => 8; |  | ||||||
|  |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										120
									
								
								tests/test_orphans.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								tests/test_orphans.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | |||||||
|  | [[case]] # orphan test | ||||||
|  | in = "lfs2.c" | ||||||
|  | if = 'LFS2_PROG_SIZE <= 0x3fe' # only works with one crc per commit | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "parent") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "parent/orphan") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "parent/child") => 0; | ||||||
|  |     lfs2_remove(&lfs2, "parent/orphan") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // corrupt the child's most recent commit, this should be the update | ||||||
|  |     // to the linked-list entry, which should orphan the orphan. Note this | ||||||
|  |     // makes a lot of assumptions about the remove operation. | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "parent/child") => 0; | ||||||
|  |     lfs2_block_t block = dir.m.pair[0]; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |     uint8_t bbuffer[LFS2_BLOCK_SIZE]; | ||||||
|  |     cfg.read(&cfg, block, 0, bbuffer, LFS2_BLOCK_SIZE) => 0; | ||||||
|  |     int off = LFS2_BLOCK_SIZE-1; | ||||||
|  |     while (off >= 0 && bbuffer[off] == LFS2_ERASE_VALUE) { | ||||||
|  |         off -= 1; | ||||||
|  |     } | ||||||
|  |     memset(&bbuffer[off-3], LFS2_BLOCK_SIZE, 3); | ||||||
|  |     cfg.erase(&cfg, block) => 0; | ||||||
|  |     cfg.prog(&cfg, block, 0, bbuffer, LFS2_BLOCK_SIZE) => 0; | ||||||
|  |     cfg.sync(&cfg) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_stat(&lfs2, "parent/child", &info) => 0; | ||||||
|  |     lfs2_fs_size(&lfs2) => 8; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_stat(&lfs2, "parent/child", &info) => 0; | ||||||
|  |     lfs2_fs_size(&lfs2) => 8; | ||||||
|  |     // this mkdir should both create a dir and deorphan, so size | ||||||
|  |     // should be unchanged | ||||||
|  |     lfs2_mkdir(&lfs2, "parent/otherchild") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_stat(&lfs2, "parent/child", &info) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "parent/otherchild", &info) => 0; | ||||||
|  |     lfs2_fs_size(&lfs2) => 8; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_stat(&lfs2, "parent/child", &info) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "parent/otherchild", &info) => 0; | ||||||
|  |     lfs2_fs_size(&lfs2) => 8; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # reentrant testing for orphans, basically just spam mkdir/remove | ||||||
|  | reentrant = true | ||||||
|  | # TODO fix this case, caused by non-DAG trees | ||||||
|  | if = '!(DEPTH == 3 && LFS2_CACHE_SIZE != 64)' | ||||||
|  | define = [ | ||||||
|  |     {FILES=6,  DEPTH=1, CYCLES=20}, | ||||||
|  |     {FILES=26, DEPTH=1, CYCLES=20}, | ||||||
|  |     {FILES=3,  DEPTH=3, CYCLES=20}, | ||||||
|  | ] | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     srand(1); | ||||||
|  |     const char alpha[] = "abcdefghijklmnopqrstuvwxyz"; | ||||||
|  |     for (int i = 0; i < CYCLES; i++) { | ||||||
|  |         // create random path | ||||||
|  |         char full_path[256]; | ||||||
|  |         for (int d = 0; d < DEPTH; d++) { | ||||||
|  |             sprintf(&full_path[2*d], "/%c", alpha[rand() % FILES]); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // if it does not exist, we create it, else we destroy | ||||||
|  |         int res = lfs2_stat(&lfs2, full_path, &info); | ||||||
|  |         if (res == LFS2_ERR_NOENT) { | ||||||
|  |             // create each directory in turn, ignore if dir already exists | ||||||
|  |             for (int d = 0; d < DEPTH; d++) { | ||||||
|  |                 strcpy(path, full_path); | ||||||
|  |                 path[2*d+2] = '\0'; | ||||||
|  |                 err = lfs2_mkdir(&lfs2, path); | ||||||
|  |                 assert(!err || err == LFS2_ERR_EXIST); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             for (int d = 0; d < DEPTH; d++) { | ||||||
|  |                 strcpy(path, full_path); | ||||||
|  |                 path[2*d+2] = '\0'; | ||||||
|  |                 lfs2_stat(&lfs2, path, &info) => 0; | ||||||
|  |                 assert(strcmp(info.name, &path[2*d+1]) == 0); | ||||||
|  |                 assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             // is valid dir? | ||||||
|  |             assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0); | ||||||
|  |             assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |             // try to delete path in reverse order, ignore if dir is not empty | ||||||
|  |             for (int d = DEPTH-1; d >= 0; d--) { | ||||||
|  |                 strcpy(path, full_path); | ||||||
|  |                 path[2*d+2] = '\0'; | ||||||
|  |                 err = lfs2_remove(&lfs2, path); | ||||||
|  |                 assert(!err || err == LFS2_ERR_NOTEMPTY); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
| @@ -1,202 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| echo "=== Path tests ===" |  | ||||||
| rm -rf blocks |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "tea") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "coffee") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "soda") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "tea/hottea") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "tea/warmtea") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "tea/coldtea") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "coffee/hotcoffee") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "coffee/warmcoffee") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "coffee/coldcoffee") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "soda/hotsoda") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "soda/warmsoda") => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "soda/coldsoda") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Root path tests ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_stat(&lfs2, "tea/hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "/tea/hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mkdir(&lfs2, "/milk1") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "/milk1", &info) => 0; |  | ||||||
|     strcmp(info.name, "milk1") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Redundant slash path tests ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_stat(&lfs2, "/tea/hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "//tea//hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "///tea///hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mkdir(&lfs2, "///milk2") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "///milk2", &info) => 0; |  | ||||||
|     strcmp(info.name, "milk2") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Dot path tests ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_stat(&lfs2, "./tea/hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "/./tea/hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "/././tea/hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "/./tea/./hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mkdir(&lfs2, "/./milk3") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "/./milk3", &info) => 0; |  | ||||||
|     strcmp(info.name, "milk3") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Dot dot path tests ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_stat(&lfs2, "coffee/../tea/hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "tea/coldtea/../hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "coffee/coldcoffee/../../tea/hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "coffee/../soda/../tea/hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mkdir(&lfs2, "coffee/../milk4") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "coffee/../milk4", &info) => 0; |  | ||||||
|     strcmp(info.name, "milk4") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Trailing dot path tests ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_stat(&lfs2, "tea/hottea/", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "tea/hottea/.", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "tea/hottea/./.", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "tea/hottea/..", &info) => 0; |  | ||||||
|     strcmp(info.name, "tea") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "tea/hottea/../.", &info) => 0; |  | ||||||
|     strcmp(info.name, "tea") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Root dot dot path tests ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_stat(&lfs2, "coffee/../../../../../../tea/hottea", &info) => 0; |  | ||||||
|     strcmp(info.name, "hottea") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mkdir(&lfs2, "coffee/../../../../../../milk5") => 0; |  | ||||||
|     lfs2_stat(&lfs2, "coffee/../../../../../../milk5", &info) => 0; |  | ||||||
|     strcmp(info.name, "milk5") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Root tests ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_stat(&lfs2, "/", &info) => 0; |  | ||||||
|     info.type => LFS2_TYPE_DIR; |  | ||||||
|     strcmp(info.name, "/") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_mkdir(&lfs2, "/") => LFS2_ERR_EXIST; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "/", LFS2_O_WRONLY | LFS2_O_CREAT) |  | ||||||
|         => LFS2_ERR_ISDIR; |  | ||||||
|  |  | ||||||
|     // more corner cases |  | ||||||
|     lfs2_remove(&lfs2, "") => LFS2_ERR_INVAL; |  | ||||||
|     lfs2_remove(&lfs2, ".") => LFS2_ERR_INVAL; |  | ||||||
|     lfs2_remove(&lfs2, "..") => LFS2_ERR_INVAL; |  | ||||||
|     lfs2_remove(&lfs2, "/") => LFS2_ERR_INVAL; |  | ||||||
|     lfs2_remove(&lfs2, "//") => LFS2_ERR_INVAL; |  | ||||||
|     lfs2_remove(&lfs2, "./") => LFS2_ERR_INVAL; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Sketchy path tests ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "dirt/ground") => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_mkdir(&lfs2, "dirt/ground/earth") => LFS2_ERR_NOENT; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Superblock conflict test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "littlefs") => 0; |  | ||||||
|     lfs2_remove(&lfs2, "littlefs") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Max path test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     memset(path, 'w', LFS2_NAME_MAX+1); |  | ||||||
|     path[LFS2_NAME_MAX+2] = '\0'; |  | ||||||
|     lfs2_mkdir(&lfs2, path) => LFS2_ERR_NAMETOOLONG; |  | ||||||
|     lfs2_file_open(&lfs2, &file, path, |  | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => LFS2_ERR_NAMETOOLONG; |  | ||||||
|  |  | ||||||
|     memcpy(path, "coffee/", strlen("coffee/")); |  | ||||||
|     memset(path+strlen("coffee/"), 'w', LFS2_NAME_MAX+1); |  | ||||||
|     path[strlen("coffee/")+LFS2_NAME_MAX+2] = '\0'; |  | ||||||
|     lfs2_mkdir(&lfs2, path) => LFS2_ERR_NAMETOOLONG; |  | ||||||
|     lfs2_file_open(&lfs2, &file, path, |  | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => LFS2_ERR_NAMETOOLONG; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Really big path test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     memset(path, 'w', LFS2_NAME_MAX); |  | ||||||
|     path[LFS2_NAME_MAX] = '\0'; |  | ||||||
|     lfs2_mkdir(&lfs2, path) => 0; |  | ||||||
|     lfs2_remove(&lfs2, path) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, path, |  | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_remove(&lfs2, path) => 0; |  | ||||||
|  |  | ||||||
|     memcpy(path, "coffee/", strlen("coffee/")); |  | ||||||
|     memset(path+strlen("coffee/"), 'w', LFS2_NAME_MAX); |  | ||||||
|     path[strlen("coffee/")+LFS2_NAME_MAX] = '\0'; |  | ||||||
|     lfs2_mkdir(&lfs2, path) => 0; |  | ||||||
|     lfs2_remove(&lfs2, path) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, path, |  | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_remove(&lfs2, path) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										293
									
								
								tests/test_paths.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								tests/test_paths.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,293 @@ | |||||||
|  |  | ||||||
|  | [[case]] # simple path test | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/hottea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/warmtea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/coldtea") => 0; | ||||||
|  |  | ||||||
|  |     lfs2_stat(&lfs2, "tea/hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "/tea/hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |  | ||||||
|  |     lfs2_mkdir(&lfs2, "/milk") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "/milk", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "milk") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "milk", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "milk") == 0); | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # redundant slashes | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/hottea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/warmtea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/coldtea") => 0; | ||||||
|  |  | ||||||
|  |     lfs2_stat(&lfs2, "/tea/hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "//tea//hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "///tea///hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |  | ||||||
|  |     lfs2_mkdir(&lfs2, "////milk") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "////milk", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "milk") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "milk", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "milk") == 0); | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # dot path test | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/hottea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/warmtea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/coldtea") => 0; | ||||||
|  |  | ||||||
|  |     lfs2_stat(&lfs2, "./tea/hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "/./tea/hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "/././tea/hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "/./tea/./hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |  | ||||||
|  |     lfs2_mkdir(&lfs2, "/./milk") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "/./milk", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "milk") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "milk", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "milk") == 0); | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # dot dot path test | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/hottea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/warmtea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/coldtea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/hotcoffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/warmcoffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/coldcoffee") => 0; | ||||||
|  |  | ||||||
|  |     lfs2_stat(&lfs2, "coffee/../tea/hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "tea/coldtea/../hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "coffee/coldcoffee/../../tea/hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "coffee/../coffee/../tea/hottea", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |  | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/../milk") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "coffee/../milk", &info) => 0; | ||||||
|  |     strcmp(info.name, "milk") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "milk", &info) => 0; | ||||||
|  |     strcmp(info.name, "milk") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # trailing dot path test | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/hottea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/warmtea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/coldtea") => 0; | ||||||
|  |  | ||||||
|  |     lfs2_stat(&lfs2, "tea/hottea/", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "tea/hottea/.", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "tea/hottea/./.", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "hottea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "tea/hottea/..", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "tea") == 0); | ||||||
|  |     lfs2_stat(&lfs2, "tea/hottea/../.", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "tea") == 0); | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # leading dot path test | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, ".milk") => 0; | ||||||
|  |     lfs2_stat(&lfs2, ".milk", &info) => 0; | ||||||
|  |     strcmp(info.name, ".milk") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "tea/.././.milk", &info) => 0; | ||||||
|  |     strcmp(info.name, ".milk") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # root dot dot path test | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/hottea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/warmtea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "tea/coldtea") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/hotcoffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/warmcoffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/coldcoffee") => 0; | ||||||
|  |  | ||||||
|  |     lfs2_stat(&lfs2, "coffee/../../../../../../tea/hottea", &info) => 0; | ||||||
|  |     strcmp(info.name, "hottea") => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/../../../../../../milk") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "coffee/../../../../../../milk", &info) => 0; | ||||||
|  |     strcmp(info.name, "milk") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "milk", &info) => 0; | ||||||
|  |     strcmp(info.name, "milk") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # invalid path tests | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg); | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "dirt", &info) => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_stat(&lfs2, "dirt/ground", &info) => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_stat(&lfs2, "dirt/ground/earth", &info) => LFS2_ERR_NOENT; | ||||||
|  |  | ||||||
|  |     lfs2_remove(&lfs2, "dirt") => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_remove(&lfs2, "dirt/ground") => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_remove(&lfs2, "dirt/ground/earth") => LFS2_ERR_NOENT; | ||||||
|  |  | ||||||
|  |     lfs2_mkdir(&lfs2, "dirt/ground") => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "dirt/ground", LFS2_O_WRONLY | LFS2_O_CREAT) | ||||||
|  |             => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_mkdir(&lfs2, "dirt/ground/earth") => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "dirt/ground/earth", LFS2_O_WRONLY | LFS2_O_CREAT) | ||||||
|  |             => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # root operations | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "/", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "/") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |     lfs2_mkdir(&lfs2, "/") => LFS2_ERR_EXIST; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "/", LFS2_O_WRONLY | LFS2_O_CREAT) | ||||||
|  |             => LFS2_ERR_ISDIR; | ||||||
|  |  | ||||||
|  |     lfs2_remove(&lfs2, "/") => LFS2_ERR_INVAL; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # root representations | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "/", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "/") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_stat(&lfs2, "", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "/") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_stat(&lfs2, ".", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "/") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_stat(&lfs2, "..", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "/") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_stat(&lfs2, "//", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "/") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_stat(&lfs2, "./", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "/") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # superblock conflict test | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "littlefs", &info) => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_remove(&lfs2, "littlefs") => LFS2_ERR_NOENT; | ||||||
|  |  | ||||||
|  |     lfs2_mkdir(&lfs2, "littlefs") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "littlefs", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "littlefs") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |     lfs2_remove(&lfs2, "littlefs") => 0; | ||||||
|  |     lfs2_stat(&lfs2, "littlefs", &info) => LFS2_ERR_NOENT; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # max path test | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/hotcoffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/warmcoffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/coldcoffee") => 0; | ||||||
|  |  | ||||||
|  |     memset(path, 'w', LFS2_NAME_MAX+1); | ||||||
|  |     path[LFS2_NAME_MAX+1] = '\0'; | ||||||
|  |     lfs2_mkdir(&lfs2, path) => LFS2_ERR_NAMETOOLONG; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY | LFS2_O_CREAT) | ||||||
|  |             => LFS2_ERR_NAMETOOLONG; | ||||||
|  |  | ||||||
|  |     memcpy(path, "coffee/", strlen("coffee/")); | ||||||
|  |     memset(path+strlen("coffee/"), 'w', LFS2_NAME_MAX+1); | ||||||
|  |     path[strlen("coffee/")+LFS2_NAME_MAX+1] = '\0'; | ||||||
|  |     lfs2_mkdir(&lfs2, path) => LFS2_ERR_NAMETOOLONG; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY | LFS2_O_CREAT) | ||||||
|  |             => LFS2_ERR_NAMETOOLONG; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # really big path test | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/hotcoffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/warmcoffee") => 0; | ||||||
|  |     lfs2_mkdir(&lfs2, "coffee/coldcoffee") => 0; | ||||||
|  |  | ||||||
|  |     memset(path, 'w', LFS2_NAME_MAX); | ||||||
|  |     path[LFS2_NAME_MAX] = '\0'; | ||||||
|  |     lfs2_mkdir(&lfs2, path) => 0; | ||||||
|  |     lfs2_remove(&lfs2, path) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_remove(&lfs2, path) => 0; | ||||||
|  |  | ||||||
|  |     memcpy(path, "coffee/", strlen("coffee/")); | ||||||
|  |     memset(path+strlen("coffee/"), 'w', LFS2_NAME_MAX); | ||||||
|  |     path[strlen("coffee/")+LFS2_NAME_MAX] = '\0'; | ||||||
|  |     lfs2_mkdir(&lfs2, path) => 0; | ||||||
|  |     lfs2_remove(&lfs2, path) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, path, | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_remove(&lfs2, path) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
| @@ -1,139 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| ITERATIONS=20 |  | ||||||
| COUNT=10 |  | ||||||
|  |  | ||||||
| echo "=== Relocation tests ===" |  | ||||||
| rm -rf blocks |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|     // fill up filesystem so only ~16 blocks are left |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "padding", LFS2_O_CREAT | LFS2_O_WRONLY) => 0; |  | ||||||
|     memset(buffer, 0, 512); |  | ||||||
|     while (LFS2_BLOCK_COUNT - lfs2_fs_size(&lfs2) > 16) { |  | ||||||
|         lfs2_file_write(&lfs2, &file, buffer, 512) => 512; |  | ||||||
|     } |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     // make a child dir to use in bounded space |  | ||||||
|     lfs2_mkdir(&lfs2, "child") => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Dangling split dir test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (int j = 0; j < $ITERATIONS; j++) { |  | ||||||
|         for (int i = 0; i < $COUNT; i++) { |  | ||||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); |  | ||||||
|             lfs2_file_open(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_WRONLY) => 0; |  | ||||||
|             lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         lfs2_dir_open(&lfs2, &dir, "child") => 0; |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         for (int i = 0; i < $COUNT; i++) { |  | ||||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); |  | ||||||
|             lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|             strcmp(info.name, path) => 0; |  | ||||||
|         } |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|         lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|  |  | ||||||
|         if (j == $ITERATIONS-1) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (int i = 0; i < $COUNT; i++) { |  | ||||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); |  | ||||||
|             lfs2_remove(&lfs2, path) => 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "child") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     for (int i = 0; i < $COUNT; i++) { |  | ||||||
|         sprintf(path, "test%03d_loooooooooooooooooong_name", i); |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         strcmp(info.name, path) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     for (int i = 0; i < $COUNT; i++) { |  | ||||||
|         sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); |  | ||||||
|         lfs2_remove(&lfs2, path) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Outdated head test ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (int j = 0; j < $ITERATIONS; j++) { |  | ||||||
|         for (int i = 0; i < $COUNT; i++) { |  | ||||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); |  | ||||||
|             lfs2_file_open(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_WRONLY) => 0; |  | ||||||
|             lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         lfs2_dir_open(&lfs2, &dir, "child") => 0; |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         for (int i = 0; i < $COUNT; i++) { |  | ||||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); |  | ||||||
|             lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|             strcmp(info.name, path) => 0; |  | ||||||
|             info.size => 0; |  | ||||||
|  |  | ||||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); |  | ||||||
|             lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY) => 0; |  | ||||||
|             lfs2_file_write(&lfs2, &file, "hi", 2) => 2; |  | ||||||
|             lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|         } |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|  |  | ||||||
|         lfs2_dir_rewind(&lfs2, &dir) => 0; |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         for (int i = 0; i < $COUNT; i++) { |  | ||||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); |  | ||||||
|             lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|             strcmp(info.name, path) => 0; |  | ||||||
|             info.size => 2; |  | ||||||
|  |  | ||||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); |  | ||||||
|             lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY) => 0; |  | ||||||
|             lfs2_file_write(&lfs2, &file, "hi", 2) => 2; |  | ||||||
|             lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|         } |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|  |  | ||||||
|         lfs2_dir_rewind(&lfs2, &dir) => 0; |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         for (int i = 0; i < $COUNT; i++) { |  | ||||||
|             sprintf(path, "test%03d_loooooooooooooooooong_name", i); |  | ||||||
|             lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|             strcmp(info.name, path) => 0; |  | ||||||
|             info.size => 2; |  | ||||||
|         } |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|         lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|  |  | ||||||
|         for (int i = 0; i < $COUNT; i++) { |  | ||||||
|             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); |  | ||||||
|             lfs2_remove(&lfs2, path) => 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										305
									
								
								tests/test_relocations.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								tests/test_relocations.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,305 @@ | |||||||
|  | # specific corner cases worth explicitly testing for | ||||||
|  | [[case]] # dangling split dir test | ||||||
|  | define.ITERATIONS = 20 | ||||||
|  | define.COUNT = 10 | ||||||
|  | define.LFS2_BLOCK_CYCLES = [8, 1] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     // fill up filesystem so only ~16 blocks are left | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "padding", LFS2_O_CREAT | LFS2_O_WRONLY) => 0; | ||||||
|  |     memset(buffer, 0, 512); | ||||||
|  |     while (LFS2_BLOCK_COUNT - lfs2_fs_size(&lfs2) > 16) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, 512) => 512; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // make a child dir to use in bounded space | ||||||
|  |     lfs2_mkdir(&lfs2, "child") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int j = 0; j < ITERATIONS; j++) { | ||||||
|  |         for (int i = 0; i < COUNT; i++) { | ||||||
|  |             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_WRONLY) => 0; | ||||||
|  |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_dir_open(&lfs2, &dir, "child") => 0; | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         for (int i = 0; i < COUNT; i++) { | ||||||
|  |             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||||
|  |             lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |             strcmp(info.name, path) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |         lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |         if (j == ITERATIONS-1) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (int i = 0; i < COUNT; i++) { | ||||||
|  |             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||||
|  |             lfs2_remove(&lfs2, path) => 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_dir_open(&lfs2, &dir, "child") => 0; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |     for (int i = 0; i < COUNT; i++) { | ||||||
|  |         sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         strcmp(info.name, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |     lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |     for (int i = 0; i < COUNT; i++) { | ||||||
|  |         sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||||
|  |         lfs2_remove(&lfs2, path) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # outdated head test | ||||||
|  | define.ITERATIONS = 20 | ||||||
|  | define.COUNT = 10 | ||||||
|  | define.LFS2_BLOCK_CYCLES = [8, 1] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     // fill up filesystem so only ~16 blocks are left | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "padding", LFS2_O_CREAT | LFS2_O_WRONLY) => 0; | ||||||
|  |     memset(buffer, 0, 512); | ||||||
|  |     while (LFS2_BLOCK_COUNT - lfs2_fs_size(&lfs2) > 16) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, 512) => 512; | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     // make a child dir to use in bounded space | ||||||
|  |     lfs2_mkdir(&lfs2, "child") => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int j = 0; j < ITERATIONS; j++) { | ||||||
|  |         for (int i = 0; i < COUNT; i++) { | ||||||
|  |             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_WRONLY) => 0; | ||||||
|  |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_dir_open(&lfs2, &dir, "child") => 0; | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         for (int i = 0; i < COUNT; i++) { | ||||||
|  |             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||||
|  |             lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |             strcmp(info.name, path) => 0; | ||||||
|  |             info.size => 0; | ||||||
|  |  | ||||||
|  |             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY) => 0; | ||||||
|  |             lfs2_file_write(&lfs2, &file, "hi", 2) => 2; | ||||||
|  |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |  | ||||||
|  |         lfs2_dir_rewind(&lfs2, &dir) => 0; | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         for (int i = 0; i < COUNT; i++) { | ||||||
|  |             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||||
|  |             lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |             strcmp(info.name, path) => 0; | ||||||
|  |             info.size => 2; | ||||||
|  |  | ||||||
|  |             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||||
|  |             lfs2_file_open(&lfs2, &file, path, LFS2_O_WRONLY) => 0; | ||||||
|  |             lfs2_file_write(&lfs2, &file, "hi", 2) => 2; | ||||||
|  |             lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         } | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |  | ||||||
|  |         lfs2_dir_rewind(&lfs2, &dir) => 0; | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |         for (int i = 0; i < COUNT; i++) { | ||||||
|  |             sprintf(path, "test%03d_loooooooooooooooooong_name", i); | ||||||
|  |             lfs2_dir_read(&lfs2, &dir, &info) => 1; | ||||||
|  |             strcmp(info.name, path) => 0; | ||||||
|  |             info.size => 2; | ||||||
|  |         } | ||||||
|  |         lfs2_dir_read(&lfs2, &dir, &info) => 0; | ||||||
|  |         lfs2_dir_close(&lfs2, &dir) => 0; | ||||||
|  |  | ||||||
|  |         for (int i = 0; i < COUNT; i++) { | ||||||
|  |             sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); | ||||||
|  |             lfs2_remove(&lfs2, path) => 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # reentrant testing for relocations, this is the same as the | ||||||
|  |          # orphan testing, except here we also set block_cycles so that | ||||||
|  |          # almost every tree operation needs a relocation | ||||||
|  | reentrant = true | ||||||
|  | # TODO fix this case, caused by non-DAG trees | ||||||
|  | if = '!(DEPTH == 3 && LFS2_CACHE_SIZE != 64)' | ||||||
|  | define = [ | ||||||
|  |     {FILES=6,  DEPTH=1, CYCLES=20, LFS2_BLOCK_CYCLES=1}, | ||||||
|  |     {FILES=26, DEPTH=1, CYCLES=20, LFS2_BLOCK_CYCLES=1}, | ||||||
|  |     {FILES=3,  DEPTH=3, CYCLES=20, LFS2_BLOCK_CYCLES=1}, | ||||||
|  | ] | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     srand(1); | ||||||
|  |     const char alpha[] = "abcdefghijklmnopqrstuvwxyz"; | ||||||
|  |     for (int i = 0; i < CYCLES; i++) { | ||||||
|  |         // create random path | ||||||
|  |         char full_path[256]; | ||||||
|  |         for (int d = 0; d < DEPTH; d++) { | ||||||
|  |             sprintf(&full_path[2*d], "/%c", alpha[rand() % FILES]); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // if it does not exist, we create it, else we destroy | ||||||
|  |         int res = lfs2_stat(&lfs2, full_path, &info); | ||||||
|  |         if (res == LFS2_ERR_NOENT) { | ||||||
|  |             // create each directory in turn, ignore if dir already exists | ||||||
|  |             for (int d = 0; d < DEPTH; d++) { | ||||||
|  |                 strcpy(path, full_path); | ||||||
|  |                 path[2*d+2] = '\0'; | ||||||
|  |                 err = lfs2_mkdir(&lfs2, path); | ||||||
|  |                 assert(!err || err == LFS2_ERR_EXIST); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             for (int d = 0; d < DEPTH; d++) { | ||||||
|  |                 strcpy(path, full_path); | ||||||
|  |                 path[2*d+2] = '\0'; | ||||||
|  |                 lfs2_stat(&lfs2, path, &info) => 0; | ||||||
|  |                 assert(strcmp(info.name, &path[2*d+1]) == 0); | ||||||
|  |                 assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             // is valid dir? | ||||||
|  |             assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0); | ||||||
|  |             assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |             // try to delete path in reverse order, ignore if dir is not empty | ||||||
|  |             for (int d = DEPTH-1; d >= 0; d--) { | ||||||
|  |                 strcpy(path, full_path); | ||||||
|  |                 path[2*d+2] = '\0'; | ||||||
|  |                 err = lfs2_remove(&lfs2, path); | ||||||
|  |                 assert(!err || err == LFS2_ERR_NOTEMPTY); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # reentrant testing for relocations, but now with random renames! | ||||||
|  | reentrant = true | ||||||
|  | # TODO fix this case, caused by non-DAG trees | ||||||
|  | if = '!(DEPTH == 3 && LFS2_CACHE_SIZE != 64)' | ||||||
|  | define = [ | ||||||
|  |     {FILES=6,  DEPTH=1, CYCLES=20, LFS2_BLOCK_CYCLES=1}, | ||||||
|  |     {FILES=26, DEPTH=1, CYCLES=20, LFS2_BLOCK_CYCLES=1}, | ||||||
|  |     {FILES=3,  DEPTH=3, CYCLES=20, LFS2_BLOCK_CYCLES=1}, | ||||||
|  | ] | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     srand(1); | ||||||
|  |     const char alpha[] = "abcdefghijklmnopqrstuvwxyz"; | ||||||
|  |     for (int i = 0; i < CYCLES; i++) { | ||||||
|  |         // create random path | ||||||
|  |         char full_path[256]; | ||||||
|  |         for (int d = 0; d < DEPTH; d++) { | ||||||
|  |             sprintf(&full_path[2*d], "/%c", alpha[rand() % FILES]); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // if it does not exist, we create it, else we destroy | ||||||
|  |         int res = lfs2_stat(&lfs2, full_path, &info); | ||||||
|  |         assert(!res || res == LFS2_ERR_NOENT); | ||||||
|  |         if (res == LFS2_ERR_NOENT) { | ||||||
|  |             // create each directory in turn, ignore if dir already exists | ||||||
|  |             for (int d = 0; d < DEPTH; d++) { | ||||||
|  |                 strcpy(path, full_path); | ||||||
|  |                 path[2*d+2] = '\0'; | ||||||
|  |                 err = lfs2_mkdir(&lfs2, path); | ||||||
|  |                 assert(!err || err == LFS2_ERR_EXIST); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             for (int d = 0; d < DEPTH; d++) { | ||||||
|  |                 strcpy(path, full_path); | ||||||
|  |                 path[2*d+2] = '\0'; | ||||||
|  |                 lfs2_stat(&lfs2, path, &info) => 0; | ||||||
|  |                 assert(strcmp(info.name, &path[2*d+1]) == 0); | ||||||
|  |                 assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0); | ||||||
|  |             assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |  | ||||||
|  |             // create new random path | ||||||
|  |             char new_path[256]; | ||||||
|  |             for (int d = 0; d < DEPTH; d++) { | ||||||
|  |                 sprintf(&new_path[2*d], "/%c", alpha[rand() % FILES]); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // if new path does not exist, rename, otherwise destroy | ||||||
|  |             res = lfs2_stat(&lfs2, new_path, &info); | ||||||
|  |             assert(!res || res == LFS2_ERR_NOENT); | ||||||
|  |             if (res == LFS2_ERR_NOENT) { | ||||||
|  |                 // stop once some dir is renamed | ||||||
|  |                 for (int d = 0; d < DEPTH; d++) { | ||||||
|  |                     strcpy(&path[2*d], &full_path[2*d]); | ||||||
|  |                     path[2*d+2] = '\0'; | ||||||
|  |                     strcpy(&path[128+2*d], &new_path[2*d]); | ||||||
|  |                     path[128+2*d+2] = '\0'; | ||||||
|  |                     err = lfs2_rename(&lfs2, path, path+128); | ||||||
|  |                     assert(!err || err == LFS2_ERR_NOTEMPTY); | ||||||
|  |                     if (!err) { | ||||||
|  |                         strcpy(path, path+128); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 for (int d = 0; d < DEPTH; d++) { | ||||||
|  |                     strcpy(path, new_path); | ||||||
|  |                     path[2*d+2] = '\0'; | ||||||
|  |                     lfs2_stat(&lfs2, path, &info) => 0; | ||||||
|  |                     assert(strcmp(info.name, &path[2*d+1]) == 0); | ||||||
|  |                     assert(info.type == LFS2_TYPE_DIR); | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  |                 lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT; | ||||||
|  |             } else { | ||||||
|  |                 // try to delete path in reverse order, | ||||||
|  |                 // ignore if dir is not empty | ||||||
|  |                 for (int d = DEPTH-1; d >= 0; d--) { | ||||||
|  |                     strcpy(path, full_path); | ||||||
|  |                     path[2*d+2] = '\0'; | ||||||
|  |                     err = lfs2_remove(&lfs2, path); | ||||||
|  |                     assert(!err || err == LFS2_ERR_NOTEMPTY); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 lfs2_stat(&lfs2, full_path, &info) => LFS2_ERR_NOENT; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
| @@ -1,505 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| echo "=== Seek tests ===" |  | ||||||
|  |  | ||||||
| SMALLSIZE=4 |  | ||||||
| MEDIUMSIZE=128 |  | ||||||
| LARGESIZE=132 |  | ||||||
|  |  | ||||||
| rm -rf blocks |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_mkdir(&lfs2, "hello") => 0; |  | ||||||
|     for (int i = 0; i < $LARGESIZE; i++) { |  | ||||||
|         sprintf(path, "hello/kitty%03d", i); |  | ||||||
|         lfs2_file_open(&lfs2, &file, path, |  | ||||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; |  | ||||||
|  |  | ||||||
|         lfs2_size_t size = strlen("kittycatcat"); |  | ||||||
|         memcpy(buffer, "kittycatcat", size); |  | ||||||
|         for (int j = 0; j < $LARGESIZE; j++) { |  | ||||||
|             lfs2_file_write(&lfs2, &file, buffer, size); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Simple dir seek ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "hello") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_soff_t pos; |  | ||||||
|     int i; |  | ||||||
|     for (i = 0; i < $SMALLSIZE; i++) { |  | ||||||
|         sprintf(path, "kitty%03d", i); |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         strcmp(info.name, path) => 0; |  | ||||||
|         pos = lfs2_dir_tell(&lfs2, &dir); |  | ||||||
|     } |  | ||||||
|     pos >= 0 => 1; |  | ||||||
|  |  | ||||||
|     lfs2_dir_seek(&lfs2, &dir, pos) => 0; |  | ||||||
|     sprintf(path, "kitty%03d", i); |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, path) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_rewind(&lfs2, &dir) => 0; |  | ||||||
|     sprintf(path, "kitty%03d", 0); |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, path) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_seek(&lfs2, &dir, pos) => 0; |  | ||||||
|     sprintf(path, "kitty%03d", i); |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, path) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Large dir seek ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "hello") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|  |  | ||||||
|     lfs2_soff_t pos; |  | ||||||
|     int i; |  | ||||||
|     for (i = 0; i < $MEDIUMSIZE; i++) { |  | ||||||
|         sprintf(path, "kitty%03d", i); |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         strcmp(info.name, path) => 0; |  | ||||||
|         pos = lfs2_dir_tell(&lfs2, &dir); |  | ||||||
|     } |  | ||||||
|     pos >= 0 => 1; |  | ||||||
|  |  | ||||||
|     lfs2_dir_seek(&lfs2, &dir, pos) => 0; |  | ||||||
|     sprintf(path, "kitty%03d", i); |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, path) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_rewind(&lfs2, &dir) => 0; |  | ||||||
|     sprintf(path, "kitty%03d", 0); |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, ".") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, "..") => 0; |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, path) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_seek(&lfs2, &dir, pos) => 0; |  | ||||||
|     sprintf(path, "kitty%03d", i); |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|     strcmp(info.name, path) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Simple file seek ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "hello/kitty042", LFS2_O_RDONLY) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_soff_t pos; |  | ||||||
|     lfs2_size_t size = strlen("kittycatcat"); |  | ||||||
|     for (int i = 0; i < $SMALLSIZE; i++) { |  | ||||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|         memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|         pos = lfs2_file_tell(&lfs2, &file); |  | ||||||
|     } |  | ||||||
|     pos >= 0 => 1; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_rewind(&lfs2, &file) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, size, LFS2_SEEK_CUR) => 3*size; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_CUR) => pos; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     size = lfs2_file_size(&lfs2, &file); |  | ||||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Large file seek ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "hello/kitty042", LFS2_O_RDONLY) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_soff_t pos; |  | ||||||
|     lfs2_size_t size = strlen("kittycatcat"); |  | ||||||
|     for (int i = 0; i < $MEDIUMSIZE; i++) { |  | ||||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|         memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|         pos = lfs2_file_tell(&lfs2, &file); |  | ||||||
|     } |  | ||||||
|     pos >= 0 => 1; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_rewind(&lfs2, &file) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, size, LFS2_SEEK_CUR) => 3*size; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_CUR) => pos; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     size = lfs2_file_size(&lfs2, &file); |  | ||||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Simple file seek and write ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "hello/kitty042", LFS2_O_RDWR) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_soff_t pos; |  | ||||||
|     lfs2_size_t size = strlen("kittycatcat"); |  | ||||||
|     for (int i = 0; i < $SMALLSIZE; i++) { |  | ||||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|         memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|         pos = lfs2_file_tell(&lfs2, &file); |  | ||||||
|     } |  | ||||||
|     pos >= 0 => 1; |  | ||||||
|  |  | ||||||
|     memcpy(buffer, "doggodogdog", size); |  | ||||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; |  | ||||||
|     lfs2_file_write(&lfs2, &file, buffer, size) => size; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "doggodogdog", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_rewind(&lfs2, &file) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "doggodogdog", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     size = lfs2_file_size(&lfs2, &file); |  | ||||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Large file seek and write ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "hello/kitty042", LFS2_O_RDWR) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_soff_t pos; |  | ||||||
|     lfs2_size_t size = strlen("kittycatcat"); |  | ||||||
|     for (int i = 0; i < $MEDIUMSIZE; i++) { |  | ||||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|         if (i != $SMALLSIZE) { |  | ||||||
|             memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|         } |  | ||||||
|         pos = lfs2_file_tell(&lfs2, &file); |  | ||||||
|     } |  | ||||||
|     pos >= 0 => 1; |  | ||||||
|  |  | ||||||
|     memcpy(buffer, "doggodogdog", size); |  | ||||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; |  | ||||||
|     lfs2_file_write(&lfs2, &file, buffer, size) => size; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "doggodogdog", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_rewind(&lfs2, &file) => 0; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "doggodogdog", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|     size = lfs2_file_size(&lfs2, &file); |  | ||||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Boundary seek and write ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "hello/kitty042", LFS2_O_RDWR) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_size_t size = strlen("hedgehoghog"); |  | ||||||
|     const lfs2_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019}; |  | ||||||
|  |  | ||||||
|     for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { |  | ||||||
|         lfs2_soff_t off = offsets[i]; |  | ||||||
|         memcpy(buffer, "hedgehoghog", size); |  | ||||||
|         lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off; |  | ||||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; |  | ||||||
|         lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off; |  | ||||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|         memcmp(buffer, "hedgehoghog", size) => 0; |  | ||||||
|  |  | ||||||
|         lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; |  | ||||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|         memcmp(buffer, "kittycatcat", size) => 0; |  | ||||||
|  |  | ||||||
|         lfs2_file_sync(&lfs2, &file) => 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Out-of-bounds seek ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "hello/kitty042", LFS2_O_RDWR) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_size_t size = strlen("kittycatcat"); |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $LARGESIZE*size; |  | ||||||
|     lfs2_file_seek(&lfs2, &file, ($LARGESIZE+$SMALLSIZE)*size, |  | ||||||
|             LFS2_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => 0; |  | ||||||
|  |  | ||||||
|     memcpy(buffer, "porcupineee", size); |  | ||||||
|     lfs2_file_write(&lfs2, &file, buffer, size) => size; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, ($LARGESIZE+$SMALLSIZE)*size, |  | ||||||
|             LFS2_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "porcupineee", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, $LARGESIZE*size, |  | ||||||
|             LFS2_SEEK_SET) => $LARGESIZE*size; |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|     memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, -(($LARGESIZE+$SMALLSIZE)*size), |  | ||||||
|             LFS2_SEEK_CUR) => LFS2_ERR_INVAL; |  | ||||||
|     lfs2_file_tell(&lfs2, &file) => ($LARGESIZE+1)*size; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, -(($LARGESIZE+2*$SMALLSIZE)*size), |  | ||||||
|             LFS2_SEEK_END) => LFS2_ERR_INVAL; |  | ||||||
|     lfs2_file_tell(&lfs2, &file) => ($LARGESIZE+1)*size; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Inline write and seek ---" |  | ||||||
| for SIZE in $SMALLSIZE $MEDIUMSIZE $LARGESIZE |  | ||||||
| do |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "hello/tinykitty$SIZE", |  | ||||||
|             LFS2_O_RDWR | LFS2_O_CREAT) => 0; |  | ||||||
|     int j = 0; |  | ||||||
|     int k = 0; |  | ||||||
|  |  | ||||||
|     memcpy(buffer, "abcdefghijklmnopqrstuvwxyz", 26); |  | ||||||
|     for (unsigned i = 0; i < $SIZE; i++) { |  | ||||||
|         lfs2_file_write(&lfs2, &file, &buffer[j++ % 26], 1) => 1; |  | ||||||
|         lfs2_file_tell(&lfs2, &file) => i+1; |  | ||||||
|         lfs2_file_size(&lfs2, &file) => i+1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; |  | ||||||
|     lfs2_file_tell(&lfs2, &file) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $SIZE; |  | ||||||
|     for (unsigned i = 0; i < $SIZE; i++) { |  | ||||||
|         uint8_t c; |  | ||||||
|         lfs2_file_read(&lfs2, &file, &c, 1) => 1; |  | ||||||
|         c => buffer[k++ % 26]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_file_sync(&lfs2, &file) => 0; |  | ||||||
|     lfs2_file_tell(&lfs2, &file) => $SIZE; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $SIZE; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; |  | ||||||
|     for (unsigned i = 0; i < $SIZE; i++) { |  | ||||||
|         lfs2_file_write(&lfs2, &file, &buffer[j++ % 26], 1) => 1; |  | ||||||
|         lfs2_file_tell(&lfs2, &file) => i+1; |  | ||||||
|         lfs2_file_size(&lfs2, &file) => $SIZE; |  | ||||||
|         lfs2_file_sync(&lfs2, &file) => 0; |  | ||||||
|         lfs2_file_tell(&lfs2, &file) => i+1; |  | ||||||
|         lfs2_file_size(&lfs2, &file) => $SIZE; |  | ||||||
|         if (i < $SIZE-2) { |  | ||||||
|             uint8_t c[3]; |  | ||||||
|             lfs2_file_seek(&lfs2, &file, -1, LFS2_SEEK_CUR) => i; |  | ||||||
|             lfs2_file_read(&lfs2, &file, &c, 3) => 3; |  | ||||||
|             lfs2_file_tell(&lfs2, &file) => i+3; |  | ||||||
|             lfs2_file_size(&lfs2, &file) => $SIZE; |  | ||||||
|             lfs2_file_seek(&lfs2, &file, i+1, LFS2_SEEK_SET) => i+1; |  | ||||||
|             lfs2_file_tell(&lfs2, &file) => i+1; |  | ||||||
|             lfs2_file_size(&lfs2, &file) => $SIZE; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; |  | ||||||
|     lfs2_file_tell(&lfs2, &file) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $SIZE; |  | ||||||
|     for (unsigned i = 0; i < $SIZE; i++) { |  | ||||||
|         uint8_t c; |  | ||||||
|         lfs2_file_read(&lfs2, &file, &c, 1) => 1; |  | ||||||
|         c => buffer[k++ % 26]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_file_sync(&lfs2, &file) => 0; |  | ||||||
|     lfs2_file_tell(&lfs2, &file) => $SIZE; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $SIZE; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| done |  | ||||||
|  |  | ||||||
| echo "--- Root seek test ---" |  | ||||||
| ./scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     for (int i = 3; i < $MEDIUMSIZE; i++) { |  | ||||||
|         sprintf(path, "hi%03d", i); |  | ||||||
|         lfs2_mkdir(&lfs2, path) => 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|     for (int i = 0; i < $MEDIUMSIZE; i++) { |  | ||||||
|         if (i == 0) { |  | ||||||
|             sprintf(path, "."); |  | ||||||
|         } else if (i == 1) { |  | ||||||
|             sprintf(path, ".."); |  | ||||||
|         } else if (i == 2) { |  | ||||||
|             sprintf(path, "hello"); |  | ||||||
|         } else { |  | ||||||
|             sprintf(path, "hi%03d", i); |  | ||||||
|         } |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|         strcmp(path, info.name) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|     lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|  |  | ||||||
|     for (int j = 0; j < $MEDIUMSIZE; j++) { |  | ||||||
|         lfs2_soff_t off = -1; |  | ||||||
|  |  | ||||||
|         lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|         for (int i = 0; i < $MEDIUMSIZE; i++) { |  | ||||||
|             if (i == 0) { |  | ||||||
|                 sprintf(path, "."); |  | ||||||
|             } else if (i == 1) { |  | ||||||
|                 sprintf(path, ".."); |  | ||||||
|             } else if (i == 2) { |  | ||||||
|                 sprintf(path, "hello"); |  | ||||||
|             } else { |  | ||||||
|                 sprintf(path, "hi%03d", i); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (i == j) { |  | ||||||
|                 off = lfs2_dir_tell(&lfs2, &dir); |  | ||||||
|                 off >= 0 => true; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|             strcmp(path, info.name) => 0; |  | ||||||
|         } |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|         lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|  |  | ||||||
|         lfs2_dir_open(&lfs2, &dir, "/") => 0; |  | ||||||
|         lfs2_dir_seek(&lfs2, &dir, off) => 0; |  | ||||||
|         for (int i = j; i < $MEDIUMSIZE; i++) { |  | ||||||
|             if (i == 0) { |  | ||||||
|                 sprintf(path, "."); |  | ||||||
|             } else if (i == 1) { |  | ||||||
|                 sprintf(path, ".."); |  | ||||||
|             } else if (i == 2) { |  | ||||||
|                 sprintf(path, "hello"); |  | ||||||
|             } else { |  | ||||||
|                 sprintf(path, "hi%03d", i); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             lfs2_dir_read(&lfs2, &dir, &info) => 1; |  | ||||||
|             strcmp(path, info.name) => 0; |  | ||||||
|         } |  | ||||||
|         lfs2_dir_read(&lfs2, &dir, &info) => 0; |  | ||||||
|         lfs2_dir_close(&lfs2, &dir) => 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										380
									
								
								tests/test_seek.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										380
									
								
								tests/test_seek.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,380 @@ | |||||||
|  |  | ||||||
|  | [[case]] # simple file seek | ||||||
|  | define = [ | ||||||
|  |     {COUNT=132, SKIP=4}, | ||||||
|  |     {COUNT=132, SKIP=128}, | ||||||
|  |     {COUNT=200, SKIP=10}, | ||||||
|  |     {COUNT=200, SKIP=100}, | ||||||
|  |     {COUNT=4,   SKIP=1}, | ||||||
|  |     {COUNT=4,   SKIP=2}, | ||||||
|  | ] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "kitty", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||||
|  |     size = strlen("kittycatcat"); | ||||||
|  |     memcpy(buffer, "kittycatcat", size); | ||||||
|  |     for (int j = 0; j < COUNT; j++) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size); | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDONLY) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_soff_t pos = -1; | ||||||
|  |     size = strlen("kittycatcat"); | ||||||
|  |     for (int i = 0; i < SKIP; i++) { | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |         pos = lfs2_file_tell(&lfs2, &file); | ||||||
|  |     } | ||||||
|  |     assert(pos >= 0); | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_rewind(&lfs2, &file) => 0; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, size, LFS2_SEEK_CUR) => 3*size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_CUR) => pos; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |  | ||||||
|  |     size = lfs2_file_size(&lfs2, &file); | ||||||
|  |     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # simple file seek and write | ||||||
|  | define = [ | ||||||
|  |     {COUNT=132, SKIP=4}, | ||||||
|  |     {COUNT=132, SKIP=128}, | ||||||
|  |     {COUNT=200, SKIP=10}, | ||||||
|  |     {COUNT=200, SKIP=100}, | ||||||
|  |     {COUNT=4,   SKIP=1}, | ||||||
|  |     {COUNT=4,   SKIP=2}, | ||||||
|  | ] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "kitty", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||||
|  |     size = strlen("kittycatcat"); | ||||||
|  |     memcpy(buffer, "kittycatcat", size); | ||||||
|  |     for (int j = 0; j < COUNT; j++) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size); | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_soff_t pos = -1; | ||||||
|  |     size = strlen("kittycatcat"); | ||||||
|  |     for (int i = 0; i < SKIP; i++) { | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |         pos = lfs2_file_tell(&lfs2, &file); | ||||||
|  |     } | ||||||
|  |     assert(pos >= 0); | ||||||
|  |  | ||||||
|  |     memcpy(buffer, "doggodogdog", size); | ||||||
|  |     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; | ||||||
|  |     lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "doggodogdog", size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_rewind(&lfs2, &file) => 0; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, pos, LFS2_SEEK_SET) => pos; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "doggodogdog", size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, -size, LFS2_SEEK_END) >= 0 => 1; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |  | ||||||
|  |     size = lfs2_file_size(&lfs2, &file); | ||||||
|  |     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_CUR) => size; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # boundary seek and writes | ||||||
|  | define.COUNT = 132 | ||||||
|  | define.OFFSETS = '"{512, 1020, 513, 1021, 511, 1019, 1441}"' | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "kitty", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||||
|  |     size = strlen("kittycatcat"); | ||||||
|  |     memcpy(buffer, "kittycatcat", size); | ||||||
|  |     for (int j = 0; j < COUNT; j++) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size); | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0; | ||||||
|  |  | ||||||
|  |     size = strlen("hedgehoghog"); | ||||||
|  |     const lfs2_soff_t offsets[] = OFFSETS; | ||||||
|  |  | ||||||
|  |     for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { | ||||||
|  |         lfs2_soff_t off = offsets[i]; | ||||||
|  |         memcpy(buffer, "hedgehoghog", size); | ||||||
|  |         lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off; | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |         lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off; | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         memcmp(buffer, "hedgehoghog", size) => 0; | ||||||
|  |  | ||||||
|  |         lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |  | ||||||
|  |         lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off; | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         memcmp(buffer, "hedgehoghog", size) => 0; | ||||||
|  |  | ||||||
|  |         lfs2_file_sync(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |         lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         memcmp(buffer, "kittycatcat", size) => 0; | ||||||
|  |  | ||||||
|  |         lfs2_file_seek(&lfs2, &file, off, LFS2_SEEK_SET) => off; | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         memcmp(buffer, "hedgehoghog", size) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # out of bounds seek | ||||||
|  | define = [ | ||||||
|  |     {COUNT=132, SKIP=4}, | ||||||
|  |     {COUNT=132, SKIP=128}, | ||||||
|  |     {COUNT=200, SKIP=10}, | ||||||
|  |     {COUNT=200, SKIP=100}, | ||||||
|  |     {COUNT=4,   SKIP=2}, | ||||||
|  |     {COUNT=4,   SKIP=3}, | ||||||
|  | ] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "kitty", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0; | ||||||
|  |     size = strlen("kittycatcat"); | ||||||
|  |     memcpy(buffer, "kittycatcat", size); | ||||||
|  |     for (int j = 0; j < COUNT; j++) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size); | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0; | ||||||
|  |  | ||||||
|  |     size = strlen("kittycatcat"); | ||||||
|  |     lfs2_file_size(&lfs2, &file) => COUNT*size; | ||||||
|  |     lfs2_file_seek(&lfs2, &file, (COUNT+SKIP)*size, | ||||||
|  |             LFS2_SEEK_SET) => (COUNT+SKIP)*size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => 0; | ||||||
|  |  | ||||||
|  |     memcpy(buffer, "porcupineee", size); | ||||||
|  |     lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, (COUNT+SKIP)*size, | ||||||
|  |             LFS2_SEEK_SET) => (COUNT+SKIP)*size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "porcupineee", size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, COUNT*size, | ||||||
|  |             LFS2_SEEK_SET) => COUNT*size; | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |     memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, -((COUNT+SKIP)*size), | ||||||
|  |             LFS2_SEEK_CUR) => LFS2_ERR_INVAL; | ||||||
|  |     lfs2_file_tell(&lfs2, &file) => (COUNT+1)*size; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, -((COUNT+2*SKIP)*size), | ||||||
|  |             LFS2_SEEK_END) => LFS2_ERR_INVAL; | ||||||
|  |     lfs2_file_tell(&lfs2, &file) => (COUNT+1)*size; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # inline write and seek | ||||||
|  | define.SIZE = [2, 4, 128, 132] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "tinykitty", | ||||||
|  |             LFS2_O_RDWR | LFS2_O_CREAT) => 0; | ||||||
|  |     int j = 0; | ||||||
|  |     int k = 0; | ||||||
|  |  | ||||||
|  |     memcpy(buffer, "abcdefghijklmnopqrstuvwxyz", 26); | ||||||
|  |     for (unsigned i = 0; i < SIZE; i++) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, &buffer[j++ % 26], 1) => 1; | ||||||
|  |         lfs2_file_tell(&lfs2, &file) => i+1; | ||||||
|  |         lfs2_file_size(&lfs2, &file) => i+1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; | ||||||
|  |     lfs2_file_tell(&lfs2, &file) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE; | ||||||
|  |     for (unsigned i = 0; i < SIZE; i++) { | ||||||
|  |         uint8_t c; | ||||||
|  |         lfs2_file_read(&lfs2, &file, &c, 1) => 1; | ||||||
|  |         c => buffer[k++ % 26]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_file_sync(&lfs2, &file) => 0; | ||||||
|  |     lfs2_file_tell(&lfs2, &file) => SIZE; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; | ||||||
|  |     for (unsigned i = 0; i < SIZE; i++) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, &buffer[j++ % 26], 1) => 1; | ||||||
|  |         lfs2_file_tell(&lfs2, &file) => i+1; | ||||||
|  |         lfs2_file_size(&lfs2, &file) => SIZE; | ||||||
|  |         lfs2_file_sync(&lfs2, &file) => 0; | ||||||
|  |         lfs2_file_tell(&lfs2, &file) => i+1; | ||||||
|  |         lfs2_file_size(&lfs2, &file) => SIZE; | ||||||
|  |         if (i < SIZE-2) { | ||||||
|  |             uint8_t c[3]; | ||||||
|  |             lfs2_file_seek(&lfs2, &file, -1, LFS2_SEEK_CUR) => i; | ||||||
|  |             lfs2_file_read(&lfs2, &file, &c, 3) => 3; | ||||||
|  |             lfs2_file_tell(&lfs2, &file) => i+3; | ||||||
|  |             lfs2_file_size(&lfs2, &file) => SIZE; | ||||||
|  |             lfs2_file_seek(&lfs2, &file, i+1, LFS2_SEEK_SET) => i+1; | ||||||
|  |             lfs2_file_tell(&lfs2, &file) => i+1; | ||||||
|  |             lfs2_file_size(&lfs2, &file) => SIZE; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; | ||||||
|  |     lfs2_file_tell(&lfs2, &file) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE; | ||||||
|  |     for (unsigned i = 0; i < SIZE; i++) { | ||||||
|  |         uint8_t c; | ||||||
|  |         lfs2_file_read(&lfs2, &file, &c, 1) => 1; | ||||||
|  |         c => buffer[k++ % 26]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_file_sync(&lfs2, &file) => 0; | ||||||
|  |     lfs2_file_tell(&lfs2, &file) => SIZE; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SIZE; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # file seek and write with power-loss | ||||||
|  | # must be power-of-2 for quadratic probing to be exhaustive | ||||||
|  | define.COUNT = [4, 64, 128] | ||||||
|  | reentrant = true | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |     err = lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDONLY); | ||||||
|  |     assert(!err || err == LFS2_ERR_NOENT); | ||||||
|  |     if (!err) { | ||||||
|  |         if (lfs2_file_size(&lfs2, &file) != 0) { | ||||||
|  |             lfs2_file_size(&lfs2, &file) => 11*COUNT; | ||||||
|  |             for (int j = 0; j < COUNT; j++) { | ||||||
|  |                 memset(buffer, 0, 11+1); | ||||||
|  |                 lfs2_file_read(&lfs2, &file, buffer, 11) => 11; | ||||||
|  |                 assert(memcmp(buffer, "kittycatcat", 11) == 0 || | ||||||
|  |                        memcmp(buffer, "doggodogdog", 11) == 0); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |     if (lfs2_file_size(&lfs2, &file) == 0) { | ||||||
|  |         for (int j = 0; j < COUNT; j++) { | ||||||
|  |             strcpy((char*)buffer, "kittycatcat"); | ||||||
|  |             size = strlen((char*)buffer); | ||||||
|  |             lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     strcpy((char*)buffer, "doggodogdog"); | ||||||
|  |     size = strlen((char*)buffer); | ||||||
|  |  | ||||||
|  |     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => COUNT*size; | ||||||
|  |     // seek and write using quadratic probing to touch all | ||||||
|  |     // 11-byte words in the file | ||||||
|  |     lfs2_off_t off = 0; | ||||||
|  |     for (int j = 0; j < COUNT; j++) { | ||||||
|  |         off = (5*off + 1) % COUNT; | ||||||
|  |         lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size; | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         assert(memcmp(buffer, "kittycatcat", size) == 0 || | ||||||
|  |                memcmp(buffer, "doggodogdog", size) == 0); | ||||||
|  |         if (memcmp(buffer, "doggodogdog", size) != 0) { | ||||||
|  |             lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size; | ||||||
|  |             strcpy((char*)buffer, "doggodogdog"); | ||||||
|  |             lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |             lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size; | ||||||
|  |             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |             assert(memcmp(buffer, "doggodogdog", size) == 0); | ||||||
|  |             lfs2_file_sync(&lfs2, &file) => 0; | ||||||
|  |             lfs2_file_seek(&lfs2, &file, off*size, LFS2_SEEK_SET) => off*size; | ||||||
|  |             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |             assert(memcmp(buffer, "doggodogdog", size) == 0); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_open(&lfs2, &file, "kitty", LFS2_O_RDWR) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => COUNT*size; | ||||||
|  |     for (int j = 0; j < COUNT; j++) { | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         assert(memcmp(buffer, "doggodogdog", size) == 0); | ||||||
|  |     } | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
							
								
								
									
										127
									
								
								tests/test_superblocks.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								tests/test_superblocks.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | |||||||
|  | [[case]] # simple formatting test | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # mount/unmount | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # reentrant format | ||||||
|  | reentrant = true | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # invalid mount | ||||||
|  | code = ''' | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # expanding superblock | ||||||
|  | define.LFS2_BLOCK_CYCLES = [32, 33, 1] | ||||||
|  | define.N = [10, 100, 1000] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         lfs2_file_open(&lfs2, &file, "dummy", | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         lfs2_stat(&lfs2, "dummy", &info) => 0; | ||||||
|  |         assert(strcmp(info.name, "dummy") == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_REG); | ||||||
|  |         lfs2_remove(&lfs2, "dummy") => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // one last check after power-cycle | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "dummy", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "dummy", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "dummy") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_REG); | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # expanding superblock with power cycle | ||||||
|  | define.LFS2_BLOCK_CYCLES = [32, 33, 1] | ||||||
|  | define.N = [10, 100, 1000] | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |         // remove lingering dummy? | ||||||
|  |         err = lfs2_stat(&lfs2, "dummy", &info); | ||||||
|  |         assert(err == 0 || (err == LFS2_ERR_NOENT && i == 0)); | ||||||
|  |         if (!err) { | ||||||
|  |             assert(strcmp(info.name, "dummy") == 0); | ||||||
|  |             assert(info.type == LFS2_TYPE_REG); | ||||||
|  |             lfs2_remove(&lfs2, "dummy") => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_file_open(&lfs2, &file, "dummy", | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         lfs2_stat(&lfs2, "dummy", &info) => 0; | ||||||
|  |         assert(strcmp(info.name, "dummy") == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_REG); | ||||||
|  |         lfs2_unmount(&lfs2) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // one last check after power-cycle | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "dummy", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "dummy") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_REG); | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # reentrant expanding superblock | ||||||
|  | define.LFS2_BLOCK_CYCLES = [2, 1] | ||||||
|  | define.N = 24 | ||||||
|  | reentrant = true | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < N; i++) { | ||||||
|  |         // remove lingering dummy? | ||||||
|  |         err = lfs2_stat(&lfs2, "dummy", &info); | ||||||
|  |         assert(err == 0 || (err == LFS2_ERR_NOENT && i == 0)); | ||||||
|  |         if (!err) { | ||||||
|  |             assert(strcmp(info.name, "dummy") == 0); | ||||||
|  |             assert(info.type == LFS2_TYPE_REG); | ||||||
|  |             lfs2_remove(&lfs2, "dummy") => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_file_open(&lfs2, &file, "dummy", | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |         lfs2_stat(&lfs2, "dummy", &info) => 0; | ||||||
|  |         assert(strcmp(info.name, "dummy") == 0); | ||||||
|  |         assert(info.type == LFS2_TYPE_REG); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     // one last check after power-cycle | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_stat(&lfs2, "dummy", &info) => 0; | ||||||
|  |     assert(strcmp(info.name, "dummy") == 0); | ||||||
|  |     assert(info.type == LFS2_TYPE_REG); | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
| @@ -1,355 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -eu |  | ||||||
| export TEST_FILE=$0 |  | ||||||
| trap 'export TEST_LINE=$LINENO' DEBUG |  | ||||||
|  |  | ||||||
| echo "=== Truncate tests ===" |  | ||||||
|  |  | ||||||
| SMALLSIZE=32 |  | ||||||
| MEDIUMSIZE=2048 |  | ||||||
| LARGESIZE=8192 |  | ||||||
|  |  | ||||||
| rm -rf blocks |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_format(&lfs2, &cfg) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Simple truncate ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "baldynoop", |  | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|  |  | ||||||
|     strcpy((char*)buffer, "hair"); |  | ||||||
|     lfs2_size_t size = strlen((char*)buffer); |  | ||||||
|     for (lfs2_off_t j = 0; j < $LARGESIZE; j += size) { |  | ||||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; |  | ||||||
|     } |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $LARGESIZE; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDWR) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $LARGESIZE; |  | ||||||
|  |  | ||||||
|     lfs2_file_truncate(&lfs2, &file, $MEDIUMSIZE) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $MEDIUMSIZE; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDONLY) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $MEDIUMSIZE; |  | ||||||
|  |  | ||||||
|     lfs2_size_t size = strlen("hair"); |  | ||||||
|     for (lfs2_off_t j = 0; j < $MEDIUMSIZE; j += size) { |  | ||||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|         memcmp(buffer, "hair", size) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Truncate and read ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "baldyread", |  | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|  |  | ||||||
|     strcpy((char*)buffer, "hair"); |  | ||||||
|     lfs2_size_t size = strlen((char*)buffer); |  | ||||||
|     for (lfs2_off_t j = 0; j < $LARGESIZE; j += size) { |  | ||||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; |  | ||||||
|     } |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $LARGESIZE; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "baldyread", LFS2_O_RDWR) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $LARGESIZE; |  | ||||||
|  |  | ||||||
|     lfs2_file_truncate(&lfs2, &file, $MEDIUMSIZE) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $MEDIUMSIZE; |  | ||||||
|  |  | ||||||
|     lfs2_size_t size = strlen("hair"); |  | ||||||
|     for (lfs2_off_t j = 0; j < $MEDIUMSIZE; j += size) { |  | ||||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|         memcmp(buffer, "hair", size) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "baldyread", LFS2_O_RDONLY) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $MEDIUMSIZE; |  | ||||||
|  |  | ||||||
|     lfs2_size_t size = strlen("hair"); |  | ||||||
|     for (lfs2_off_t j = 0; j < $MEDIUMSIZE; j += size) { |  | ||||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|         memcmp(buffer, "hair", size) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Write, truncate, and read ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "sequence", |  | ||||||
|             LFS2_O_RDWR | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_size_t size = lfs2.cfg->cache_size; |  | ||||||
|     lfs2_size_t qsize = size / 4; |  | ||||||
|     uint8_t *wb = buffer; |  | ||||||
|     uint8_t *rb = buffer + size; |  | ||||||
|     for (lfs2_off_t j = 0; j < size; ++j) { |  | ||||||
|         wb[j] = j; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Spread sequence over size */ |  | ||||||
|     lfs2_file_write(&lfs2, &file, wb, size) => size; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => size; |  | ||||||
|     lfs2_file_tell(&lfs2, &file) => size; |  | ||||||
|  |  | ||||||
|     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; |  | ||||||
|     lfs2_file_tell(&lfs2, &file) => 0; |  | ||||||
|  |  | ||||||
|     /* Chop off the last quarter */ |  | ||||||
|     lfs2_size_t trunc = size - qsize; |  | ||||||
|     lfs2_file_truncate(&lfs2, &file, trunc) => 0; |  | ||||||
|     lfs2_file_tell(&lfs2, &file) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => trunc; |  | ||||||
|  |  | ||||||
|     /* Read should produce first 3/4 */ |  | ||||||
|     lfs2_file_read(&lfs2, &file, rb, size) => trunc; |  | ||||||
|     memcmp(rb, wb, trunc) => 0; |  | ||||||
|  |  | ||||||
|     /* Move to 1/4 */ |  | ||||||
|     lfs2_file_size(&lfs2, &file) => trunc; |  | ||||||
|     lfs2_file_seek(&lfs2, &file, qsize, LFS2_SEEK_SET) => qsize; |  | ||||||
|     lfs2_file_tell(&lfs2, &file) => qsize; |  | ||||||
|  |  | ||||||
|     /* Chop to 1/2 */ |  | ||||||
|     trunc -= qsize; |  | ||||||
|     lfs2_file_truncate(&lfs2, &file, trunc) => 0; |  | ||||||
|     lfs2_file_tell(&lfs2, &file) => qsize; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => trunc; |  | ||||||
|      |  | ||||||
|     /* Read should produce second quarter */ |  | ||||||
|     lfs2_file_read(&lfs2, &file, rb, size) => trunc - qsize; |  | ||||||
|     memcmp(rb, wb + qsize, trunc - qsize) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| echo "--- Truncate and write ---" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "baldywrite", |  | ||||||
|             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; |  | ||||||
|  |  | ||||||
|     strcpy((char*)buffer, "hair"); |  | ||||||
|     lfs2_size_t size = strlen((char*)buffer); |  | ||||||
|     for (lfs2_off_t j = 0; j < $LARGESIZE; j += size) { |  | ||||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; |  | ||||||
|     } |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $LARGESIZE; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "baldywrite", LFS2_O_RDWR) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $LARGESIZE; |  | ||||||
|  |  | ||||||
|     lfs2_file_truncate(&lfs2, &file, $MEDIUMSIZE) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $MEDIUMSIZE; |  | ||||||
|  |  | ||||||
|     strcpy((char*)buffer, "bald"); |  | ||||||
|     lfs2_size_t size = strlen((char*)buffer); |  | ||||||
|     for (lfs2_off_t j = 0; j < $MEDIUMSIZE; j += size) { |  | ||||||
|         lfs2_file_write(&lfs2, &file, buffer, size) => size; |  | ||||||
|     } |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $MEDIUMSIZE; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|     lfs2_file_open(&lfs2, &file, "baldywrite", LFS2_O_RDONLY) => 0; |  | ||||||
|     lfs2_file_size(&lfs2, &file) => $MEDIUMSIZE; |  | ||||||
|  |  | ||||||
|     lfs2_size_t size = strlen("bald"); |  | ||||||
|     for (lfs2_off_t j = 0; j < $MEDIUMSIZE; j += size) { |  | ||||||
|         lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|         memcmp(buffer, "bald", size) => 0; |  | ||||||
|     } |  | ||||||
|     lfs2_file_read(&lfs2, &file, buffer, size) => 0; |  | ||||||
|  |  | ||||||
|     lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
|  |  | ||||||
| # More aggressive general truncation tests |  | ||||||
| truncate_test() { |  | ||||||
| STARTSIZES="$1" |  | ||||||
| STARTSEEKS="$2" |  | ||||||
| HOTSIZES="$3" |  | ||||||
| COLDSIZES="$4" |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     static const lfs2_off_t startsizes[] = {$STARTSIZES}; |  | ||||||
|     static const lfs2_off_t startseeks[] = {$STARTSEEKS}; |  | ||||||
|     static const lfs2_off_t hotsizes[]   = {$HOTSIZES}; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { |  | ||||||
|         sprintf(path, "hairyhead%d", i); |  | ||||||
|         lfs2_file_open(&lfs2, &file, path, |  | ||||||
|                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; |  | ||||||
|  |  | ||||||
|         strcpy((char*)buffer, "hair"); |  | ||||||
|         lfs2_size_t size = strlen((char*)buffer); |  | ||||||
|         for (lfs2_off_t j = 0; j < startsizes[i]; j += size) { |  | ||||||
|             lfs2_file_write(&lfs2, &file, buffer, size) => size; |  | ||||||
|         } |  | ||||||
|         lfs2_file_size(&lfs2, &file) => startsizes[i]; |  | ||||||
|  |  | ||||||
|         if (startseeks[i] != startsizes[i]) { |  | ||||||
|             lfs2_file_seek(&lfs2, &file, |  | ||||||
|                     startseeks[i], LFS2_SEEK_SET) => startseeks[i]; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         lfs2_file_truncate(&lfs2, &file, hotsizes[i]) => 0; |  | ||||||
|         lfs2_file_size(&lfs2, &file) => hotsizes[i]; |  | ||||||
|  |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     static const lfs2_off_t startsizes[] = {$STARTSIZES}; |  | ||||||
|     static const lfs2_off_t hotsizes[]   = {$HOTSIZES}; |  | ||||||
|     static const lfs2_off_t coldsizes[]  = {$COLDSIZES}; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { |  | ||||||
|         sprintf(path, "hairyhead%d", i); |  | ||||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDWR) => 0; |  | ||||||
|         lfs2_file_size(&lfs2, &file) => hotsizes[i]; |  | ||||||
|  |  | ||||||
|         lfs2_size_t size = strlen("hair"); |  | ||||||
|         lfs2_off_t j = 0; |  | ||||||
|         for (; j < startsizes[i] && j < hotsizes[i]; j += size) { |  | ||||||
|             lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|             memcmp(buffer, "hair", size) => 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (; j < hotsizes[i]; j += size) { |  | ||||||
|             lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|             memcmp(buffer, "\0\0\0\0", size) => 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         lfs2_file_truncate(&lfs2, &file, coldsizes[i]) => 0; |  | ||||||
|         lfs2_file_size(&lfs2, &file) => coldsizes[i]; |  | ||||||
|  |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| scripts/test.py << TEST |  | ||||||
|     static const lfs2_off_t startsizes[] = {$STARTSIZES}; |  | ||||||
|     static const lfs2_off_t hotsizes[]   = {$HOTSIZES}; |  | ||||||
|     static const lfs2_off_t coldsizes[]  = {$COLDSIZES}; |  | ||||||
|  |  | ||||||
|     lfs2_mount(&lfs2, &cfg) => 0; |  | ||||||
|  |  | ||||||
|     for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { |  | ||||||
|         sprintf(path, "hairyhead%d", i); |  | ||||||
|         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; |  | ||||||
|         lfs2_file_size(&lfs2, &file) => coldsizes[i]; |  | ||||||
|  |  | ||||||
|         lfs2_size_t size = strlen("hair"); |  | ||||||
|         lfs2_off_t j = 0; |  | ||||||
|         for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i]; |  | ||||||
|                 j += size) { |  | ||||||
|             lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|             memcmp(buffer, "hair", size) => 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (; j < coldsizes[i]; j += size) { |  | ||||||
|             lfs2_file_read(&lfs2, &file, buffer, size) => size; |  | ||||||
|             memcmp(buffer, "\0\0\0\0", size) => 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         lfs2_file_close(&lfs2, &file) => 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lfs2_unmount(&lfs2) => 0; |  | ||||||
| TEST |  | ||||||
| } |  | ||||||
|  |  | ||||||
| echo "--- Cold shrinking truncate ---" |  | ||||||
| truncate_test \ |  | ||||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" |  | ||||||
|  |  | ||||||
| echo "--- Cold expanding truncate ---" |  | ||||||
| truncate_test \ |  | ||||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" |  | ||||||
|  |  | ||||||
| echo "--- Warm shrinking truncate ---" |  | ||||||
| truncate_test \ |  | ||||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "           0,            0,            0,            0,            0" |  | ||||||
|  |  | ||||||
| echo "--- Warm expanding truncate ---" |  | ||||||
| truncate_test \ |  | ||||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" |  | ||||||
|  |  | ||||||
| echo "--- Mid-file shrinking truncate ---" |  | ||||||
| truncate_test \ |  | ||||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "  $LARGESIZE,   $LARGESIZE,   $LARGESIZE,   $LARGESIZE,   $LARGESIZE" \ |  | ||||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "           0,            0,            0,            0,            0" |  | ||||||
|  |  | ||||||
| echo "--- Mid-file expanding truncate ---" |  | ||||||
| truncate_test \ |  | ||||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "           0,            0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE" \ |  | ||||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ |  | ||||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" |  | ||||||
|  |  | ||||||
| scripts/results.py |  | ||||||
							
								
								
									
										394
									
								
								tests/test_truncate.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										394
									
								
								tests/test_truncate.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,394 @@ | |||||||
|  | [[case]] # simple truncate | ||||||
|  | define.MEDIUMSIZE = [32, 2048] | ||||||
|  | define.LARGESIZE = 8192 | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldynoop", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |  | ||||||
|  |     strcpy((char*)buffer, "hair"); | ||||||
|  |     size = strlen((char*)buffer); | ||||||
|  |     for (lfs2_off_t j = 0; j < LARGESIZE; j += size) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |      | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDWR) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||||
|  |  | ||||||
|  |     lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldynoop", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||||
|  |  | ||||||
|  |     size = strlen("hair"); | ||||||
|  |     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         memcmp(buffer, "hair", size) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # truncate and read | ||||||
|  | define.MEDIUMSIZE = [32, 2048] | ||||||
|  | define.LARGESIZE = 8192 | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldyread", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |  | ||||||
|  |     strcpy((char*)buffer, "hair"); | ||||||
|  |     size = strlen((char*)buffer); | ||||||
|  |     for (lfs2_off_t j = 0; j < LARGESIZE; j += size) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldyread", LFS2_O_RDWR) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||||
|  |  | ||||||
|  |     lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||||
|  |  | ||||||
|  |     size = strlen("hair"); | ||||||
|  |     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         memcmp(buffer, "hair", size) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldyread", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||||
|  |  | ||||||
|  |     size = strlen("hair"); | ||||||
|  |     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         memcmp(buffer, "hair", size) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # write, truncate, and read | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "sequence", | ||||||
|  |             LFS2_O_RDWR | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |  | ||||||
|  |     size = lfs2_min(lfs2.cfg->cache_size, sizeof(buffer)/2); | ||||||
|  |     lfs2_size_t qsize = size / 4; | ||||||
|  |     uint8_t *wb = buffer; | ||||||
|  |     uint8_t *rb = buffer + size; | ||||||
|  |     for (lfs2_off_t j = 0; j < size; ++j) { | ||||||
|  |         wb[j] = j; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* Spread sequence over size */ | ||||||
|  |     lfs2_file_write(&lfs2, &file, wb, size) => size; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => size; | ||||||
|  |     lfs2_file_tell(&lfs2, &file) => size; | ||||||
|  |  | ||||||
|  |     lfs2_file_seek(&lfs2, &file, 0, LFS2_SEEK_SET) => 0; | ||||||
|  |     lfs2_file_tell(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     /* Chop off the last quarter */ | ||||||
|  |     lfs2_size_t trunc = size - qsize; | ||||||
|  |     lfs2_file_truncate(&lfs2, &file, trunc) => 0; | ||||||
|  |     lfs2_file_tell(&lfs2, &file) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => trunc; | ||||||
|  |  | ||||||
|  |     /* Read should produce first 3/4 */ | ||||||
|  |     lfs2_file_read(&lfs2, &file, rb, size) => trunc; | ||||||
|  |     memcmp(rb, wb, trunc) => 0; | ||||||
|  |  | ||||||
|  |     /* Move to 1/4 */ | ||||||
|  |     lfs2_file_size(&lfs2, &file) => trunc; | ||||||
|  |     lfs2_file_seek(&lfs2, &file, qsize, LFS2_SEEK_SET) => qsize; | ||||||
|  |     lfs2_file_tell(&lfs2, &file) => qsize; | ||||||
|  |  | ||||||
|  |     /* Chop to 1/2 */ | ||||||
|  |     trunc -= qsize; | ||||||
|  |     lfs2_file_truncate(&lfs2, &file, trunc) => 0; | ||||||
|  |     lfs2_file_tell(&lfs2, &file) => qsize; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => trunc; | ||||||
|  |      | ||||||
|  |     /* Read should produce second quarter */ | ||||||
|  |     lfs2_file_read(&lfs2, &file, rb, size) => trunc - qsize; | ||||||
|  |     memcmp(rb, wb + qsize, trunc - qsize) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # truncate and write | ||||||
|  | define.MEDIUMSIZE = [32, 2048] | ||||||
|  | define.LARGESIZE = 8192 | ||||||
|  | code = ''' | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldywrite", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT) => 0; | ||||||
|  |  | ||||||
|  |     strcpy((char*)buffer, "hair"); | ||||||
|  |     size = strlen((char*)buffer); | ||||||
|  |     for (lfs2_off_t j = 0; j < LARGESIZE; j += size) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldywrite", LFS2_O_RDWR) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||||
|  |  | ||||||
|  |     lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||||
|  |  | ||||||
|  |     strcpy((char*)buffer, "bald"); | ||||||
|  |     size = strlen((char*)buffer); | ||||||
|  |     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldywrite", LFS2_O_RDONLY) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||||
|  |  | ||||||
|  |     size = strlen("bald"); | ||||||
|  |     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||||
|  |         lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |         memcmp(buffer, "bald", size) => 0; | ||||||
|  |     } | ||||||
|  |     lfs2_file_read(&lfs2, &file, buffer, size) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # truncate write under powerloss | ||||||
|  | define.SMALLSIZE = [4, 512] | ||||||
|  | define.MEDIUMSIZE = [32, 1024] | ||||||
|  | define.LARGESIZE = 2048 | ||||||
|  | reentrant = true | ||||||
|  | code = ''' | ||||||
|  |     err = lfs2_mount(&lfs2, &cfg); | ||||||
|  |     if (err) { | ||||||
|  |         lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |         lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |     } | ||||||
|  |     err = lfs2_file_open(&lfs2, &file, "baldy", LFS2_O_RDONLY); | ||||||
|  |     assert(!err || err == LFS2_ERR_NOENT); | ||||||
|  |     if (!err) { | ||||||
|  |         size = lfs2_file_size(&lfs2, &file); | ||||||
|  |         assert(size == 0 || | ||||||
|  |                 size == LARGESIZE || | ||||||
|  |                 size == MEDIUMSIZE || | ||||||
|  |                 size == SMALLSIZE); | ||||||
|  |         for (lfs2_off_t j = 0; j < size; j += 4) { | ||||||
|  |             lfs2_file_read(&lfs2, &file, buffer, 4) => 4; | ||||||
|  |             assert(memcmp(buffer, "hair", 4) == 0 || | ||||||
|  |                    memcmp(buffer, "bald", 4) == 0 || | ||||||
|  |                    memcmp(buffer, "comb", 4) == 0); | ||||||
|  |         } | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldy", | ||||||
|  |             LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => 0; | ||||||
|  |     strcpy((char*)buffer, "hair"); | ||||||
|  |     size = strlen((char*)buffer); | ||||||
|  |     for (lfs2_off_t j = 0; j < LARGESIZE; j += size) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldy", LFS2_O_RDWR) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => LARGESIZE; | ||||||
|  |     lfs2_file_truncate(&lfs2, &file, MEDIUMSIZE) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||||
|  |     strcpy((char*)buffer, "bald"); | ||||||
|  |     size = strlen((char*)buffer); | ||||||
|  |     for (lfs2_off_t j = 0; j < MEDIUMSIZE; j += size) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_file_open(&lfs2, &file, "baldy", LFS2_O_RDWR) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => MEDIUMSIZE; | ||||||
|  |     lfs2_file_truncate(&lfs2, &file, SMALLSIZE) => 0; | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SMALLSIZE; | ||||||
|  |     strcpy((char*)buffer, "comb"); | ||||||
|  |     size = strlen((char*)buffer); | ||||||
|  |     for (lfs2_off_t j = 0; j < SMALLSIZE; j += size) { | ||||||
|  |         lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |     } | ||||||
|  |     lfs2_file_size(&lfs2, &file) => SMALLSIZE; | ||||||
|  |     lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | [[case]] # more aggressive general truncation tests | ||||||
|  | define.CONFIG = 'range(6)' | ||||||
|  | define.SMALLSIZE = 32 | ||||||
|  | define.MEDIUMSIZE = 2048 | ||||||
|  | define.LARGESIZE = 8192 | ||||||
|  | code = ''' | ||||||
|  |     #define COUNT 5 | ||||||
|  |     const struct { | ||||||
|  |         lfs2_off_t startsizes[COUNT]; | ||||||
|  |         lfs2_off_t startseeks[COUNT]; | ||||||
|  |         lfs2_off_t hotsizes[COUNT]; | ||||||
|  |         lfs2_off_t coldsizes[COUNT]; | ||||||
|  |     } configs[] = { | ||||||
|  |         // cold shrinking | ||||||
|  |         {{2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {          0,   SMALLSIZE,  MEDIUMSIZE,   LARGESIZE, 2*LARGESIZE}}, | ||||||
|  |         // cold expanding | ||||||
|  |         {{2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {          0,   SMALLSIZE,  MEDIUMSIZE,   LARGESIZE, 2*LARGESIZE}}, | ||||||
|  |         // warm shrinking truncate | ||||||
|  |         {{2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {          0,   SMALLSIZE,  MEDIUMSIZE,   LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {          0,           0,           0,           0,           0}}, | ||||||
|  |         // warm expanding truncate | ||||||
|  |         {{          0,   SMALLSIZE,  MEDIUMSIZE,   LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {          0,   SMALLSIZE,  MEDIUMSIZE,   LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}}, | ||||||
|  |         // mid-file shrinking truncate | ||||||
|  |         {{2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {  LARGESIZE,   LARGESIZE,   LARGESIZE,   LARGESIZE,   LARGESIZE}, | ||||||
|  |          {          0,   SMALLSIZE,  MEDIUMSIZE,   LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {          0,           0,           0,           0,           0}}, | ||||||
|  |         // mid-file expanding truncate | ||||||
|  |         {{          0,   SMALLSIZE,   MEDIUMSIZE,  LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {          0,           0,   SMALLSIZE,  MEDIUMSIZE,   LARGESIZE}, | ||||||
|  |          {2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}, | ||||||
|  |          {2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE, 2*LARGESIZE}}, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const lfs2_off_t *startsizes = configs[CONFIG].startsizes; | ||||||
|  |     const lfs2_off_t *startseeks = configs[CONFIG].startseeks; | ||||||
|  |     const lfs2_off_t *hotsizes   = configs[CONFIG].hotsizes; | ||||||
|  |     const lfs2_off_t *coldsizes  = configs[CONFIG].coldsizes; | ||||||
|  |  | ||||||
|  |     lfs2_format(&lfs2, &cfg) => 0; | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     for (unsigned i = 0; i < COUNT; i++) { | ||||||
|  |         sprintf(path, "hairyhead%d", i); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, | ||||||
|  |                 LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0; | ||||||
|  |  | ||||||
|  |         strcpy((char*)buffer, "hair"); | ||||||
|  |         size = strlen((char*)buffer); | ||||||
|  |         for (lfs2_off_t j = 0; j < startsizes[i]; j += size) { | ||||||
|  |             lfs2_file_write(&lfs2, &file, buffer, size) => size; | ||||||
|  |         } | ||||||
|  |         lfs2_file_size(&lfs2, &file) => startsizes[i]; | ||||||
|  |  | ||||||
|  |         if (startseeks[i] != startsizes[i]) { | ||||||
|  |             lfs2_file_seek(&lfs2, &file, | ||||||
|  |                     startseeks[i], LFS2_SEEK_SET) => startseeks[i]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_file_truncate(&lfs2, &file, hotsizes[i]) => 0; | ||||||
|  |         lfs2_file_size(&lfs2, &file) => hotsizes[i]; | ||||||
|  |  | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     for (unsigned i = 0; i < COUNT; i++) { | ||||||
|  |         sprintf(path, "hairyhead%d", i); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDWR) => 0; | ||||||
|  |         lfs2_file_size(&lfs2, &file) => hotsizes[i]; | ||||||
|  |  | ||||||
|  |         size = strlen("hair"); | ||||||
|  |         lfs2_off_t j = 0; | ||||||
|  |         for (; j < startsizes[i] && j < hotsizes[i]; j += size) { | ||||||
|  |             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |             memcmp(buffer, "hair", size) => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (; j < hotsizes[i]; j += size) { | ||||||
|  |             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |             memcmp(buffer, "\0\0\0\0", size) => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_file_truncate(&lfs2, &file, coldsizes[i]) => 0; | ||||||
|  |         lfs2_file_size(&lfs2, &file) => coldsizes[i]; | ||||||
|  |  | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  |  | ||||||
|  |     lfs2_mount(&lfs2, &cfg) => 0; | ||||||
|  |  | ||||||
|  |     for (unsigned i = 0; i < COUNT; i++) { | ||||||
|  |         sprintf(path, "hairyhead%d", i); | ||||||
|  |         lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0; | ||||||
|  |         lfs2_file_size(&lfs2, &file) => coldsizes[i]; | ||||||
|  |  | ||||||
|  |         size = strlen("hair"); | ||||||
|  |         lfs2_off_t j = 0; | ||||||
|  |         for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i]; | ||||||
|  |                 j += size) { | ||||||
|  |             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |             memcmp(buffer, "hair", size) => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (; j < coldsizes[i]; j += size) { | ||||||
|  |             lfs2_file_read(&lfs2, &file, buffer, size) => size; | ||||||
|  |             memcmp(buffer, "\0\0\0\0", size) => 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs2_file_close(&lfs2, &file) => 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs2_unmount(&lfs2) => 0; | ||||||
|  | ''' | ||||||
		Reference in New Issue
	
	Block a user