Merge pull request #820 from brendanzagaeski/master
[mono.git] / mcs / class / Microsoft.Build / Test / Microsoft.Build.Internal / ExpressionParserTest.cs
1 //
2 // ExpressionParserTest.cs
3 //
4 // Author:
5 //   Atsushi Enomoto (atsushi@xamarin.com)
6 //
7 // Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.IO;
30 using System.Linq;
31 using System.Xml;
32 using Microsoft.Build.Construction;
33 using Microsoft.Build.Evaluation;
34 using NUnit.Framework;
35 using Microsoft.Build.Execution;
36 using Microsoft.Build.Exceptions;
37 using System.Collections.Generic;
38
39 namespace MonoTests.Microsoft.Build.Internal
40 {
41         [TestFixture]
42         public class ExpressionParserTest
43         {
44                         string [] invalid_always = {
45                                 "$(Foo..Bar)",
46                                 "$([DateTime.Now])", // fullname required
47                                 "$([System.DateTime.Now])", // member cannot be invoked with '.'
48                         };
49                         string [] invalid_as_boolean = {
50                                 "$",
51                                 "@",
52                                 "%",
53                                 "%-1",
54                                 "$(",
55                                 "%(",
56                                 "$)",
57                                 "%)",
58                                 "%24",
59                                 "()",
60                                 "{}",
61                                 "A", // must be evaluated as a boolean
62                                 "1", // ditto (no default conversion to bool)
63                                 "$ (foo) == ''",
64                                 "@ (foo) == ''",
65                                 "$(1)",
66                                 "$(Foo) == And", // reserved keyword 'and'
67                                 "$(Foo) == Or", // reserved keyword 'or'
68                                 "$(Foo) == $(Bar) == $(Baz)", // unexpected '=='
69                                 "$([System.DateTime]::Now)", // it is DateTime
70                                 "$([System.String]::Format('Tr'))$([System.String]::Format('ue'))", // only one expression is accepted
71                                 "$([System.String]::Format(null))", // causing ANE, wrapped by InvalidProjectFileException
72                                 "yep",
73                                 "nope",
74                                 "ONN",
75                                 "OFFF",
76                         };
77                         string [] valid = {
78                                 "'%24' == 0",
79                                 "true",
80                                 "fAlSe",
81                                 "(false)",
82                                 "A==A",
83                                 "A ==A",
84                                 "A== A",
85                                 "A=='A'",
86                                 "A==\tA",
87                                 "\tA== A",
88                                 "$([System.String]::Format('True'))",
89                                 "$([System.String]::Format('True', null))",
90                                 "False And True == True And True",
91                                 "True or True or False",
92                                 "(True or True or False)",
93                                 "True and False",
94                                 "(True) and (False)",
95                                 "yes",
96                                 "nO",
97                                 "oN",
98                                 "oFf",
99                         };
100                         string [] depends = {
101                                 // valid only if evaluated to boolean
102                                 "$(foo)",
103                                 "@(foo)",
104                         };
105                         
106                 [Test]
107                 public void EvaluateAsBoolean ()
108                 {
109                         foreach (var expr in invalid_always.Concat (invalid_as_boolean).Concat (valid)) {
110                                 string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
111   <ItemGroup>
112     <Foo Condition=""{0}"" Include='x' />
113   </ItemGroup>
114 </Project>";
115                                 var xml = XmlReader.Create (new StringReader (string.Format (project_xml, expr)));
116                                 var root = ProjectRootElement.Create (xml);
117                                 try {
118                                         new Project (root);
119                                         if (invalid_as_boolean.Contains (expr) || invalid_always.Contains (expr))
120                                                 Assert.Fail ("Parsing Condition '{0}' should fail", expr);
121                                 } catch (Exception ex) {
122                                         if (valid.Contains (expr))
123                                                 throw new Exception (string.Format ("failed to parse '{0}'", expr), ex);
124                                         else if (ex is InvalidProjectFileException)
125                                                 continue;
126                                         throw new Exception (string.Format ("unexpected exception to parse '{0}'", expr), ex);
127                                 }
128                         }
129                 }
130                 
131                 [Test]
132                 public void EvaluateAsString ()
133                 {
134                         foreach (var expr in invalid_always.Concat (invalid_as_boolean).Concat (valid)) {
135                                 try {
136                                         string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
137           <ItemGroup>
138             <Foo Include=""{0}"" />
139           </ItemGroup>
140         </Project>";
141                                         var xml = XmlReader.Create (new StringReader (string.Format (project_xml, expr)));
142                                         var root = ProjectRootElement.Create (xml);
143                                         // everything but 'invalid_always' should pass
144                                         new Project (root);
145                                 } catch (Exception ex) {
146                                         if (!invalid_always.Contains (expr))
147                                                 throw new Exception (string.Format ("failed to parse '{0}'", expr), ex);
148                                 }
149                         }
150                 }
151                 
152                 [Test]
153                 public void EvaluatePropertyReferencesWithProperties ()
154                 {
155                         string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
156   <ItemGroup>
157     <Foo Condition=""$(foo)"" Include='x' />
158   </ItemGroup>
159 </Project>";
160                         var xml = XmlReader.Create (new StringReader (project_xml));
161                         var root = ProjectRootElement.Create (xml);
162                         var props = new Dictionary<string,string> ();
163                         props ["foo"] = "true";
164                         new Project (root, props, null);
165                 }
166                 
167                 [Test]
168                 public void EvaluateItemReferences ()
169                 {
170                         string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
171   <ItemGroup>
172     <Foo Include='false' />
173     <!-- by the time Bar is evaluated, Foo is already evaluated and taken into consideration in expansion -->
174     <Bar Condition=""@(foo)"" Include='x' />
175   </ItemGroup>
176 </Project>";
177                         var xml = XmlReader.Create (new StringReader (project_xml));
178                         var root = ProjectRootElement.Create (xml);
179                         new Project (root);
180                 }
181                 
182                 [Test]
183                 public void EvaluateReferencesWithoutProperties ()
184                 {
185                         foreach (var expr in depends) {
186                                 string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
187   <ItemGroup>
188     <Foo Condition=""{0}"" Include='x' />
189   </ItemGroup>
190 </Project>";
191                                 var xml = XmlReader.Create (new StringReader (string.Format (project_xml, expr)));
192                                 var root = ProjectRootElement.Create (xml);
193                                 try {
194                                         new Project (root);
195                                         Assert.Fail ("Parsing Condition '{0}' should fail", expr);
196                                 } catch (InvalidProjectFileException) {
197                                         continue;
198                                 }
199                         }
200                 }
201                 
202                 [Test]
203                 public void SemicolonHandling ()
204                 {
205                         string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
206   <PropertyGroup>
207     <Foo Condition=""'A;B'=='A;B'"">'A;B'</Foo>
208   </PropertyGroup>
209   <ItemGroup>
210     <Bar Include='$(Foo)' />
211   </ItemGroup>
212 </Project>";
213                         var xml = XmlReader.Create (new StringReader (project_xml));
214                         var root = ProjectRootElement.Create (xml);
215                         var proj = new Project (root); // at this state property is parsed without error i.e. Condition evaluates fine.
216                         var prop = proj.GetProperty ("Foo");
217                         Assert.AreEqual ("'A;B'", prop.EvaluatedValue, "#1");
218                         var items = proj.GetItems ("Bar");
219                         Assert.AreEqual ("'A", items.First ().EvaluatedInclude, "#2");
220                         Assert.AreEqual ("$(Foo)", items.First ().UnevaluatedInclude, "#3");
221                         Assert.AreEqual (2, items.Count, "#4");
222                         Assert.AreEqual ("B'", items.Last ().EvaluatedInclude, "#5");
223                         Assert.AreEqual ("$(Foo)", items.Last ().UnevaluatedInclude, "#6");
224                         Assert.IsTrue (items.First ().Xml == items.Last ().Xml, "#7");
225                 }
226                 
227                 // the same as above except that ItemGroup goes first (and yet evaluated the same).
228                 [Test]
229                 public void EvaluationOrderPropertiesPrecedesItems ()
230                 {
231                         string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
232   <ItemGroup>
233     <Bar Include='$(Foo)' />
234   </ItemGroup>
235   <PropertyGroup>
236     <Foo Condition=""'A;B'=='A;B'"">'A;B'</Foo>
237   </PropertyGroup>
238 </Project>";
239                         var xml = XmlReader.Create (new StringReader (project_xml));
240                         var root = ProjectRootElement.Create (xml);
241                         var proj = new Project (root); // at this state property is parsed without error i.e. Condition evaluates fine.
242                         var prop = proj.GetProperty ("Foo");
243                         Assert.AreEqual ("'A;B'", prop.EvaluatedValue, "#1");
244                         var items = proj.GetItems ("Bar");
245                         Assert.AreEqual ("'A", items.First ().EvaluatedInclude, "#2");
246                         Assert.AreEqual ("$(Foo)", items.First ().UnevaluatedInclude, "#3");
247                         Assert.AreEqual (2, items.Count, "#4");
248                         Assert.AreEqual ("B'", items.Last ().EvaluatedInclude, "#5");
249                         Assert.AreEqual ("$(Foo)", items.Last ().UnevaluatedInclude, "#6");
250                         Assert.IsTrue (items.First ().Xml == items.Last ().Xml, "#7");
251                 }
252                 
253                 [Test]
254                 [ExpectedException (typeof (InvalidProjectFileException))]
255                 public void PropertyReferencesItem ()
256                 {
257                         string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
258   <ItemGroup>
259     <Bar Include='True' />
260   </ItemGroup>
261   <PropertyGroup>
262     <Foo Condition='@(Bar)'>X</Foo><!-- not allowed -->
263   </PropertyGroup>
264 </Project>";
265                         var xml = XmlReader.Create (new StringReader (project_xml));
266                         var root = ProjectRootElement.Create (xml);
267                         new Project (root);
268                 }
269                 
270                 [Test]
271                 [ExpectedException (typeof (InvalidProjectFileException))]
272                 public void SequentialPropertyReferenceNotAllowed ()
273                 {
274                         string xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
275   <PropertyGroup>
276     <A>x</A>
277     <B>y</B>
278     <C Condition=""$(A)$(B)==''"">z</C>
279   </PropertyGroup>
280 </Project>";
281                         var reader = XmlReader.Create (new StringReader (xml));
282                         var root = ProjectRootElement.Create (reader);
283                         new Project (root);
284                 }
285         }
286 }
287