Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .cargo/mutants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@

examine_globs = ["crates/sharecli-thermal-tui/src/lib.rs"]

exclude_re = ["render", "event_loop", "replace run ", "count_cargo_builds"]
# "render"/"event_loop"/"replace run "/"count_cargo_builds": terminal-bound or
# live-process paths that cannot be unit-tested headlessly (see
# docs/ops/mutants-hard-gate.md triage table: render/event-loop noise).
# "replace run_with_pool_status": same class as `run` — the terminal lifecycle
# entry point (raw mode + alternate screen + blocking event loop).
# "replace < with <= in agent_forest_lines": equivalent mutant — the loop
# counter `i` is always < forests.len() inside the loop, so `<` and `<=` are
# semantically identical and no test can distinguish them.
exclude_re = [
"render",
"event_loop",
"replace run ",
"replace run_with_pool_status",
"replace < with <= in agent_forest_lines",
"count_cargo_builds",
]

test_package = ["sharecli-thermal-tui"]
2 changes: 1 addition & 1 deletion .github/workflows/container-cosign-soft.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install cosign
uses: sigstore/cosign-installer@d0e95c1e597a85f6211ad6ce8f3dd2d5ed8ecf0e # v3.7.1
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1 (v3.7.1 SHA no longer resolvable upstream)

- name: Container cosign soft gate
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/container-cosign.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install cosign
uses: sigstore/cosign-installer@d0e95c1e597a85f6211ad6ce8f3dd2d5ed8ecf0e # v3.7.1
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1 (v3.7.1 SHA no longer resolvable upstream)

- name: Hard container cosign publish
id: publish
Expand Down
62 changes: 61 additions & 1 deletion crates/sharecli-fuse/mutants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,65 @@
# * parallelism 4 (use `--jobserver-tasks 4` on the CLI)
# When this file is loaded via `--config`, those CLI flags should accompany
# the cargo-mutants invocation in CI/local.
#
# Triage (mirrors the survivor-triage table in docs/ops/mutants-hard-gate.md):
# * src/bin/* — CLI probe utilities (fuse-mount-smoke, fuse-runtime-probe,
# mfmount-probe) are macOS/daemon glue with no testable surface, the same
# terminal-bound class as the thermal-tui `replace run` exclusion.
# * src/winfsp_mount.rs — Windows-only (cfg(windows)); on the Linux lane the
# mutations compile to no-ops and can never be observed.
# * src/mount_smoke.rs — privileged-mount smoke harness; requires a live
# kernel mount (CI has no /dev/fuse).
# * Filesystem impls + mount/install helpers — fuser's `Reply*` constructors
# are pub(crate), so the callbacks (lookup/getattr/readdir/open/read/write/
# create/mknod/unlink/mkdir/rmdir/rename/setattr) and the created-entry
# helpers can only be exercised through a live kernel mount; mount-bound.
# * FuseSessionRegistry methods + with_context_mount — registry state is
# only reachable through a live mount (no registration API exists), so
# every method is mount-bound.
# * backend.rs macFUSE capability probes (fskit_*, kernel_backend_loaded,
# probe_runtime) — cfg'd-out on Linux; the `&&` -> `||` fskit_approved
# line is equivalent there (both operands are false).
# * provenance.rs windows ADS helpers (ads_path / set_attr / get_attr) —
# cfg(windows); no-ops on the Linux lane.
# * Equivalent mutants: AgentsConf::empty -> Default (derive Default is the
# identical pattern set); strip_mount_prefix `<`->`<=` / `==`->`!=` (the
# guarded branches are unreachable); global_read_cache_meters (documented
# Default stub); commit_pending EXDEV-guard -> true (the copy fallback is
# behavior-identical for EXDEV; the widened guard only fires on non-EXDEV
# rename errors that the CI lane cannot produce).
Comment on lines +34 to +39

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

ast-grep outline crates/sharecli-fuse/src \
  --items all --type function > /tmp/fuse-functions.txt

rg -n -A60 -B5 'fn commit_pending|raw_os_error|libc_exdev' \
  crates/sharecli-fuse/src

cargo mutants --list --no-config -p sharecli-fuse |
  rg 'replace match guard|commit_pending|raw_os_error|libc_exdev'

Repository: KooshaPari/sharecli

Length of output: 17425


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '--- mutants config snippets ---\n'
sed -n '1,120p' crates/sharecli-fuse/mutants.toml

printf '\n--- EXDEV coverage mentions ---\n'
rg -n -C 6 'EXDEV|libc_exdev|raw_os_error|\b18\b|rename.*fall|cross-device|NoCrossDeviceLink|WriteSerializeError' crates/sharecli-fuse crates 2>/dev/null | head -n 240

printf '\n--- cargo mutants availability ---\n'
command -v cargo-mutants || true
command -v cargo || true
if command -v cargo >/dev/null 2>&1; then
  cargo --version
fi

Repository: KooshaPari/sharecli

Length of output: 248


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- mutants config snippets ---'
sed -n '1,120p' crates/sharecli-fuse/mutants.toml

printf '%s\n' ''
printf '%s\n' '--- EXDEV coverage mentions ---'
rg -n -C 6 'EXDEV|libc_exdev|raw_os_error|\b18\b|rename.*fall|cross-device|NoCrossDeviceLink|WriteSerializeError' crates/sharecli-fuse crates 2>/dev/null | head -n 240

printf '%s\n' ''
printf '%s\n' '--- cargo mutants availability ---'
command -v cargo-mutants || true
command -v cargo || true
if command -v cargo >/dev/null 2>&1; then
  cargo --version
fi

Repository: KooshaPari/sharecli

Length of output: 22255


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- c01 tests around EXDEV/cross-device ---'
rg -n -C 10 'EXDEV|cross-device|raw_os_error|18|rename' crates/sharecli-fuse/tests/c01_climb2_fuse.rs crates/sharecli-fuse/src/write_serialize.rs 2>/dev/null || true

printf '%s\n' ''
printf '%s\n' '--- WriteSerializeError ---'
rg -n -C 8 'enum WriteSerializeError|WriteSerializeError::Io' crates/sharecli-fuse/src/write_serialize.rs crates/sharecli-fuse/src 2>/dev/null

printf '%s\n' ''
printf '%s\n' '--- relevant write_serialize unit tests ---'
sed -n '236,290p' crates/sharecli-fuse/src/write_serialize.rs

Repository: KooshaPari/sharecli

Length of output: 16498


Do not classify the EXDEV guard mutation as equivalent.

WriteSerialize::commit_pending only falls back to copy+remove when fs::rename returns libc::EXDEV; it returns WriteSerializeError::Io for other rename errors. The existing tests exercise EXDEV and regular errors, but there is no EXDEV cross-device coverage and non-EXDEV fallback is not equivalent. Keep this as an explicit CI-lane limitation or add deterministic coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/sharecli-fuse/mutants.toml` around lines 34 - 39, The mutants.toml
classification must not mark the commit_pending EXDEV guard mutation as
equivalent. Update the comment to describe it as a CI-lane limitation, or add
deterministic coverage that distinguishes EXDEV copy-and-remove fallback from
non-EXDEV rename errors returning WriteSerializeError::Io; retain the existing
classifications for the other listed mutations.

# Everything else (InterceptFs no-mount methods, CowMountHandle, AgentCowStore,
# AgentsConf, WriteSerialize, caches, meters, inode/path mapping, backend
# selection, provenance session id) is covered by tests/.

# Glob paths are relative to the WORKSPACE root (cargo-mutants
# `tree_relative_path`), not the crate root.
exclude_globs = [
"crates/sharecli-fuse/src/bin/*",
"crates/sharecli-fuse/src/winfsp_mount.rs",
"crates/sharecli-fuse/src/mount_smoke.rs",
]

exclude_globs = ["tests/*"]
exclude_re = [
"impl Filesystem for SharedInterceptFs",
"impl Filesystem for InterceptFs",
"mount_with_session",
"replace mount -> anyhow::Result<\\(\\)> with Ok\\(\\(\\)\\)",
"install_created_entry",
"FuseSessionRegistry::",
"with_context_mount",
"fskit_framework_available",
"fskit_approval_requested",
"fskit_backend_approved",
"kernel_backend_loaded",
"in probe_runtime",
"ads_path",
"set_attr",
"get_attr",
"replace AgentsConf::empty -> Self with Default::default()",
"replace < with <= in strip_mount_prefix",
"replace == with != in strip_mount_prefix",
"replace > with >= in NegativeDentryCache::is_negative",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

ast-grep outline crates/sharecli-fuse/src \
  --items all --type function > /tmp/fuse-functions.txt

rg -n -A40 -B5 'fn is_negative|NegativeDentryCache' \
  crates/sharecli-fuse/src

Repository: KooshaPari/sharecli

Length of output: 24206


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

if command -v cargo >/dev/null 2>&1; then
  if ! cargo mutants --help >/dev/null 2>&1; then
    echo "cargo-mutants command unavailable"
    exit 0
  fi
  echo "cargo-mutants version:"
  cargo mutants --version
  echo "mutant list output:"
  cargo mutants --list --output-format json 2>/tmp/cargo_mutants_logs.json | \
    python3 - <<'PY'
import sys, json
data = json.load(sys.stdin)
for x in data.get("file", []):
    name = x.get("name") or ""
    if "NegativeDentryCache::is_negative" in name:
        print(json.dumps({
            "file": x.get("srcfile"),
            "line": x.get("line"),
            "col": x.get("col"),
            "function": x.get("function"),
            "name": name,
            "replacement": x.get("replacement")
        }))
PY
else
  echo "cargo command unavailable"
fi

# Behavioral probe for source-level time comparison semantics.
python3 - <<'PY'
expires_at = 100
now_expiring = 100
now_past = 101
for now, label in [(now_expiring, "expired"), (now_past, "past")]:
    current_gt = expires_at > now
    current_ge = expires_at >= now
    changed = current_gt != current_ge
    print(f"{label}: expires_at={expires_at} now={now} current_gt={current_gt} current_ge={current_ge} changed={changed}")
PY

Repository: KooshaPari/sharecli

Length of output: 191


Add a deterministic boundary test or justify the NegativeDentryCache::is_negative exclusion.

remember_miss() stores Instant::now() + self.ttl, and is_negative() treats expires_at == Instant::now() as expired. The > vs >= change would count an expiring entry as a hit, so document why this state is impossible or add a deterministic boundary test instead of excluding the mutant.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/sharecli-fuse/mutants.toml` at line 71, Replace the current
NegativeDentryCache::is_negative mutant exclusion with a deterministic boundary
test covering an entry whose expiration instant equals the evaluation time, and
assert the entry is treated as expired. If the implementation cannot inject or
control time, document the invariant proving this equality state is impossible
instead.

"global_read_cache_meters",
"replace match guard err.raw_os_error\\(\\) == Some\\(libc_exdev\\(\\)\\) with true",
]
23 changes: 23 additions & 0 deletions crates/sharecli-fuse/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,4 +382,27 @@ mod tests {
assert!(evidence.non_fuse_fallback);
assert!(!evidence.selection.backend.as_str().is_empty());
}

#[test]
fn backend_labels_are_stable_operator_strings() {
assert_eq!(FuseBackend::Kernel.as_str(), "kext");
assert_eq!(FuseBackend::Fskit.as_str(), "fskit");
assert_eq!(FuseBackend::Unavailable.as_str(), "non-fuse");
}

#[test]
fn diagnostics_are_distinct_and_operator_facing() {
let no_backend = FuseBackendDiagnostic::NoVerifiedBackend.message();
let volumes = FuseBackendDiagnostic::FskitRequiresVolumes.message();
assert!(!no_backend.is_empty());
assert!(!volumes.is_empty());
assert_ne!(no_backend, volumes);
}

#[test]
#[cfg(not(target_os = "macos"))]
fn runtime_diagnostics_is_non_empty_off_macos() {
// Exact match: String::new() / "xyzzy" mutants both differ.
assert_eq!(runtime_diagnostics(), "macFUSE diagnostics unavailable on this platform");
}
}
2 changes: 2 additions & 0 deletions crates/sharecli-fuse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ pub use read_cache::{global_read_cache_meters, ReadCacheMeters, ReadContentCache
pub use session_registry::default_fuser_config;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub use session_registry::smoke_fuser_config;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub use session_registry::smoke_fuser_config_for_backend;
pub use session_registry::{FuseMountInfo, FuseMountOptions, FuseSessionRegistry};
pub use write_serialize::{WriteSerialize, WriteSerializeError};
pub use write_serialize_meters::{
Expand Down
47 changes: 46 additions & 1 deletion crates/sharecli-fuse/tests/c01_fuse_backend_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,29 @@
//! cross-test contamination of `SHARECLI_FUSE_BACKEND`. Tests do NOT
//! require a live FUSE mount — they exercise the pure negotiation
//! contract and `mount_with_session`'s error-shape contract.
//!
//! Platform split: the override-handling tests (fskit / kernel / invalid /
//! deterministic) run on Linux and macOS because `select_backend` honors
//! `SHARECLI_FUSE_BACKEND` on both. The no-override test and the two
//! mount-error-envelope tests are macOS-only: on Linux/Windows the backend is
//! documented as always `Unavailable` (`backend.rs`) and the Linux mount path
//! goes straight to `fuser::mount` without backend negotiation, so those
//! assertions cannot hold there.
Comment on lines +24 to +31

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align the platform documentation with the test branches.

The module documentation says that the no-override test is macOS-only. The test runs on Linux and asserts FuseBackend::Unavailable there at Line 163 through Line 168. The no-override contract paragraph also needs an explicit macOS qualifier.

Proposed documentation fix
-//! `SHARECLI_FUSE_BACKEND` on both. The no-override test and the two
-//! mount-error-envelope tests are macOS-only: on Linux/Windows the backend is
-//! documented as always `Unavailable` (`backend.rs`) and the Linux mount path
-//! goes straight to `fuser::mount` without backend negotiation, so those
-//! assertions cannot hold there.
+//! `SHARECLI_FUSE_BACKEND` on both. The no-override test runs on both
+//! supported platforms with platform-specific expectations. The two
+//! mount-error-envelope tests are macOS-only because the backend is
+//! documented as `Unavailable` on non-macOS and the Linux mount path goes
+//! straight to `fuser::mount` without backend negotiation.

-/// Backend selection — when no env var is set, `select_backend` returns
+/// On macOS, when no env var is set, `select_backend` returns

Also applies to: 139-148

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/sharecli-fuse/tests/c01_fuse_backend_selection.rs` around lines 24 -
31, Update the module documentation in c01_fuse_backend_selection.rs to state
that the no-override test runs on Linux and macOS, asserting
FuseBackend::Unavailable on Linux and the documented macOS behavior on macOS.
Add an explicit macOS qualifier to the no-override contract paragraph, keeping
the existing platform descriptions for the mount-error tests unchanged.


#![cfg(any(target_os = "linux", target_os = "macos"))]

//! The mount-error-envelope tests below are macOS-only; on Linux their
//! imports would be unused, so gate them alongside.

#[cfg(target_os = "macos")]
use std::fs;
#[cfg(target_os = "macos")]
use std::path::Path;
use std::sync::Mutex;
#[cfg(target_os = "macos")]
use tempfile::TempDir;

use sharecli_fuse::{select_backend, FuseBackend};
use tempfile::TempDir;

/// Serialize env-var-mutating tests so the `SHARECLI_FUSE_BACKEND` slot
/// never carries state across tests. (Different env var keys can run in
Expand Down Expand Up @@ -111,13 +125,27 @@ fn backend_select_kernel_override_is_respected() {
matches!(backend, FuseBackend::Kernel | FuseBackend::Unavailable),
"kernel override MUST yield Kernel or Unavailable (got {backend:?})"
);
// On Linux the macFUSE kext can never load (`kmutil` does not exist), so
// the kernel override degrades closed deterministically — a mutant that
// widens the loaded-guard to unconditional would return Kernel here.
#[cfg(target_os = "linux")]
assert_eq!(
backend,
FuseBackend::Unavailable,
"on Linux the kernel override MUST degrade to Unavailable (got {backend:?})"
);
}

/// Backend selection — when no env var is set, `select_backend` returns
/// either `Kernel` (when loaded) or `Fskit` (the documented macOS
/// fallback). It MUST never return `Unavailable` without an explicit
/// override — operators who did not opt out expect at least one of the
/// two macFUSE paths to be picked.
///
/// On non-macOS the contract is the mirror image: the FUSE layer is
/// platform-native (libfuse3 / WinFsp) and `backend.rs` documents the
/// backend as always `Unavailable`, so no-override MUST degrade to
/// `Unavailable` rather than pretending a macFUSE backend exists.
#[test]
#[serial_test::serial]
fn backend_select_no_override_picks_kernel_or_fskit() {
Expand All @@ -126,11 +154,18 @@ fn backend_select_no_override_picks_kernel_or_fskit() {
std::env::remove_var("SHARECLI_FUSE_BACKEND");
let backend = select_backend();
restore_env("SHARECLI_FUSE_BACKEND", prev);
#[cfg(target_os = "macos")]
assert_ne!(
backend,
FuseBackend::Unavailable,
"no-override selection MUST yield Kernel or Fskit (got {backend:?})"
);
#[cfg(not(target_os = "macos"))]
assert_eq!(
backend,
FuseBackend::Unavailable,
"non-macOS has no macFUSE backend; no-override MUST degrade to Unavailable (got {backend:?})"
);
}

/// Backend selection — calling `select_backend` twice with the same
Expand All @@ -155,8 +190,13 @@ fn backend_select_is_deterministic_for_same_state() {
/// (so the operator can see *why* the mount failed without digging into
/// `kmutil`). We don't require a live FUSE backend for this test — we
/// only require the error envelope to be correct.
///
/// macOS-only: the Linux mount path skips backend negotiation entirely
/// (`fuser::mount` direct), so the backend-unavailable envelope exists
/// only on macOS.
#[test]
#[serial_test::serial]
#[cfg(target_os = "macos")]
fn mount_failure_unavailable_backend_mentions_backend() {
let _guard = BACKEND_ENV_LOCK.lock().expect("env lock");
let prev = std::env::var("SHARECLI_FUSE_BACKEND").ok();
Expand Down Expand Up @@ -189,8 +229,13 @@ fn mount_failure_unavailable_backend_mentions_backend() {
/// surface the mountpoint path so the operator can correlate the
/// failure with their `--mountpoint` flag. This catches regressions in
/// the error wrapping in `lib.rs`.
///
/// macOS-only: on Linux `fuser::mount` reports bare io errors (e.g.
/// `No such file or directory`) without the mountpoint, and there is no
/// macFUSE negotiation error to wrap.
#[test]
#[serial_test::serial]
#[cfg(target_os = "macos")]
fn mount_failure_invalid_mountpoint_mentions_path() {
let _guard = BACKEND_ENV_LOCK.lock().expect("env lock");
let prev = std::env::var("SHARECLI_FUSE_BACKEND").ok();
Expand Down
Loading
Loading