mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 06:06:38 +00:00
`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.
66 lines
3.4 KiB
Rust
66 lines
3.4 KiB
Rust
//! `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);
|
||
}
|