Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/test/common/json.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
import assert from 'assert';
6
import { createScanner, Node, parse, ParseError, ParseErrorCode, ParseOptions, parseTree, ScanError, SyntaxKind } from '../../common/json.js';
7
import { getParseErrorMessage } from '../../common/jsonErrorMessages.js';
8
import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js';
9
10
function assertKinds(text: string, ...kinds: SyntaxKind[]): void {
11
const scanner = createScanner(text);
12
let kind: SyntaxKind;
13
while ((kind = scanner.scan()) !== SyntaxKind.EOF) {
14
assert.strictEqual(kind, kinds.shift());
15
}
16
assert.strictEqual(kinds.length, 0);
17
}
18
function assertScanError(text: string, expectedKind: SyntaxKind, scanError: ScanError): void {
19
const scanner = createScanner(text);
20
scanner.scan();
21
assert.strictEqual(scanner.getToken(), expectedKind);
22
assert.strictEqual(scanner.getTokenError(), scanError);
23
}
24
25
function assertValidParse(input: string, expected: any, options?: ParseOptions): void {
26
const errors: ParseError[] = [];
27
const actual = parse(input, errors, options);
28
29
if (errors.length !== 0) {
30
assert(false, getParseErrorMessage(errors[0].error));
31
}
32
assert.deepStrictEqual(actual, expected);
33
}
34
35
function assertInvalidParse(input: string, expected: any, options?: ParseOptions): void {
36
const errors: ParseError[] = [];
37
const actual = parse(input, errors, options);
38
39
assert(errors.length > 0);
40
assert.deepStrictEqual(actual, expected);
41
}
42
43
function assertTree(input: string, expected: any, expectedErrors: number[] = [], options?: ParseOptions): void {
44
const errors: ParseError[] = [];
45
const actual = parseTree(input, errors, options);
46
47
assert.deepStrictEqual(errors.map(e => e.error, expected), expectedErrors);
48
const checkParent = (node: Node) => {
49
if (node.children) {
50
for (const child of node.children) {
51
assert.strictEqual(node, child.parent);
52
delete (<any>child).parent; // delete to avoid recursion in deep equal
53
checkParent(child);
54
}
55
}
56
};
57
checkParent(actual);
58
59
assert.deepStrictEqual(actual, expected);
60
}
61
62
suite('JSON', () => {
63
64
ensureNoDisposablesAreLeakedInTestSuite();
65
66
test('tokens', () => {
67
assertKinds('{', SyntaxKind.OpenBraceToken);
68
assertKinds('}', SyntaxKind.CloseBraceToken);
69
assertKinds('[', SyntaxKind.OpenBracketToken);
70
assertKinds(']', SyntaxKind.CloseBracketToken);
71
assertKinds(':', SyntaxKind.ColonToken);
72
assertKinds(',', SyntaxKind.CommaToken);
73
});
74
75
test('comments', () => {
76
assertKinds('// this is a comment', SyntaxKind.LineCommentTrivia);
77
assertKinds('// this is a comment\n', SyntaxKind.LineCommentTrivia, SyntaxKind.LineBreakTrivia);
78
assertKinds('/* this is a comment*/', SyntaxKind.BlockCommentTrivia);
79
assertKinds('/* this is a \r\ncomment*/', SyntaxKind.BlockCommentTrivia);
80
assertKinds('/* this is a \ncomment*/', SyntaxKind.BlockCommentTrivia);
81
82
// unexpected end
83
assertKinds('/* this is a', SyntaxKind.BlockCommentTrivia);
84
assertKinds('/* this is a \ncomment', SyntaxKind.BlockCommentTrivia);
85
86
// broken comment
87
assertKinds('/ ttt', SyntaxKind.Unknown, SyntaxKind.Trivia, SyntaxKind.Unknown);
88
});
89
90
test('strings', () => {
91
assertKinds('"test"', SyntaxKind.StringLiteral);
92
assertKinds('"\\""', SyntaxKind.StringLiteral);
93
assertKinds('"\\/"', SyntaxKind.StringLiteral);
94
assertKinds('"\\b"', SyntaxKind.StringLiteral);
95
assertKinds('"\\f"', SyntaxKind.StringLiteral);
96
assertKinds('"\\n"', SyntaxKind.StringLiteral);
97
assertKinds('"\\r"', SyntaxKind.StringLiteral);
98
assertKinds('"\\t"', SyntaxKind.StringLiteral);
99
assertKinds('"\\v"', SyntaxKind.StringLiteral);
100
assertKinds('"\u88ff"', SyntaxKind.StringLiteral);
101
assertKinds('"​\u2028"', SyntaxKind.StringLiteral);
102
103
// unexpected end
104
assertKinds('"test', SyntaxKind.StringLiteral);
105
assertKinds('"test\n"', SyntaxKind.StringLiteral, SyntaxKind.LineBreakTrivia, SyntaxKind.StringLiteral);
106
107
// invalid characters
108
assertScanError('"\t"', SyntaxKind.StringLiteral, ScanError.InvalidCharacter);
109
assertScanError('"\t "', SyntaxKind.StringLiteral, ScanError.InvalidCharacter);
110
});
111
112
test('numbers', () => {
113
assertKinds('0', SyntaxKind.NumericLiteral);
114
assertKinds('0.1', SyntaxKind.NumericLiteral);
115
assertKinds('-0.1', SyntaxKind.NumericLiteral);
116
assertKinds('-1', SyntaxKind.NumericLiteral);
117
assertKinds('1', SyntaxKind.NumericLiteral);
118
assertKinds('123456789', SyntaxKind.NumericLiteral);
119
assertKinds('10', SyntaxKind.NumericLiteral);
120
assertKinds('90', SyntaxKind.NumericLiteral);
121
assertKinds('90E+123', SyntaxKind.NumericLiteral);
122
assertKinds('90e+123', SyntaxKind.NumericLiteral);
123
assertKinds('90e-123', SyntaxKind.NumericLiteral);
124
assertKinds('90E-123', SyntaxKind.NumericLiteral);
125
assertKinds('90E123', SyntaxKind.NumericLiteral);
126
assertKinds('90e123', SyntaxKind.NumericLiteral);
127
128
// zero handling
129
assertKinds('01', SyntaxKind.NumericLiteral, SyntaxKind.NumericLiteral);
130
assertKinds('-01', SyntaxKind.NumericLiteral, SyntaxKind.NumericLiteral);
131
132
// unexpected end
133
assertKinds('-', SyntaxKind.Unknown);
134
assertKinds('.0', SyntaxKind.Unknown);
135
});
136
137
test('keywords: true, false, null', () => {
138
assertKinds('true', SyntaxKind.TrueKeyword);
139
assertKinds('false', SyntaxKind.FalseKeyword);
140
assertKinds('null', SyntaxKind.NullKeyword);
141
142
143
assertKinds('true false null',
144
SyntaxKind.TrueKeyword,
145
SyntaxKind.Trivia,
146
SyntaxKind.FalseKeyword,
147
SyntaxKind.Trivia,
148
SyntaxKind.NullKeyword);
149
150
// invalid words
151
assertKinds('nulllll', SyntaxKind.Unknown);
152
assertKinds('True', SyntaxKind.Unknown);
153
assertKinds('foo-bar', SyntaxKind.Unknown);
154
assertKinds('foo bar', SyntaxKind.Unknown, SyntaxKind.Trivia, SyntaxKind.Unknown);
155
});
156
157
test('trivia', () => {
158
assertKinds(' ', SyntaxKind.Trivia);
159
assertKinds(' \t ', SyntaxKind.Trivia);
160
assertKinds(' \t \n \t ', SyntaxKind.Trivia, SyntaxKind.LineBreakTrivia, SyntaxKind.Trivia);
161
assertKinds('\r\n', SyntaxKind.LineBreakTrivia);
162
assertKinds('\r', SyntaxKind.LineBreakTrivia);
163
assertKinds('\n', SyntaxKind.LineBreakTrivia);
164
assertKinds('\n\r', SyntaxKind.LineBreakTrivia, SyntaxKind.LineBreakTrivia);
165
assertKinds('\n \n', SyntaxKind.LineBreakTrivia, SyntaxKind.Trivia, SyntaxKind.LineBreakTrivia);
166
});
167
168
test('parse: literals', () => {
169
170
assertValidParse('true', true);
171
assertValidParse('false', false);
172
assertValidParse('null', null);
173
assertValidParse('"foo"', 'foo');
174
assertValidParse('"\\"-\\\\-\\/-\\b-\\f-\\n-\\r-\\t"', '"-\\-/-\b-\f-\n-\r-\t');
175
assertValidParse('"\\u00DC"', 'Ü');
176
assertValidParse('9', 9);
177
assertValidParse('-9', -9);
178
assertValidParse('0.129', 0.129);
179
assertValidParse('23e3', 23e3);
180
assertValidParse('1.2E+3', 1.2E+3);
181
assertValidParse('1.2E-3', 1.2E-3);
182
assertValidParse('1.2E-3 // comment', 1.2E-3);
183
});
184
185
test('parse: objects', () => {
186
assertValidParse('{}', {});
187
assertValidParse('{ "foo": true }', { foo: true });
188
assertValidParse('{ "bar": 8, "xoo": "foo" }', { bar: 8, xoo: 'foo' });
189
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} });
190
assertValidParse('{ "a": false, "b": true, "c": [ 7.4 ] }', { a: false, b: true, c: [7.4] });
191
assertValidParse('{ "lineComment": "//", "blockComment": ["/*", "*/"], "brackets": [ ["{", "}"], ["[", "]"], ["(", ")"] ] }', { lineComment: '//', blockComment: ['/*', '*/'], brackets: [['{', '}'], ['[', ']'], ['(', ')']] });
192
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} });
193
assertValidParse('{ "hello": { "again": { "inside": 5 }, "world": 1 }}', { hello: { again: { inside: 5 }, world: 1 } });
194
assertValidParse('{ "foo": /*hello*/true }', { foo: true });
195
});
196
197
test('parse: arrays', () => {
198
assertValidParse('[]', []);
199
assertValidParse('[ [], [ [] ]]', [[], [[]]]);
200
assertValidParse('[ 1, 2, 3 ]', [1, 2, 3]);
201
assertValidParse('[ { "a": null } ]', [{ a: null }]);
202
});
203
204
test('parse: objects with errors', () => {
205
assertInvalidParse('{,}', {});
206
assertInvalidParse('{ "foo": true, }', { foo: true }, { allowTrailingComma: false });
207
assertInvalidParse('{ "bar": 8 "xoo": "foo" }', { bar: 8, xoo: 'foo' });
208
assertInvalidParse('{ ,"bar": 8 }', { bar: 8 });
209
assertInvalidParse('{ ,"bar": 8, "foo" }', { bar: 8 });
210
assertInvalidParse('{ "bar": 8, "foo": }', { bar: 8 });
211
assertInvalidParse('{ 8, "foo": 9 }', { foo: 9 });
212
});
213
214
test('parse: array with errors', () => {
215
assertInvalidParse('[,]', []);
216
assertInvalidParse('[ 1, 2, ]', [1, 2], { allowTrailingComma: false });
217
assertInvalidParse('[ 1 2, 3 ]', [1, 2, 3]);
218
assertInvalidParse('[ ,1, 2, 3 ]', [1, 2, 3]);
219
assertInvalidParse('[ ,1, 2, 3, ]', [1, 2, 3], { allowTrailingComma: false });
220
});
221
222
test('parse: disallow commments', () => {
223
const options = { disallowComments: true };
224
225
assertValidParse('[ 1, 2, null, "foo" ]', [1, 2, null, 'foo'], options);
226
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} }, options);
227
228
assertInvalidParse('{ "foo": /*comment*/ true }', { foo: true }, options);
229
});
230
231
test('parse: trailing comma', () => {
232
// default is allow
233
assertValidParse('{ "hello": [], }', { hello: [] });
234
235
let options = { allowTrailingComma: true };
236
assertValidParse('{ "hello": [], }', { hello: [] }, options);
237
assertValidParse('{ "hello": [] }', { hello: [] }, options);
238
assertValidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options);
239
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} }, options);
240
assertValidParse('{ "hello": [1,] }', { hello: [1] }, options);
241
242
options = { allowTrailingComma: false };
243
assertInvalidParse('{ "hello": [], }', { hello: [] }, options);
244
assertInvalidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options);
245
});
246
247
test('tree: literals', () => {
248
assertTree('true', { type: 'boolean', offset: 0, length: 4, value: true });
249
assertTree('false', { type: 'boolean', offset: 0, length: 5, value: false });
250
assertTree('null', { type: 'null', offset: 0, length: 4, value: null });
251
assertTree('23', { type: 'number', offset: 0, length: 2, value: 23 });
252
assertTree('-1.93e-19', { type: 'number', offset: 0, length: 9, value: -1.93e-19 });
253
assertTree('"hello"', { type: 'string', offset: 0, length: 7, value: 'hello' });
254
});
255
256
test('tree: arrays', () => {
257
assertTree('[]', { type: 'array', offset: 0, length: 2, children: [] });
258
assertTree('[ 1 ]', { type: 'array', offset: 0, length: 5, children: [{ type: 'number', offset: 2, length: 1, value: 1 }] });
259
assertTree('[ 1,"x"]', {
260
type: 'array', offset: 0, length: 8, children: [
261
{ type: 'number', offset: 2, length: 1, value: 1 },
262
{ type: 'string', offset: 4, length: 3, value: 'x' }
263
]
264
});
265
assertTree('[[]]', {
266
type: 'array', offset: 0, length: 4, children: [
267
{ type: 'array', offset: 1, length: 2, children: [] }
268
]
269
});
270
});
271
272
test('tree: objects', () => {
273
assertTree('{ }', { type: 'object', offset: 0, length: 3, children: [] });
274
assertTree('{ "val": 1 }', {
275
type: 'object', offset: 0, length: 12, children: [
276
{
277
type: 'property', offset: 2, length: 8, colonOffset: 7, children: [
278
{ type: 'string', offset: 2, length: 5, value: 'val' },
279
{ type: 'number', offset: 9, length: 1, value: 1 }
280
]
281
}
282
]
283
});
284
assertTree('{"id": "$", "v": [ null, null] }',
285
{
286
type: 'object', offset: 0, length: 32, children: [
287
{
288
type: 'property', offset: 1, length: 9, colonOffset: 5, children: [
289
{ type: 'string', offset: 1, length: 4, value: 'id' },
290
{ type: 'string', offset: 7, length: 3, value: '$' }
291
]
292
},
293
{
294
type: 'property', offset: 12, length: 18, colonOffset: 15, children: [
295
{ type: 'string', offset: 12, length: 3, value: 'v' },
296
{
297
type: 'array', offset: 17, length: 13, children: [
298
{ type: 'null', offset: 19, length: 4, value: null },
299
{ type: 'null', offset: 25, length: 4, value: null }
300
]
301
}
302
]
303
}
304
]
305
}
306
);
307
assertTree('{ "id": { "foo": { } } , }',
308
{
309
type: 'object', offset: 0, length: 27, children: [
310
{
311
type: 'property', offset: 3, length: 20, colonOffset: 7, children: [
312
{ type: 'string', offset: 3, length: 4, value: 'id' },
313
{
314
type: 'object', offset: 9, length: 14, children: [
315
{
316
type: 'property', offset: 11, length: 10, colonOffset: 16, children: [
317
{ type: 'string', offset: 11, length: 5, value: 'foo' },
318
{ type: 'object', offset: 18, length: 3, children: [] }
319
]
320
}
321
]
322
}
323
]
324
}
325
]
326
}
327
, [ParseErrorCode.PropertyNameExpected, ParseErrorCode.ValueExpected], { allowTrailingComma: false });
328
});
329
});
330
331