context: //
  // 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]
  
gt: 		[ExpectedException (typeof (InvalidProjectFileException))]