Skip to content

Commit

Permalink
improve test coverage for generated code
Browse files Browse the repository at this point in the history
  • Loading branch information
mourner committed Jul 4, 2024
1 parent 85561da commit 24b6d10
Show file tree
Hide file tree
Showing 19 changed files with 358 additions and 45 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const buffer = pbf.finish();
Alternatively, you can compile a protobuf schema file directly in the code:

```js
import compile from 'pbf/compile';
import {compile} from 'pbf/compile';
import schema from 'protocol-buffers-schema';

const proto = schema.parse(fs.readFileSync('example.proto'));
Expand Down
4 changes: 2 additions & 2 deletions bench/bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import {fileURLToPath} from 'node:url';
import protocolBuffers from 'protocol-buffers';
import protobufjs from 'protobufjs';

import {readTile, writeTile} from './vector_tile.js';
import {readTile, writeTile} from '../test/fixtures/vector_tile.js';
import Pbf from '../index.js';

var data = fs.readFileSync(new URL('../test/fixtures/12665.vector.pbf', import.meta.url)),
suite = new Benchmark.Suite(),
vtProtoUrl = new URL('vector_tile.proto', import.meta.url),
vtProtoUrl = new URL('../test/fixtures/vector_tile.proto', import.meta.url),
ProtocolBuffersTile = protocolBuffers(fs.readFileSync(vtProtoUrl)).Tile,
ProtobufjsTile = protobufjs.loadSync(fileURLToPath(vtProtoUrl)).lookup('vector_tile.Tile');

Expand Down
8 changes: 4 additions & 4 deletions compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function compile(proto) {

export function compileRaw(proto, options = {}) {
const context = buildDefaults(buildContext(proto, null), proto.syntax);
return `// code generated by pbf v${version}\n${writeContext(context, options)}`;
return `${options.dev ? '' : `// code generated by pbf v${version}\n`}${writeContext(context, options)}`;
}

function writeContext(ctx, options) {
Expand All @@ -32,9 +32,9 @@ function writeMessage(ctx, options) {
const readName = `read${ctx._name}`;
code +=
`${writeFunctionExport(options, readName)}function ${readName}(pbf, end) {
return pbf.readFields(${readName}Tag, ${compileDest(ctx)}, end);
return pbf.readFields(${readName}Field, ${compileDest(ctx)}, end);
}
function ${readName}Tag(tag, obj, pbf) {
function ${readName}Field(tag, obj, pbf) {
`;
for (let i = 0; i < fields.length; i++) {
const field = fields[i];
Expand Down Expand Up @@ -138,7 +138,7 @@ function fieldShouldUseStringAsNumber(field) {
function compileFieldRead(ctx, field) {
const type = getType(ctx, field);
if (type) {
if (type._proto.fields) return `read${type._name}(pbf, pbf.readEnd())`;
if (type._proto.fields) return `read${type._name}(pbf, pbf.readVarint() + pbf.pos)`;
if (!isEnum(type)) throw new Error(`Unexpected type: ${type._name}`);
}

Expand Down
3 changes: 2 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ export default [
}
},
{
files: ['bench/*.js'],
files: ['test/fixtures/*.js'],
rules: {
camelcase: 0,
'@stylistic/js/quotes': 0,
'@stylistic/js/semi': 0,
'@stylistic/js/brace-style': 0,
'no-unused-vars': 0
}
}
Expand Down
13 changes: 5 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default class Pbf {
}

readMessage(readField, result) {
return this.readFields(readField, result, this.readEnd());
return this.readFields(readField, result, this.readVarint() + this.pos);
}

readFixed32() {
Expand Down Expand Up @@ -111,7 +111,7 @@ export default class Pbf {
}

readString() {
const end = this.readEnd();
const end = this.readVarint() + this.pos;
const pos = this.pos;
this.pos = end;

Expand All @@ -124,7 +124,7 @@ export default class Pbf {
}

readBytes() {
const end = this.readEnd(),
const end = this.readVarint() + this.pos,
buffer = this.buf.subarray(this.pos, end);
this.pos = end;
return buffer;
Expand Down Expand Up @@ -177,17 +177,14 @@ export default class Pbf {
while (this.pos < end) arr.push(this.readSFixed64());
return arr;
}
readEnd() {
return this.readVarint() + this.pos;
}
readPackedEnd() {
return this.type === PBF_BYTES ? this.readEnd() : this.pos + 1;
return this.type === PBF_BYTES ? this.readVarint() + this.pos : this.pos + 1;
}

skip(val) {
const type = val & 0x7;
if (type === PBF_VARINT) while (this.buf[this.pos++] > 0x7f) {}
else if (type === PBF_BYTES) this.pos = this.readEnd();
else if (type === PBF_BYTES) this.pos = this.readVarint() + this.pos;
else if (type === PBF_FIXED32) this.pos += 4;
else if (type === PBF_FIXED64) this.pos += 8;
else throw new Error(`Unimplemented type: ${type}`);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"scripts": {
"bench": "node bench/bench.js",
"pretest": "eslint *.js compile.js test/*.js bench/vector_tile.js bin/pbf",
"pretest": "eslint *.js compile.js test/*.js test/fixtures/*.js bin/pbf",
"test": "node --test",
"cov": "node --test --experimental-test-covetage",
"build": "rollup -c",
Expand Down
48 changes: 32 additions & 16 deletions test/compile.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,26 @@ import assert from 'node:assert/strict';
import {sync as resolve} from 'resolve-protobuf-schema';

import Pbf from '../index.js';
import {compile} from '../compile.js';
import {compile, compileRaw} from '../compile.js';

test('compiles all proto files to proper js', () => {
const files = fs.readdirSync(new URL('fixtures', import.meta.url));

for (const path of files) {
if (!path.endsWith('.proto')) continue;
const proto = resolve(new URL(`fixtures/${path}`, import.meta.url));
const js = compileRaw(proto, {dev: true});

// uncomment to update the fixtures
// fs.writeFileSync(new URL(`fixtures/${path}`.replace('.proto', '.js'), import.meta.url), js);

const expectedJS = fs.readFileSync(new URL(`fixtures/${path}`.replace('.proto', '.js'), import.meta.url), 'utf8');
assert.equal(js, expectedJS);
}
});

test('compiles vector tile proto', () => {
const proto = resolve(new URL('../bench/vector_tile.proto', import.meta.url));
const proto = resolve(new URL('fixtures/vector_tile.proto', import.meta.url));
const tileBuf = fs.readFileSync(new URL('fixtures/12665.vector.pbf', import.meta.url));
const {readTile, writeTile} = compile(proto);

Expand All @@ -21,12 +37,12 @@ test('compiles vector tile proto', () => {
});

test('compiles proto with embedded type reference', () => {
const proto = resolve(new URL('./fixtures/embedded_type.proto', import.meta.url));
const proto = resolve(new URL('fixtures/embedded_type.proto', import.meta.url));
compile(proto);
});

test('compiles packed proto', () => {
const proto = resolve(new URL('./fixtures/packed.proto', import.meta.url));
const proto = resolve(new URL('fixtures/packed.proto', import.meta.url));
const {writeNotPacked, readFalsePacked} = compile(proto);

const original = {
Expand All @@ -43,7 +59,7 @@ test('compiles packed proto', () => {
});

test('reads packed with unpacked field', () => {
const proto = resolve(new URL('./fixtures/packed.proto', import.meta.url));
const proto = resolve(new URL('fixtures/packed.proto', import.meta.url));
const {writePacked, readFalsePacked} = compile(proto);

const original = {
Expand All @@ -60,7 +76,7 @@ test('reads packed with unpacked field', () => {
});

test('compiles packed proto3', () => {
const proto = resolve(new URL('./fixtures/packed_proto3.proto', import.meta.url));
const proto = resolve(new URL('fixtures/packed_proto3.proto', import.meta.url));
const {readNotPacked, writeNotPacked, writeFalsePacked} = compile(proto);

const original = {
Expand All @@ -82,7 +98,7 @@ test('compiles packed proto3', () => {
});

test('compiles packed with multi-byte tags', () => {
const proto = resolve(new URL('./fixtures/packed_proto3.proto', import.meta.url));
const proto = resolve(new URL('fixtures/packed_proto3.proto', import.meta.url));
const {readPacked, writePacked} = compile(proto);

const original = {
Expand All @@ -98,7 +114,7 @@ test('compiles packed with multi-byte tags', () => {
});

test('compiles defaults', () => {
const proto = resolve(new URL('./fixtures/defaults.proto', import.meta.url));
const proto = resolve(new URL('fixtures/defaults.proto', import.meta.url));
const {readEnvelope, writeEnvelope} = compile(proto);
const pbf = new Pbf();

Expand All @@ -118,7 +134,7 @@ test('compiles defaults', () => {
});

test('compiles proto3 ignoring defaults', () => {
const proto = resolve(new URL('./fixtures/defaults_proto3.proto', import.meta.url));
const proto = resolve(new URL('fixtures/defaults_proto3.proto', import.meta.url));
const {readEnvelope, writeEnvelope} = compile(proto);
const pbf = new Pbf();

Expand All @@ -137,7 +153,7 @@ test('compiles proto3 ignoring defaults', () => {
});

test('compiles maps', () => {
const proto = resolve(new URL('./fixtures/map.proto', import.meta.url));
const proto = resolve(new URL('fixtures/map.proto', import.meta.url));
const {readEnvelope, writeEnvelope} = compile(proto);

const original = {
Expand All @@ -161,7 +177,7 @@ test('compiles maps', () => {
});

test('does not write undefined or null values', () => {
const proto = resolve(new URL('./fixtures/embedded_type.proto', import.meta.url));
const proto = resolve(new URL('fixtures/embedded_type.proto', import.meta.url));
const {writeEmbeddedType} = compile(proto);
const pbf = new Pbf();

Expand All @@ -177,7 +193,7 @@ test('does not write undefined or null values', () => {
});

test('handles all implicit default values', () => {
const proto = resolve(new URL('./fixtures/defaults_implicit.proto', import.meta.url));
const proto = resolve(new URL('fixtures/defaults_implicit.proto', import.meta.url));
const {readEnvelope, writeEnvelope} = compile(proto);
const pbf = new Pbf();

Expand All @@ -200,7 +216,7 @@ test('handles all implicit default values', () => {
});

test('sets oneof field name', () => {
const proto = resolve(new URL('./fixtures/oneof.proto', import.meta.url));
const proto = resolve(new URL('fixtures/oneof.proto', import.meta.url));
const {readEnvelope, writeEnvelope} = compile(proto);
let pbf = new Pbf();

Expand All @@ -221,7 +237,7 @@ test('sets oneof field name', () => {
});

test('handles jstype=JS_STRING', () => {
const proto = resolve(new URL('./fixtures/type_string.proto', import.meta.url));
const proto = resolve(new URL('fixtures/type_string.proto', import.meta.url));
const {readTypeString, writeTypeString, readTypeNotString} = compile(proto);
const pbf = new Pbf();

Expand Down Expand Up @@ -250,7 +266,7 @@ test('handles jstype=JS_STRING', () => {
});

test('handles negative varint', () => {
const proto = resolve(new URL('./fixtures/varint.proto', import.meta.url));
const proto = resolve(new URL('fixtures/varint.proto', import.meta.url));
const {readEnvelope, writeEnvelope} = compile(proto);
const pbf = new Pbf();

Expand All @@ -267,7 +283,7 @@ test('handles negative varint', () => {
});

test('handles unsigned varint', () => {
const proto = resolve(new URL('./fixtures/varint.proto', import.meta.url));
const proto = resolve(new URL('fixtures/varint.proto', import.meta.url));
const {readEnvelope, writeEnvelope} = compile(proto);
const pbf = new Pbf();

Expand Down
23 changes: 23 additions & 0 deletions test/fixtures/defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

export const MessageType = {
"UNKNOWN": 0,
"GREETING": 1
};

export function readEnvelope(pbf, end) {
return pbf.readFields(readEnvelopeField, {type: 1, name: "test", flag: true, weight: 1.5, id: 1}, end);
}
function readEnvelopeField(tag, obj, pbf) {
if (tag === 1) obj.type = pbf.readVarint();
else if (tag === 2) obj.name = pbf.readString();
else if (tag === 3) obj.flag = pbf.readBoolean();
else if (tag === 4) obj.weight = pbf.readFloat();
else if (tag === 5) obj.id = pbf.readVarint(true);
}
export function writeEnvelope(obj, pbf) {
if (obj.type != null && obj.type !== 1) pbf.writeVarintField(1, obj.type);
if (obj.name != null && obj.name !== "test") pbf.writeStringField(2, obj.name);
if (obj.flag != null && obj.flag !== true) pbf.writeBooleanField(3, obj.flag);
if (obj.weight != null && obj.weight !== 1.5) pbf.writeFloatField(4, obj.weight);
if (obj.id != null && obj.id !== 1) pbf.writeVarintField(5, obj.id);
}
41 changes: 41 additions & 0 deletions test/fixtures/defaults_implicit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

export const MessageType = {
"UNKNOWN": 0,
"GREETING": 1
};

export function readCustomType(pbf, end) {
return pbf.readFields(readCustomTypeField, {}, end);
}
function readCustomTypeField(tag, obj, pbf) {
}
export function writeCustomType(obj, pbf) {
}

export function readEnvelope(pbf, end) {
return pbf.readFields(readEnvelopeField, {type: 0, name: "", flag: false, weight: 0, id: 0, tags: [], numbers: [], bytes: undefined, custom: undefined, types: []}, end);
}
function readEnvelopeField(tag, obj, pbf) {
if (tag === 1) obj.type = pbf.readVarint();
else if (tag === 2) obj.name = pbf.readString();
else if (tag === 3) obj.flag = pbf.readBoolean();
else if (tag === 4) obj.weight = pbf.readFloat();
else if (tag === 5) obj.id = pbf.readVarint(true);
else if (tag === 6) obj.tags.push(pbf.readString());
else if (tag === 7) pbf.readPackedVarint(obj.numbers, true);
else if (tag === 8) obj.bytes = pbf.readBytes();
else if (tag === 9) obj.custom = readCustomType(pbf, pbf.readVarint() + pbf.pos);
else if (tag === 10) pbf.readPackedVarint(obj.types);
}
export function writeEnvelope(obj, pbf) {
if (obj.type) pbf.writeVarintField(1, obj.type);
if (obj.name) pbf.writeStringField(2, obj.name);
if (obj.flag) pbf.writeBooleanField(3, obj.flag);
if (obj.weight) pbf.writeFloatField(4, obj.weight);
if (obj.id) pbf.writeVarintField(5, obj.id);
if (obj.tags) for (const item of obj.tags) pbf.writeStringField(6, item);
if (obj.numbers) for (const item of obj.numbers) pbf.writeVarintField(7, item);
if (obj.bytes != null) pbf.writeBytesField(8, obj.bytes);
if (obj.custom) pbf.writeMessage(9, writeCustomType, obj.custom);
if (obj.types) for (const item of obj.types) pbf.writeVarintField(10, item);
}
23 changes: 23 additions & 0 deletions test/fixtures/defaults_proto3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

export const MessageType = {
"UNKNOWN": 0,
"GREETING": 1
};

export function readEnvelope(pbf, end) {
return pbf.readFields(readEnvelopeField, {type: 0, name: "", flag: false, weight: 0, id: 0}, end);
}
function readEnvelopeField(tag, obj, pbf) {
if (tag === 1) obj.type = pbf.readVarint();
else if (tag === 2) obj.name = pbf.readString();
else if (tag === 3) obj.flag = pbf.readBoolean();
else if (tag === 4) obj.weight = pbf.readFloat();
else if (tag === 5) obj.id = pbf.readVarint(true);
}
export function writeEnvelope(obj, pbf) {
if (obj.type) pbf.writeVarintField(1, obj.type);
if (obj.name) pbf.writeStringField(2, obj.name);
if (obj.flag) pbf.writeBooleanField(3, obj.flag);
if (obj.weight) pbf.writeFloatField(4, obj.weight);
if (obj.id) pbf.writeVarintField(5, obj.id);
}
Loading

0 comments on commit 24b6d10

Please sign in to comment.