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
5249 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
// eslint-disable-next-line local/code-no-any-casts
53
delete (<any>child).parent; // delete to avoid recursion in deep equal
54
checkParent(child);
55
}
56
}
57
};
58
checkParent(actual);
59
60
assert.deepStrictEqual(actual, expected);
61
}
62
63
suite('JSON', () => {
64
65
ensureNoDisposablesAreLeakedInTestSuite();
66
67
test('tokens', () => {
68
assertKinds('{', SyntaxKind.OpenBraceToken);
69
assertKinds('}', SyntaxKind.CloseBraceToken);
70
assertKinds('[', SyntaxKind.OpenBracketToken);
71
assertKinds(']', SyntaxKind.CloseBracketToken);
72
assertKinds(':', SyntaxKind.ColonToken);
73
assertKinds(',', SyntaxKind.CommaToken);
74
});
75
76
test('comments', () => {
77
assertKinds('// this is a comment', SyntaxKind.LineCommentTrivia);
78
assertKinds('// this is a comment\n', SyntaxKind.LineCommentTrivia, SyntaxKind.LineBreakTrivia);
79
assertKinds('/* this is a comment*/', SyntaxKind.BlockCommentTrivia);
80
assertKinds('/* this is a \r\ncomment*/', SyntaxKind.BlockCommentTrivia);
81
assertKinds('/* this is a \ncomment*/', SyntaxKind.BlockCommentTrivia);
82
83
// unexpected end
84
assertKinds('/* this is a', SyntaxKind.BlockCommentTrivia);
85
assertKinds('/* this is a \ncomment', SyntaxKind.BlockCommentTrivia);
86
87
// broken comment
88
assertKinds('/ ttt', SyntaxKind.Unknown, SyntaxKind.Trivia, SyntaxKind.Unknown);
89
});
90
91
test('strings', () => {
92
assertKinds('"test"', SyntaxKind.StringLiteral);
93
assertKinds('"\\""', SyntaxKind.StringLiteral);
94
assertKinds('"\\/"', SyntaxKind.StringLiteral);
95
assertKinds('"\\b"', SyntaxKind.StringLiteral);
96
assertKinds('"\\f"', SyntaxKind.StringLiteral);
97
assertKinds('"\\n"', SyntaxKind.StringLiteral);
98
assertKinds('"\\r"', SyntaxKind.StringLiteral);
99
assertKinds('"\\t"', SyntaxKind.StringLiteral);
100
assertKinds('"\\v"', SyntaxKind.StringLiteral);
101
assertKinds('"\u88ff"', SyntaxKind.StringLiteral);
102
assertKinds('"​\u2028"', SyntaxKind.StringLiteral);
103
104
// unexpected end
105
assertKinds('"test', SyntaxKind.StringLiteral);
106
assertKinds('"test\n"', SyntaxKind.StringLiteral, SyntaxKind.LineBreakTrivia, SyntaxKind.StringLiteral);
107
108
// invalid characters
109
assertScanError('"\t"', SyntaxKind.StringLiteral, ScanError.InvalidCharacter);
110
assertScanError('"\t "', SyntaxKind.StringLiteral, ScanError.InvalidCharacter);
111
});
112
113
test('numbers', () => {
114
assertKinds('0', SyntaxKind.NumericLiteral);
115
assertKinds('0.1', SyntaxKind.NumericLiteral);
116
assertKinds('-0.1', SyntaxKind.NumericLiteral);
117
assertKinds('-1', SyntaxKind.NumericLiteral);
118
assertKinds('1', SyntaxKind.NumericLiteral);
119
assertKinds('123456789', SyntaxKind.NumericLiteral);
120
assertKinds('10', SyntaxKind.NumericLiteral);
121
assertKinds('90', SyntaxKind.NumericLiteral);
122
assertKinds('90E+123', SyntaxKind.NumericLiteral);
123
assertKinds('90e+123', SyntaxKind.NumericLiteral);
124
assertKinds('90e-123', SyntaxKind.NumericLiteral);
125
assertKinds('90E-123', SyntaxKind.NumericLiteral);
126
assertKinds('90E123', SyntaxKind.NumericLiteral);
127
assertKinds('90e123', SyntaxKind.NumericLiteral);
128
129
// zero handling
130
assertKinds('01', SyntaxKind.NumericLiteral, SyntaxKind.NumericLiteral);
131
assertKinds('-01', SyntaxKind.NumericLiteral, SyntaxKind.NumericLiteral);
132
133
// unexpected end
134
assertKinds('-', SyntaxKind.Unknown);
135
assertKinds('.0', SyntaxKind.Unknown);
136
});
137
138
test('keywords: true, false, null', () => {
139
assertKinds('true', SyntaxKind.TrueKeyword);
140
assertKinds('false', SyntaxKind.FalseKeyword);
141
assertKinds('null', SyntaxKind.NullKeyword);
142
143
144
assertKinds('true false null',
145
SyntaxKind.TrueKeyword,
146
SyntaxKind.Trivia,
147
SyntaxKind.FalseKeyword,
148
SyntaxKind.Trivia,
149
SyntaxKind.NullKeyword);
150
151
// invalid words
152
assertKinds('nulllll', SyntaxKind.Unknown);
153
assertKinds('True', SyntaxKind.Unknown);
154
assertKinds('foo-bar', SyntaxKind.Unknown);
155
assertKinds('foo bar', SyntaxKind.Unknown, SyntaxKind.Trivia, SyntaxKind.Unknown);
156
});
157
158
test('trivia', () => {
159
assertKinds(' ', SyntaxKind.Trivia);
160
assertKinds(' \t ', SyntaxKind.Trivia);
161
assertKinds(' \t \n \t ', SyntaxKind.Trivia, SyntaxKind.LineBreakTrivia, SyntaxKind.Trivia);
162
assertKinds('\r\n', SyntaxKind.LineBreakTrivia);
163
assertKinds('\r', SyntaxKind.LineBreakTrivia);
164
assertKinds('\n', SyntaxKind.LineBreakTrivia);
165
assertKinds('\n\r', SyntaxKind.LineBreakTrivia, SyntaxKind.LineBreakTrivia);
166
assertKinds('\n \n', SyntaxKind.LineBreakTrivia, SyntaxKind.Trivia, SyntaxKind.LineBreakTrivia);
167
});
168
169
test('parse: literals', () => {
170
171
assertValidParse('true', true);
172
assertValidParse('false', false);
173
assertValidParse('null', null);
174
assertValidParse('"foo"', 'foo');
175
assertValidParse('"\\"-\\\\-\\/-\\b-\\f-\\n-\\r-\\t"', '"-\\-/-\b-\f-\n-\r-\t');
176
assertValidParse('"\\u00DC"', 'Ü');
177
assertValidParse('9', 9);
178
assertValidParse('-9', -9);
179
assertValidParse('0.129', 0.129);
180
assertValidParse('23e3', 23e3);
181
assertValidParse('1.2E+3', 1.2E+3);
182
assertValidParse('1.2E-3', 1.2E-3);
183
assertValidParse('1.2E-3 // comment', 1.2E-3);
184
});
185
186
test('parse: objects', () => {
187
assertValidParse('{}', {});
188
assertValidParse('{ "foo": true }', { foo: true });
189
assertValidParse('{ "bar": 8, "xoo": "foo" }', { bar: 8, xoo: 'foo' });
190
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} });
191
assertValidParse('{ "a": false, "b": true, "c": [ 7.4 ] }', { a: false, b: true, c: [7.4] });
192
assertValidParse('{ "lineComment": "//", "blockComment": ["/*", "*/"], "brackets": [ ["{", "}"], ["[", "]"], ["(", ")"] ] }', { lineComment: '//', blockComment: ['/*', '*/'], brackets: [['{', '}'], ['[', ']'], ['(', ')']] });
193
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} });
194
assertValidParse('{ "hello": { "again": { "inside": 5 }, "world": 1 }}', { hello: { again: { inside: 5 }, world: 1 } });
195
assertValidParse('{ "foo": /*hello*/true }', { foo: true });
196
});
197
198
test('parse: arrays', () => {
199
assertValidParse('[]', []);
200
assertValidParse('[ [], [ [] ]]', [[], [[]]]);
201
assertValidParse('[ 1, 2, 3 ]', [1, 2, 3]);
202
assertValidParse('[ { "a": null } ]', [{ a: null }]);
203
});
204
205
test('parse: objects with errors', () => {
206
assertInvalidParse('{,}', {});
207
assertInvalidParse('{ "foo": true, }', { foo: true }, { allowTrailingComma: false });
208
assertInvalidParse('{ "bar": 8 "xoo": "foo" }', { bar: 8, xoo: 'foo' });
209
assertInvalidParse('{ ,"bar": 8 }', { bar: 8 });
210
assertInvalidParse('{ ,"bar": 8, "foo" }', { bar: 8 });
211
assertInvalidParse('{ "bar": 8, "foo": }', { bar: 8 });
212
assertInvalidParse('{ 8, "foo": 9 }', { foo: 9 });
213
});
214
215
test('parse: array with errors', () => {
216
assertInvalidParse('[,]', []);
217
assertInvalidParse('[ 1, 2, ]', [1, 2], { allowTrailingComma: false });
218
assertInvalidParse('[ 1 2, 3 ]', [1, 2, 3]);
219
assertInvalidParse('[ ,1, 2, 3 ]', [1, 2, 3]);
220
assertInvalidParse('[ ,1, 2, 3, ]', [1, 2, 3], { allowTrailingComma: false });
221
});
222
223
test('parse: disallow commments', () => {
224
const options = { disallowComments: true };
225
226
assertValidParse('[ 1, 2, null, "foo" ]', [1, 2, null, 'foo'], options);
227
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} }, options);
228
229
assertInvalidParse('{ "foo": /*comment*/ true }', { foo: true }, options);
230
});
231
232
test('parse: trailing comma', () => {
233
// default is allow
234
assertValidParse('{ "hello": [], }', { hello: [] });
235
236
let options = { allowTrailingComma: true };
237
assertValidParse('{ "hello": [], }', { hello: [] }, options);
238
assertValidParse('{ "hello": [] }', { hello: [] }, options);
239
assertValidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options);
240
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} }, options);
241
assertValidParse('{ "hello": [1,] }', { hello: [1] }, options);
242
243
options = { allowTrailingComma: false };
244
assertInvalidParse('{ "hello": [], }', { hello: [] }, options);
245
assertInvalidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options);
246
});
247
248
test('tree: literals', () => {
249
assertTree('true', { type: 'boolean', offset: 0, length: 4, value: true });
250
assertTree('false', { type: 'boolean', offset: 0, length: 5, value: false });
251
assertTree('null', { type: 'null', offset: 0, length: 4, value: null });
252
assertTree('23', { type: 'number', offset: 0, length: 2, value: 23 });
253
assertTree('-1.93e-19', { type: 'number', offset: 0, length: 9, value: -1.93e-19 });
254
assertTree('"hello"', { type: 'string', offset: 0, length: 7, value: 'hello' });
255
});
256
257
test('tree: arrays', () => {
258
assertTree('[]', { type: 'array', offset: 0, length: 2, children: [] });
259
assertTree('[ 1 ]', { type: 'array', offset: 0, length: 5, children: [{ type: 'number', offset: 2, length: 1, value: 1 }] });
260
assertTree('[ 1,"x"]', {
261
type: 'array', offset: 0, length: 8, children: [
262
{ type: 'number', offset: 2, length: 1, value: 1 },
263
{ type: 'string', offset: 4, length: 3, value: 'x' }
264
]
265
});
266
assertTree('[[]]', {
267
type: 'array', offset: 0, length: 4, children: [
268
{ type: 'array', offset: 1, length: 2, children: [] }
269
]
270
});
271
});
272
273
test('tree: objects', () => {
274
assertTree('{ }', { type: 'object', offset: 0, length: 3, children: [] });
275
assertTree('{ "val": 1 }', {
276
type: 'object', offset: 0, length: 12, children: [
277
{
278
type: 'property', offset: 2, length: 8, colonOffset: 7, children: [
279
{ type: 'string', offset: 2, length: 5, value: 'val' },
280
{ type: 'number', offset: 9, length: 1, value: 1 }
281
]
282
}
283
]
284
});
285
assertTree('{"id": "$", "v": [ null, null] }',
286
{
287
type: 'object', offset: 0, length: 32, children: [
288
{
289
type: 'property', offset: 1, length: 9, colonOffset: 5, children: [
290
{ type: 'string', offset: 1, length: 4, value: 'id' },
291
{ type: 'string', offset: 7, length: 3, value: '$' }
292
]
293
},
294
{
295
type: 'property', offset: 12, length: 18, colonOffset: 15, children: [
296
{ type: 'string', offset: 12, length: 3, value: 'v' },
297
{
298
type: 'array', offset: 17, length: 13, children: [
299
{ type: 'null', offset: 19, length: 4, value: null },
300
{ type: 'null', offset: 25, length: 4, value: null }
301
]
302
}
303
]
304
}
305
]
306
}
307
);
308
assertTree('{ "id": { "foo": { } } , }',
309
{
310
type: 'object', offset: 0, length: 27, children: [
311
{
312
type: 'property', offset: 3, length: 20, colonOffset: 7, children: [
313
{ type: 'string', offset: 3, length: 4, value: 'id' },
314
{
315
type: 'object', offset: 9, length: 14, children: [
316
{
317
type: 'property', offset: 11, length: 10, colonOffset: 16, children: [
318
{ type: 'string', offset: 11, length: 5, value: 'foo' },
319
{ type: 'object', offset: 18, length: 3, children: [] }
320
]
321
}
322
]
323
}
324
]
325
}
326
]
327
}
328
, [ParseErrorCode.PropertyNameExpected, ParseErrorCode.ValueExpected], { allowTrailingComma: false });
329
});
330
});
331
332