Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split easysync tests into multiple files #5300

Merged
merged 6 commits into from
Nov 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/node/utils/Minify.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ const minify = async (req, res) => {
filename = path.join('../node_modules/', library, libraryPath);
}
}
const [, spec] = /^plugins\/ep_etherpad-lite\/(tests\/frontend\/specs\/.*)/.exec(filename) || [];
if (spec != null) filename = `../${spec}`;
const [, testf] = /^plugins\/ep_etherpad-lite\/(tests\/frontend\/.*)/.exec(filename) || [];
if (testf != null) filename = `../${testf}`;

const contentType = mime.lookup(filename);

Expand Down
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