mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Add test_replay for easily replaying data sent to the sandbox purely within python
Summary: Run JS with a value for SANDBOX_BUFFERS_DIR, then run test_replay in python with the same value to replay just the python code. See test_replay.py for more info. Test Plan: Record some data, e.g. `SANDBOX_BUFFERS_DIR=manual npm start` or `SANDBOX_BUFFERS_DIR=server ./test/testrun.sh server`. Then run `SANDBOX_BUFFERS_DIR=server python -m unittest test_replay` from within `core/sandbox/grist` to replay the input from the JS. Sample of the output will look like this: ``` Checking /tmp/sandbox_buffers/server/2021-06-16T15:13:59.958Z True Checking /tmp/sandbox_buffers/server/2021-06-16T15:16:37.170Z True Checking /tmp/sandbox_buffers/server/2021-06-16T15:14:22.378Z True ``` Reviewers: paulfitz, dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D2866
This commit is contained in:
@@ -12,6 +12,7 @@ import {ChildProcess, spawn, SpawnOptions} from 'child_process';
|
||||
import * as path from 'path';
|
||||
import {Stream, Writable} from 'stream';
|
||||
import * as _ from 'lodash';
|
||||
import * as fs from "fs";
|
||||
|
||||
type SandboxMethod = (...args: any[]) => any;
|
||||
|
||||
@@ -33,6 +34,10 @@ type ResolveRejectPair = [(value?: any) => void, (reason?: unknown) => void];
|
||||
// Type for basic message identifiers, available as constants in sandboxUtil.
|
||||
type MsgCode = null | true | false;
|
||||
|
||||
// Optional root folder to store binary data sent to and from the sandbox
|
||||
// See test_replay.py
|
||||
const recordBuffersRoot = process.env.RECORD_SANDBOX_BUFFERS_DIR;
|
||||
|
||||
export class NSandbox implements ISandbox {
|
||||
/**
|
||||
* Helper function to run the nacl sandbox. It takes care of most arguments, similarly to
|
||||
@@ -92,6 +97,9 @@ export class NSandbox implements ISandbox {
|
||||
|
||||
private _throttle: Throttle | undefined;
|
||||
|
||||
// Create a unique subdirectory for each sandbox process so they can be replayed separately
|
||||
private _recordBuffersDir = recordBuffersRoot ? path.resolve(recordBuffersRoot, new Date().toISOString()) : null;
|
||||
|
||||
/*
|
||||
* Callers may listen to events from sandbox.childProc (a ChildProcess), e.g. 'close' and 'error'.
|
||||
* The sandbox listens for 'aboutToExit' event on the process, to properly shut down.
|
||||
@@ -136,6 +144,11 @@ export class NSandbox implements ISandbox {
|
||||
logMeta: this._logMeta,
|
||||
});
|
||||
}
|
||||
|
||||
if (this._recordBuffersDir) {
|
||||
log.rawDebug(`Recording sandbox buffers in ${this._recordBuffersDir}`, this._logMeta);
|
||||
fs.mkdirSync(this._recordBuffersDir, {recursive: true});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,7 +244,11 @@ export class NSandbox implements ISandbox {
|
||||
}
|
||||
this._marshaller.marshal(msgCode);
|
||||
this._marshaller.marshal(data);
|
||||
return this._streamToSandbox.write(this._marshaller.dumpAsBuffer());
|
||||
const buf = this._marshaller.dumpAsBuffer();
|
||||
if (this._recordBuffersDir) {
|
||||
fs.appendFileSync(path.resolve(this._recordBuffersDir, "input"), buf);
|
||||
}
|
||||
return this._streamToSandbox.write(buf);
|
||||
}
|
||||
|
||||
|
||||
@@ -241,6 +258,9 @@ export class NSandbox implements ISandbox {
|
||||
private _onSandboxData(data: any) {
|
||||
this._unmarshaller.parse(data, buf => {
|
||||
const value = marshal.loads(buf, { bufferToString: true });
|
||||
if (this._recordBuffersDir) {
|
||||
fs.appendFileSync(path.resolve(this._recordBuffersDir, "output"), buf);
|
||||
}
|
||||
this._onSandboxMsg(value[0], value[1]);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user