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
Original file line number Diff line number Diff line change
Expand Up @@ -225,52 +225,55 @@ steps = [
]

[[e2e]]
name = "disable_cache_forces_reexecution"
name = "disable_cache_noop_allows_cache_hit"
comment = """
Exercises `disableCache`. The tool asks the runner not to cache this run,
so the next invocation re-executes instead of hitting a prior entry.
Exercises the temporary `disableCache` no-op workaround. The tool asks the
runner not to cache this run, but the client ignores that request, so the next
invocation hits the cache.
"""
ignore = true
steps = [
{ argv = [
"vt",
"run",
"disable-cache",
], comment = "first run — tool calls disableCache" },
], comment = "first run — tool calls disableCache, currently ignored by the client" },
{ argv = [
"vt",
"run",
"disable-cache",
], comment = "cache miss (NotFound) because nothing was cached" },
], comment = "cache hit because disableCache is temporarily a no-op" },
{ argv = [
"vt",
"run",
"--last-details",
], comment = "summary names the opt-out as the not-cached reason" },
], comment = "summary reports the replayed cache hit" },
]

[[e2e]]
name = "disable_cache_works_with_explicit_inputs"
name = "disable_cache_noop_with_explicit_inputs"
comment = """
Exercises `disableCache` on a cached task with explicit inputs. The runner must still inject IPC even when fspy auto-input inference is disabled, or the tool's cache opt-out becomes a no-op and the second run incorrectly hits.
Exercises the temporary `disableCache` no-op workaround on a cached task with
explicit inputs. The client ignores the opt-out request, so the second run hits
even when fspy auto-input inference is disabled.
"""
ignore = true
steps = [
{ argv = [
"vt",
"run",
"disable-cache-explicit-input",
], comment = "first run uses input: [] and asks the runner not to cache" },
], comment = "first run uses input: [] and calls disableCache, currently ignored by the client" },
{ argv = [
"vt",
"run",
"disable-cache-explicit-input",
], comment = "re-executes because the first run was not cached" },
], comment = "cache hit because disableCache is temporarily a no-op" },
{ argv = [
"vt",
"run",
"--last-details",
], comment = "summary names the opt-out as the not-cached reason" },
], comment = "summary reports the replayed cache hit" },
]

[[e2e]]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
# disable_cache_forces_reexecution
# disable_cache_noop_allows_cache_hit

Exercises `disableCache`. The tool asks the runner not to cache this run,
so the next invocation re-executes instead of hitting a prior entry.
Exercises the temporary `disableCache` no-op workaround. The tool asks the
runner not to cache this run, but the client ignores that request, so the next
invocation hits the cache.

## `vt run disable-cache`

first run — tool calls disableCache
first run — tool calls disableCache, currently ignored by the client

```
$ node scripts/disable_cache.mjs
```

## `vt run disable-cache`

cache miss (NotFound) because nothing was cached
cache hit because disableCache is temporarily a no-op

```
$ node scripts/disable_cache.mjs
$ node scripts/disable_cache.mjs ◉ cache hit, replaying

---
vt run: cache hit.
```

## `vt run --last-details`

summary names the opt-out as the not-cached reason
summary reports the replayed cache hit

```

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Vite+ Task Runner • Execution Summary
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 0 cache hits • 1 cache misses
Performance: 0% cache hit rate
Statistics: 1 tasks • 1 cache hits • 0 cache misses
Performance: 100% cache hit rate

Task Details:
────────────────────────────────────────────────
[1] ipc-client-test#disable-cache: $ node scripts/disable_cache.mjs ✓
Not cached: the task opted out of caching
Cache hit - output replayed -
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
# disable_cache_works_with_explicit_inputs
# disable_cache_noop_with_explicit_inputs

Exercises `disableCache` on a cached task with explicit inputs. The runner must still inject IPC even when fspy auto-input inference is disabled, or the tool's cache opt-out becomes a no-op and the second run incorrectly hits.
Exercises the temporary `disableCache` no-op workaround on a cached task with
explicit inputs. The client ignores the opt-out request, so the second run hits
even when fspy auto-input inference is disabled.

## `vt run disable-cache-explicit-input`

first run uses input: [] and asks the runner not to cache
first run uses input: [] and calls disableCache, currently ignored by the client

```
$ node scripts/disable_cache.mjs
```

## `vt run disable-cache-explicit-input`

re-executes because the first run was not cached
cache hit because disableCache is temporarily a no-op

```
$ node scripts/disable_cache.mjs
$ node scripts/disable_cache.mjs ◉ cache hit, replaying

---
vt run: cache hit.
```

## `vt run --last-details`

summary names the opt-out as the not-cached reason
summary reports the replayed cache hit

```

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Vite+ Task Runner • Execution Summary
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 0 cache hits • 1 cache misses
Performance: 0% cache hit rate
Statistics: 1 tasks • 1 cache hits • 0 cache misses
Performance: 100% cache hit rate

Task Details:
────────────────────────────────────────────────
[1] ipc-client-test#disable-cache-explicit-input: $ node scripts/disable_cache.mjs ✓
Not cached: the task opted out of caching
Cache hit - output replayed -
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
[[e2e]]
name = "vite_dev_disables_cache"
name = "vite_dev_disable_cache_noop_allows_cache_hit"
comment = """
`vt run --cache dev` brings up a Vite dev server programmatically on an ephemeral port and closes it immediately. Vite's `_createServer` calls `disableCache()` via `@voidzero-dev/vite-task-client`, so this run is never stored — the next invocation re-executes (cache miss / NotFound).
`vt run --cache dev` brings up a Vite dev server programmatically on an
ephemeral port and closes it immediately. Vite calls `disableCache()` via
`@voidzero-dev/vite-task-client`, but the client temporarily ignores that
request, so the next invocation hits the cache.
"""
ignore = true
steps = [
Expand All @@ -10,11 +13,11 @@ steps = [
"run",
"--cache",
"dev",
], comment = "first run — Vite dev start calls disableCache" },
], comment = "first run — Vite dev calls disableCache, currently ignored by the client" },
{ argv = [
"vt",
"run",
"--cache",
"dev",
], comment = "cache miss (NotFound) because the first run was not stored" },
], comment = "cache hit because disableCache is temporarily a no-op" },
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# vite_dev_disable_cache_noop_allows_cache_hit

`vt run --cache dev` brings up a Vite dev server programmatically on an
ephemeral port and closes it immediately. Vite calls `disableCache()` via
`@voidzero-dev/vite-task-client`, but the client temporarily ignores that
request, so the next invocation hits the cache.

## `vt run --cache dev`

first run — Vite dev calls disableCache, currently ignored by the client

```
$ node dev.mjs
```

## `vt run --cache dev`

cache hit because disableCache is temporarily a no-op

```
$ node dev.mjs ◉ cache hit, replaying

---
vt run: cache hit.
```

This file was deleted.

17 changes: 14 additions & 3 deletions crates/vite_task_client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,24 @@ impl Client {
self.send(&Request::IgnoreOutput(&ns))
}

/// Fire-and-forget — see [`Self::ignore_input`].
/// Temporary no-op.
///
/// `disableCache` currently causes too many false opt-outs because tools
/// call it during configuration, before they know whether they will start
/// an uncached operation such as listening on a port or watching the
/// filesystem.
///
/// # Errors
///
/// Returns an error if the request fails to send.
/// This temporary no-op does not currently return errors.
#[expect(
clippy::missing_const_for_fn,
clippy::unnecessary_wraps,
clippy::unused_self,
reason = "temporary no-op preserves the public API until disableCache semantics are redesigned"
)]
pub fn disable_cache(&self) -> io::Result<()> {
self.send(&Request::DisableCache)
Ok(())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update stale disableCache e2e snapshots

With this new Ok(()) path, public clients no longer send Request::DisableCache, but the include-ignored e2e fixture still asserts that disableCache() forces the next run to miss; running cargo test -p vite_task_bin --test e2e_snapshots -- --include-ignored disable_cache_forces_reexecution now fails because the second run is a cache hit. Please update or remove the affected ipc_client_test/vite_dev_disable_cache snapshots alongside this behavior change so the documented Node-client suite matches the new contract.

Useful? React with 👍 / 👎.

}

/// Requests an env value from the runner. Returns `None` if the runner
Expand Down
29 changes: 28 additions & 1 deletion crates/vite_task_server/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type RawStream = std::fs::File;
use rustc_hash::FxHashMap;
use tokio::runtime::Builder;
use vite_task_client::{Client, GetEnvsQuery};
use vite_task_ipc_shared::Request;
use vite_task_ipc_shared::{GetEnvResponse, Request};
use vite_task_server::{EnvQuery, Error, Recorder, Reports, ServerHandle, serve};

fn env_map(pairs: &[(&str, &str)]) -> FxHashMap<Arc<OsStr>, Arc<OsStr>> {
Expand Down Expand Up @@ -82,6 +82,15 @@ fn send_frame(stream: &mut RawStream, request: &Request<'_>) {
stream.flush().expect("flush");
}

fn recv_get_env_response(stream: &mut RawStream) -> GetEnvResponse {
let mut len_bytes = [0u8; 4];
stream.read_exact(&mut len_bytes).expect("read len");
let len = u32::from_le_bytes(len_bytes) as usize;
let mut buf = vec![0; len];
stream.read_exact(&mut buf).expect("read body");
wincode::deserialize_exact(&buf).expect("deserialize response")
}

#[test]
fn single_client_fire_and_forget() {
#[cfg(unix)]
Expand All @@ -93,6 +102,9 @@ fn single_client_fire_and_forget() {
let client = connect(&envs);
client.ignore_input(OsStr::new(in_path)).unwrap();
client.ignore_output(OsStr::new(out_path)).unwrap();
// Temporary workaround: the client currently ignores disableCache so
// tools cannot opt out at configuration time before they perform the
// operation that actually makes a task uncacheable.
client.disable_cache().unwrap();
flush(&client);
})
Expand All @@ -102,6 +114,21 @@ fn single_client_fire_and_forget() {
let outputs: Vec<_> = reports.ignored_outputs.iter().map(|p| p.as_path().as_os_str()).collect();
assert_eq!(inputs, vec![OsStr::new(in_path)]);
assert_eq!(outputs, vec![OsStr::new(out_path)]);
assert!(!reports.cache_disabled);
}

#[test]
fn raw_disable_cache_request_disables_cache() {
let reports = run_with_server(env_map(&[]), |envs| {
let name = &envs[0].1;
let mut stream = connect_raw(name);
send_frame(&mut stream, &Request::DisableCache);
let flush_name: Box<NativeStr> = OsStr::new("__VP_TEST_FLUSH__").into();
send_frame(&mut stream, &Request::GetEnv { name: &flush_name, tracked: false });
let _ = recv_get_env_response(&mut stream);
})
.expect("driver returned error");

assert!(reports.cache_disabled);
}

Expand Down
Loading