Skip to content

Commit

Permalink
easysync tests: Split into multiple files
Browse files Browse the repository at this point in the history
  • Loading branch information
webzwo0i authored and rhansen committed Nov 24, 2021
1 parent 617515b commit 0983985
Show file tree
Hide file tree
Showing 9 changed files with 973 additions and 962 deletions.
222 changes: 222 additions & 0 deletions src/tests/frontend/easysync-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
'use strict';

const Changeset = require('../../static/js/Changeset');
const AttributePool = require('../../static/js/AttributePool');

const randInt = (maxValue) => Math.floor(Math.random() * maxValue);

const poolOrArray = (attribs) => {
if (attribs.getAttrib) {
return attribs; // it's already an attrib pool
} else {
// assume it's an array of attrib strings to be split and added
const p = new AttributePool();
attribs.forEach((kv) => {
p.putAttrib(kv.split(','));
});
return p;
}
};
exports.poolOrArray = poolOrArray;

const randomInlineString = (len) => {
const assem = Changeset.stringAssembler();
for (let i = 0; i < len; i++) {
assem.append(String.fromCharCode(randInt(26) + 97));
}
return assem.toString();
};

const randomMultiline = (approxMaxLines, approxMaxCols) => {
const numParts = randInt(approxMaxLines * 2) + 1;
const txt = Changeset.stringAssembler();
txt.append(randInt(2) ? '\n' : '');
for (let i = 0; i < numParts; i++) {
if ((i % 2) === 0) {
if (randInt(10)) {
txt.append(randomInlineString(randInt(approxMaxCols) + 1));
} else {
txt.append('\n');
}
} else {
txt.append('\n');
}
}
return txt.toString();
};
exports.randomMultiline = randomMultiline;

const randomStringOperation = (numCharsLeft) => {
let result;
switch (randInt(9)) {
case 0:
{
// insert char
result = {
insert: randomInlineString(1),
};
break;
}
case 1:
{
// delete char
result = {
remove: 1,
};
break;
}
case 2:
{
// skip char
result = {
skip: 1,
};
break;
}
case 3:
{
// insert small
result = {
insert: randomInlineString(randInt(4) + 1),
};
break;
}
case 4:
{
// delete small
result = {
remove: randInt(4) + 1,
};
break;
}
case 5:
{
// skip small
result = {
skip: randInt(4) + 1,
};
break;
}
case 6:
{
// insert multiline;
result = {
insert: randomMultiline(5, 20),
};
break;
}
case 7:
{
// delete multiline
result = {
remove: Math.round(numCharsLeft * Math.random() * Math.random()),
};
break;
}
case 8:
{
// skip multiline
result = {
skip: Math.round(numCharsLeft * Math.random() * Math.random()),
};
break;
}
case 9:
{
// delete to end
result = {
remove: numCharsLeft,
};
break;
}
case 10:
{
// skip to end
result = {
skip: numCharsLeft,
};
break;
}
}
const maxOrig = numCharsLeft - 1;
if ('remove' in result) {
result.remove = Math.min(result.remove, maxOrig);
} else if ('skip' in result) {
result.skip = Math.min(result.skip, maxOrig);
}
return result;
};

const randomTwoPropAttribs = (opcode) => {
// assumes attrib pool like ['apple,','apple,true','banana,','banana,true']
if (opcode === '-' || randInt(3)) {
return '';
} else if (randInt(3)) { // eslint-disable-line no-dupe-else-if
if (opcode === '+' || randInt(2)) {
return `*${Changeset.numToString(randInt(2) * 2 + 1)}`;
} else {
return `*${Changeset.numToString(randInt(2) * 2)}`;
}
} else if (opcode === '+' || randInt(4) === 0) {
return '*1*3';
} else {
return ['*0*2', '*0*3', '*1*2'][randInt(3)];
}
};

const randomTestChangeset = (origText, withAttribs) => {
const charBank = Changeset.stringAssembler();
let textLeft = origText; // always keep final newline
const outTextAssem = Changeset.stringAssembler();
const opAssem = Changeset.smartOpAssembler();
const oldLen = origText.length;

const nextOp = new Changeset.Op();

const appendMultilineOp = (opcode, txt) => {
nextOp.opcode = opcode;
if (withAttribs) {
nextOp.attribs = randomTwoPropAttribs(opcode);
}
txt.replace(/\n|[^\n]+/g, (t) => {
if (t === '\n') {
nextOp.chars = 1;
nextOp.lines = 1;
opAssem.append(nextOp);
} else {
nextOp.chars = t.length;
nextOp.lines = 0;
opAssem.append(nextOp);
}
return '';
});
};

const doOp = () => {
const o = randomStringOperation(textLeft.length);
if (o.insert) {
const txt = o.insert;
charBank.append(txt);
outTextAssem.append(txt);
appendMultilineOp('+', txt);
} else if (o.skip) {
const txt = textLeft.substring(0, o.skip);
textLeft = textLeft.substring(o.skip);
outTextAssem.append(txt);
appendMultilineOp('=', txt);
} else if (o.remove) {
const txt = textLeft.substring(0, o.remove);
textLeft = textLeft.substring(o.remove);
appendMultilineOp('-', txt);
}
};

while (textLeft.length > 1) doOp();
for (let i = 0; i < 5; i++) doOp(); // do some more (only insertions will happen)
const outText = `${outTextAssem.toString()}\n`;
opAssem.endDocument();
const cs = Changeset.pack(oldLen, outText.length, opAssem.toString(), charBank.toString());
Changeset.checkRep(cs);
return [cs, outText];
};
exports.randomTestChangeset = randomTestChangeset;
63 changes: 63 additions & 0 deletions src/tests/frontend/specs/easysync-assembler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
'use strict';

const Changeset = require('../../../static/js/Changeset');

describe('easysync-assembler', function () {
it('opAssembler', async function () {
const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1';
const assem = Changeset.opAssembler();
for (const op of Changeset.deserializeOps(x)) assem.append(op);
expect(assem.toString()).to.equal(x);
});

it('smartOpAssembler', async function () {
const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1';
const assem = Changeset.smartOpAssembler();
for (const op of Changeset.deserializeOps(x)) assem.append(op);
assem.endDocument();
expect(assem.toString()).to.equal(x);
});

describe('append atext to assembler', function () {
const testAppendATextToAssembler = (testId, atext, correctOps) => {
it(`testAppendATextToAssembler#${testId}`, async function () {
const assem = Changeset.smartOpAssembler();
for (const op of Changeset.opsFromAText(atext)) assem.append(op);
expect(assem.toString()).to.equal(correctOps);
});
};

testAppendATextToAssembler(1, {
text: '\n',
attribs: '|1+1',
}, '');
testAppendATextToAssembler(2, {
text: '\n\n',
attribs: '|2+2',
}, '|1+1');
testAppendATextToAssembler(3, {
text: '\n\n',
attribs: '*x|2+2',
}, '*x|1+1');
testAppendATextToAssembler(4, {
text: '\n\n',
attribs: '*x|1+1|1+1',
}, '*x|1+1');
testAppendATextToAssembler(5, {
text: 'foo\n',
attribs: '|1+4',
}, '+3');
testAppendATextToAssembler(6, {
text: '\nfoo\n',
attribs: '|2+5',
}, '|1+1+3');
testAppendATextToAssembler(7, {
text: '\nfoo\n',
attribs: '*x|2+5',
}, '*x|1+1*x+3');
testAppendATextToAssembler(8, {
text: '\n\n\nfoo\n',
attribs: '|2+2*x|2+5',
}, '|2+2*x|1+1*x+3');
});
});
53 changes: 53 additions & 0 deletions src/tests/frontend/specs/easysync-compose.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use strict';

const Changeset = require('../../../static/js/Changeset');
const AttributePool = require('../../../static/js/AttributePool');
const {randomMultiline, randomTestChangeset} = require('../easysync-helper.js');

describe('easysync-compose', function () {
describe('compose', function () {
const testCompose = (randomSeed) => {
it(`testCompose#${randomSeed}`, async function () {
const p = new AttributePool();

const startText = `${randomMultiline(10, 20)}\n`;

const x1 = randomTestChangeset(startText);
const change1 = x1[0];
const text1 = x1[1];

const x2 = randomTestChangeset(text1);
const change2 = x2[0];
const text2 = x2[1];

const x3 = randomTestChangeset(text2);
const change3 = x3[0];
const text3 = x3[1];

const change12 = Changeset.checkRep(Changeset.compose(change1, change2, p));
const change23 = Changeset.checkRep(Changeset.compose(change2, change3, p));
const change123 = Changeset.checkRep(Changeset.compose(change12, change3, p));
const change123a = Changeset.checkRep(Changeset.compose(change1, change23, p));
expect(change123a).to.equal(change123);

expect(Changeset.applyToText(change12, startText)).to.equal(text2);
expect(Changeset.applyToText(change23, text1)).to.equal(text3);
expect(Changeset.applyToText(change123, startText)).to.equal(text3);
});
};

for (let i = 0; i < 30; i++) testCompose(i);
});

describe('compose attributes', function () {
it('simpleComposeAttributesTest', async function () {
const p = new AttributePool();
p.putAttrib(['bold', '']);
p.putAttrib(['bold', 'true']);
const cs1 = Changeset.checkRep('Z:2>1*1+1*1=1$x');
const cs2 = Changeset.checkRep('Z:3>0*0|1=3$');
const cs12 = Changeset.checkRep(Changeset.compose(cs1, cs2, p));
expect(cs12).to.equal('Z:2>1+1*0|1=2$x');
});
});
});
Loading

0 comments on commit 0983985

Please sign in to comment.