mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-10-22 17:13:20 +00:00
Preserve unchanged-file reflection after revision history pruning
This commit is contained in:
1 parent
72dc88c7ef
commit
f47db4f256
19 files changed
+524
-18
No files matched your search
@@ -102,6 +102,7 @@ export function initialiseServiceModulesCLI(
|
||||
events: services.context.events,
|
||||
API: services.API,
|
||||
database: services.database,
|
||||
path: services.path,
|
||||
appLifecycle: services.appLifecycle,
|
||||
setting: services.setting,
|
||||
remote: services.remote,
|
||||
|
||||
@@ -93,6 +93,7 @@ export function initialiseServiceModulesFSAPI(
|
||||
events: services.context.events,
|
||||
API: services.API,
|
||||
database: services.database,
|
||||
path: services.path,
|
||||
appLifecycle: services.appLifecycle,
|
||||
setting: services.setting,
|
||||
remote: services.remote,
|
||||
|
||||
@@ -37,6 +37,14 @@ export const REVIEW_HARNESS_SCENARIOS = [
|
||||
mode: "automatic",
|
||||
access: "read-only",
|
||||
},
|
||||
{
|
||||
id: "file-content-hash-performance",
|
||||
title: "File content hash performance",
|
||||
description:
|
||||
"Compares SHA-256 and xxHash64 on fixed in-memory Blob inputs of 4 KiB, 64 KiB, 1 MiB, and 50 MiB. Input construction is excluded from hash timings; engine initialisation and warm-up are reported separately. Keep Obsidian in the foreground.",
|
||||
mode: "manual",
|
||||
access: "read-only",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const REVIEW_HARNESS_SCENARIO_IDS = REVIEW_HARNESS_SCENARIOS.map(({ id }) => id);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import { NEW_VAULT_SETTINGS, type SettingsMigrationState } from "@vrtmrz/livesync-commonlib/settings";
|
||||
import type { CompatibilityPause } from "@/common/databaseCompatibility.ts";
|
||||
import {
|
||||
REVIEW_HARNESS_SCENARIOS,
|
||||
REVIEW_HARNESS_SCENARIO_IDS,
|
||||
formatReviewHarnessReport,
|
||||
inspectCompatibilityReview,
|
||||
@@ -76,7 +77,12 @@ describe("Review Harness contract", () => {
|
||||
"compatibility-review",
|
||||
"vault-round-trip",
|
||||
"id-generation-performance",
|
||||
"file-content-hash-performance",
|
||||
]);
|
||||
expect(REVIEW_HARNESS_SCENARIOS.find(({ id }) => id === "file-content-hash-performance")).toMatchObject({
|
||||
mode: "manual",
|
||||
access: "read-only",
|
||||
});
|
||||
});
|
||||
|
||||
it("checks that an existing Vault exposes the seven synchronisation choices as booleans", () => {
|
||||
|
||||
@@ -22,6 +22,7 @@ export interface ReviewHarnessRuntime {
|
||||
openCompatibilityReview(): Promise<void>;
|
||||
runVaultRoundTrip(): Promise<ReviewHarnessScenarioResult>;
|
||||
runIdBenchmark(): Promise<ReviewHarnessScenarioResult>;
|
||||
runFileContentHashBenchmark(): Promise<ReviewHarnessScenarioResult>;
|
||||
readContinuation(): string | null;
|
||||
writeContinuation(value: string): void;
|
||||
deleteContinuation(): void;
|
||||
@@ -162,6 +163,8 @@ export class ReviewHarnessController {
|
||||
result = await this.runtime.runVaultRoundTrip();
|
||||
} else if (id === "id-generation-performance") {
|
||||
result = await this.runtime.runIdBenchmark();
|
||||
} else if (id === "file-content-hash-performance") {
|
||||
result = await this.runtime.runFileContentHashBenchmark();
|
||||
} else {
|
||||
const inspection = this.inspectCompatibilityReview();
|
||||
result =
|
||||
|
||||
@@ -85,6 +85,11 @@ function createRuntime(): ReviewHarnessRuntime & {
|
||||
detail: "ID generation measurements completed.",
|
||||
observations: ["Chunk 256 B: 1000 IDs total=43.00 ms; per ID=0.0430 ms"],
|
||||
})),
|
||||
runFileContentHashBenchmark: vi.fn(async () => ({
|
||||
status: "passed" as const,
|
||||
detail: "File content hash measurements completed.",
|
||||
observations: ["SHA-256, 4 KiB: median=1.00 ms; range=1.00–1.00 ms"],
|
||||
})),
|
||||
readContinuation() {
|
||||
return this.continuation;
|
||||
},
|
||||
@@ -121,6 +126,7 @@ describe("ReviewHarnessController", () => {
|
||||
expect(controller.snapshot().results["settings-lifecycle"].status).toBe("passed");
|
||||
expect(controller.snapshot().results["vault-round-trip"].status).toBe("idle");
|
||||
expect(controller.snapshot().results["compatibility-review"].status).toBe("idle");
|
||||
expect(controller.snapshot().results["file-content-hash-performance"].status).toBe("idle");
|
||||
});
|
||||
|
||||
it("runs the Vault fixture scenario only when it is selected explicitly", async () => {
|
||||
@@ -185,6 +191,42 @@ describe("ReviewHarnessController", () => {
|
||||
expect(runtime.reportError).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("runs file content hash measurements on request without running them automatically", async () => {
|
||||
const runtime = createRuntime();
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
|
||||
await controller.runAutomaticScenarios();
|
||||
expect(runtime.runFileContentHashBenchmark).not.toHaveBeenCalled();
|
||||
|
||||
await controller.runScenario("file-content-hash-performance");
|
||||
|
||||
expect(runtime.runFileContentHashBenchmark).toHaveBeenCalledOnce();
|
||||
expect(controller.snapshot().results["file-content-hash-performance"]).toMatchObject({ status: "passed" });
|
||||
expect(runtime.runVaultRoundTrip).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("includes file content hash measurements in Full review", async () => {
|
||||
const runtime = createRuntime();
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
|
||||
await controller.runAllScenarios();
|
||||
|
||||
expect(runtime.runFileContentHashBenchmark).toHaveBeenCalledOnce();
|
||||
expect(controller.snapshot().results["file-content-hash-performance"].status).toBe("passed");
|
||||
});
|
||||
|
||||
it("excludes an unexpected file content hash error from the copied report", async () => {
|
||||
const runtime = createRuntime();
|
||||
runtime.runFileContentHashBenchmark = vi.fn().mockRejectedValue(new Error("private benchmark content"));
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
|
||||
await controller.runScenario("file-content-hash-performance");
|
||||
|
||||
expect(controller.snapshot().results["file-content-hash-performance"].status).toBe("failed");
|
||||
expect(controller.createReport()).not.toContain("private benchmark content");
|
||||
expect(runtime.reportError).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("does not overlap an ID measurement with another scenario", async () => {
|
||||
const runtime = createRuntime();
|
||||
let finish!: () => void;
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import type { ReviewHarnessScenarioResult } from "./reviewHarnessTypes";
|
||||
|
||||
export const REVIEW_HARNESS_FILE_CONTENT_HASH_INPUTS = [
|
||||
{ label: "4 KiB", bytes: 4 * 1024 },
|
||||
{ label: "64 KiB", bytes: 64 * 1024 },
|
||||
{ label: "1 MiB", bytes: 1024 * 1024 },
|
||||
{ label: "50 MiB", bytes: 50 * 1024 * 1024 },
|
||||
] as const;
|
||||
|
||||
const CONTENT_BLOCK_BYTES = 64 * 1024;
|
||||
const SAMPLE_COUNT = 3;
|
||||
|
||||
export interface FileContentHashBenchmarkOperations {
|
||||
readonly xxHashInitialisationMs: number;
|
||||
createInput(bytes: number): Blob;
|
||||
sha256(content: Blob): Promise<`sha256:${string}`>;
|
||||
xxHash64(content: Blob): Promise<bigint>;
|
||||
}
|
||||
|
||||
type BenchmarkPerformance = Pick<Performance, "now"> & {
|
||||
readonly memory?: { readonly usedJSHeapSize: number };
|
||||
};
|
||||
|
||||
type HashAlgorithm = "SHA-256" | "xxHash64";
|
||||
|
||||
function readHeap(clock: BenchmarkPerformance): number | undefined {
|
||||
try {
|
||||
const bytes = clock.memory?.usedJSHeapSize;
|
||||
return typeof bytes === "number" && Number.isFinite(bytes) && bytes >= 0 ? bytes : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function summary(samples: readonly number[]): string {
|
||||
const sorted = [...samples].sort((a, b) => a - b);
|
||||
const middle = sorted[Math.floor(sorted.length / 2)];
|
||||
return `median=${middle.toFixed(2)} ms; range=${sorted[0].toFixed(2)}–${sorted[sorted.length - 1].toFixed(2)} ms`;
|
||||
}
|
||||
|
||||
function hashKey(hash: `sha256:${string}` | bigint): string {
|
||||
if (typeof hash === "bigint") return hash.toString(16);
|
||||
return hash;
|
||||
}
|
||||
|
||||
export function createFixedFileContentHashInput(bytes: number): Blob {
|
||||
const block = new Uint8Array(CONTENT_BLOCK_BYTES);
|
||||
for (let index = 0; index < block.length; index++) block[index] = (index * 31 + 17) & 0xff;
|
||||
|
||||
const parts: BlobPart[] = [];
|
||||
for (let remaining = bytes; remaining > 0; remaining -= CONTENT_BLOCK_BYTES) {
|
||||
parts.push(block.subarray(0, Math.min(remaining, CONTENT_BLOCK_BYTES)));
|
||||
}
|
||||
return new Blob(parts);
|
||||
}
|
||||
|
||||
export async function runReviewHarnessFileContentHashBenchmark(
|
||||
operations: FileContentHashBenchmarkOperations,
|
||||
clock: BenchmarkPerformance = performance,
|
||||
yieldControl: () => Promise<void> = () => new Promise((resolve) => window.setTimeout(resolve, 0))
|
||||
): Promise<ReviewHarnessScenarioResult> {
|
||||
if (!Number.isFinite(operations.xxHashInitialisationMs) || operations.xxHashInitialisationMs < 0) {
|
||||
throw new Error("The xxHash64 initialisation time is invalid.");
|
||||
}
|
||||
|
||||
const before = readHeap(clock);
|
||||
let highest = before;
|
||||
const sampleHeap = () => {
|
||||
const value = readHeap(clock);
|
||||
if (value !== undefined) highest = Math.max(highest ?? value, value);
|
||||
return value;
|
||||
};
|
||||
|
||||
const inputs: { readonly label: string; readonly bytes: number; readonly content: Blob }[] = [];
|
||||
for (const { label, bytes } of REVIEW_HARNESS_FILE_CONTENT_HASH_INPUTS) {
|
||||
await yieldControl();
|
||||
inputs.push({ label, bytes, content: operations.createInput(bytes) });
|
||||
sampleHeap();
|
||||
}
|
||||
|
||||
const hash = (algorithm: HashAlgorithm, content: Blob) =>
|
||||
algorithm === "SHA-256" ? operations.sha256(content) : operations.xxHash64(content);
|
||||
const warmupTimes = new Map<HashAlgorithm, number>();
|
||||
const signatures = new Map<string, string>();
|
||||
const checkHash = (algorithm: HashAlgorithm, label: string, result: `sha256:${string}` | bigint): void => {
|
||||
const value = hashKey(result);
|
||||
const key = `${algorithm}:${label}`;
|
||||
const previous = signatures.get(key);
|
||||
if (previous !== undefined && previous !== value) {
|
||||
throw new Error(`The ${algorithm} result changed for fixed in-memory content.`);
|
||||
}
|
||||
signatures.set(key, value);
|
||||
};
|
||||
|
||||
for (const algorithm of ["SHA-256", "xxHash64"] as const) {
|
||||
await yieldControl();
|
||||
const started = clock.now();
|
||||
const result = await hash(algorithm, inputs[0].content);
|
||||
const elapsed = clock.now() - started;
|
||||
checkHash(algorithm, inputs[0].label, result);
|
||||
warmupTimes.set(algorithm, elapsed);
|
||||
sampleHeap();
|
||||
}
|
||||
|
||||
const observations = [
|
||||
"Fixed in-memory Blob inputs contain the same deterministic byte sequence at 4 KiB, 64 KiB, 1 MiB, and 50 MiB. Input construction is excluded from timings.",
|
||||
`xxHash64 engine initialisation: ${operations.xxHashInitialisationMs.toFixed(2)} ms; measured separately.`,
|
||||
`Warm-up on 4 KiB, one call per algorithm: SHA-256=${warmupTimes.get("SHA-256")!.toFixed(2)} ms; xxHash64=${warmupTimes.get("xxHash64")!.toFixed(2)} ms; excluded from samples.`,
|
||||
"Each algorithm has three samples per input. SHA-256 uses the Self-hosted LiveSync file content hash helper; xxHash64 timings include Blob.arrayBuffer() and h64Raw. Calls run serially, with alternating algorithm order and a UI yield between calls.",
|
||||
];
|
||||
|
||||
for (const input of inputs) {
|
||||
const samples: Record<HashAlgorithm, number[]> = { "SHA-256": [], xxHash64: [] };
|
||||
for (let sample = 0; sample < SAMPLE_COUNT; sample++) {
|
||||
const order: readonly HashAlgorithm[] =
|
||||
sample % 2 === 0 ? ["SHA-256", "xxHash64"] : ["xxHash64", "SHA-256"];
|
||||
for (const algorithm of order) {
|
||||
await yieldControl();
|
||||
const started = clock.now();
|
||||
const result = await hash(algorithm, input.content);
|
||||
const elapsed = clock.now() - started;
|
||||
checkHash(algorithm, input.label, result);
|
||||
if (!Number.isFinite(elapsed) || elapsed < 0) {
|
||||
throw new Error(`The ${algorithm} measurement returned an invalid duration.`);
|
||||
}
|
||||
samples[algorithm].push(elapsed);
|
||||
sampleHeap();
|
||||
}
|
||||
}
|
||||
for (const algorithm of ["SHA-256", "xxHash64"] as const) {
|
||||
observations.push(`${algorithm}, ${input.label}: ${summary(samples[algorithm])}.`);
|
||||
}
|
||||
}
|
||||
|
||||
const after = sampleHeap();
|
||||
if (highest === undefined) {
|
||||
observations.push("JavaScript heap: unavailable on this device.");
|
||||
} else {
|
||||
const mib = (bytes: number | undefined) =>
|
||||
bytes === undefined ? "unavailable" : `${(bytes / 1048576).toFixed(2)} MiB`;
|
||||
observations.push(
|
||||
`JavaScript heap: before=${mib(before)}; highest sampled=${mib(highest)}; after=${mib(after)}.`
|
||||
);
|
||||
}
|
||||
observations.push(
|
||||
"Heap samples are approximate, may include other Obsidian work, and are affected by garbage collection. They are neither total app RAM nor a true peak."
|
||||
);
|
||||
return { status: "passed", detail: "File content hash measurements completed.", observations };
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
REVIEW_HARNESS_FILE_CONTENT_HASH_INPUTS,
|
||||
createFixedFileContentHashInput,
|
||||
runReviewHarnessFileContentHashBenchmark,
|
||||
type FileContentHashBenchmarkOperations,
|
||||
} from "./reviewHarnessFileContentHashBenchmark";
|
||||
|
||||
function fixture() {
|
||||
let elapsed = 0;
|
||||
let activeCalls = 0;
|
||||
let maximumConcurrentCalls = 0;
|
||||
let yieldCount = 0;
|
||||
const contentSizes = new WeakMap<Blob, number>();
|
||||
const contentBySize = new Map<number, Blob>();
|
||||
const callCounts = new Map<string, number>();
|
||||
const observedInputs = new Map<string, Blob[]>();
|
||||
const operations: FileContentHashBenchmarkOperations = {
|
||||
xxHashInitialisationMs: 12.5,
|
||||
createInput: (bytes) => {
|
||||
elapsed += 10000;
|
||||
const content = new Blob([new Uint8Array(4)]);
|
||||
contentSizes.set(content, bytes);
|
||||
contentBySize.set(bytes, content);
|
||||
return content;
|
||||
},
|
||||
sha256: async (content) => {
|
||||
await measure("SHA-256", content);
|
||||
return `sha256:${contentSizes.get(content)!}`;
|
||||
},
|
||||
xxHash64: async (content) => {
|
||||
await measure("xxHash64", content);
|
||||
return BigInt(contentSizes.get(content)!);
|
||||
},
|
||||
};
|
||||
|
||||
async function measure(algorithm: string, content: Blob): Promise<void> {
|
||||
activeCalls++;
|
||||
maximumConcurrentCalls = Math.max(maximumConcurrentCalls, activeCalls);
|
||||
const bytes = contentSizes.get(content)!;
|
||||
const key = `${algorithm}:${bytes}`;
|
||||
const count = callCounts.get(key) ?? 0;
|
||||
callCounts.set(key, count + 1);
|
||||
observedInputs.set(key, [...(observedInputs.get(key) ?? []), content]);
|
||||
const durations =
|
||||
bytes === 4 * 1024
|
||||
? algorithm === "SHA-256"
|
||||
? [100, 1, 3, 5]
|
||||
: [200, 11, 13, 15]
|
||||
: algorithm === "SHA-256"
|
||||
? [1, 3, 5]
|
||||
: [11, 13, 15];
|
||||
await Promise.resolve();
|
||||
elapsed += durations[count];
|
||||
activeCalls--;
|
||||
}
|
||||
|
||||
return {
|
||||
operations,
|
||||
now: () => elapsed,
|
||||
yieldControl: async () => {
|
||||
yieldCount++;
|
||||
await Promise.resolve();
|
||||
},
|
||||
inputs: () => ({ contentBySize, callCounts, observedInputs, yieldCount, maximumConcurrentCalls }),
|
||||
};
|
||||
}
|
||||
|
||||
describe("Review Harness file content hash measurements", () => {
|
||||
it("measures fixed Blob inputs with separate initialisation, warm-up, and sample ranges", async () => {
|
||||
const f = fixture();
|
||||
const result = await runReviewHarnessFileContentHashBenchmark(f.operations, { now: f.now }, f.yieldControl);
|
||||
const report = result.observations.join("\n");
|
||||
|
||||
expect(result.status).toBe("passed");
|
||||
expect(result.detail).toBe("File content hash measurements completed.");
|
||||
expect([...f.inputs().contentBySize.keys()]).toEqual(
|
||||
REVIEW_HARNESS_FILE_CONTENT_HASH_INPUTS.map(({ bytes }) => bytes)
|
||||
);
|
||||
expect(Object.fromEntries(f.inputs().callCounts)).toEqual({
|
||||
"SHA-256:4096": 4,
|
||||
"xxHash64:4096": 4,
|
||||
...Object.fromEntries(
|
||||
REVIEW_HARNESS_FILE_CONTENT_HASH_INPUTS.slice(1).flatMap(({ bytes }) => [
|
||||
[`SHA-256:${bytes}`, 3],
|
||||
[`xxHash64:${bytes}`, 3],
|
||||
])
|
||||
),
|
||||
});
|
||||
expect(f.inputs().yieldCount).toBe(30);
|
||||
expect(f.inputs().maximumConcurrentCalls).toBe(1);
|
||||
for (const { bytes } of REVIEW_HARNESS_FILE_CONTENT_HASH_INPUTS) {
|
||||
for (const algorithm of ["SHA-256", "xxHash64"]) {
|
||||
expect(f.inputs().observedInputs.get(`${algorithm}:${bytes}`)?.every(
|
||||
(content) => content === f.inputs().contentBySize.get(bytes)
|
||||
)).toBe(true);
|
||||
}
|
||||
}
|
||||
expect(report).toContain("xxHash64 engine initialisation: 12.50 ms; measured separately.");
|
||||
expect(report).toContain(
|
||||
"Warm-up on 4 KiB, one call per algorithm: SHA-256=100.00 ms; xxHash64=200.00 ms; excluded from samples."
|
||||
);
|
||||
expect(report).toContain("SHA-256, 4 KiB: median=3.00 ms; range=1.00–5.00 ms.");
|
||||
expect(report).toContain("xxHash64, 4 KiB: median=13.00 ms; range=11.00–15.00 ms.");
|
||||
expect(report).toContain("Each algorithm has three samples per input.");
|
||||
expect(report).not.toContain("sha256:4096");
|
||||
expect(report).not.toContain("4096n");
|
||||
});
|
||||
|
||||
it("uses the same deterministic byte sequence for short and long inputs", async () => {
|
||||
const shortInput = createFixedFileContentHashInput(4 * 1024);
|
||||
const longInput = createFixedFileContentHashInput(64 * 1024);
|
||||
|
||||
expect(shortInput.size).toBe(4 * 1024);
|
||||
expect(longInput.size).toBe(64 * 1024);
|
||||
expect(new Uint8Array(await longInput.slice(0, shortInput.size).arrayBuffer())).toEqual(
|
||||
new Uint8Array(await shortInput.arrayBuffer())
|
||||
);
|
||||
});
|
||||
|
||||
it("propagates hash failures without turning a partial run into a passed measurement", async () => {
|
||||
const f = fixture();
|
||||
const operations = {
|
||||
...f.operations,
|
||||
sha256: async () => {
|
||||
throw new Error("synthetic hashing failure");
|
||||
},
|
||||
};
|
||||
|
||||
await expect(
|
||||
runReviewHarnessFileContentHashBenchmark(operations, { now: f.now }, f.yieldControl)
|
||||
).rejects.toThrow("synthetic hashing failure");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import { computeFileContentHash } from "@vrtmrz/livesync-commonlib/hashing";
|
||||
import { xxhashNew } from "octagonal-wheels/hash/xxhash.js";
|
||||
import {
|
||||
createFixedFileContentHashInput,
|
||||
type FileContentHashBenchmarkOperations,
|
||||
} from "./reviewHarnessFileContentHashBenchmark";
|
||||
|
||||
export async function createFileContentHashBenchmarkOperations(
|
||||
clock: Pick<Performance, "now"> = performance
|
||||
): Promise<FileContentHashBenchmarkOperations> {
|
||||
const started = clock.now();
|
||||
const xxHash = await xxhashNew();
|
||||
const xxHashInitialisationMs = clock.now() - started;
|
||||
return {
|
||||
xxHashInitialisationMs,
|
||||
createInput: createFixedFileContentHashInput,
|
||||
sha256: (content) => computeFileContentHash(content),
|
||||
xxHash64: async (content) => xxHash.h64Raw(new Uint8Array(await content.arrayBuffer())),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createFileContentHashBenchmarkOperations } from "./reviewHarnessFileContentHashBenchmarkRuntime";
|
||||
|
||||
describe("Review Harness file content hash benchmark adapter", () => {
|
||||
it("uses the packaged SHA-256 helper and xxHash64 engine with fixed Blob content", async () => {
|
||||
const operations = await createFileContentHashBenchmarkOperations();
|
||||
const content = operations.createInput(4 * 1024);
|
||||
|
||||
expect(operations.xxHashInitialisationMs).toBeGreaterThanOrEqual(0);
|
||||
expect(await operations.sha256(content)).toMatch(/^sha256:/u);
|
||||
expect(typeof (await operations.xxHash64(content))).toBe("bigint");
|
||||
expect(await operations.sha256(content)).toBe(await operations.sha256(content));
|
||||
expect(await operations.xxHash64(content)).toBe(await operations.xxHash64(content));
|
||||
});
|
||||
});
|
||||
@@ -115,6 +115,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
||||
events: services.context.events,
|
||||
API: services.API,
|
||||
database: services.database,
|
||||
path: services.path,
|
||||
appLifecycle: services.appLifecycle,
|
||||
setting: services.setting,
|
||||
remote: services.remote,
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
import type { CompatibilityReviewController } from "./compatibilityReview";
|
||||
import { runReviewHarnessIdBenchmark } from "@/features/ReviewHarness/reviewHarnessIdBenchmark";
|
||||
import { createIdBenchmarkOperations } from "@/features/ReviewHarness/reviewHarnessIdBenchmarkRuntime";
|
||||
import { runReviewHarnessFileContentHashBenchmark } from "@/features/ReviewHarness/reviewHarnessFileContentHashBenchmark";
|
||||
import { createFileContentHashBenchmarkOperations } from "@/features/ReviewHarness/reviewHarnessFileContentHashBenchmarkRuntime";
|
||||
|
||||
async function runVaultRoundTrip(plugin: ObsidianLiveSyncPlugin): Promise<ReviewHarnessScenarioResult> {
|
||||
const vault = plugin.app.vault;
|
||||
@@ -62,6 +64,11 @@ export function useReviewHarness(
|
||||
runVaultRoundTrip: () => runVaultRoundTrip(plugin),
|
||||
runIdBenchmark: async () =>
|
||||
runReviewHarnessIdBenchmark(await createIdBenchmarkOperations(), activeWindow.performance),
|
||||
runFileContentHashBenchmark: async () =>
|
||||
runReviewHarnessFileContentHashBenchmark(
|
||||
await createFileContentHashBenchmarkOperations(activeWindow.performance),
|
||||
activeWindow.performance
|
||||
),
|
||||
readContinuation: () => services.setting.getSmallConfig(REVIEW_HARNESS_STATE_KEY),
|
||||
writeContinuation: (value) => services.setting.setSmallConfig(REVIEW_HARNESS_STATE_KEY, value),
|
||||
deleteContinuation: () => services.setting.deleteSmallConfig(REVIEW_HARNESS_STATE_KEY),
|
||||
|
||||
Reference in new issue
Block a user