From 8b9f58ff2cd147f893e0d6e96f41fcaef92250c1 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 23 May 2016 15:14:28 +0200 Subject: [PATCH] Fixes #12: Handle differently loop case 1, improve tests and test runner --- gulpfile.js | 10 +- release/main.js | 13 +- release/tests/tests.js | 221 ++++++++++------- src/grammar.ts | 16 +- src/tests/tests.ts | 246 ++++++++++++------- test-cases/first-mate/tests.json | 251 ++++++++++++++++++-- test-cases/suite1/fixtures/testlang12.plist | 35 +++ test-cases/suite1/tests.json | 69 ++++++ 8 files changed, 660 insertions(+), 201 deletions(-) create mode 100644 test-cases/suite1/fixtures/testlang12.plist diff --git a/gulpfile.js b/gulpfile.js index 8938a58..9d8df67 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -80,8 +80,10 @@ gulp.task('watch', ['bundle'], function () { gulp.task('test', function () { var tests = require('./release/tests/tests'); - tests.runDescriptiveTests(path.join(__dirname, './test-cases/first-mate/tests.json')); - tests.runDescriptiveTests(path.join(__dirname, './test-cases/suite1/tests.json')); - - tests.runMatcherTests(path.join(__dirname, './test-cases/matcher/testData.json')); + tests.runTests([ + path.join(__dirname, './test-cases/first-mate/tests.json'), + path.join(__dirname, './test-cases/suite1/tests.json') + ], [ + path.join(__dirname, './test-cases/matcher/testData.json') + ]); }); diff --git a/release/main.js b/release/main.js index ba6c2c6..f596ac9 100644 --- a/release/main.js +++ b/release/main.js @@ -1312,10 +1312,13 @@ function _tokenizeString(grammar, lineText, isFirstLine, linePos, stack, lineTok handleCaptures(grammar, lineText, isFirstLine, stack, lineTokens, poppedRule.endCaptures, captureIndices); lineTokens.produce(stack, captureIndices[0].end); // pop - stack.pop(); + var popped = stack.pop(); if (!hasAdvanced && stackElement.enterPos === linePos) { // Grammar pushed & popped a rule without advancing - console.error('Grammar is in an endless loop - case 1'); + console.error('[1] - Grammar is in an endless loop - Grammar pushed & popped a rule without advancing'); + // See https://github.com/Microsoft/vscode-textmate/issues/12 + // Let's assume this was a mistake by the grammar author and the intent was to continue in this state + stack.push(popped); lineTokens.produce(stack, lineLength); linePos = lineLength; return false; @@ -1343,7 +1346,7 @@ function _tokenizeString(grammar, lineText, isFirstLine, linePos, stack, lineTok } if (!hasAdvanced && stackElement.ruleId === stack[stack.length - 1].ruleId) { // Grammar pushed the same rule without advancing - console.error('Grammar is in an endless loop - case 2'); + console.error('[2] - Grammar is in an endless loop - Grammar pushed the same rule without advancing'); stack.pop(); lineTokens.produce(stack, lineLength); linePos = lineLength; @@ -1361,7 +1364,7 @@ function _tokenizeString(grammar, lineText, isFirstLine, linePos, stack, lineTok } if (!hasAdvanced && stackElement.ruleId === stack[stack.length - 1].ruleId) { // Grammar pushed the same rule without advancing - console.error('Grammar is in an endless loop - case 2'); + console.error('[3] - Grammar is in an endless loop - Grammar pushed the same rule without advancing'); stack.pop(); lineTokens.produce(stack, lineLength); linePos = lineLength; @@ -1376,7 +1379,7 @@ function _tokenizeString(grammar, lineText, isFirstLine, linePos, stack, lineTok stack.pop(); if (!hasAdvanced) { // Grammar is not advancing, nor is it pushing/popping - console.error('Grammar is in an endless loop - case 3'); + console.error('[4] - Grammar is in an endless loop - Grammar is not advancing, nor is it pushing/popping'); if (stack.length > 1) { stack.pop(); } diff --git a/release/tests/tests.js b/release/tests/tests.js index 5ca5071..76bc057 100644 --- a/release/tests/tests.js +++ b/release/tests/tests.js @@ -6,67 +6,136 @@ var fs = require('fs'); var path = require('path'); var main_1 = require('../main'); require('colors'); -var errCnt = 0; -function runDescriptiveTests(testLocation) { - var tests = JSON.parse(fs.readFileSync(testLocation).toString()); - errCnt = 0; - tests.forEach(function (test, index) { - var desc = test.desc; - var noAsserts = (test.feature === 'endless-loop'); - console.log(index + ' - RUNNING ' + desc); - var locator = { - getFilePath: function (scopeName) { return null; }, - getInjections: function (scopeName) { - if (scopeName === test.grammarScopeName) { - return test.grammarInjections; - } - return void 0; +var TestResult; +(function (TestResult) { + TestResult[TestResult["Pending"] = 0] = "Pending"; + TestResult[TestResult["Success"] = 1] = "Success"; + TestResult[TestResult["Failed"] = 2] = "Failed"; +})(TestResult || (TestResult = {})); +var Test = (function () { + function Test(testName, runner) { + this.testName = testName; + this._runner = runner; + this.result = TestResult.Pending; + this.failReason = null; + } + Test.prototype.run = function () { + var ctx = new TestContext(this); + try { + this.result = TestResult.Success; + this._runner(ctx); + } + catch (err) { + this.result = TestResult.Failed; + this.failReason = err; + } + }; + Test.prototype.fail = function (message, actual, expected) { + this.result = TestResult.Failed; + var reason = [ + message + ]; + if (actual) { + 'ACTUAL: ' + reason.push(JSON.stringify(actual, null, '\t')); + } + if (expected) { + 'EXPECTED: ' + reason.push(JSON.stringify(expected, null, '\t')); + } + this.failReason = reason.join('\n'); + }; + return Test; +}()); +var TestContext = (function () { + function TestContext(test) { + this._test = test; + } + TestContext.prototype.fail = function (message, actual, expected) { + this._test.fail(message, actual, expected); + }; + return TestContext; +}()); +var TestManager = (function () { + function TestManager() { + this._tests = []; + } + TestManager.prototype.registerTest = function (testName, runner) { + this._tests.push(new Test(testName, runner)); + }; + TestManager.prototype.runTests = function () { + var len = this._tests.length; + for (var i = 0; i < len; i++) { + var test = this._tests[i]; + var progress = (i + 1) + '/' + len; + console.log(progress.yellow, ': ' + test.testName); + test.run(); + if (test.result === TestResult.Failed) { + console.log('FAILED'.red); + console.log(test.failReason); } - }; - var registry = new main_1.Registry(locator); - var grammar = null; - test.grammars.forEach(function (grammarPath) { - var tmpGrammar = registry.loadGrammarFromPathSync(path.join(path.dirname(testLocation), grammarPath)); - if (test.grammarPath === grammarPath) { - grammar = tmpGrammar; + } + var passed = this._tests.filter(function (t) { return t.result === TestResult.Success; }); + if (passed.length === len) { + console.log((passed.length + '/' + len + ' PASSED.').green); + } + else { + console.log((passed.length + '/' + len + ' PASSED.').red); + } + }; + return TestManager; +}()); +function runTests(tokenizationTestPaths, matcherTestPaths) { + var manager = new TestManager(); + tokenizationTestPaths.forEach(function (path) { + generateTokenizationTests(manager, path); + }); + matcherTestPaths.forEach(function (path) { + generateMatcherTests(manager, path); + }); + manager.runTests(); +} +exports.runTests = runTests; +function generateTokenizationTests(manager, testLocation) { + var tests = JSON.parse(fs.readFileSync(testLocation).toString()); + var suiteName = path.join(path.basename(path.dirname(testLocation)), path.basename(testLocation)); + tests.forEach(function (test, index) { + manager.registerTest(suiteName + ' > ' + test.desc, function (ctx) { + var locator = { + getFilePath: function (scopeName) { return null; }, + getInjections: function (scopeName) { + if (scopeName === test.grammarScopeName) { + return test.grammarInjections; + } + return void 0; + } + }; + var registry = new main_1.Registry(locator); + var grammar = null; + test.grammars.forEach(function (grammarPath) { + var tmpGrammar = registry.loadGrammarFromPathSync(path.join(path.dirname(testLocation), grammarPath)); + if (test.grammarPath === grammarPath) { + grammar = tmpGrammar; + } + }); + if (test.grammarScopeName) { + grammar = registry.grammarForScopeName(test.grammarScopeName); + } + var prevState = null; + if (!grammar) { + ctx.fail('I HAVE NO GRAMMAR FOR TEST'); + return; + } + for (var i = 0; i < test.lines.length; i++) { + prevState = assertTokenization(ctx, grammar, test.lines[i], prevState); } }); - if (test.grammarScopeName) { - grammar = registry.grammarForScopeName(test.grammarScopeName); - } - var prevState = null; - if (!grammar) { - console.error('I HAVE NO GRAMMAR FOR TEST ' + desc); - return; - } - for (var i = 0; i < test.lines.length; i++) { - prevState = assertTokenization(noAsserts, grammar, test.lines[i], prevState, desc); - } }); - if (errCnt === 0) { - var msg = 'Test suite at ' + testLocation + ' finished ok'; - console.log(msg.green); - } - else { - var msg = 'Test suite at ' + testLocation + ' finished with ' + errCnt + ' errors.'; - console.log(msg.red); - } } -exports.runDescriptiveTests = runDescriptiveTests; -function assertTokenization(noAsserts, grammar, testCase, prevState, desc) { +function assertTokenization(ctx, grammar, testCase, prevState) { var r = grammar.tokenizeLine(testCase.line, prevState); - if (!noAsserts) { - assertTokens(r.tokens, testCase.line, testCase.tokens, desc); - } + assertTokens(ctx, r.tokens, testCase.line, testCase.tokens); return r.ruleStack; } -function fail(message, actual, expected) { - errCnt++; - console.error(message.red); - console.log(JSON.stringify(actual, null, '\t')); - console.log(JSON.stringify(expected, null, '\t')); -} -function assertTokens(actual, line, expected, desc) { +function assertTokens(ctx, actual, line, expected) { var actualTokens = actual.map(function (token) { return { value: line.substring(token.startIndex, token.endIndex), @@ -80,32 +149,32 @@ function assertTokens(actual, line, expected, desc) { }); } if (actualTokens.length !== expected.length) { - fail('test: GOT DIFFERENT LENGTHS FOR ' + desc, actualTokens, expected); + ctx.fail('GOT DIFFERENT LENGTHS FOR ', actualTokens, expected); return; } for (var i = 0, len = actualTokens.length; i < len; i++) { - assertToken(actualTokens[i], expected[i], desc); + assertToken(ctx, actualTokens[i], expected[i]); } } -function assertToken(actual, expected, desc) { +function assertToken(ctx, actual, expected) { if (actual.value !== expected.value) { - fail('test: GOT DIFFERENT VALUES FOR ' + desc, actual.value, expected.value); + ctx.fail('test: GOT DIFFERENT VALUES FOR ', actual.value, expected.value); return; } if (actual.scopes.length !== expected.scopes.length) { - fail('test: GOT DIFFERENT scope lengths FOR ' + desc, actual.scopes, expected.scopes); + ctx.fail('test: GOT DIFFERENT scope lengths FOR ', actual.scopes, expected.scopes); return; } for (var i = 0, len = actual.scopes.length; i < len; i++) { if (actual.scopes[i] !== expected.scopes[i]) { - fail('test: GOT DIFFERENT scopes FOR ' + desc, actual.scopes, expected.scopes); + ctx.fail('test: GOT DIFFERENT scopes FOR ', actual.scopes, expected.scopes); return; } } } -function runMatcherTests(testLocation, testNum) { - if (testNum === void 0) { testNum = -1; } +function generateMatcherTests(manager, testLocation) { var tests = JSON.parse(fs.readFileSync(testLocation).toString()); + var suiteName = path.join(path.basename(path.dirname(testLocation)), path.basename(testLocation)); var nameMatcher = function (identifers, stackElements) { var lastIndex = 0; return identifers.every(function (identifier) { @@ -118,29 +187,13 @@ function runMatcherTests(testLocation, testNum) { return false; }); }; - var errCnt = 0; tests.forEach(function (test, index) { - if (testNum !== -1 && testNum !== index) { - return; - } - var matcher = main_1.createMatcher(test.expression, nameMatcher); - var result = matcher(test.input); - if (result === test.result) { - console.log(index + ': passed'); - } - else { - var message = index + ': failed , expected ' + test.result; - console.error(message.red); - errCnt++; - } + manager.registerTest(suiteName + ' > ' + index, function (ctx) { + var matcher = main_1.createMatcher(test.expression, nameMatcher); + var result = matcher(test.input); + if (result !== test.result) { + ctx.fail('matcher expected', result, test.result); + } + }); }); - if (errCnt === 0) { - var msg = 'Test suite at ' + testLocation + ' finished ok'; - console.log(msg.green); - } - else { - var msg = 'Test suite at ' + testLocation + ' finished with ' + errCnt + ' errors.'; - console.log(msg.red); - } } -exports.runMatcherTests = runMatcherTests; diff --git a/src/grammar.ts b/src/grammar.ts index f6497cf..7226628 100644 --- a/src/grammar.ts +++ b/src/grammar.ts @@ -486,13 +486,19 @@ function _tokenizeString(grammar: Grammar, lineText: OnigString, isFirstLine: bo lineTokens.produce(stack, captureIndices[0].end); // pop - stack.pop(); + let popped = stack.pop(); if (!hasAdvanced && stackElement.enterPos === linePos) { // Grammar pushed & popped a rule without advancing - console.error('Grammar is in an endless loop - case 1'); + console.error('[1] - Grammar is in an endless loop - Grammar pushed & popped a rule without advancing'); + + // See https://github.com/Microsoft/vscode-textmate/issues/12 + // Let's assume this was a mistake by the grammar author and the intent was to continue in this state + stack.push(popped); + lineTokens.produce(stack, lineLength); linePos = lineLength; + return false; } } else if (matchedRuleId === -3) { @@ -523,7 +529,7 @@ function _tokenizeString(grammar: Grammar, lineText: OnigString, isFirstLine: bo if (!hasAdvanced && stackElement.ruleId === stack[stack.length - 1].ruleId) { // Grammar pushed the same rule without advancing - console.error('Grammar is in an endless loop - case 2'); + console.error('[2] - Grammar is in an endless loop - Grammar pushed the same rule without advancing'); stack.pop(); lineTokens.produce(stack, lineLength); linePos = lineLength; @@ -543,7 +549,7 @@ function _tokenizeString(grammar: Grammar, lineText: OnigString, isFirstLine: bo if (!hasAdvanced && stackElement.ruleId === stack[stack.length - 1].ruleId) { // Grammar pushed the same rule without advancing - console.error('Grammar is in an endless loop - case 2'); + console.error('[3] - Grammar is in an endless loop - Grammar pushed the same rule without advancing'); stack.pop(); lineTokens.produce(stack, lineLength); linePos = lineLength; @@ -560,7 +566,7 @@ function _tokenizeString(grammar: Grammar, lineText: OnigString, isFirstLine: bo if (!hasAdvanced) { // Grammar is not advancing, nor is it pushing/popping - console.error('Grammar is in an endless loop - case 3'); + console.error('[4] - Grammar is in an endless loop - Grammar is not advancing, nor is it pushing/popping'); if (stack.length > 1) { stack.pop(); } diff --git a/src/tests/tests.ts b/src/tests/tests.ts index f25e78a..3c47ea3 100644 --- a/src/tests/tests.ts +++ b/src/tests/tests.ts @@ -9,61 +9,157 @@ import {Registry, createMatcher, IGrammarLocator} from '../main'; import {IToken, StackElement, IGrammar} from '../grammar'; import 'colors'; -var errCnt = 0; +enum TestResult { + Pending, + Success, + Failed +} -export function runDescriptiveTests(testLocation: string) { - let tests:IRawTest[] = JSON.parse(fs.readFileSync(testLocation).toString()); +class Test { + testName:string; + result:TestResult; + failReason: string; - errCnt = 0; - tests.forEach(function(test, index) { - let desc = test.desc; - let noAsserts = (test.feature === 'endless-loop'); + private _runner: (ctx:TestContext) => void; - console.log(index + ' - RUNNING ' + desc); - let locator : IGrammarLocator = { - getFilePath: (scopeName:string) => null, - getInjections: (scopeName:string) => { - if (scopeName === test.grammarScopeName) { - return test.grammarInjections; - } - return void 0; - } - } - - let registry = new Registry(locator); - let grammar: IGrammar = null; - test.grammars.forEach(function(grammarPath) { - let tmpGrammar = registry.loadGrammarFromPathSync(path.join(path.dirname(testLocation), grammarPath)); - if (test.grammarPath === grammarPath) { - grammar = tmpGrammar; - } - }); - if (test.grammarScopeName) { - grammar = registry.grammarForScopeName(test.grammarScopeName); - } - let prevState: StackElement[] = null; - if (!grammar) { - console.error('I HAVE NO GRAMMAR FOR TEST ' + desc); - return; - } - for (let i = 0; i < test.lines.length; i++) { - prevState = assertTokenization(noAsserts, grammar, test.lines[i], prevState, desc); - } - }); - - if (errCnt === 0) { - var msg = 'Test suite at ' + testLocation + ' finished ok'; - console.log((msg).green); - } else { - var msg = 'Test suite at ' + testLocation + ' finished with ' + errCnt + ' errors.'; - console.log((msg).red); + constructor(testName:string, runner: (ctx:TestContext) => void) { + this.testName = testName; + this._runner = runner; + this.result = TestResult.Pending; + this.failReason = null; } + public run(): void { + let ctx = new TestContext(this); + try { + this.result = TestResult.Success; + this._runner(ctx); + } catch(err) { + this.result = TestResult.Failed; + this.failReason = err; + } + } + + public fail(message:string, actual:T, expected:T): void { + this.result = TestResult.Failed; + let reason = [ + message + ]; + if (actual) { + 'ACTUAL: ' + reason.push(JSON.stringify(actual, null, '\t')) + } + if (expected) { + 'EXPECTED: ' + reason.push(JSON.stringify(expected, null, '\t')) + } + this.failReason = reason.join('\n'); + } +} + +class TestContext { + + private _test: Test; + + constructor(test: Test) { + this._test = test; + } + + public fail(message:string, actual?:T, expected?:T): void { + this._test.fail(message, actual, expected); + } +} + +class TestManager { + + private _tests:Test[]; + + constructor() { + this._tests = []; + } + + registerTest(testName: string, runner:(ctx:TestContext)=>void): void { + this._tests.push(new Test(testName, runner)); + } + + runTests(): void { + + let len = this._tests.length; + + for (let i = 0; i < len; i++) { + let test = this._tests[i]; + let progress = (i + 1) + '/' + len; + console.log((progress).yellow, ': ' + test.testName); + test.run(); + if (test.result === TestResult.Failed) { + console.log(('FAILED').red); + console.log(test.failReason); + } + } + + let passed = this._tests.filter(t => t.result === TestResult.Success); + + if (passed.length === len) { + console.log(((passed.length + '/' + len + ' PASSED.')).green); + } else { + console.log(((passed.length + '/' + len + ' PASSED.')).red); + } + } +} + +export function runTests(tokenizationTestPaths:string[], matcherTestPaths:string[]): void { + let manager = new TestManager(); + + tokenizationTestPaths.forEach((path) => { + generateTokenizationTests(manager, path) + }); + + matcherTestPaths.forEach((path) => { + generateMatcherTests(manager, path); + }); + + manager.runTests(); +} + +function generateTokenizationTests(manager:TestManager, testLocation: string): void { + let tests:IRawTest[] = JSON.parse(fs.readFileSync(testLocation).toString()); + + let suiteName = path.join(path.basename(path.dirname(testLocation)), path.basename(testLocation)); + tests.forEach(function(test, index) { + manager.registerTest(suiteName + ' > ' + test.desc, (ctx) => { + let locator : IGrammarLocator = { + getFilePath: (scopeName:string) => null, + getInjections: (scopeName:string) => { + if (scopeName === test.grammarScopeName) { + return test.grammarInjections; + } + return void 0; + } + } + + let registry = new Registry(locator); + let grammar: IGrammar = null; + test.grammars.forEach(function(grammarPath) { + let tmpGrammar = registry.loadGrammarFromPathSync(path.join(path.dirname(testLocation), grammarPath)); + if (test.grammarPath === grammarPath) { + grammar = tmpGrammar; + } + }); + if (test.grammarScopeName) { + grammar = registry.grammarForScopeName(test.grammarScopeName); + } + let prevState: StackElement[] = null; + if (!grammar) { + ctx.fail('I HAVE NO GRAMMAR FOR TEST'); + return; + } + for (let i = 0; i < test.lines.length; i++) { + prevState = assertTokenization(ctx, grammar, test.lines[i], prevState); + } + }); + }); } interface IRawTest { desc: string; - feature: string; grammars: string[]; grammarPath?: string; grammarScopeName?: string; @@ -81,22 +177,13 @@ interface IRawToken { scopes: string[]; } -function assertTokenization(noAsserts:boolean, grammar:IGrammar, testCase:IRawTestLine, prevState: StackElement[], desc:string): StackElement[] { +function assertTokenization(ctx:TestContext, grammar:IGrammar, testCase:IRawTestLine, prevState: StackElement[]): StackElement[] { let r = grammar.tokenizeLine(testCase.line, prevState); - if (!noAsserts) { - assertTokens(r.tokens, testCase.line, testCase.tokens, desc); - } + assertTokens(ctx, r.tokens, testCase.line, testCase.tokens); return r.ruleStack; } -function fail(message:string, actual:T, expected:T): void { - errCnt++; - console.error((message).red); - console.log(JSON.stringify(actual, null, '\t')); - console.log(JSON.stringify(expected, null, '\t')); -} - -function assertTokens(actual:IToken[], line:string, expected:IRawToken[], desc:string): void { +function assertTokens(ctx:TestContext, actual:IToken[], line:string, expected:IRawToken[]): void { let actualTokens:IRawToken[] = actual.map(function(token) { return { value: line.substring(token.startIndex, token.endIndex), @@ -110,26 +197,26 @@ function assertTokens(actual:IToken[], line:string, expected:IRawToken[], desc:s }); } if (actualTokens.length !== expected.length) { - fail('test: GOT DIFFERENT LENGTHS FOR ' + desc, actualTokens, expected); + ctx.fail('GOT DIFFERENT LENGTHS FOR ', actualTokens, expected); return; } for (let i = 0, len = actualTokens.length; i < len; i++) { - assertToken(actualTokens[i], expected[i], desc); + assertToken(ctx, actualTokens[i], expected[i]); } } -function assertToken(actual:IRawToken, expected:IRawToken, desc:string): void { +function assertToken(ctx:TestContext, actual:IRawToken, expected:IRawToken): void { if (actual.value !== expected.value) { - fail('test: GOT DIFFERENT VALUES FOR ' + desc, actual.value, expected.value); + ctx.fail('test: GOT DIFFERENT VALUES FOR ', actual.value, expected.value); return; } if (actual.scopes.length !== expected.scopes.length) { - fail('test: GOT DIFFERENT scope lengths FOR ' + desc, actual.scopes, expected.scopes); + ctx.fail('test: GOT DIFFERENT scope lengths FOR ', actual.scopes, expected.scopes); return; } for (let i = 0, len = actual.scopes.length; i < len; i++) { if (actual.scopes[i] !== expected.scopes[i]) { - fail('test: GOT DIFFERENT scopes FOR ' + desc, actual.scopes, expected.scopes); + ctx.fail('test: GOT DIFFERENT scopes FOR ', actual.scopes, expected.scopes); return; } } @@ -141,8 +228,9 @@ interface IMatcherTest { result: boolean; } -export function runMatcherTests(testLocation: string, testNum =-1) { +function generateMatcherTests(manager:TestManager, testLocation: string) { let tests:IMatcherTest[] = JSON.parse(fs.readFileSync(testLocation).toString()); + let suiteName = path.join(path.basename(path.dirname(testLocation)), path.basename(testLocation)); var nameMatcher = (identifers: string[], stackElements: string[]) => { var lastIndex = 0; @@ -156,27 +244,13 @@ export function runMatcherTests(testLocation: string, testNum =-1) { return false; }); }; - var errCnt = 0; tests.forEach((test, index) => { - if (testNum !== -1 && testNum !== index) { - return; - } - - var matcher = createMatcher(test.expression, nameMatcher); - var result = matcher(test.input); - if (result === test.result) { - console.log(index + ': passed'); - } else { - var message = index + ': failed , expected ' + test.result; - console.error((message).red); - errCnt++; - } + manager.registerTest(suiteName + ' > ' + index, (ctx) => { + var matcher = createMatcher(test.expression, nameMatcher); + var result = matcher(test.input); + if (result !== test.result) { + ctx.fail('matcher expected', result, test.result); + } + }); }); - if (errCnt === 0) { - var msg = 'Test suite at ' + testLocation + ' finished ok'; - console.log((msg).green); - } else { - var msg = 'Test suite at ' + testLocation + ' finished with ' + errCnt + ' errors.'; - console.log((msg).red); - } } diff --git a/test-cases/first-mate/tests.json b/test-cases/first-mate/tests.json index 3eb8d1f..81fd0c5 100644 --- a/test-cases/first-mate/tests.json +++ b/test-cases/first-mate/tests.json @@ -2680,7 +2680,21 @@ "grammarPath": "fixtures/infinite-loop.json", "lines": [ { - "line": "abc" + "line": "abc", + "tokens": [{ + "value": "a", + "scopes": [ + "source.infinite-loop", + "start" + ] + + }, + { + "value": "bc", + "scopes": [ + "source.infinite-loop" + ] + }] } ], "grammars": [ @@ -2696,8 +2710,7 @@ "fixtures/python-regex.json", "fixtures/infinite-loop.json" ], - "desc": "TEST #38", - "feature": "endless-loop" + "desc": "TEST #38" }, { "grammarScopeName": "source.css.scss", @@ -3361,7 +3374,14 @@ "grammarPath": "fixtures/forever.json", "lines": [ { - "line": "forever and ever" + "line": "forever and ever", + "tokens": [{ + "value": "forever and ever", + "scopes": [ + "source.forever", + "text" + ] + }] } ], "grammars": [ @@ -3377,8 +3397,7 @@ "fixtures/python-regex.json", "fixtures/forever.json" ], - "desc": "TEST #48", - "feature": "endless-loop" + "desc": "TEST #48" }, { "grammarScopeName": "source.ruby", @@ -5246,22 +5265,216 @@ "grammarScopeName": "source.thrift", "lines": [ { - "line": "exception SimpleErr {" + "line": "exception SimpleErr {", + "tokens": [{ + "value": "exception", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "keyword.other.exception.thrift" + ] + }, + { + "value": " ", + "scopes": [ + "source.thrift", + "meta.exception.thrift" + ] + }, + { + "value": "SimpleErr", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "entity.name.type.exception.thrift" + ] + }, + { + "value": " ", + "scopes": [ + "source.thrift", + "meta.exception.thrift" + ] + }, + { + "value": "{", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "punctuation.section.exception.begin.thrift" + ] + }] }, { - "line": " 1: string message" + "line": " 1: string message", + "tokens": [{ + "value": " ", + "scopes": [ + "source.thrift", + "meta.exception.thrift" + ] + }, + { + "value": "1:", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift", + "entity.other.field-id.thrift" + ] + }, + { + "value": " ", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift" + ] + }, + { + "value": "string", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift", + "storage.type.field.thrift" + ] + }, + { + "value": " ", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift" + ] + }, + { + "value": "message", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift", + "variable.other.field-name.thrift" + ] + }] }, { - "line": "" + "line": "", + "tokens": [{ + "value": "", + "scopes": [ + "source.thrift", + "meta.exception.thrift" + ] + }] }, { - "line": "service SimpleService {" + "line": "service SimpleService {", + "tokens": [{ + "value": "service", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift", + "storage.type.field.thrift" + ] + }, + { + "value": " ", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift" + ] + }, + { + "value": "SimpleService", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift", + "variable.other.field-name.thrift" + ] + }, + { + "value": " ", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift" + ] + }, + { + "value": "{", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift" + ] + }] }, { - "line": " void Simple() throws (1: SimpleErr simpleErr)" + "line": " void Simple() throws (1: SimpleErr simpleErr)", + "tokens": [{ + "value": " ", + "scopes": [ + "source.thrift", + "meta.exception.thrift" + ] + }, + { + "value": "void", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift", + "storage.type.field.thrift" + ] + }, + { + "value": " ", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift" + ] + }, + { + "value": "Simple", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift", + "variable.other.field-name.thrift" + ] + }, + { + "value": "(", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift" + ] + }, + { + "value": ") throws (1: SimpleErr simpleErr)", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift" + ] + }] }, { - "line": "}" + "line": "}", + "tokens": [{ + "value": "}", + "scopes": [ + "source.thrift", + "meta.exception.thrift", + "meta.field.thrift" + ] + }] } ], "grammars": [ @@ -5277,14 +5490,19 @@ "fixtures/python-regex.json", "fixtures/thrift.json" ], - "desc": "TEST #73", - "feature": "endless-loop" + "desc": "TEST #73" }, { "grammarScopeName": "source.loops", "lines": [ { - "line": "test" + "line": "test", + "tokens": [{ + "value": "test", + "scopes": [ + "source.loops" + ] + }] } ], "grammars": [ @@ -5300,7 +5518,6 @@ "fixtures/python-regex.json", "fixtures/loops.json" ], - "desc": "TEST #74", - "feature": "endless-loop" + "desc": "TEST #74" } ] \ No newline at end of file diff --git a/test-cases/suite1/fixtures/testlang12.plist b/test-cases/suite1/fixtures/testlang12.plist new file mode 100644 index 0000000..d9190e2 --- /dev/null +++ b/test-cases/suite1/fixtures/testlang12.plist @@ -0,0 +1,35 @@ + + + + + fileTypes + + testlang + + name + testlang + patterns + + + include + #indentedVerbatimOp + + + repository + + indentedVerbatimOp + + begin + ^([ \t]*)(?=(.*?)\|$) + end + ^(?!\1[ \t])(?=[ \t]*\S) + name + string.unquoted.verbatim.youki + + + scopeName + text.testlang + uuid + 159375af-d9b4-448c-a9da-d235eadf3556 + + \ No newline at end of file diff --git a/test-cases/suite1/tests.json b/test-cases/suite1/tests.json index 083d55b..f12c418 100644 --- a/test-cases/suite1/tests.json +++ b/test-cases/suite1/tests.json @@ -161,6 +161,7 @@ "fixtures/aspvbnet.plist" ], "grammarPath": "fixtures/aspvbnet.plist", + "desc": "asp", "lines": [ { "line": "Dim tmpStr As String", @@ -224,6 +225,7 @@ "fixtures/aspvbnet.plist" ], "grammarPath": "fixtures/aspvbnet.plist", + "desc": "asp2", "lines": [ { "line": "Dim tmpStr As String", @@ -848,5 +850,72 @@ ] } ] + }, + { + "grammars": [ + "fixtures/testlang12.plist" + ], + "grammarPath": "fixtures/testlang12.plist", + "desc": "Issue #12", + "lines": [ + { + "line": "[test]|", + "tokens": [{ + "value": "[test]|", + "scopes": [ + "text.testlang", + "string.unquoted.verbatim.youki" + ] + }] + }, + { + "line": "\tverb", + "tokens": [{ + "value": "\tverb", + "scopes": [ + "text.testlang", + "string.unquoted.verbatim.youki" + ] + }] + }, + { + "line": "asd", + "tokens": [{ + "value": "asd", + "scopes": [ + "text.testlang" + ] + }] + }, + { + "line": "asd", + "tokens": [{ + "value": "asd", + "scopes": [ + "text.testlang" + ] + }] + }, + { + "line": "[test]|", + "tokens": [{ + "value": "[test]|", + "scopes": [ + "text.testlang", + "string.unquoted.verbatim.youki" + ] + }] + }, + { + "line": "\tverbatim", + "tokens": [{ + "value": "\tverbatim", + "scopes": [ + "text.testlang", + "string.unquoted.verbatim.youki" + ] + }] + } + ] } ]