diff options
| author | Jim Mussared <jim.mussared@gmail.com> | 2023-06-14 15:38:01 +1000 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2024-10-09 16:39:00 +1100 |
| commit | 6461ffd9d174fdf799c8de27f92e4505ec731c7e (patch) | |
| tree | 556094b579587ef8638ca1ce0fabf797ee3fc05b | |
| parent | dd6f78f0142a0567ed21a7b29aa6d810c85e1bdb (diff) | |
tools/mpremote: Add initial regression tests for mpremote.
These tests are specifically for the command-line interface and cover:
- resume/soft-reset/connect/disconnect
- mount
- fs cp,touch,mkdir,cat,sha256sum,rm,rmdir
- eval/exec/run
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Damien George <damien@micropython.org>
| -rw-r--r-- | tools/mpremote/tests/README.md | 14 | ||||
| -rwxr-xr-x | tools/mpremote/tests/run-mpremote-tests.sh | 30 | ||||
| -rwxr-xr-x | tools/mpremote/tests/test_eval_exec_run.sh | 26 | ||||
| -rw-r--r-- | tools/mpremote/tests/test_eval_exec_run.sh.exp | 6 | ||||
| -rwxr-xr-x | tools/mpremote/tests/test_filesystem.sh | 162 | ||||
| -rw-r--r-- | tools/mpremote/tests/test_filesystem.sh.exp | 183 | ||||
| -rwxr-xr-x | tools/mpremote/tests/test_mount.sh | 28 | ||||
| -rw-r--r-- | tools/mpremote/tests/test_mount.sh.exp | 7 | ||||
| -rwxr-xr-x | tools/mpremote/tests/test_recursive_cp.sh | 60 | ||||
| -rw-r--r-- | tools/mpremote/tests/test_recursive_cp.sh.exp | 98 | ||||
| -rwxr-xr-x | tools/mpremote/tests/test_resume.sh | 23 | ||||
| -rw-r--r-- | tools/mpremote/tests/test_resume.sh.exp | 17 |
12 files changed, 654 insertions, 0 deletions
diff --git a/tools/mpremote/tests/README.md b/tools/mpremote/tests/README.md new file mode 100644 index 000000000..5b924d2fb --- /dev/null +++ b/tools/mpremote/tests/README.md @@ -0,0 +1,14 @@ +# Tests for mpremote + +This directory contains a set of tests for `mpremote`. + +Requirements: +- A device running MicroPython connected to a serial port on the host. +- Python 3.x, `bash` and various Unix tools such as `find`, `mktemp`, `sed`, `sort`, `tr`. + +To run the tests do: + + $ ./run-mpremote-tests.sh + +Each test should print "OK" if it passed. Otherwise it will print "CRASH", or "FAIL" +and a diff of the expected and actual test output. diff --git a/tools/mpremote/tests/run-mpremote-tests.sh b/tools/mpremote/tests/run-mpremote-tests.sh new file mode 100755 index 000000000..11d82c9bb --- /dev/null +++ b/tools/mpremote/tests/run-mpremote-tests.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -e + +TEST_DIR=$(dirname $0) +MPREMOTE=${TEST_DIR}/../mpremote.py + +if [ -z "$1" ]; then + # Find tests matching test_*.sh + TESTS=${TEST_DIR}/test_*.sh +else + # Specific test path from the command line. + TESTS="$1" +fi + +for t in $TESTS; do + TMP=$(mktemp -d) + echo -n "${t}: " + # Strip CR and replace the random temp dir with a token. + if env MPREMOTE=${MPREMOTE} TMP="${TMP}" "${t}" | tr -d '\r' | sed "s,${TMP},"'${TMP},g' > "${t}.out"; then + if diff "${t}.out" "${t}.exp" > /dev/null; then + echo "OK" + else + echo "FAIL" + diff "${t}.out" "${t}.exp" || true + fi + else + echo "CRASH" + fi + rm -r "${TMP}" +done diff --git a/tools/mpremote/tests/test_eval_exec_run.sh b/tools/mpremote/tests/test_eval_exec_run.sh new file mode 100755 index 000000000..dd30b2594 --- /dev/null +++ b/tools/mpremote/tests/test_eval_exec_run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e + +$MPREMOTE exec "print('mpremote')" + +$MPREMOTE exec "print('before sleep'); import time; time.sleep(0.1); print('after sleep')" +$MPREMOTE exec --no-follow "print('before sleep'); import time; time.sleep(0.1); print('after sleep')" +sleep 0.3 + +$MPREMOTE eval "1+2" +$MPREMOTE eval "[{'a': 'b'}, (1,2,3,), True]" + +cat << EOF > /tmp/run.py +print("run") +EOF + +$MPREMOTE run /tmp/run.py + +cat << EOF > /tmp/run.py +import time +for i in range(3): + time.sleep(0.1) + print("run") +EOF +$MPREMOTE run --no-follow /tmp/run.py +sleep 0.5 diff --git a/tools/mpremote/tests/test_eval_exec_run.sh.exp b/tools/mpremote/tests/test_eval_exec_run.sh.exp new file mode 100644 index 000000000..3ea7a9d7c --- /dev/null +++ b/tools/mpremote/tests/test_eval_exec_run.sh.exp @@ -0,0 +1,6 @@ +mpremote +before sleep +after sleep +3 +[{'a': 'b'}, (1, 2, 3), True] +run diff --git a/tools/mpremote/tests/test_filesystem.sh b/tools/mpremote/tests/test_filesystem.sh new file mode 100755 index 000000000..b6d5a7feb --- /dev/null +++ b/tools/mpremote/tests/test_filesystem.sh @@ -0,0 +1,162 @@ +#!/bin/bash +set -e + +# Creates a RAM disk big enough to hold two copies of the test directory +# structure. +cat << EOF > "${TMP}/ramdisk.py" +class RAMBlockDev: + def __init__(self, block_size, num_blocks): + self.block_size = block_size + self.data = bytearray(block_size * num_blocks) + + def readblocks(self, block_num, buf): + for i in range(len(buf)): + buf[i] = self.data[block_num * self.block_size + i] + + def writeblocks(self, block_num, buf): + for i in range(len(buf)): + self.data[block_num * self.block_size + i] = buf[i] + + def ioctl(self, op, arg): + if op == 4: # get number of blocks + return len(self.data) // self.block_size + if op == 5: # get block size + return self.block_size + +import os + +bdev = RAMBlockDev(512, 50) +os.VfsFat.mkfs(bdev) +os.mount(bdev, '/ramdisk') +os.chdir('/ramdisk') +EOF + + +echo ----- +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume ls + +echo ----- +$MPREMOTE resume touch a.py +$MPREMOTE resume touch :b.py +$MPREMOTE resume ls : +$MPREMOTE resume cat a.py +$MPREMOTE resume cat :b.py +$MPREMOTE resume sha256sum a.py +echo -n "" | sha256sum + +echo ----- +cat << EOF > "${TMP}/a.py" +print("Hello") +print("World") +EOF +$MPREMOTE resume cp "${TMP}/a.py" : +$MPREMOTE resume cp "${TMP}/a.py" :b.py +$MPREMOTE resume cp "${TMP}/a.py" :c.py +$MPREMOTE resume cp :a.py :d.py +$MPREMOTE resume ls +$MPREMOTE resume exec "import a; import b; import c" +$MPREMOTE resume sha256sum a.py +cat "${TMP}/a.py" | sha256sum + +echo ----- +$MPREMOTE resume mkdir aaa +$MPREMOTE resume mkdir :bbb +$MPREMOTE resume cp "${TMP}/a.py" :aaa +$MPREMOTE resume cp "${TMP}/a.py" :bbb/b.py +$MPREMOTE resume cat :aaa/a.py bbb/b.py + +echo ----- +$MPREMOTE resume rm :b.py c.py +$MPREMOTE resume ls +$MPREMOTE resume rm :aaa/a.py bbb/b.py +$MPREMOTE resume rmdir aaa :bbb +$MPREMOTE resume ls + +echo ----- +env EDITOR="sed -i s/Hello/Goodbye/" $MPREMOTE resume edit d.py +$MPREMOTE resume sha256sum :d.py +$MPREMOTE resume exec "import d" + + +# Create a local directory structure and copy it to `:` on the device. +echo ----- +mkdir -p "${TMP}/package" +mkdir -p "${TMP}/package/subpackage" +cat << EOF > "${TMP}/package/__init__.py" +from .x import x +from .subpackage import y +EOF +cat << EOF > "${TMP}/package/x.py" +def x(): + print("x") +EOF +cat << EOF > "${TMP}/package/subpackage/__init__.py" +from .y import y +EOF +cat << EOF > "${TMP}/package/subpackage/y.py" +def y(): + print("y") +EOF +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume cp -r "${TMP}/package" : +$MPREMOTE resume ls : :package :package/subpackage +$MPREMOTE resume exec "import package; package.x(); package.y()" + + +# Same thing except with a destination directory name. +echo ----- +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume cp -r "${TMP}/package" :package2 +$MPREMOTE resume ls : :package2 :package2/subpackage +$MPREMOTE resume exec "import package2; package2.x(); package2.y()" + + +# Copy to an existing directory, it will be copied inside. +echo ----- +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume mkdir :test +$MPREMOTE resume cp -r "${TMP}/package" :test +$MPREMOTE resume ls :test :test/package :test/package/subpackage + +# Copy to non-existing sub-directory. +echo ----- +$MPREMOTE resume cp -r "${TMP}/package" :test/package2 +$MPREMOTE resume ls :test :test/package2 :test/package2/subpackage + +# Copy from the device back to local. +echo ----- +mkdir "${TMP}/copy" +$MPREMOTE resume cp -r :test/package "${TMP}/copy" +ls "${TMP}/copy" "${TMP}/copy/package" "${TMP}/copy/package/subpackage" + +# Copy from the device back to local with destination directory name. +echo ----- +$MPREMOTE resume cp -r :test/package "${TMP}/copy/package2" +ls "${TMP}/copy" "${TMP}/copy/package2" "${TMP}/copy/package2/subpackage" + + +# Copy from device to another location on the device with destination directory name. +echo ----- +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume cp -r "${TMP}/package" : +$MPREMOTE resume cp -r :package :package3 +$MPREMOTE resume ls : :package3 :package3/subpackage + +# Copy from device to another location on the device into an existing directory. +echo ----- +$MPREMOTE run "${TMP}/ramdisk.py" +$MPREMOTE resume cp -r "${TMP}/package" : +$MPREMOTE resume mkdir :package4 +$MPREMOTE resume cp -r :package :package4 +$MPREMOTE resume ls : :package4 :package4/package :package4/package/subpackage + +# Repeat an existing copy with one file modified. +echo ----- +cat << EOF > "${TMP}/package/subpackage/y.py" +def y(): + print("y2") +EOF +$MPREMOTE resume cp -r "${TMP}/package" : +$MPREMOTE resume ls : :package :package/subpackage +$MPREMOTE resume exec "import package; package.x(); package.y()" diff --git a/tools/mpremote/tests/test_filesystem.sh.exp b/tools/mpremote/tests/test_filesystem.sh.exp new file mode 100644 index 000000000..7220dc41e --- /dev/null +++ b/tools/mpremote/tests/test_filesystem.sh.exp @@ -0,0 +1,183 @@ +----- +ls : +----- +touch :a.py +touch :b.py +ls : + 0 a.py + 0 b.py +sha256sum :a.py +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 - +----- +cp ${TMP}/a.py : +cp ${TMP}/a.py :b.py +cp ${TMP}/a.py :c.py +cp :a.py :d.py +ls : + 30 a.py + 30 b.py + 30 c.py + 30 d.py +Hello +World +Hello +World +Hello +World +sha256sum :a.py +50f0a701dd6cd6125387b96515300c9d5294c006518f8e62fa9eea3b66587f21 +50f0a701dd6cd6125387b96515300c9d5294c006518f8e62fa9eea3b66587f21 - +----- +mkdir :aaa +mkdir :bbb +cp ${TMP}/a.py :aaa +cp ${TMP}/a.py :bbb/b.py +print("Hello") +print("World") +print("Hello") +print("World") +----- +rm :b.py +rm :c.py +ls : + 30 a.py + 30 d.py + 0 aaa/ + 0 bbb/ +rm :aaa/a.py +rm :bbb/b.py +rmdir :aaa +rmdir :bbb +ls : + 30 a.py + 30 d.py +----- +edit :d.py +sha256sum :d.py +612c7ddb88390ac86b4174b26a6e5b52fc2f2838b234efd8f6f7c41631a49d04 +Goodbye +World +----- +cp ${TMP}/package : +ls : + 0 package/ +ls :package + 0 subpackage/ + 43 __init__.py + 22 x.py +ls :package/subpackage + 17 __init__.py + 22 y.py +x +y +----- +cp ${TMP}/package :package2 +ls : + 0 package2/ +ls :package2 + 0 subpackage/ + 43 __init__.py + 22 x.py +ls :package2/subpackage + 17 __init__.py + 22 y.py +x +y +----- +mkdir :test +cp ${TMP}/package :test +ls :test + 0 package/ +ls :test/package + 0 subpackage/ + 43 __init__.py + 22 x.py +ls :test/package/subpackage + 17 __init__.py + 22 y.py +----- +cp ${TMP}/package :test/package2 +ls :test + 0 package/ + 0 package2/ +ls :test/package2 + 0 subpackage/ + 43 __init__.py + 22 x.py +ls :test/package2/subpackage + 17 __init__.py + 22 y.py +----- +cp :test/package ${TMP}/copy +${TMP}/copy: +package + +${TMP}/copy/package: +__init__.py +subpackage +x.py + +${TMP}/copy/package/subpackage: +__init__.py +y.py +----- +cp :test/package ${TMP}/copy/package2 +${TMP}/copy: +package +package2 + +${TMP}/copy/package2: +__init__.py +subpackage +x.py + +${TMP}/copy/package2/subpackage: +__init__.py +y.py +----- +cp ${TMP}/package : +cp :package :package3 +ls : + 0 package/ + 0 package3/ +ls :package3 + 0 subpackage/ + 43 __init__.py + 22 x.py +ls :package3/subpackage + 17 __init__.py + 22 y.py +----- +cp ${TMP}/package : +mkdir :package4 +cp :package :package4 +ls : + 0 package/ + 0 package4/ +ls :package4 + 0 package/ +ls :package4/package + 0 subpackage/ + 43 __init__.py + 22 x.py +ls :package4/package/subpackage + 17 __init__.py + 22 y.py +----- +cp ${TMP}/package : +Up to date: ./package/__init__.py +Up to date: ./package/subpackage/__init__.py +Up to date: ./package/x.py +ls : + 0 package/ + 0 package4/ +ls :package + 0 subpackage/ + 43 __init__.py + 22 x.py +ls :package/subpackage + 17 __init__.py + 23 y.py +x +y2 diff --git a/tools/mpremote/tests/test_mount.sh b/tools/mpremote/tests/test_mount.sh new file mode 100755 index 000000000..9eab0b0d6 --- /dev/null +++ b/tools/mpremote/tests/test_mount.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -e + +# Create a local directory structure and mount the parent directory on the device. +echo ----- +mkdir -p "${TMP}/mount_package" +mkdir -p "${TMP}/mount_package/subpackage" +cat << EOF > "${TMP}/mount_package/__init__.py" +from .x import x +from .subpackage import y +EOF +cat << EOF > "${TMP}/mount_package/x.py" +def x(): + print("x") +EOF +cat << EOF > "${TMP}/mount_package/subpackage/__init__.py" +from .y import y +EOF +cat << EOF > "${TMP}/mount_package/subpackage/y.py" +def y(): + print("y") +EOF +$MPREMOTE mount ${TMP} exec "import mount_package; mount_package.x(); mount_package.y()" + +# Write to a file on the device and see that it's written locally. +echo ----- +$MPREMOTE mount ${TMP} exec "open('test.txt', 'w').write('hello world\n')" +cat "${TMP}/test.txt" diff --git a/tools/mpremote/tests/test_mount.sh.exp b/tools/mpremote/tests/test_mount.sh.exp new file mode 100644 index 000000000..560f5e4f1 --- /dev/null +++ b/tools/mpremote/tests/test_mount.sh.exp @@ -0,0 +1,7 @@ +----- +x +y +Local directory ${TMP} is mounted at /remote +----- +Local directory ${TMP} is mounted at /remote +hello world diff --git a/tools/mpremote/tests/test_recursive_cp.sh b/tools/mpremote/tests/test_recursive_cp.sh new file mode 100755 index 000000000..397b5fd57 --- /dev/null +++ b/tools/mpremote/tests/test_recursive_cp.sh @@ -0,0 +1,60 @@ +#!/bin/bash +set -e + +echo ----- +mkdir -p $TMP/a $TMP/a/b +touch $TMP/a/x.py $TMP/a/b/y.py +ls $TMP/a | sort +ls $TMP/a/b | sort + +# TODO +echo ----- +touch $TMP/y.py +ls $TMP | sort + +# Recursive copy to a directory that doesn't exist. The source directory will +# be copied to the destination (i.e. bX will the same as a). +echo ----- +cp -r $TMP/a $TMP/b1 +cp -r $TMP/a/ $TMP/b2 +cp -r $TMP/a $TMP/b3/ +cp -r $TMP/a/ $TMP/b4/ + +# Recursive copy to a directory that does exist. The source directory will be +# copied into the destination (i.e. bX will contain a copy of a). +echo ----- +mkdir $TMP/c{1,2,3,4} +cp -r $TMP/a $TMP/c1 +cp -r $TMP/a/ $TMP/c2 +cp -r $TMP/a $TMP/c3/ +cp -r $TMP/a/ $TMP/c4/ + +echo ----- +find $TMP | sort + +echo ----- +rm -rf $TMP/b{1,2,3,4} $TMP/c{1,2,3,4} + + + +# Now replicate the same thing using `mpremote cp`. + +# Recursive copy to a directory that doesn't exist. The source directory will +# be copied to the destination (i.e. bX will the same as a). +echo ----- +$MPREMOTE cp --no-verbose -r $TMP/a $TMP/b1 +$MPREMOTE cp --no-verbose -r $TMP/a/ $TMP/b2 +$MPREMOTE cp --no-verbose -r $TMP/a $TMP/b3/ +$MPREMOTE cp --no-verbose -r $TMP/a/ $TMP/b4/ + +# Recursive copy to a directory that does exist. The source directory will be +# copied into the destination (i.e. bX will contain a copy of a). +echo ----- +mkdir $TMP/c{1,2,3,4} +$MPREMOTE cp --no-verbose -r $TMP/a $TMP/c1 +$MPREMOTE cp --no-verbose -r $TMP/a/ $TMP/c2 +$MPREMOTE cp --no-verbose -r $TMP/a $TMP/c3/ +$MPREMOTE cp --no-verbose -r $TMP/a/ $TMP/c4/ + +echo ----- +find $TMP | sort diff --git a/tools/mpremote/tests/test_recursive_cp.sh.exp b/tools/mpremote/tests/test_recursive_cp.sh.exp new file mode 100644 index 000000000..3b787dfe7 --- /dev/null +++ b/tools/mpremote/tests/test_recursive_cp.sh.exp @@ -0,0 +1,98 @@ +----- +b +x.py +y.py +----- +a +y.py +----- +----- +----- +${TMP} +${TMP}/a +${TMP}/a/b +${TMP}/a/b/y.py +${TMP}/a/x.py +${TMP}/b1 +${TMP}/b1/b +${TMP}/b1/b/y.py +${TMP}/b1/x.py +${TMP}/b2 +${TMP}/b2/b +${TMP}/b2/b/y.py +${TMP}/b2/x.py +${TMP}/b3 +${TMP}/b3/b +${TMP}/b3/b/y.py +${TMP}/b3/x.py +${TMP}/b4 +${TMP}/b4/b +${TMP}/b4/b/y.py +${TMP}/b4/x.py +${TMP}/c1 +${TMP}/c1/a +${TMP}/c1/a/b +${TMP}/c1/a/b/y.py +${TMP}/c1/a/x.py +${TMP}/c2 +${TMP}/c2/a +${TMP}/c2/a/b +${TMP}/c2/a/b/y.py +${TMP}/c2/a/x.py +${TMP}/c3 +${TMP}/c3/a +${TMP}/c3/a/b +${TMP}/c3/a/b/y.py +${TMP}/c3/a/x.py +${TMP}/c4 +${TMP}/c4/a +${TMP}/c4/a/b +${TMP}/c4/a/b/y.py +${TMP}/c4/a/x.py +${TMP}/y.py +----- +----- +----- +----- +${TMP} +${TMP}/a +${TMP}/a/b +${TMP}/a/b/y.py +${TMP}/a/x.py +${TMP}/b1 +${TMP}/b1/b +${TMP}/b1/b/y.py +${TMP}/b1/x.py +${TMP}/b2 +${TMP}/b2/b +${TMP}/b2/b/y.py +${TMP}/b2/x.py +${TMP}/b3 +${TMP}/b3/b +${TMP}/b3/b/y.py +${TMP}/b3/x.py +${TMP}/b4 +${TMP}/b4/b +${TMP}/b4/b/y.py +${TMP}/b4/x.py +${TMP}/c1 +${TMP}/c1/a +${TMP}/c1/a/b +${TMP}/c1/a/b/y.py +${TMP}/c1/a/x.py +${TMP}/c2 +${TMP}/c2/a +${TMP}/c2/a/b +${TMP}/c2/a/b/y.py +${TMP}/c2/a/x.py +${TMP}/c3 +${TMP}/c3/a +${TMP}/c3/a/b +${TMP}/c3/a/b/y.py +${TMP}/c3/a/x.py +${TMP}/c4 +${TMP}/c4/a +${TMP}/c4/a/b +${TMP}/c4/a/b/y.py +${TMP}/c4/a/x.py +${TMP}/y.py diff --git a/tools/mpremote/tests/test_resume.sh b/tools/mpremote/tests/test_resume.sh new file mode 100755 index 000000000..dfc351c83 --- /dev/null +++ b/tools/mpremote/tests/test_resume.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +# The eval command will continue the state of the exec. +echo ----- +$MPREMOTE exec "a = 'hello'" eval "a" + +# Automatic soft reset. `a` will trigger NameError. +echo ----- +$MPREMOTE eval "a" || true + +# Resume will skip soft reset. +echo ----- +$MPREMOTE exec "a = 'resume'" +$MPREMOTE resume eval "a" + +# The eval command will continue the state of the exec. +echo ----- +$MPREMOTE exec "a = 'soft-reset'" eval "a" soft-reset eval "1+1" eval "a" || true + +# A disconnect will trigger auto-reconnect. +echo ----- +$MPREMOTE eval "1+2" disconnect eval "2+3" diff --git a/tools/mpremote/tests/test_resume.sh.exp b/tools/mpremote/tests/test_resume.sh.exp new file mode 100644 index 000000000..20c75928a --- /dev/null +++ b/tools/mpremote/tests/test_resume.sh.exp @@ -0,0 +1,17 @@ +----- +hello +----- +Traceback (most recent call last): + File "<stdin>", line 1, in <module> +NameError: name 'a' isn't defined +----- +resume +----- +soft-reset +2 +Traceback (most recent call last): + File "<stdin>", line 1, in <module> +NameError: name 'a' isn't defined +----- +3 +5 |
