Files
OpenRA/OpenRA.Test/OpenRA.Game/MiniYamlTest.cs
Paul Chote df31690332 Extend MiniYaml parser with new features:
- 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.
2018-05-12 16:42:54 +02:00

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);
}
}
}