Files
vectorcraft/crates/engine/tests/plugin_clear_cache.rs
T
dotCooCoo 4b665ad13c Plug-ins: clearing every cache is tested in a binary of its own
`plugin.clearCache` without an id empties the cache of every plug-in
installed in the process. In the engine's library tests it could empty
the cache of another storage test running at the same time, and its
`bytesFreed` could count that test's cache. It now runs in
`crates/engine/tests/plugin_clear_cache.rs`, and the library test checks
that clearing one plug-in's cache leaves the other plug-in's cache.

The storage fuzz test starts each case with an empty plug-in store,
deletes the store when the case ends, and installs `fuzz.storage` with
`replace: true`, so the cases that shrink a failure install their
module.

docs/mcp.md says that files `plugin.remove` can't delete stay in the
folder renamed with `.removed` until the plug-in is removed again.
2026-11-08 13:03:31 -08:00

66 lines
3.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! `plugin.clearCache` without an id empties the cache of every installed plug-in and leaves their
//! settings.
//!
//! A test binary of its own: installed plug-ins are process-wide, so clearing every plug-in's cache
//! in the engine's other tests would empty the caches those tests check.
// Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md › Robustness).
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use serde_json::{Value, json};
use vectorcraft_engine::Session;
use vectorcraft_plugins::wat as tpl;
/// Sets "zoom" to "hello" and writes "hel" to the cache entry "tile".
const WRITES: &str = "(if (call $vc_settings_set (i32.const 0x2000) (i32.const 4) (i32.const 0x2100) (i32.const 5)) (then unreachable))
(if (call $vc_cache_write (i32.const 0x2004) (i32.const 4) (i32.const 0x2100) (i32.const 3)) (then unreachable))";
/// Traps unless "zoom" is "hello".
const READS: &str =
"(if (i32.ne (call $vc_settings_get (i32.const 0x2000) (i32.const 4) (i32.const 0x3000) (i32.const 16)) (i32.const 5)) (then unreachable))
(if (i32.ne (i32.load (i32.const 0x3000)) (i32.load (i32.const 0x2100))) (then unreachable))";
fn run(s: &mut Session, cmd: &str, p: Value) -> Value {
s.execute(cmd, &p).unwrap_or_else(|e| panic!("{cmd}: {e}"))
}
/// Installs a filter `id` of API level 2 that lists `settings` and `cache` and whose `vc_run` runs
/// `first`, in place of any module installed with its id (`replace: true`).
fn install_storing(s: &mut Session, id: &str, first: &str) {
let manifest = format!(r#"{{"id":"{id}","name":"Test {id}","kind":"filter","api":2,"permissions":["settings","cache"]}}"#);
let data = format!(r#"{} (data (i32.const 0x2000) "zoomtile") (data (i32.const 0x2100) "hello")"#, tpl::STORAGE_IMPORTS);
let wasm = wat::parse_str(tpl::with_imports(&manifest, &data, first, r#"{"objects":[]}"#)).unwrap();
let data = vectorcraft_format::base64_encode(&wasm);
run(s, "plugin.install", json!({"dataBase64": data, "grant": ["settings", "cache"], "replace": true}));
}
#[test]
fn clearing_every_cache_empties_each_plugins_cache_and_keeps_its_settings() {
let folder = std::env::temp_dir().join(format!("vc-plugin-clear-cache-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&folder);
std::fs::create_dir_all(&folder).unwrap();
let ids = ["test.clearcache.a", "test.clearcache.b"];
let mut s = Session::new();
s.execute("file.new", &json!({"width": 400, "height": 200})).unwrap();
s.attach_plugin_store(folder.clone());
for id in ids {
install_storing(&mut s, id, WRITES);
run(&mut s, "plugin.run", json!({"id": id, "ids": []}));
assert_eq!(run(&mut s, "plugin.info", json!({"id": id}))["cacheBytes"], json!(3));
}
assert_eq!(run(&mut s, "plugin.clearCache", json!({})), json!({"cleared": ids, "bytesFreed": 6}));
for id in ids {
assert_eq!(run(&mut s, "plugin.info", json!({"id": id}))["cacheBytes"], json!(0));
// The settings stay: a module that reads them runs.
install_storing(&mut s, id, READS);
run(&mut s, "plugin.run", json!({"id": id, "ids": []}));
}
// Nothing is left to clear.
assert_eq!(run(&mut s, "plugin.clearCache", json!({})), json!({"cleared": ids, "bytesFreed": 0}));
for id in ids {
run(&mut s, "plugin.remove", json!({"id": id}));
}
let _ = std::fs::remove_dir_all(&folder);
}