Skip to content

Commit

Permalink
Properties are now stored in insertion-order.
Browse files Browse the repository at this point in the history
  • Loading branch information
iwoplaza committed Jan 25, 2022
1 parent f1372a2 commit 6f61079
Show file tree
Hide file tree
Showing 14 changed files with 156 additions and 108 deletions.
9 changes: 0 additions & 9 deletions src/structure/_mock.test.ts

This file was deleted.

64 changes: 0 additions & 64 deletions src/structure/baseTypes.test.ts

This file was deleted.

10 changes: 4 additions & 6 deletions src/structure/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@ export class ObjectSchema<T extends SchemaProperties, O extends InferedPropertie
}

write(output: ISerialOutput, value: O): void {
// Sorting the keys in ASCII ascending order, so that the order is platform independent.
const keys: string[] = Object.keys(this.properties).sort();
const keys: string[] = Object.keys(this.properties);

for (const key of keys) {
this.properties[key].write(output, value[key]);
}
}

read(input: ISerialInput): O {
// Sorting the keys in ASCII ascending order, so that the order is platform independent.
const keys: (keyof T)[] = Object.keys(this.properties).sort();
const keys: (keyof T)[] = Object.keys(this.properties);
const result = {} as O;

for (const key of keys) {
Expand Down Expand Up @@ -89,7 +87,7 @@ export class GenericObjectSchema<

// Extra sub-type fields
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const extraKeys: string[] = Object.keys(subTypeDescription.properties).sort();
const extraKeys: string[] = Object.keys(subTypeDescription.properties);

for (const key of extraKeys) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
Expand All @@ -114,7 +112,7 @@ export class GenericObjectSchema<
result.type = subTypeKey as keyof S;

if (subTypeDescription !== null) {
const extraKeys = Object.keys(subTypeDescription.properties).sort();
const extraKeys = Object.keys(subTypeDescription.properties);

for (const key of extraKeys) {
const prop = (subTypeDescription.properties)[key];
Expand Down
19 changes: 19 additions & 0 deletions src/test/_mock.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { BufferReader, BufferWriter } from '../io';
import { ISchema } from '../structure/types';
import { Parsed } from '../utilityTypes';

export function makeIO(bufferSize: number) {
const buffer = Buffer.alloc(bufferSize);
return {
output: new BufferWriter(buffer),
input: new BufferReader(buffer),
};
}

export function encodeAndDecode<T extends ISchema<Parsed<T>>>(schema: T, value: Parsed<T>): Parsed<T> {
const buffer = Buffer.alloc(schema.sizeOf(value));

schema.write(new BufferWriter(buffer), value);

return schema.read(new BufferReader(buffer));
}
4 changes: 2 additions & 2 deletions src/structure/array.test.ts → src/test/array.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as chai from 'chai';
import { randIntBetween } from '../test/random';
import { randIntBetween } from './random';
import { makeIO } from './_mock.test';
import { ArraySchema, INT } from '.';
import { ArraySchema, INT } from '../structure';

const expect = chai.expect;

Expand Down
13 changes: 13 additions & 0 deletions src/test/bool.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as chai from 'chai';
import { BOOL} from '../structure';
import { encodeAndDecode } from './_mock.test';

const expect = chai.expect;
describe('BoolSchema', () => {
it('should encode and decode a bool value', () => {
const value = Math.random() < 0.5;
const decoded = encodeAndDecode(BOOL, value);

expect(decoded).to.equal(value);
});
});
14 changes: 14 additions & 0 deletions src/test/byte.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as chai from 'chai';
import { randIntBetween } from './random';
import { BYTE } from '../structure';
import { encodeAndDecode } from './_mock.test';

const expect = chai.expect;
describe('ByteSchema', () => {
it('should encode and decode a byte value', () => {
const value = randIntBetween(0, 256);
const decoded = encodeAndDecode(BYTE, value);

expect(decoded).to.equal(value);
});
});
6 changes: 3 additions & 3 deletions src/structure/chars.test.ts → src/test/chars.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import * as chai from 'chai';
import { randIntBetween } from '../test/random';
import { randIntBetween } from './random';
import { makeIO } from './_mock.test';
import { CharsSchema } from '.';
import { CharsSchema } from '../structure';

const expect = chai.expect;

describe('(read/write)Chars', () => {
describe('CharsSchema', () => {
it('should encode and decode fixed-size char array', () => {
const length = randIntBetween(0, 100);
let value = '';
Expand Down
14 changes: 14 additions & 0 deletions src/test/float.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as chai from 'chai';
import { randBetween } from './random';
import { FLOAT } from '../structure';
import { encodeAndDecode } from './_mock.test';

const expect = chai.expect;
describe('FloatSchema', () => {
it('should encode and decode a float value', () => {
const value = randBetween(-100, 100);
const decoded = encodeAndDecode(FLOAT, value);

expect(decoded).to.closeTo(value, 0.01);
});
});
14 changes: 14 additions & 0 deletions src/test/int.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as chai from 'chai';
import { randIntBetween } from './random';
import { INT } from '../structure';
import { encodeAndDecode } from './_mock.test';

const expect = chai.expect;
describe('IntSchema', () => {
it('should encode and decode an int value', () => {
const value = randIntBetween(-100, 100);
const decoded = encodeAndDecode(INT, value);

expect(decoded).to.equal(value);
});
});
63 changes: 43 additions & 20 deletions src/structure/object.test.ts → src/test/object.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import * as chai from 'chai';
import { makeIO } from './_mock.test';
import { ObjectSchema } from './object';
import { INT, STRING } from './baseTypes';
import { GenericObjectSchema } from './_internal';
import { SubTypeKey } from '.';
import { INT, STRING } from '../structure/baseTypes';
import { generic, genericEnum, object } from '../describe';
import { Parsed } from '../utilityTypes';

const expect = chai.expect;

describe('(read/write)Object', () => {
describe('ObjectSchema', () => {
it('should encode and decode a simple object', () => {
const description = new ObjectSchema({
const description = object({
value: INT,
label: STRING,
});
Expand All @@ -25,13 +24,14 @@ describe('(read/write)Object', () => {
});

it('should encode and decode a generic object', () => {
const genericDescription = new GenericObjectSchema(SubTypeKey.STRING, {
sharedValue: INT,
}, {
'concrete': new ObjectSchema({
extraValue: INT,
}),
});
const genericDescription =
generic({
sharedValue: INT,
}, {
'concrete': object({
extraValue: INT,
}),
});

const value = {
type: 'concrete' as const,
Expand All @@ -47,13 +47,14 @@ describe('(read/write)Object', () => {
});

it('should encode and decode an enum generic object', () => {
const genericDescription = new GenericObjectSchema(SubTypeKey.ENUM, {
sharedValue: INT,
}, {
0: new ObjectSchema({
extraValue: INT,
}),
});
const genericDescription =
genericEnum({
sharedValue: INT,
}, {
0: object({
extraValue: INT,
}),
});

const value = {
type: 0 as const,
Expand All @@ -67,4 +68,26 @@ describe('(read/write)Object', () => {
// Reading with the generic description.
expect(genericDescription.read(input)).to.deep.equal(value);
});

it('preserves insertion-order of properties', () => {
const schema = object({
a: INT,
c: INT,
b: INT,
});

// Purpusefully out-of-order.
const value: Parsed<typeof schema> = {
a: 1,
b: 2,
c: 3,
};

const { output, input } = makeIO(schema.sizeOf(value));
schema.write(output, value);

expect(input.readInt() === 1); // a
expect(input.readInt() === 3); // c
expect(input.readInt() === 2); // b
});
});
6 changes: 3 additions & 3 deletions src/structure/optional.test.ts → src/test/optional.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import * as chai from 'chai';
import { randIntBetween } from '../test/random';
import { OptionalSchema, INT } from '.';
import { randIntBetween } from './random';
import { OptionalSchema, INT } from '../structure';
import { makeIO } from './_mock.test';

const expect = chai.expect;

describe('(read/write)Optiona;', () => {
describe('OptionalSchema', () => {
it('should encode and decode an optional int, with a value', () => {
const innerValue = randIntBetween(-10000, 10000);

Expand Down
2 changes: 1 addition & 1 deletion src/test/random.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export function randBetween(from: number, to: number) {

export function randIntBetween(from: number, to: number) {
return Math.floor(from + Math.random() * (to - from));
}
}
26 changes: 26 additions & 0 deletions src/test/string.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as chai from 'chai';
import { randIntBetween } from './random';
import { STRING, } from '../structure';
import { encodeAndDecode } from './_mock.test';

const expect = chai.expect;
describe('StringSchema', () => {
it('should encode and decode an empty string value', () => {
const decoded = encodeAndDecode(STRING, '');

expect(decoded).to.equal('');
});

it('should encode and decode an alphanumerical string', () => {
const length = randIntBetween(0, 100);
let value = '';
const ranges = [['A', 'Z'], ['a', 'z'], ['0', '9']];
for (let i = 0; i < length; ++i) {
const range = ranges[randIntBetween(0, ranges.length) % ranges.length];
value += String.fromCharCode(randIntBetween(range[0].charCodeAt(0), range[1].charCodeAt(0)));
}

const decoded = encodeAndDecode(STRING, value);
expect(decoded).to.equal(value);
});
});

0 comments on commit 6f61079

Please sign in to comment.