summaryrefslogtreecommitdiff
path: root/mcs/class/Microsoft.Build/Test/Microsoft.Build.Internal/ExpressionParserTest.cs
diff options
context:
space:
mode:
Diffstat (limited to 'mcs/class/Microsoft.Build/Test/Microsoft.Build.Internal/ExpressionParserTest.cs')
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Internal/ExpressionParserTest.cs287
1 files changed, 287 insertions, 0 deletions
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Internal/ExpressionParserTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Internal/ExpressionParserTest.cs
new file mode 100644
index 0000000000..7654538a1a
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Internal/ExpressionParserTest.cs
@@ -0,0 +1,287 @@
+//
+// ExpressionParserTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using NUnit.Framework;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Exceptions;
+using System.Collections.Generic;
+
+namespace MonoTests.Microsoft.Build.Internal
+{
+ [TestFixture]
+ public class ExpressionParserTest
+ {
+ string [] invalid_always = {
+ "$(Foo..Bar)",
+ "$([DateTime.Now])", // fullname required
+ "$([System.DateTime.Now])", // member cannot be invoked with '.'
+ };
+ string [] invalid_as_boolean = {
+ "$",
+ "@",
+ "%",
+ "%-1",
+ "$(",
+ "%(",
+ "$)",
+ "%)",
+ "%24",
+ "()",
+ "{}",
+ "A", // must be evaluated as a boolean
+ "1", // ditto (no default conversion to bool)
+ "$ (foo) == ''",
+ "@ (foo) == ''",
+ "$(1)",
+ "$(Foo) == And", // reserved keyword 'and'
+ "$(Foo) == Or", // reserved keyword 'or'
+ "$(Foo) == $(Bar) == $(Baz)", // unexpected '=='
+ "$([System.DateTime]::Now)", // it is DateTime
+ "$([System.String]::Format('Tr'))$([System.String]::Format('ue'))", // only one expression is accepted
+ "$([System.String]::Format(null))", // causing ANE, wrapped by InvalidProjectFileException
+ "yep",
+ "nope",
+ "ONN",
+ "OFFF",
+ };
+ string [] valid = {
+ "'%24' == 0",
+ "true",
+ "fAlSe",
+ "(false)",
+ "A==A",
+ "A ==A",
+ "A== A",
+ "A=='A'",
+ "A==\tA",
+ "\tA== A",
+ "$([System.String]::Format('True'))",
+ "$([System.String]::Format('True', null))",
+ "False And True == True And True",
+ "True or True or False",
+ "(True or True or False)",
+ "True and False",
+ "(True) and (False)",
+ "yes",
+ "nO",
+ "oN",
+ "oFf",
+ };
+ string [] depends = {
+ // valid only if evaluated to boolean
+ "$(foo)",
+ "@(foo)",
+ };
+
+ [Test]
+ public void EvaluateAsBoolean ()
+ {
+ foreach (var expr in invalid_always.Concat (invalid_as_boolean).Concat (valid)) {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Condition=""{0}"" Include='x' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (string.Format (project_xml, expr)));
+ var root = ProjectRootElement.Create (xml);
+ try {
+ new Project (root);
+ if (invalid_as_boolean.Contains (expr) || invalid_always.Contains (expr))
+ Assert.Fail ("Parsing Condition '{0}' should fail", expr);
+ } catch (Exception ex) {
+ if (valid.Contains (expr))
+ throw new Exception (string.Format ("failed to parse '{0}'", expr), ex);
+ else if (ex is InvalidProjectFileException)
+ continue;
+ throw new Exception (string.Format ("unexpected exception to parse '{0}'", expr), ex);
+ }
+ }
+ }
+
+ [Test]
+ public void EvaluateAsString ()
+ {
+ foreach (var expr in invalid_always.Concat (invalid_as_boolean).Concat (valid)) {
+ try {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Include=""{0}"" />
+ </ItemGroup>
+ </Project>";
+ var xml = XmlReader.Create (new StringReader (string.Format (project_xml, expr)));
+ var root = ProjectRootElement.Create (xml);
+ // everything but 'invalid_always' should pass
+ new Project (root);
+ } catch (Exception ex) {
+ if (!invalid_always.Contains (expr))
+ throw new Exception (string.Format ("failed to parse '{0}'", expr), ex);
+ }
+ }
+ }
+
+ [Test]
+ public void EvaluatePropertyReferencesWithProperties ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Condition=""$(foo)"" Include='x' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var props = new Dictionary<string,string> ();
+ props ["foo"] = "true";
+ new Project (root, props, null);
+ }
+
+ [Test]
+ public void EvaluateItemReferences ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Include='false' />
+ <!-- by the time Bar is evaluated, Foo is already evaluated and taken into consideration in expansion -->
+ <Bar Condition=""@(foo)"" Include='x' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ new Project (root);
+ }
+
+ [Test]
+ public void EvaluateReferencesWithoutProperties ()
+ {
+ foreach (var expr in depends) {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Condition=""{0}"" Include='x' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (string.Format (project_xml, expr)));
+ var root = ProjectRootElement.Create (xml);
+ try {
+ new Project (root);
+ Assert.Fail ("Parsing Condition '{0}' should fail", expr);
+ } catch (InvalidProjectFileException) {
+ continue;
+ }
+ }
+ }
+
+ [Test]
+ public void SemicolonHandling ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <Foo Condition=""'A;B'=='A;B'"">'A;B'</Foo>
+ </PropertyGroup>
+ <ItemGroup>
+ <Bar Include='$(Foo)' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root); // at this state property is parsed without error i.e. Condition evaluates fine.
+ var prop = proj.GetProperty ("Foo");
+ Assert.AreEqual ("'A;B'", prop.EvaluatedValue, "#1");
+ var items = proj.GetItems ("Bar");
+ Assert.AreEqual ("'A", items.First ().EvaluatedInclude, "#2");
+ Assert.AreEqual ("$(Foo)", items.First ().UnevaluatedInclude, "#3");
+ Assert.AreEqual (2, items.Count, "#4");
+ Assert.AreEqual ("B'", items.Last ().EvaluatedInclude, "#5");
+ Assert.AreEqual ("$(Foo)", items.Last ().UnevaluatedInclude, "#6");
+ Assert.IsTrue (items.First ().Xml == items.Last ().Xml, "#7");
+ }
+
+ // the same as above except that ItemGroup goes first (and yet evaluated the same).
+ [Test]
+ public void EvaluationOrderPropertiesPrecedesItems ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Bar Include='$(Foo)' />
+ </ItemGroup>
+ <PropertyGroup>
+ <Foo Condition=""'A;B'=='A;B'"">'A;B'</Foo>
+ </PropertyGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root); // at this state property is parsed without error i.e. Condition evaluates fine.
+ var prop = proj.GetProperty ("Foo");
+ Assert.AreEqual ("'A;B'", prop.EvaluatedValue, "#1");
+ var items = proj.GetItems ("Bar");
+ Assert.AreEqual ("'A", items.First ().EvaluatedInclude, "#2");
+ Assert.AreEqual ("$(Foo)", items.First ().UnevaluatedInclude, "#3");
+ Assert.AreEqual (2, items.Count, "#4");
+ Assert.AreEqual ("B'", items.Last ().EvaluatedInclude, "#5");
+ Assert.AreEqual ("$(Foo)", items.Last ().UnevaluatedInclude, "#6");
+ Assert.IsTrue (items.First ().Xml == items.Last ().Xml, "#7");
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidProjectFileException))]
+ public void PropertyReferencesItem ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Bar Include='True' />
+ </ItemGroup>
+ <PropertyGroup>
+ <Foo Condition='@(Bar)'>X</Foo><!-- not allowed -->
+ </PropertyGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ new Project (root);
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidProjectFileException))]
+ public void SequentialPropertyReferenceNotAllowed ()
+ {
+ string xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <A>x</A>
+ <B>y</B>
+ <C Condition=""$(A)$(B)==''"">z</C>
+ </PropertyGroup>
+</Project>";
+ var reader = XmlReader.Create (new StringReader (xml));
+ var root = ProjectRootElement.Create (reader);
+ new Project (root);
+ }
+ }
+}
+