Add mount lifecycle tests and CI mount-tests job#45
Merged
Conversation
Adds fuse_available, mount_image, and pfs3_mount fixtures to conftest.py with cross-platform support (drive-letter mounts on Windows, tmpdir on Unix, 3-stage teardown escalation). Implements 10 tests covering mount/unmount lifecycle, status discovery, file hash verification, error paths, and BW7 regression.
Adds mount-tests CI job with per-platform FUSE backend install (FUSE-T on macOS, fuse3+libfuse-dev on Linux, WinFSP on Windows), post-job cleanup steps, and fusermount3 fallback in platform.py for Linux systems with only fuse3 installed.
Use os.listdir() as the authoritative mount readiness signal instead of os.path.ismount(), which returns True on Windows before the filesystem is fully initialized. Use the same approach for unmount detection. Skip .info files and handle PermissionError in file read tests for WinFSP read-only mounts.
Drop test_mount_missing_fuse_detected and test_winfsp_eject_behavior which were always skipped and never ran. Remove unused Path import.
Add mount test layer (fuse marker) to pytest table and quick start. Update CI table to show all four jobs including mount-tests, with FUSE backend details per platform. Reflect that Windows now runs integration and tools-smoke via machine68k-amifuse fork.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
AmiFUSE has extensive test coverage at the unit level (pytest) and integration level (HandlerBridge tests,
tools/amifuse_matrix.py), but none of these test the actual FUSE mount path.amifuse_matrix.pyusesHandlerBridgedirectly — it bypasses FUSE entirely. There are zero tests anywhere that mount an image, browse it through the OS filesystem, and unmount it. This PR closes that gap.The mount lifecycle is the critical path for users — if mount/unmount breaks, nothing else matters. On Windows specifically, WinFSP mounts have unique behaviors (drive-letter allocation, process-termination unmount, file handle cleanup) that need automated regression protection. With this PR, every code change touching the mount path can be validated end-to-end before merge.
How
FUSE backend strategy
Each platform needed a different approach to get FUSE working in CI:
libfuse-t.dylib, notlibfuse.dylib), so the CI step creates a compatibility symlink and setsDYLD_LIBRARY_PATH. Validated in a throwaway spike workflow before implementation.libfuse.so), not fuse3. Installingfuse3alone is insufficient —libfuse-dev(the fuse2 compatibility package) must also be installed.choco install winfsp -yworks without reboot on GitHub Actions runners.Test fixture design
The
mount_imagefactory fixture handles the full mount lifecycle via subprocess:amifuse mount --interactiveviasubprocess.Popen(notsubprocess.run— mount is long-running).--interactiveprevents daemonization so PID tracking works for teardown.os.path.ismount()returns False for directory-based WinFSP mounts.os.listdir()for mount readiness (more reliable thanos.path.ismount()which can return True before the filesystem is initialized on Windows).amifuse unmount→process.terminate()→process.kill(), with pipe draining and mountpoint cleanup.Marker strategy
Tests use
@pytest.mark.fuse(not@pytest.mark.integration) to avoid the existing machine68k gating inpytest_collection_modifyitems. Thefuse_availablefixture handles its own skip logic independently.fusermount3 fix
get_unmount_command()on Linux only checked forfusermount(fuse2). On fuse3-only systems (like Ubuntu CI runners), it fell back toumount -fwhich may require root. Addedfusermount3as a fallback betweenfusermountandumount.Summary
fuse_available,mount_image,pfs3_mount) with cross-platform support (drive-letter mounts on Windows, tmpdir on Unix, 3-stage teardown escalation)mount-testsjob with per-platform FUSE backend install: FUSE-T (macOS), fuse3+libfuse-dev (Linux), WinFSP (Windows)fusermount3fallback inplatform.pyfor fuse3-only Linux systemsTest plan
Local validation (Windows, WinFSP)
os.path.ismount()amifuse status --jsondiscovers the mount with correct image path and PIDos.listdir()amifuse unmountcleanly terminates the mount process and releases the drive letterCI spike validation (all 3 platforms)
A throwaway workflow tested fusepy + each FUSE backend before writing the mount tests:
ismount=True)CI validation (this PR)
fusermount3fallback works on Linux runners with fuse3