Befor generating
This commit is contained in:
+67
@@ -0,0 +1,67 @@
|
||||
import { assert } from "chai";
|
||||
import * as YAML from '../src/';
|
||||
import * as util from './testUtil';
|
||||
|
||||
suite('YAML Syntax', () => {
|
||||
test('Allow astral characters', function () {
|
||||
const key = '𝑘𝑒𝑦';
|
||||
const value = '𝑣𝑎𝑙𝑢𝑒';
|
||||
const document = YAML.safeLoad(`${key}: ${value}`);
|
||||
|
||||
assert.deepEqual(document.mappings[0].key.value, key);
|
||||
assert.deepEqual(document.mappings[0].value.value, value);
|
||||
});
|
||||
|
||||
test('Forbid non-printable characters', function () {
|
||||
testErrors('\x01', [{
|
||||
line: 1,
|
||||
column: 0,
|
||||
message:'the stream contains non-printable characters',
|
||||
isWarning: false
|
||||
}]);
|
||||
|
||||
testErrors('\x7f', [{
|
||||
line: 1,
|
||||
column: 0,
|
||||
message:'the stream contains non-printable characters',
|
||||
isWarning: false
|
||||
}]);
|
||||
|
||||
testErrors('\x9f', [{
|
||||
line: 1,
|
||||
column: 0,
|
||||
message:'the stream contains non-printable characters',
|
||||
isWarning: false
|
||||
}]);
|
||||
});
|
||||
|
||||
test('Forbid lone surrogates', function () {
|
||||
testErrors('\udc00\ud800', [{
|
||||
line: 1,
|
||||
column: 0,
|
||||
message:'the stream contains non-printable characters',
|
||||
isWarning: false
|
||||
}]);
|
||||
});
|
||||
|
||||
test('Allow non-printable characters inside quoted scalars', function () {
|
||||
const key = '"\x7f\x9f\udc00\ud800"';
|
||||
const document = YAML.safeLoad(key);
|
||||
|
||||
assert.deepEqual(document.value, '\x7f\x9f\udc00\ud800');
|
||||
});
|
||||
|
||||
test('Forbid control sequences inside quoted scalars', function () {
|
||||
testErrors('"\x03"', [{
|
||||
line: 0,
|
||||
column: 2,
|
||||
message:'expected valid JSON character',
|
||||
isWarning: false
|
||||
}]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function testErrors(input: string, expectedErrors: util.TestError[]) {
|
||||
util.testErrors(input, expectedErrors);
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
import * as YAML from '../src/'
|
||||
import { AbstractVisitor } from './visitor'
|
||||
|
||||
import * as chai from 'chai'
|
||||
const assert = chai.assert
|
||||
|
||||
function structure(node) {
|
||||
return new DuplicateStructureBuilder().accept(node);
|
||||
}
|
||||
|
||||
suite('Loading a single document', () => {
|
||||
test('should work with document-end delimiters', function () {
|
||||
const input = `---
|
||||
whatever: true
|
||||
...`
|
||||
const doc = YAML.safeLoad(input)
|
||||
const expected_structure =
|
||||
YAML.newMap(
|
||||
[YAML.newMapping(
|
||||
YAML.newScalar('whatever'),
|
||||
YAML.newScalar('true'))]);
|
||||
|
||||
assert.deepEqual(structure(doc), expected_structure)
|
||||
|
||||
assert.lengthOf(doc.errors, 0,
|
||||
`Found error(s): ${doc.errors.toString()} when expecting none.`)
|
||||
});
|
||||
|
||||
test('Document end position should be equal to input length', function () {
|
||||
const input = `
|
||||
outer:
|
||||
inner:
|
||||
`;
|
||||
const doc1 = YAML.load(input);
|
||||
assert.deepEqual(doc1.endPosition,input.length);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Loading multiple documents', () => {
|
||||
test('should work with document-end delimiters', function () {
|
||||
const docs = []
|
||||
YAML.loadAll(`---
|
||||
whatever: true
|
||||
...
|
||||
---
|
||||
whatever: false
|
||||
...`, d => docs.push(d))
|
||||
|
||||
const expected_structure = [
|
||||
YAML.newMap(
|
||||
[YAML.newMapping(
|
||||
YAML.newScalar('whatever'),
|
||||
YAML.newScalar('true'))]),
|
||||
YAML.newMap(
|
||||
[YAML.newMapping(
|
||||
YAML.newScalar('whatever'),
|
||||
YAML.newScalar('false'))])
|
||||
];
|
||||
|
||||
assert.deepEqual(docs.map(d => structure(d)), expected_structure)
|
||||
|
||||
docs.forEach(doc =>
|
||||
assert.lengthOf(doc.errors, 0,
|
||||
`Found error(s): ${doc.errors.toString()} when expecting none.`))
|
||||
});
|
||||
|
||||
test('Last document end position should be equal to input length', function () {
|
||||
const input = `
|
||||
outer1:
|
||||
inner1:
|
||||
...
|
||||
---
|
||||
outer2:
|
||||
inner2:
|
||||
`;
|
||||
const documents: YAML.YAMLDocument[] = [];
|
||||
YAML.loadAll(input,x=>documents.push(x));
|
||||
const doc2 = documents[1];
|
||||
assert.deepEqual(doc2.endPosition,input.length);
|
||||
});
|
||||
});
|
||||
|
||||
class DuplicateStructureBuilder extends AbstractVisitor {
|
||||
visitScalar(node: YAML.YAMLScalar) {
|
||||
return YAML.newScalar(node.value)
|
||||
}
|
||||
visitMapping(node: YAML.YAMLMapping) {
|
||||
return YAML.newMapping(this.visitScalar(node.key), this.accept(node.value))
|
||||
}
|
||||
visitSequence(node: YAML.YAMLSequence) {
|
||||
const seq = YAML.newSeq()
|
||||
seq.items = node.items.map(n => this.accept(n))
|
||||
return seq
|
||||
}
|
||||
visitMap(node: YAML.YamlMap) {
|
||||
return YAML.newMap(node.mappings.map(n => this.accept(n)));
|
||||
}
|
||||
visitAnchorRef(node: YAML.YAMLAnchorReference) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
visitIncludeRef(node: YAML.YAMLNode) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
import * as chai from 'chai'
|
||||
const assert = chai.assert
|
||||
import { determineScalarType as sut, ScalarType, parseYamlBoolean, parseYamlInteger, parseYamlFloat } from '../src/scalarInference'
|
||||
|
||||
import * as Yaml from '../src/index'
|
||||
|
||||
suite('determineScalarType', () => {
|
||||
|
||||
function determineScalarType(scalar: Yaml.YAMLDocument) {
|
||||
return sut(<Yaml.YAMLScalar>scalar)
|
||||
}
|
||||
|
||||
function safeLoad(input) {
|
||||
return Yaml.safeLoad(input, {})
|
||||
}
|
||||
|
||||
let _test = test;
|
||||
|
||||
// http://www.yaml.org/spec/1.2/spec.html#id2805071
|
||||
suite('Plain Tag Resolution', () => {
|
||||
|
||||
function test(name, type, acceptable) {
|
||||
_test(name, function () {
|
||||
for (const word of acceptable) {
|
||||
assert.strictEqual(determineScalarType(safeLoad(word)), type, word)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
test('boolean', ScalarType.bool, ["true", "True", "TRUE", "false", "False", "FALSE"])
|
||||
|
||||
test("null", ScalarType.null, ["null", "Null", "NULL", "~", ""])
|
||||
_test("null as from an array", function () {
|
||||
const node = Yaml.newScalar('');
|
||||
node.plainScalar = true;
|
||||
assert.strictEqual(determineScalarType(node), ScalarType.null, "unquoted empty string")
|
||||
})
|
||||
|
||||
test("integer", ScalarType.int, ["0", "0o7", "0x3A", "-19"])
|
||||
|
||||
test("float", ScalarType.float, ["0.", "-0.0", ".5", "+12e03", "-2E+05"])
|
||||
|
||||
test("float-infinity", ScalarType.float, [".inf", "-.Inf", "+.INF"])
|
||||
|
||||
test("float-NaN", ScalarType.float, [".nan", ".NaN", ".NAN"])
|
||||
|
||||
test("string", ScalarType.string, ["'true'", "TrUe", "nULl", "''", "'0'", '"1"', '" .5"', ".inF", ".nAn"])
|
||||
})
|
||||
|
||||
suite('Flow style', () => {
|
||||
test('still recognizes types', function () {
|
||||
const node = <Yaml.YAMLSequence>safeLoad(`[ null,
|
||||
true,
|
||||
0,
|
||||
0.,
|
||||
.inf,
|
||||
.nan,
|
||||
"-123\n345"
|
||||
]`)
|
||||
|
||||
const expected = [ScalarType.null, ScalarType.bool, ScalarType.int, ScalarType.float, ScalarType.float, ScalarType.float, ScalarType.string]
|
||||
|
||||
assert.deepEqual(node.items.map(d => determineScalarType(d)), expected)
|
||||
})
|
||||
})
|
||||
|
||||
suite('Block styles', () => {
|
||||
var variations = ['>', '|', '>8', '|+1', '>-', '>+', '|-', '|+']
|
||||
|
||||
test('are always strings', function () {
|
||||
for (const variant of variations) {
|
||||
assert.deepEqual(determineScalarType(safeLoad(variant + "\n 123")), ScalarType.string);
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
suite('parseYamlInteger', () => {
|
||||
test('decimal', function () {
|
||||
assert.strictEqual(parseYamlInteger("0"), 0)
|
||||
assert.strictEqual(parseYamlInteger("-19"), -19)
|
||||
assert.strictEqual(parseYamlInteger("+1"), 1)
|
||||
})
|
||||
|
||||
test('hexadecimal', function () {
|
||||
assert.strictEqual(parseYamlInteger("0x3A"), 58)
|
||||
})
|
||||
|
||||
test('octal', function () {
|
||||
assert.strictEqual(parseYamlInteger("0o7"), 7)
|
||||
})
|
||||
|
||||
test('otherwise', function () {
|
||||
let error;
|
||||
try {
|
||||
parseYamlInteger("'1'")
|
||||
}
|
||||
catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
assert(error, "should have thrown")
|
||||
})
|
||||
})
|
||||
|
||||
suite('parseYamlBoolean', () => {
|
||||
test('true', function () {
|
||||
for (const value of ["true", "True", "TRUE"]) {
|
||||
assert.strictEqual(parseYamlBoolean(value), true, value);
|
||||
}
|
||||
})
|
||||
|
||||
test('false', function () {
|
||||
for (const value of ["false", "False", "FALSE"]) {
|
||||
assert.strictEqual(parseYamlBoolean(value), false, value);
|
||||
}
|
||||
})
|
||||
|
||||
test('otherwise', function () {
|
||||
let error;
|
||||
try {
|
||||
parseYamlBoolean("tRUE")
|
||||
}
|
||||
catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
assert(error, "should have thrown")
|
||||
})
|
||||
})
|
||||
|
||||
suite('parseYamlFloat', () => {
|
||||
test('float', function () {
|
||||
const values = ["0.", "-0.0", ".5", "+12e03", "-2E+05"]
|
||||
const expected = [0, -0, 0.5, 12000, -200000]
|
||||
for (var index = 0; index < values.length; index++) {
|
||||
assert.strictEqual(parseYamlFloat(values[index]), expected[index])
|
||||
}
|
||||
})
|
||||
|
||||
test('NaN', function () {
|
||||
for (const value of [".nan", ".NaN", ".NAN"]) {
|
||||
assert(isNaN(parseYamlFloat(value)), `isNaN(${value})`)
|
||||
}
|
||||
})
|
||||
|
||||
test('infinity', function () {
|
||||
assert.strictEqual(parseYamlFloat(".inf"),
|
||||
Infinity)
|
||||
assert.strictEqual(parseYamlFloat("-.Inf"), -Infinity)
|
||||
assert.strictEqual(parseYamlFloat(".INF"), Infinity)
|
||||
})
|
||||
|
||||
test('otherwise', function () {
|
||||
let error;
|
||||
try {
|
||||
parseYamlFloat("text")
|
||||
}
|
||||
catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
assert(error, "should have thrown")
|
||||
})
|
||||
})
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import util = require("./testUtil");
|
||||
|
||||
suite('YAML Syntax', () => {
|
||||
|
||||
suite('Warnings for tab symbols', () => {
|
||||
|
||||
test('test 001', function () {
|
||||
testErrors(
|
||||
"schemas:\n" +
|
||||
" - !i",
|
||||
[
|
||||
{
|
||||
line: 1,
|
||||
column: 4,
|
||||
message: "unknown tag <!i>",
|
||||
isWarning: false
|
||||
}
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
test('test 002', function () {
|
||||
testErrors(
|
||||
"schemas:\n" +
|
||||
" - !in",
|
||||
[
|
||||
{
|
||||
line: 1,
|
||||
column: 4,
|
||||
message: "unknown tag <!in>",
|
||||
isWarning: false
|
||||
}
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
test('test 003', function () {
|
||||
testErrors(
|
||||
"schemas:\n" +
|
||||
" - !inc",
|
||||
[
|
||||
{
|
||||
line: 1,
|
||||
column: 4,
|
||||
message: "unknown tag <!inc>",
|
||||
isWarning: false
|
||||
}
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
test('test 004', function () {
|
||||
testErrors(
|
||||
"schemas:\n" +
|
||||
" - !incl",
|
||||
[
|
||||
{
|
||||
line: 1,
|
||||
column: 4,
|
||||
message: "unknown tag <!incl>",
|
||||
isWarning: false
|
||||
}
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function testErrors(input:string,expectedErrors: util.TestError[]) {
|
||||
util.testErrors(input, expectedErrors);
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import YAMLException = require("../src/exception");
|
||||
|
||||
import * as chai from 'chai';
|
||||
const assert = chai.assert;
|
||||
import {safeLoad as loadYaml} from '../src/index';
|
||||
import ast=require("../src/yamlAST");
|
||||
|
||||
export interface TestError{
|
||||
message: string
|
||||
line: number
|
||||
column: number
|
||||
isWarning:boolean
|
||||
}
|
||||
|
||||
export function testErrors(input:string,expectedErrors: TestError[]) {
|
||||
|
||||
let errorsMap: {[key:string]:boolean} = {};
|
||||
for(let e of expectedErrors){
|
||||
let key = `${e.message} at line ${e.line} column ${e.column}`;
|
||||
if(e.isWarning){
|
||||
key += " (warning)";
|
||||
}
|
||||
errorsMap[key] = true;
|
||||
}
|
||||
|
||||
let ast = safeLoad(input);
|
||||
if(!ast){
|
||||
assert.fail("The parser has failed to load YAML AST");
|
||||
}
|
||||
let actualErrors = ast.errors;
|
||||
if(actualErrors.length==0 && expectedErrors.length==0){
|
||||
assert(true);
|
||||
return;
|
||||
}
|
||||
let unexpectedErrorsMap: {[key:string]:YAMLException} = {};
|
||||
for(let e of actualErrors){
|
||||
let key = `${e.reason} at line ${e.mark.line} column ${e.mark.column}`;
|
||||
if(e.isWarning){
|
||||
key += " (warning)";
|
||||
}
|
||||
if(!errorsMap[key]){
|
||||
unexpectedErrorsMap[key] = e;
|
||||
}
|
||||
else{
|
||||
delete errorsMap[key];
|
||||
}
|
||||
}
|
||||
let missingErrors = Object.keys(errorsMap);
|
||||
let unexpectedErrorKeys = Object.keys(unexpectedErrorsMap);
|
||||
if(missingErrors.length==0 && unexpectedErrorKeys.length==0){
|
||||
assert(true);
|
||||
return;
|
||||
}
|
||||
let messageComponents:string[] = [];
|
||||
if(unexpectedErrorKeys.length>0) {
|
||||
messageComponents.push(`Unexpected errors:\n${unexpectedErrorKeys.join('\n')}`);
|
||||
}
|
||||
if(missingErrors.length>0){
|
||||
messageComponents.push(`Missing errors:\n${missingErrors.join('\n')}`);
|
||||
}
|
||||
let testFailureMessage = `\n${messageComponents.join("\n\n")}`;
|
||||
assert(false,testFailureMessage);
|
||||
};
|
||||
|
||||
export function safeLoad(input):ast.YAMLNode {
|
||||
return loadYaml(input, {})
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import * as YAML from '../src/'
|
||||
|
||||
export interface NodeVisitor {
|
||||
visitScalar(node: YAML.YAMLScalar);
|
||||
visitMapping(node: YAML.YAMLMapping);
|
||||
visitSequence(node: YAML.YAMLSequence);
|
||||
visitMap(node: YAML.YamlMap);
|
||||
visitAnchorRef(node: YAML.YAMLAnchorReference);
|
||||
visitIncludeRef(node: YAML.YAMLNode);
|
||||
}
|
||||
|
||||
export abstract class AbstractVisitor implements NodeVisitor {
|
||||
// Needed in lieu of `accept` method on nodes
|
||||
accept(node: YAML.YAMLNode) {
|
||||
switch (node.kind) {
|
||||
case YAML.Kind.SCALAR: {
|
||||
return this.visitScalar(<YAML.YAMLScalar>node);
|
||||
}
|
||||
case YAML.Kind.MAP: {
|
||||
return this.visitMap(<YAML.YamlMap>node);
|
||||
}
|
||||
case YAML.Kind.MAPPING: {
|
||||
return this.visitMapping(<YAML.YAMLMapping>node);
|
||||
}
|
||||
case YAML.Kind.SEQ: {
|
||||
return this.visitSequence(<YAML.YAMLSequence>node);
|
||||
}
|
||||
case YAML.Kind.ANCHOR_REF: {
|
||||
return this.visitAnchorRef(<YAML.YAMLAnchorReference>node);
|
||||
}
|
||||
case YAML.Kind.INCLUDE_REF: {
|
||||
return this.visitIncludeRef(node);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Kind, ${node.kind} not implemented.`);
|
||||
}
|
||||
abstract visitScalar(node: YAML.YAMLScalar);
|
||||
abstract visitMapping(node: YAML.YAMLMapping);
|
||||
abstract visitSequence(node: YAML.YAMLSequence);
|
||||
abstract visitMap(node: YAML.YamlMap);
|
||||
abstract visitAnchorRef(node: YAML.YAMLAnchorReference);
|
||||
abstract visitIncludeRef(node: YAML.YAMLNode);
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import util = require("./testUtil");
|
||||
|
||||
suite('YAML Syntax', () => {
|
||||
|
||||
suite('Warnings for tab symbols', () => {
|
||||
|
||||
test('test 001', function () {
|
||||
testErrors(
|
||||
"schemas:\n" +
|
||||
"\tsch1:\n",
|
||||
[
|
||||
{
|
||||
line: 1,
|
||||
column: 0,
|
||||
message: "Using tabs can lead to unpredictable results",
|
||||
isWarning: true
|
||||
}
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
test('test 002', function () {
|
||||
testErrors(
|
||||
"level0:\n" +
|
||||
" level1:\n" +
|
||||
" level2:\n" +
|
||||
" \t level3:\n",
|
||||
[
|
||||
{
|
||||
line: 3,
|
||||
column: 2,
|
||||
message: "Using tabs can lead to unpredictable results",
|
||||
isWarning: true
|
||||
}
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function testErrors(input:string,expectedErrors: util.TestError[]) {
|
||||
util.testErrors(input, expectedErrors);
|
||||
}
|
||||
Reference in New Issue
Block a user