- Add support for escaping '#' inside values - Add support for escaping leading and trailing whitespace And when discardCommentsAndWhitespace is set to false: - Add proper support for comments - Persist empty lines Whitespace and comment support requires an explicit opt-in because they produce MiniYamlNodes with null keys. Supporting these through the entire game engine would require changing all yaml enumerations to explicitly check and account for these keys with no benefit. Comments and whitespace are now treated as real nodes during parsing, which means that the yaml parser will throw errors if they have incorrect indentation, even if these nodes will be discarded.
276 lines
7.9 KiB
C#
276 lines
7.9 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2018 The OpenRA Developers (see AUTHORS)
|
|
* This file is part of OpenRA, which is free software. It is made
|
|
* available to you under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation, either version 3 of
|
|
* the License, or (at your option) any later version. For more
|
|
* information, see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using NUnit.Framework;
|
|
|
|
namespace OpenRA.Test
|
|
{
|
|
[TestFixture]
|
|
public class MiniYamlTest
|
|
{
|
|
readonly string yamlTabStyle = @"
|
|
Root1:
|
|
Child1:
|
|
Attribute1: Test
|
|
Attribute2: Test
|
|
Child2:
|
|
Attribute1: Test
|
|
Attribute2: Test
|
|
Root2:
|
|
Child1:
|
|
Attribute1: Test
|
|
";
|
|
|
|
readonly string yamlMixedStyle = @"
|
|
Root1:
|
|
Child1:
|
|
Attribute1: Test
|
|
Attribute2: Test
|
|
Child2:
|
|
Attribute1: Test
|
|
Attribute2: Test
|
|
Root2:
|
|
Child1:
|
|
Attribute1: Test
|
|
";
|
|
|
|
[TestCase(TestName = "Mixed tabs & spaces indents")]
|
|
public void TestIndents()
|
|
{
|
|
var tabs = MiniYaml.FromString(yamlTabStyle, "yamlTabStyle").WriteToString();
|
|
Console.WriteLine(tabs);
|
|
var mixed = MiniYaml.FromString(yamlMixedStyle, "yamlMixedStyle").WriteToString();
|
|
Console.WriteLine(mixed);
|
|
Assert.That(tabs, Is.EqualTo(mixed));
|
|
}
|
|
|
|
[TestCase(TestName = "Inheritance and removal can be composed")]
|
|
public void InheritanceAndRemovalCanBeComposed()
|
|
{
|
|
var baseYaml = @"
|
|
^BaseA:
|
|
MockA2:
|
|
^BaseB:
|
|
Inherits@a: ^BaseA
|
|
MockB2:
|
|
";
|
|
var extendedYaml = @"
|
|
Test:
|
|
Inherits@b: ^BaseB
|
|
-MockA2:
|
|
";
|
|
var mapYaml = @"
|
|
^BaseC:
|
|
MockC2:
|
|
Test:
|
|
Inherits@c: ^BaseC
|
|
";
|
|
var result = MiniYaml.Merge(new[] { baseYaml, extendedYaml, mapYaml }.Select(s => MiniYaml.FromString(s, "")))
|
|
.First(n => n.Key == "Test").Value.Nodes;
|
|
|
|
Assert.IsFalse(result.Any(n => n.Key == "MockA2"), "Node should not have the MockA2 child, but does.");
|
|
Assert.IsTrue(result.Any(n => n.Key == "MockB2"), "Node should have the MockB2 child, but does not.");
|
|
Assert.IsTrue(result.Any(n => n.Key == "MockC2"), "Node should have the MockC2 child, but does not.");
|
|
}
|
|
|
|
[TestCase(TestName = "Child can be removed after multiple inheritance")]
|
|
public void ChildCanBeRemovedAfterMultipleInheritance()
|
|
{
|
|
var baseYaml = @"
|
|
^BaseA:
|
|
MockA2:
|
|
Test:
|
|
Inherits: ^BaseA
|
|
MockA2:
|
|
";
|
|
var overrideYaml = @"
|
|
Test:
|
|
-MockA2
|
|
";
|
|
|
|
var result = MiniYaml.Merge(new[] { baseYaml, overrideYaml }.Select(s => MiniYaml.FromString(s, "")))
|
|
.First(n => n.Key == "Test").Value.Nodes;
|
|
|
|
Assert.IsFalse(result.Any(n => n.Key == "MockA2"), "Node should not have the MockA2 child, but does.");
|
|
}
|
|
|
|
[TestCase(TestName = "Child can be removed and later overridden")]
|
|
public void ChildCanBeRemovedAndLaterOverridden()
|
|
{
|
|
var baseYaml = @"
|
|
^BaseA:
|
|
MockString:
|
|
AString: Base
|
|
Test:
|
|
Inherits: ^BaseA
|
|
-MockString:
|
|
";
|
|
var overrideYaml = @"
|
|
Test:
|
|
MockString:
|
|
AString: Override
|
|
";
|
|
|
|
var result = MiniYaml.Merge(new[] { baseYaml, overrideYaml }.Select(s => MiniYaml.FromString(s, "")))
|
|
.First(n => n.Key == "Test").Value.Nodes;
|
|
|
|
Assert.IsTrue(result.Any(n => n.Key == "MockString"), "Node should have the MockString child, but does not.");
|
|
Assert.IsTrue(result.First(n => n.Key == "MockString").Value.ToDictionary()["AString"].Value == "Override",
|
|
"MockString value has not been set with the correct override value for AString.");
|
|
}
|
|
|
|
[TestCase(TestName = "Child can be removed from intermediate parent")]
|
|
public void ChildCanBeOverriddenThenRemoved()
|
|
{
|
|
var baseYaml = @"
|
|
^BaseA:
|
|
MockString:
|
|
AString: Base
|
|
^BaseB:
|
|
Inherits: ^BaseA
|
|
MockString:
|
|
AString: Override
|
|
";
|
|
var overrideYaml = @"
|
|
Test:
|
|
Inherits: ^BaseB
|
|
MockString:
|
|
-AString:
|
|
";
|
|
|
|
var result = MiniYaml.Merge(new[] { baseYaml, overrideYaml }.Select(s => MiniYaml.FromString(s, "")))
|
|
.First(n => n.Key == "Test").Value.Nodes;
|
|
Assert.IsTrue(result.Any(n => n.Key == "MockString"), "Node should have the MockString child, but does not.");
|
|
Assert.IsFalse(result.First(n => n.Key == "MockString").Value.Nodes.Any(n => n.Key == "AString"),
|
|
"MockString value should have been removed, but was not.");
|
|
}
|
|
|
|
[TestCase(TestName = "Empty lines should count toward line numbers")]
|
|
public void EmptyLinesShouldCountTowardLineNumbers()
|
|
{
|
|
var yaml = @"
|
|
TestA:
|
|
Nothing:
|
|
|
|
TestB:
|
|
Nothing:
|
|
";
|
|
|
|
var result = MiniYaml.FromString(yaml).First(n => n.Key == "TestB");
|
|
Assert.AreEqual(5, result.Location.Line);
|
|
}
|
|
|
|
[TestCase(TestName = "Duplicated nodes are correctly merged")]
|
|
public void TestSelfMerging()
|
|
{
|
|
var baseYaml = @"
|
|
Test:
|
|
Merge: original
|
|
Child: original
|
|
Original:
|
|
Test:
|
|
Merge: override
|
|
Child: override
|
|
Override:
|
|
";
|
|
|
|
var result = MiniYaml.Merge(new[] { baseYaml }.Select(s => MiniYaml.FromString(s, "")));
|
|
Assert.That(result.Count(n => n.Key == "Test"), Is.EqualTo(1), "Result should have exactly one Test node.");
|
|
|
|
var testNodes = result.First(n => n.Key == "Test").Value.Nodes;
|
|
Assert.That(testNodes.Select(n => n.Key), Is.EqualTo(new[] { "Merge", "Original", "Override" }), "Merged Test node has incorrect child nodes.");
|
|
|
|
var mergeNode = testNodes.First(n => n.Key == "Merge").Value;
|
|
Assert.That(mergeNode.Value, Is.EqualTo("override"), "Merge node has incorrect value.");
|
|
Assert.That(mergeNode.Nodes[0].Value.Value, Is.EqualTo("override"), "Merge node Child value should be 'override', but is not");
|
|
}
|
|
|
|
[TestCase(TestName = "Comments are correctly separated from values")]
|
|
public void TestEscapedHashInValues()
|
|
{
|
|
var trailingWhitespace = @"key: value # comment";
|
|
Assert.AreEqual("value", MiniYaml.FromString(trailingWhitespace, "trailingWhitespace")[0].Value.Value);
|
|
|
|
var noWhitespace = @"key:value# comment";
|
|
Assert.AreEqual("value", MiniYaml.FromString(noWhitespace, "noWhitespace")[0].Value.Value);
|
|
|
|
var escapedHashInValue = @"key: before \# after # comment";
|
|
Assert.AreEqual("before # after", MiniYaml.FromString(escapedHashInValue, "escapedHashInValue")[0].Value.Value);
|
|
|
|
var emptyValue = @"key:# comment";
|
|
Assert.AreEqual(null, MiniYaml.FromString(emptyValue, "emptyValue")[0].Value.Value);
|
|
}
|
|
|
|
[TestCase(TestName = "Leading and trailing whitespace can be guarded using a backslash")]
|
|
public void TestGuardedWhitespace()
|
|
{
|
|
var testYaml = @"key: \ test value \ ";
|
|
var nodes = MiniYaml.FromString(testYaml, "testYaml");
|
|
Assert.AreEqual(" test value ", nodes[0].Value.Value);
|
|
}
|
|
|
|
[TestCase(TestName = "Comments should count toward line numbers")]
|
|
public void CommentsShouldCountTowardLineNumbers()
|
|
{
|
|
var yaml = @"
|
|
TestA:
|
|
Nothing:
|
|
|
|
# Comment
|
|
TestB:
|
|
Nothing:
|
|
";
|
|
|
|
var resultDiscard = MiniYaml.FromString(yaml).First(n => n.Key == "TestB");
|
|
Assert.That(resultDiscard.Location.Line, Is.EqualTo(6), "Node TestB should report its location as line 6, but is not (discarding comments)");
|
|
|
|
var resultKeep = MiniYaml.FromString(yaml, discardCommentsAndWhitespace: false).First(n => n.Key == "TestB");
|
|
Assert.That(resultDiscard.Location.Line, Is.EqualTo(6), "Node TestB should report its location as line 6, but is not (parsing comments)");
|
|
}
|
|
|
|
[TestCase(TestName = "Comments should survive a round trip intact")]
|
|
public void CommentsSurviveRoundTrip()
|
|
{
|
|
var yaml = @"
|
|
# Top level comment node
|
|
Parent: # comment without value
|
|
# Indented comment node
|
|
First: value containing a \# character
|
|
Second: value # node with inline comment
|
|
";
|
|
|
|
var result = MiniYaml.FromString(yaml, discardCommentsAndWhitespace: false).WriteToString();
|
|
Assert.AreEqual(yaml, result);
|
|
}
|
|
|
|
[TestCase(TestName = "Comments should be be removed when discardCommentsAndWhitespace is false")]
|
|
public void CommentsShouldntSurviveRoundTrip()
|
|
{
|
|
var yaml = @"
|
|
# Top level comment node
|
|
Parent: # comment without value
|
|
# Indented comment node
|
|
First: value containing a \# character
|
|
Second: value # node with inline comment
|
|
";
|
|
|
|
var strippedYaml = @"Parent:
|
|
First: value containing a \# character
|
|
Second: value";
|
|
|
|
var result = MiniYaml.FromString(yaml).WriteToString();
|
|
Assert.AreEqual(strippedYaml, result);
|
|
}
|
|
}
|
|
}
|