2015-03-31 21:21:11 +02:00
|
|
|
/// <reference path="..\..\..\src\harness\harness.ts" />
|
2015-03-31 21:20:30 +02:00
|
|
|
/// <reference path="..\..\..\src\server\editorServices.ts" />
|
|
|
|
|
2015-03-31 22:32:53 +02:00
|
|
|
module ts {
|
|
|
|
function editFlat(position: number, deletedLength: number, newText: string, source: string) {
|
|
|
|
return source.substring(0, position) + newText + source.substring(position + deletedLength, source.length);
|
2015-03-31 21:20:30 +02:00
|
|
|
}
|
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
function lineColToPosition(lineIndex: server.LineIndex, line: number, col: number) {
|
|
|
|
var lineInfo = lineIndex.lineNumberToInfo(line);
|
|
|
|
return (lineInfo.offset + col - 1);
|
|
|
|
}
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
function validateEdit(lineIndex: server.LineIndex, sourceText: string, position: number, deleteLength: number, insertString: string): void {
|
|
|
|
let checkText = editFlat(position, deleteLength, insertString, sourceText);
|
|
|
|
let snapshot = lineIndex.edit(position, deleteLength, insertString);
|
|
|
|
let editedText = snapshot.getText(0, snapshot.getLength());
|
2015-03-31 21:21:11 +02:00
|
|
|
|
|
|
|
assert.equal(editedText, checkText);
|
2015-03-31 23:43:36 +02:00
|
|
|
}
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
describe('VersionCache TS code', () => {
|
|
|
|
var testContent = `/// <reference path="z.ts" />
|
|
|
|
var x = 10;
|
|
|
|
var y = { zebra: 12, giraffe: "ell" };
|
|
|
|
z.a;
|
|
|
|
class Point {
|
|
|
|
x: number;
|
|
|
|
}
|
|
|
|
k=y;
|
|
|
|
var p:Point=new Point();
|
|
|
|
var q:Point=<Point>p;`
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
let {lines, lineMap} = server.LineIndex.linesFromText(testContent);
|
|
|
|
assert.isTrue(lines.length > 0, "Failed to initialize test text. Expected text to have at least one line");
|
2015-03-31 21:21:11 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
let lineIndex = new server.LineIndex();
|
|
|
|
lineIndex.load(lines);
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
function validateEditAtLineCharIndex(line: number, char: number, deleteLength: number, insertString: string): void {
|
|
|
|
let position = lineColToPosition(lineIndex, line, char);
|
|
|
|
validateEdit(lineIndex, testContent, position, deleteLength, insertString);
|
|
|
|
}
|
2015-03-31 21:21:11 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
it('change 9 1 0 1 {"y"}', () => {
|
|
|
|
validateEditAtLineCharIndex(9, 1, 0, "y");
|
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
it('change 9 2 0 1 {"."}', () => {
|
|
|
|
validateEditAtLineCharIndex(9, 2, 0, ".");
|
|
|
|
});
|
2015-03-31 21:21:11 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
it('change 9 3 0 1 {"\\n"}', () => {
|
|
|
|
validateEditAtLineCharIndex(9, 3, 0, "\n");
|
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
it('change 10 1 0 10 {"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n"}', () => {
|
|
|
|
validateEditAtLineCharIndex(10, 1, 0, "\n\n\n\n\n\n\n\n\n\n");
|
|
|
|
});
|
2015-03-31 21:21:11 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
it('change 19 1 1 0', () => {
|
|
|
|
validateEditAtLineCharIndex(19, 1, 1, "");
|
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
it('change 18 1 1 0', () => {
|
|
|
|
validateEditAtLineCharIndex(18, 1, 1, "");
|
|
|
|
});
|
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
describe('VersionCache simple text', () => {
|
2015-03-31 22:45:11 +02:00
|
|
|
let testContent = `in this story:
|
2015-03-31 21:42:40 +02:00
|
|
|
the lazy brown fox
|
|
|
|
jumped over the cow
|
|
|
|
that ate the grass
|
|
|
|
that was purple at the tips
|
|
|
|
and grew 1cm per day`;
|
|
|
|
|
2015-03-31 22:45:11 +02:00
|
|
|
let {lines, lineMap} = server.LineIndex.linesFromText(testContent);
|
|
|
|
assert.isTrue(lines.length > 0, "Failed to initialize test text. Expected text to have at least one line");
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:45:11 +02:00
|
|
|
let lineIndex = new server.LineIndex();
|
2015-03-31 21:20:30 +02:00
|
|
|
lineIndex.load(lines);
|
|
|
|
|
2015-03-31 23:43:36 +02:00
|
|
|
function validateEditAtPosition(position: number, deleteLength: number, insertString: string): void {
|
|
|
|
validateEdit(lineIndex, testContent, position, deleteLength, insertString);
|
2015-03-31 22:40:17 +02:00
|
|
|
}
|
2015-03-31 22:45:11 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Insert at end of file', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(testContent.length, 0, "hmmmm...\r\n");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Unusual line endings merge', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(lines[0].length - 1, lines[1].length, "");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Delete whole line and nothing but line (last line)', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(lineMap[lineMap.length - 2], lines[lines.length - 1].length, "");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Delete whole line and nothing but line (first line)', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(0, lines[0].length, "");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Delete whole line (first line) and insert with no line breaks', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(0, lines[0].length, "moo, moo, moo! ");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Delete whole line (first line) and insert with multiple line breaks', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(0, lines[0].length, "moo, \r\nmoo, \r\nmoo! ");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Delete multiple lines and nothing but lines (first and second lines)', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(0, lines[0].length + lines[1].length, "");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Delete multiple lines and nothing but lines (second and third lines)', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(lines[0].length, lines[1].length + lines[2].length, "");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Insert multiple line breaks', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(21, 1, "cr...\r\ncr...\r\ncr...\r\ncr...\r\ncr...\r\ncr...\r\ncr...\r\ncr...\r\ncr...\r\ncr...\r\ncr...\r\ncr");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:21:11 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Insert multiple line breaks', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(21, 1, "cr...\r\ncr...\r\ncr");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Insert multiple line breaks with leading \\n', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(21, 1, "\ncr...\r\ncr...\r\ncr");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Single line no line breaks deleted or inserted, delete 1 char', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(21, 1, "");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Single line no line breaks deleted or inserted, insert 1 char', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(21, 0, "b");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Single line no line breaks deleted or inserted, delete 1, insert 2 chars', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(21, 1, "cr");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Delete across line break (just the line break)', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(21, 22, "");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Delete across line break', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(21, 32, "");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:21:11 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Delete across multiple line breaks and insert no line breaks', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(21, 42, "");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
2015-03-31 21:21:11 +02:00
|
|
|
|
2015-03-31 22:48:53 +02:00
|
|
|
it('Delete across multiple line breaks and insert text', () => {
|
2015-03-31 23:43:36 +02:00
|
|
|
validateEditAtPosition(21, 42, "slithery ");
|
2015-03-31 22:32:53 +02:00
|
|
|
});
|
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-04-01 02:30:48 +02:00
|
|
|
describe('VersionCache stress test', () => {
|
2015-04-01 05:42:45 +02:00
|
|
|
const iterationCount = 20;
|
2015-04-01 02:30:48 +02:00
|
|
|
//const interationCount = 20000; // uncomment for testing
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-04-01 02:30:48 +02:00
|
|
|
// Use scanner.ts, decent size, does not change frequentlly
|
|
|
|
let testFileName = "src/compiler/scanner.ts";
|
|
|
|
let testContent = Harness.IO.readFile(testFileName);
|
|
|
|
let totalChars = testContent.length;
|
|
|
|
assert.isTrue(totalChars > 0, "Failed to read test file.");
|
|
|
|
|
|
|
|
let {lines, lineMap} = server.LineIndex.linesFromText(testContent);
|
|
|
|
assert.isTrue(lines.length > 0, "Failed to initialize test text. Expected text to have at least one line");
|
|
|
|
|
|
|
|
let lineIndex = new server.LineIndex();
|
2015-03-31 21:20:30 +02:00
|
|
|
lineIndex.load(lines);
|
2015-04-01 02:30:48 +02:00
|
|
|
|
|
|
|
let rsa: number[] = [];
|
|
|
|
let la: number[] = [];
|
|
|
|
let las: number[] = [];
|
|
|
|
let elas: number[] = [];
|
|
|
|
let ersa: number[] = [];
|
|
|
|
let ela: number[] = [];
|
|
|
|
let etotalChars = totalChars;
|
|
|
|
|
|
|
|
for (let j = 0; j < 100000; j++) {
|
2015-03-31 21:20:30 +02:00
|
|
|
rsa[j] = Math.floor(Math.random() * totalChars);
|
|
|
|
la[j] = Math.floor(Math.random() * (totalChars - rsa[j]));
|
|
|
|
if (la[j] > 4) {
|
|
|
|
las[j] = 4;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
las[j] = la[j];
|
|
|
|
}
|
|
|
|
if (j < 4000) {
|
|
|
|
ersa[j] = Math.floor(Math.random() * etotalChars);
|
|
|
|
ela[j] = Math.floor(Math.random() * (etotalChars - ersa[j]));
|
|
|
|
if (ela[j] > 4) {
|
|
|
|
elas[j] = 4;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
elas[j] = ela[j];
|
|
|
|
}
|
|
|
|
etotalChars += (las[j] - elas[j]);
|
|
|
|
}
|
|
|
|
}
|
2015-04-01 02:30:48 +02:00
|
|
|
|
|
|
|
it("Range (average length 1/4 file size)", () => {
|
2015-04-01 05:42:45 +02:00
|
|
|
for (let i = 0; i < iterationCount; i++) {
|
2015-04-01 02:30:48 +02:00
|
|
|
let s2 = lineIndex.getText(rsa[i], la[i]);
|
|
|
|
let s1 = testContent.substring(rsa[i], rsa[i] + la[i]);
|
2015-03-31 21:21:11 +02:00
|
|
|
assert.equal(s1, s2);
|
2015-03-31 21:20:30 +02:00
|
|
|
}
|
2015-04-01 02:30:48 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it("Range (average length 4 chars)", () => {
|
2015-04-01 05:42:45 +02:00
|
|
|
for (let j = 0; j < iterationCount; j++) {
|
2015-04-01 02:30:48 +02:00
|
|
|
let s2 = lineIndex.getText(rsa[j], las[j]);
|
|
|
|
let s1 = testContent.substring(rsa[j], rsa[j] + las[j]);
|
2015-03-31 21:21:11 +02:00
|
|
|
assert.equal(s1, s2);
|
2015-03-31 21:20:30 +02:00
|
|
|
}
|
2015-04-01 02:30:48 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-04-01 02:30:48 +02:00
|
|
|
it("Edit (average length 4)", () => {
|
2015-04-01 05:42:45 +02:00
|
|
|
for (let i = 0; i < iterationCount; i++) {
|
2015-04-01 02:30:48 +02:00
|
|
|
let insertString = testContent.substring(rsa[100000 - i], rsa[100000 - i] + las[100000 - i]);
|
|
|
|
let snapshot = lineIndex.edit(rsa[i], las[i], insertString);
|
|
|
|
let checkText = editFlat(rsa[i], las[i], insertString, testContent);
|
|
|
|
let snapText = snapshot.getText(0, checkText.length);
|
|
|
|
assert.equal(checkText, snapText);
|
2015-03-31 21:20:30 +02:00
|
|
|
}
|
2015-04-01 02:30:48 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-04-01 02:30:48 +02:00
|
|
|
it("Edit ScriptVersionCache ", () => {
|
2015-06-11 01:19:10 +02:00
|
|
|
let svc = server.ScriptVersionCache.fromString(ts.sys, testContent);
|
2015-04-01 02:30:48 +02:00
|
|
|
let checkText = testContent;
|
|
|
|
|
2015-04-01 05:42:45 +02:00
|
|
|
for (let i = 0; i < iterationCount; i++) {
|
2015-04-01 02:30:48 +02:00
|
|
|
let insertString = testContent.substring(rsa[i], rsa[i] + las[i]);
|
|
|
|
svc.edit(ersa[i], elas[i], insertString);
|
|
|
|
checkText = editFlat(ersa[i], elas[i], insertString, checkText);
|
|
|
|
if (0 == (i % 4)) {
|
|
|
|
let snap = svc.getSnapshot();
|
|
|
|
let snapText = snap.getText(0, checkText.length);
|
|
|
|
assert.equal(checkText, snapText);
|
2015-03-31 21:20:30 +02:00
|
|
|
}
|
|
|
|
}
|
2015-04-01 02:30:48 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-04-01 02:30:48 +02:00
|
|
|
it("Edit (average length 1/4th file size)", () => {
|
2015-04-01 05:42:45 +02:00
|
|
|
for (let i = 0; i < iterationCount; i++) {
|
2015-04-01 02:30:48 +02:00
|
|
|
let insertString = testContent.substring(rsa[100000 - i], rsa[100000 - i] + la[100000 - i]);
|
|
|
|
let snapshot = lineIndex.edit(rsa[i], la[i], insertString);
|
|
|
|
let checkText = editFlat(rsa[i], la[i], insertString, testContent);
|
|
|
|
let snapText = snapshot.getText(0, checkText.length);
|
|
|
|
assert.equal(checkText, snapText);
|
2015-03-31 21:20:30 +02:00
|
|
|
}
|
2015-04-01 02:30:48 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-04-01 02:30:48 +02:00
|
|
|
it("Line/offset from pos", () => {
|
2015-04-01 05:42:45 +02:00
|
|
|
for (let i = 0; i < iterationCount; i++) {
|
2015-04-01 02:30:48 +02:00
|
|
|
let lp = lineIndex.charOffsetToLineNumberAndPos(rsa[i]);
|
|
|
|
let lac = ts.computeLineAndCharacterOfPosition(lineMap, rsa[i]);
|
|
|
|
assert.equal(lac.line + 1, lp.line, "Line number mismatch " + (lac.line + 1) + " " + lp.line + " " + i);
|
|
|
|
assert.equal(lac.character, (lp.offset), "Charachter offset mismatch " + lac.character + " " + lp.offset + " " + i);
|
2015-03-31 21:20:30 +02:00
|
|
|
}
|
2015-04-01 02:30:48 +02:00
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
|
2015-04-01 02:30:48 +02:00
|
|
|
it("Start pos from line", () => {
|
2015-04-01 05:42:45 +02:00
|
|
|
for (let i = 0; i < iterationCount; i++) {
|
2015-04-01 02:30:48 +02:00
|
|
|
for (let j = 0, llen = lines.length; j < llen; j++) {
|
|
|
|
let lineInfo = lineIndex.lineNumberToInfo(j + 1);
|
|
|
|
let lineIndexOffset = lineInfo.offset;
|
|
|
|
let lineMapOffset = lineMap[j];
|
2015-03-31 21:21:11 +02:00
|
|
|
assert.equal(lineIndexOffset, lineMapOffset);
|
2015-03-31 21:20:30 +02:00
|
|
|
}
|
|
|
|
}
|
2015-04-01 02:30:48 +02:00
|
|
|
});
|
|
|
|
});
|
2015-03-31 21:20:30 +02:00
|
|
|
}
|