Skip to content

Commit

Permalink
Fix encoding of large snapshot data
Browse files Browse the repository at this point in the history
* Encode snapshot data asynchronously, fixes #2932
* Async I/O when saving snapshots
  • Loading branch information
novemberborn committed Jan 8, 2022
1 parent 9bc615e commit c384e9a
Show file tree
Hide file tree
Showing 9 changed files with 41 additions and 14 deletions.
4 changes: 2 additions & 2 deletions lib/runner.js
Expand Up @@ -224,8 +224,8 @@ export default class Runner extends Emittery {
return this.snapshots.skipSnapshot(options);
}

saveSnapshotState() {
return {touchedFiles: this.snapshots.save()};
async saveSnapshotState() {
return {touchedFiles: await this.snapshots.save()};
}

onRun(runnable) {
Expand Down
16 changes: 9 additions & 7 deletions lib/snapshot-manager.js
Expand Up @@ -182,8 +182,8 @@ function sortBlocks(blocksByTitle, blockIndices) {
);
}

function encodeSnapshots(snapshotData) {
const encoded = cbor.encodeOne(snapshotData, {
async function encodeSnapshots(snapshotData) {
const encoded = await cbor.encodeAsync(snapshotData, {
omitUndefinedProperties: true,
canonical: true,
});
Expand Down Expand Up @@ -351,7 +351,7 @@ class Manager {
this.recordSerialized({belongsTo, index, ...snapshot});
}

save() {
async save() {
const {dir, relFile, snapFile, snapPath, reportPath} = this;

if (this.updating && this.newBlocksByTitle.size === 0) {
Expand All @@ -371,15 +371,17 @@ class Manager {
),
};

const buffer = encodeSnapshots(snapshots);
const buffer = await encodeSnapshots(snapshots);
const reportBuffer = generateReport(relFile, snapFile, snapshots);

fs.mkdirSync(dir, {recursive: true});
await fs.promises.mkdir(dir, {recursive: true});

const temporaryFiles = [];
const tmpfileCreated = file => temporaryFiles.push(file);
writeFileAtomic.sync(snapPath, buffer, {tmpfileCreated});
writeFileAtomic.sync(reportPath, reportBuffer, {tmpfileCreated});
await Promise.all([
writeFileAtomic(snapPath, buffer, {tmpfileCreated}),
writeFileAtomic(reportPath, reportBuffer, {tmpfileCreated}),
]);
return {
changedFiles: [snapPath, reportPath],
temporaryFiles,
Expand Down
2 changes: 1 addition & 1 deletion lib/worker/base.js
Expand Up @@ -81,7 +81,7 @@ const run = async options => {

runner.on('finish', async () => {
try {
const {touchedFiles} = runner.saveSnapshotState();
const {touchedFiles} = await runner.saveSnapshotState();
if (touchedFiles) {
channel.send({type: 'touched-files', files: touchedFiles});
}
Expand Down
4 changes: 2 additions & 2 deletions test-tap/assert.js
Expand Up @@ -1303,7 +1303,7 @@ test('.notThrowsAsync() fails if passed a bad value', t => {
t.end();
});

test('.snapshot()', t => {
test('.snapshot()', async t => {
// Set to `true` to update the snapshot, then run:
// npx tap test-tap/assert.js
//
Expand Down Expand Up @@ -1411,7 +1411,7 @@ test('.snapshot()', t => {
});
}

manager.save();
await manager.save();
t.end();
});

Expand Down
2 changes: 1 addition & 1 deletion test-tap/try-snapshot.js
Expand Up @@ -84,5 +84,5 @@ test(async t => {
t.equal(result.error.name, 'Error');
});

manager.save();
await manager.save();
});
2 changes: 1 addition & 1 deletion test/snapshot-regenerate-report/test.js
Expand Up @@ -38,7 +38,7 @@ test('snapshot report can be regenerated from .snap file', async t => {

// Regenerate report
snapshots.hasChanges = true; // Force.
snapshots.save();
await snapshots.save();

// Assert that reports match
t.is(await fs.readFile(reportPath, 'utf8'), report);
Expand Down
1 change: 1 addition & 0 deletions test/snapshot-tests/fixtures/large/package.json
@@ -0,0 +1 @@
{}
9 changes: 9 additions & 0 deletions test/snapshot-tests/fixtures/large/test.js
@@ -0,0 +1,9 @@
const {Buffer} = require('buffer');

const test = require(process.env.TEST_AVA_IMPORT_FROM);

for (let i = 0; i < 2; i++) {
test(`large snapshot ${i}`, t => {
t.snapshot(Buffer.alloc(1024 * 16));
});
}
15 changes: 15 additions & 0 deletions test/snapshot-tests/large.js
@@ -0,0 +1,15 @@
import test from '@ava/test';

import {cwd, fixture} from '../helpers/exec.js';
import {withTemporaryFixture} from '../helpers/with-temporary-fixture.js';

// Reproduction for https://github.com/avajs/ava/issues/2932.
test('can encode and decode large snapshots', async t => {
await withTemporaryFixture(cwd('large'), async cwd => {
const env = {
AVA_FORCE_CI: 'not-ci',
};
await fixture(['--update-snapshots'], {cwd, env});
await t.notThrowsAsync(fixture([], {cwd, env}));
});
});

0 comments on commit c384e9a

Please sign in to comment.