Skip to content

Commit 6e6b131

Browse files
committed
Initial commit
0 parents  commit 6e6b131

File tree

6 files changed

+312
-0
lines changed

6 files changed

+312
-0
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

Diff for: LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) 2015 MagicStack Inc. http://magic.io
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Unit testing framework for TextMate/Sublime/Atom syntaxes.

Diff for: bin/syntaxtest.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env node
2+
3+
'use strict';
4+
5+
6+
var syntaxtest = require('../index'),
7+
argparse = require('argparse'),
8+
packageInfo = require('../package.json'),
9+
_ = require('underscore');
10+
11+
12+
var cli = new argparse.ArgumentParser({
13+
prog: packageInfo.name,
14+
version: packageInfo.version,
15+
addHelp: true
16+
});
17+
18+
19+
cli.addArgument([ '--tests' ], {
20+
help: 'Test files, ex: "--tests test/**/*.test"',
21+
nargs: '*',
22+
action: 'append',
23+
required: true
24+
});
25+
26+
27+
cli.addArgument([ '--no-color' ], {
28+
help: "Don't use colored output",
29+
action: 'storeTrue',
30+
default: false
31+
});
32+
33+
34+
cli.addArgument([ '--syntax' ], {
35+
help: 'Syntax file in YAML format, ex: "--syntax FooLang.YAML-tmLanguage"',
36+
required: true
37+
});
38+
39+
40+
function main() {
41+
var options = cli.parseArgs();
42+
syntaxtest(
43+
_.chain(options.tests).flatten().uniq().sort().value(),
44+
options.syntax,
45+
{
46+
no_color: options.no_color
47+
});
48+
}
49+
50+
51+
main();

Diff for: index.js

+214
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
'use strict';
2+
3+
4+
var chalk = require("chalk"),
5+
fs = require("fs"),
6+
yaml = require("js-yaml"),
7+
mate = require("first-mate"),
8+
jsdiff = require("diff"),
9+
temp = require("temp").track(),
10+
_ = require('underscore');
11+
12+
13+
function compileGrammar(grammarFile) {
14+
var tmp = temp.openSync();
15+
16+
try {
17+
var yamlSource = fs.readFileSync(grammarFile, 'utf8'),
18+
yamlSchema = yaml.safeLoad(yamlSource);
19+
20+
fs.writeSync(tmp.fd, JSON.stringify(yamlSchema));
21+
fs.closeSync(tmp.fd);
22+
23+
var registry = new mate.GrammarRegistry,
24+
grammar = registry.loadGrammarSync(tmp.path);
25+
}
26+
finally {
27+
temp.cleanupSync();
28+
}
29+
30+
return grammar;
31+
}
32+
33+
34+
function testFile(file, grammar, options) {
35+
function padRight(str, pad) {
36+
if (str.length < pad) {
37+
return str + (new Array(pad - str.length)).join(' ');
38+
}
39+
40+
return str;
41+
}
42+
43+
function stripnl(str){
44+
return str.replace(/^\n+/, '').replace(/\n+$/, '');
45+
}
46+
47+
function rpartition(str, separator) {
48+
if (!separator) {
49+
throw new Error('empty separator')
50+
}
51+
52+
var seplen = separator.length,
53+
lastpos = str.lastIndexOf(separator);
54+
55+
if (lastpos != -1) {
56+
return [str.substr(0, lastpos),
57+
separator,
58+
str.substr(lastpos + seplen)];
59+
}
60+
else {
61+
return ['', '', str];
62+
}
63+
}
64+
65+
function tokenize(lines) {
66+
var lines = grammar.tokenizeLines(stripnl(lines)),
67+
result = [];
68+
69+
result = []
70+
for (var i = 0; i < lines.length; i++) {
71+
var line = lines[i];
72+
73+
for (var j = 0; j < line.length; j++) {
74+
var value = line[j].value,
75+
scopes = line[j].scopes;
76+
77+
result.push(
78+
[
79+
padRight(value, 14),
80+
' : ',
81+
_.chain(scopes).uniq().sort().value().join(', ')
82+
].join('')
83+
)
84+
}
85+
}
86+
87+
return result.join('\n')
88+
}
89+
90+
function getDiff(test, result) {
91+
var diff = jsdiff.structuredPatch('', '', test, result, '', ''),
92+
result = [];
93+
94+
for (var i = 0; i < diff.hunks.length; i++) {
95+
var hunk = diff.hunks[i];
96+
97+
for (var j = 0; j < hunk.lines.length; j++) {
98+
var line = hunk.lines[j];
99+
if (line && !options.no_color) {
100+
if (line[0] == '+') {
101+
line = chalk.green(line)
102+
} else if (line[0] == '-') {
103+
line = chalk.red(line)
104+
}
105+
}
106+
result.push(line);
107+
}
108+
109+
return result.join('\n');
110+
}
111+
}
112+
113+
var buf = fs.readFileSync(file, 'utf8'),
114+
parts = rpartition(buf, '\n\n\n'),
115+
source = parts[0],
116+
test = parts[2];
117+
118+
if (test) {
119+
test = stripnl(test);
120+
}
121+
122+
if (!test) {
123+
test = null;
124+
}
125+
126+
if (!source && test) {
127+
source = test;
128+
test = null;
129+
}
130+
131+
if (!source) {
132+
return {
133+
file: file,
134+
status: 'fail',
135+
error: 'Empty file'
136+
}
137+
}
138+
139+
var result = tokenize(source);
140+
141+
if (test != result) {
142+
if (test) {
143+
return {
144+
file: file,
145+
status: 'fail',
146+
error: 'Output different from expected',
147+
body: getDiff(test, result)
148+
}
149+
} else {
150+
return {
151+
file: file,
152+
status: 'fail',
153+
error: 'No expected output set',
154+
body: result
155+
}
156+
}
157+
} else {
158+
return {
159+
file: file,
160+
status: 'pass'
161+
}
162+
}
163+
}
164+
165+
166+
function run(testFiles, grammarFile, options) {
167+
options = options || {};
168+
169+
var grammar = compileGrammar(grammarFile),
170+
sep = '--------',
171+
passed = 0,
172+
failed = 0,
173+
failedRes = [];
174+
175+
console.log(sep);
176+
console.log(testFiles.length + ' tests; grammar: ' + grammarFile);
177+
console.log(sep);
178+
179+
for (var i = 0; i < testFiles.length; i++) {
180+
var res = testFile(testFiles[i], grammar, options);
181+
182+
if (res.status == 'fail') {
183+
process.stdout.write('E');
184+
failed ++;
185+
failedRes.push(res);
186+
} else {
187+
process.stdout.write('.');
188+
passed ++;
189+
}
190+
}
191+
process.stdout.write('\n');
192+
193+
console.log(sep);
194+
console.log(passed + ' passed; ' + failed + ' failed.');
195+
console.log(sep);
196+
197+
for (var i = 0; i < failedRes.length; i++) {
198+
var res = failedRes[i];
199+
200+
console.log(sep);
201+
console.log('Failed test ' + res.file + ': ' + res.error);
202+
console.log(sep);
203+
if (res.body) {
204+
console.log(res.body)
205+
}
206+
}
207+
208+
if (failed) {
209+
process.exit(1);
210+
}
211+
}
212+
213+
214+
module.exports = run;

Diff for: package.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "syntaxtest",
3+
"version": "0.0.1",
4+
"description": "Unit testing framework for TextMate/Sublime/Atom syntaxes.",
5+
"main": "index.js",
6+
"author": "Yury Selivanov <[email protected]>",
7+
"license": "MIT",
8+
"bin": {
9+
"syntaxtest": "bin/syntaxtest.js"
10+
},
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/MagicStack/syntaxtest"
14+
},
15+
"dependencies": {
16+
"first-mate": "^5.0.2",
17+
"js-yaml": "^3.4.2",
18+
"temp": "^0.8.3",
19+
"argparse": "^1.0.2",
20+
"diff": "^2.1.3",
21+
"chalk": "^1.1.1",
22+
"underscore": "^1.8.3"
23+
}
24+
}

0 commit comments

Comments
 (0)