plugins: write the first state save after a reload (#3733)

This commit is contained in:
Mario Mohar authored and GitHub committed 2026-10-09 04:20:19 -04:00
1 parent 39b0d40877
commit f9574ef9ae
2 files changed
+60 -15

No files matched your search

+2 -15
View File
@@ -1108,16 +1108,11 @@ Singleton {
}
const path = getPluginStatePath(pluginId);
try {
const fv = stateSaveFvComp.createObject(root, {
const fv = stateLoadFvComp.createObject(root, {
path: path
});
_stateWriters[pluginId] = fv;
fv.loaded.connect(function () {
fv.setText(content);
});
fv.loadFailed.connect(function () {
fv.setText(content);
});
fv.setText(content);
} catch (e) {
log.warn("Failed to write state for", pluginId, e.message);
}
@@ -1132,14 +1127,6 @@ Singleton {
}
}
Component {
id: stateSaveFvComp
FileView {
blockWrites: true
atomicWrites: true
}
}
function _flushDirtyStates() {
const dirty = _stateDirtyPlugins;
_stateDirtyPlugins = {};
+58
View File
@@ -0,0 +1,58 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import vm from "node:vm";
import test from "node:test";
const source = readFileSync(new URL("../Services/PluginService.qml", import.meta.url), "utf8");
// Mirrors Quickshell's FileView, where `loaded` is a bool property and not a connectable signal.
function fileView(disk, blockLoading) {
return {
createObject(_, props) {
return {
path: props.path,
loaded: !!blockLoading,
text() { return disk.get(this.path) ?? ""; },
setText(content) { disk.set(this.path, content); },
destroy() {}
};
}
};
}
function service() {
const disk = new Map();
const context = vm.createContext({
JSON, Object,
_stateCache: {}, _stateLoaded: {}, _stateWriters: {}, _stateDirtyPlugins: {}, _stateDirCreated: true,
_stateWriteTimer: { restart() {} },
stateLoadFvComp: fileView(disk, true),
stateSaveFvComp: fileView(disk, false),
Paths: { state: "/state", strip: path => path, mkdir() {} },
log: { warn() {} },
pluginStateChanged() {}
});
context.root = context;
for (const match of source.matchAll(/^ function (\w+)\([^\n]*\)(?:: \w+)? \{\n[\s\S]*?^ \}/gm))
vm.runInContext(match[0], context);
return { context, disk };
}
test("the first state save after a plugin reload reaches disk", () => {
const { context, disk } = service();
const path = context.getPluginStatePath("fixture");
for (const existing of [false, true]) {
disk.clear();
context._stateCache = {};
context._stateLoaded = {};
context._stateWriters = {};
if (existing)
disk.set(path, JSON.stringify({ count: 0 }));
context.savePluginState("fixture", "count", 1);
context._flushDirtyStates();
context._cleanupPluginStateWriter("fixture");
context.savePluginState("fixture", "count", 2);
context._flushDirtyStates();
assert.equal(JSON.parse(disk.get(path)).count, 2);
}
});