5 // Jonathan Pryor (jonp@xamarin.com)
7 // (C) 2013 Xamarin Inc.
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Diagnostics;
35 using Microsoft.Build.Framework;
36 using Microsoft.Build.Utilities;
38 using NUnit.Framework;
40 namespace MonoTests.Microsoft.Build.Utilities {
43 public class ToolTaskTest {
45 //NUnit 2.6 gives much better reporting of individual failures, but Mono currently uses 2.4.x
48 public void LogEventsFromTextOutput ()
50 foreach (var test in GetErrorParsingTestData ()) {
51 LogEventsFromTextOutput (test.Raw, test.ExpectedResult);
57 public TestCaseData (string raw, LogEvent expectedResult)
60 ExpectedResult = expectedResult;
63 public string Raw { get; private set; }
64 public LogEvent ExpectedResult { get; private set; }
66 public TestCaseData SetName (string name)
72 [Test, TestCaseSource ("GetErrorParsingTestData")]
74 public void LogEventsFromTextOutput (string lineText, LogEvent expected)
76 var task = new LogEventsFromTextOutputToolTask ();
77 task.LogEventsFromTextOutput (lineText);
78 Assert.AreEqual (task.LogEvents.Count, 1);
79 var result = task.LogEvents[0];
80 task.LogEvents.Clear ();
82 if (result != null && result.Origin == task.GetType ().Name.ToUpper ())
83 result.Origin = "#TASKNAME";
85 if (expected == null) {
86 Assert.IsNull (result, "#nomatch");
90 Assert.IsNotNull (result, "#match");
91 Assert.AreEqual (expected.Origin, result.Origin, "#origin");
92 Assert.AreEqual (expected.Line, result.Line, "#line");
93 Assert.AreEqual (expected.Column, result.Column, "#column");
94 Assert.AreEqual (expected.EndLine, result.EndLine, "#endline");
95 Assert.AreEqual (expected.EndColumn, result.EndColumn, "#endcolumn");
96 Assert.AreEqual (expected.IsError, result.IsError, "#iserror");
97 Assert.AreEqual (expected.Subcategory ?? "", result.Subcategory, "#subcategory");
98 Assert.AreEqual (expected.Code, result.Code, "number");
99 Assert.AreEqual (expected.Message ?? "", result.Message, "#message");
102 static IEnumerable<TestCaseData> GetErrorParsingTestData ()
104 yield return new TestCaseData (
107 ).SetName ("NoColon");
109 yield return new TestCaseData (
112 Origin = "#TASKNAME",
116 ).SetName ("Minimal");
118 yield return new TestCaseData (
121 ).SetName ("InvalidCategory");
123 yield return new TestCaseData (
126 Origin = "#TASKNAME",
130 ).SetName ("CaseInsensitivity");
132 yield return new TestCaseData (
135 Origin = "#TASKNAME",
139 ).SetName ("EmptyOrigin");
141 yield return new TestCaseData (
144 Origin = "#TASKNAME",
148 ).SetName ("BlankOrigin");
150 yield return new TestCaseData (
151 "error CS66 : error in 'hello:thing'",
153 Origin = "#TASKNAME",
156 Message = "error in 'hello:thing'",
158 ).SetName ("NoOriginButErrorLikeMessage");
160 yield return new TestCaseData (
161 " C:\\class.cs (23,344) : error CS66 : blah ",
163 Origin = "C:\\class.cs",
170 ).SetName ("Whitespace");
172 yield return new TestCaseData (
173 "class1.cs(16,4): error CS0152: The label `case 1:' already occurs in this switch statement",
175 Origin = "class1.cs",
180 Message = "The label `case 1:' already occurs in this switch statement",
182 ).SetName ("RangeLineCol");
184 yield return new TestCaseData (
185 "class1.cs(16,4-56): error X: blah",
187 Origin = "class1.cs",
195 ).SetName ("RangeLineColCol");
197 yield return new TestCaseData (
198 "class1.cs(16,4,56,7): error X: blah",
200 Origin = "class1.cs",
209 ).SetName ("RangeLineColLineCol");
211 yield return new TestCaseData (
212 "class1.cs(1-77): error X: blah",
214 Origin = "class1.cs",
221 ).SetName ("RangeLineLine");
223 yield return new TestCaseData (
224 "class1.cs(1-77-89): error X: blah",
226 Origin = "class1.cs",
231 ).SetName ("BadRangeTooManyDashes");
233 yield return new TestCaseData (
234 "class1.cs(1&77-89): error X: blah",
236 Origin = "class1.cs(1&77-89)",
241 ).SetName ("BadRangePunctuation");
243 yield return new TestCaseData (
244 "class1.cs(ASDF): error X: blah",
246 Origin = "class1.cs(ASDF)",
251 ).SetName ("BadRangeAlpha");
253 yield return new TestCaseData (
254 "class1.cs(12AA45): error X: blah",
256 Origin = "class1.cs(12AA45)",
261 ).SetName ("BadRangeAlphaNumeric");
263 yield return new TestCaseData (
264 "class1.cs(1-77,89-56): error X: blah",
266 Origin = "class1.cs",
271 ).SetName ("BadRangeLineLineColCol");
273 yield return new TestCaseData (
274 "class1.cs(1,77,89): error X: blah",
276 Origin = "class1.cs",
281 ).SetName ("BadRangeThreeCommas");
283 yield return new TestCaseData (
284 "class1.cs(0): error X:",
286 Origin = "class1.cs",
290 ).SetName ("RangeZero");
292 yield return new TestCaseData (
293 "class1.cs(2,1234567890192929293833838380): error X:",
295 Origin = "class1.cs",
300 ).SetName ("BadRangeOverflowCol");
302 yield return new TestCaseData (
303 "class1.cs(2,1234567890192929293833838380,5,7): error X:",
308 Origin = "class1.cs",
312 ).SetName ("BadRangeOverflowBeforeValues");
314 yield return new TestCaseData (
315 "c:\\foo error XXX: fatal error YYY : error blah : thing",
317 Origin = "c:\\foo error XXX",
319 Subcategory = "fatal",
321 Message = "error blah : thing",
323 ).SetName ("LotsOfColons");
325 yield return new TestCaseData (
326 "Main.cs(17,20): warning CS0168: The variable 'foo' is declared but never used",
332 Message = "The variable 'foo' is declared but never used",
334 ).SetName ("MSExample1");
336 yield return new TestCaseData (
337 "C:\\dir1\\foo.resx(2) : error BC30188: Declaration expected.",
339 Origin = "C:\\dir1\\foo.resx",
343 Message = "Declaration expected.",
345 ).SetName ("MSExample2");
347 yield return new TestCaseData (
348 "cl : Command line warning D4024 : unrecognized source file type 'foo.cs', object file assumed",
351 Subcategory = "Command line",
353 Message = "unrecognized source file type 'foo.cs', object file assumed",
355 ).SetName ("MSExample3");
357 yield return new TestCaseData (
358 "error CS0006: Metadata file 'System.dll' could not be found.",
360 Origin = "#TASKNAME",
363 Message = "Metadata file 'System.dll' could not be found.",
365 ).SetName ("MSExample4");
367 yield return new TestCaseData (
368 "C:\\sourcefile.cpp(134) : error C2143: syntax error : missing ';' before '}'",
370 Origin = "C:\\sourcefile.cpp",
374 Message = "syntax error : missing ';' before '}'",
376 ).SetName ("MSExample5");
378 yield return new TestCaseData (
379 "LINK : fatal error LNK1104: cannot open file 'somelib.lib'",
382 Subcategory = "fatal",
385 Message = "cannot open file 'somelib.lib'",
387 ).SetName ("MSExample6");
389 yield return new TestCaseData (
390 "/foo (bar)/baz/Component1.fs(3,5): error FS0201: Namespaces cannot contain values.",
392 Origin = "/foo (bar)/baz/Component1.fs",
397 Message = "Namespaces cannot contain values.",
399 ).SetName ("ParensInFilename");
401 yield return new TestCaseData (
402 "fatal error XXX: stuff.",
404 Origin = "#TASKNAME",
406 Subcategory = "fatal",
410 ).SetName ("SubcategoryNoOrigin");
414 public void ToolExeAndPath ()
416 TestToolTask a = new TestToolTask ();
417 Assert.AreEqual (a.ToolExe, "TestTool.exe", "#1");
419 Assert.AreEqual (a.ToolExe, "Foo", "#2");
421 Assert.AreEqual (a.ToolExe, "TestTool.exe", "#3");
423 Assert.AreEqual (a.ToolPath, null, "#4");
425 Assert.AreEqual (a.ToolPath, "Bar", "#5");
427 Assert.AreEqual (a.ToolPath, "", "#6");
433 public void Execute_1 ()
435 var t = new TestExecuteToolTask ();
436 t.OnExecuteTool = delegate { Assert.Fail ("#1"); };
437 t.BuildEngine = new MockBuildEngine ();
438 Assert.IsFalse (t.Execute (), "result");
442 public void Execute_2 ()
444 var t = new TestExecuteToolTask ();
445 t.BuildEngine = new MockBuildEngine ();
446 t.ToolPath = Directory.GetCurrentDirectory ();
447 t.ToolExe = "Makefile";
449 t.OnExecuteTool = (pathToTool, responseFileCommands, commandLineCommands) => {
450 Assert.AreEqual (Path.Combine (Directory.GetCurrentDirectory (), "Makefile"), pathToTool, "#1");
451 Assert.AreEqual ("", responseFileCommands, "#2");
452 Assert.AreEqual ("", commandLineCommands, "#3");
456 Assert.IsTrue (t.Execute (), "result");
460 public void Execute_3 ()
462 var t = new TestExecuteToolTask ();
463 t.FullPathToTool = "fpt";
464 t.BuildEngine = new MockBuildEngine ();
465 t.ToolExe = "Makefile.mk";
467 t.OnExecuteTool = (pathToTool, responseFileCommands, commandLineCommands) => {
468 Assert.AreEqual ("Makefile.mk", pathToTool, "#1");
469 Assert.AreEqual ("", responseFileCommands, "#2");
470 Assert.AreEqual ("", commandLineCommands, "#3");
474 Assert.IsFalse (t.Execute (), "result");
478 public class LogEvent
480 public string Origin { get; set; }
481 public int Line { get; set; }
482 public int Column { get; set; }
483 public int EndLine { get; set; }
484 public int EndColumn { get; set; }
485 public string Subcategory { get; set; }
486 public bool IsError { get; set; }
487 public string Code { get; set; }
488 public string Message { get; set; }
491 class LogEventsFromTextOutputToolTask : ToolTask {
493 public List<LogEvent> LogEvents {
494 get { return engine.LogEvents; }
497 CodeLoggingBuildEngine engine = new CodeLoggingBuildEngine ();
499 public LogEventsFromTextOutputToolTask ()
501 BuildEngine = engine;
504 protected override string GenerateFullPathToTool ()
506 throw new NotImplementedException ();
509 protected override string ToolName {
510 get {throw new NotImplementedException ();}
513 public void LogEventsFromTextOutput (string line)
515 base.LogEventsFromTextOutput (line, MessageImportance.Normal);
519 class CodeLoggingBuildEngine : IBuildEngine {
521 public List<LogEvent> LogEvents = new List<LogEvent> ();
523 public int ColumnNumberOfTaskNode {
525 throw new NotImplementedException ();
529 public bool ContinueOnError {
531 throw new NotImplementedException ();
535 public int LineNumberOfTaskNode {
537 throw new NotImplementedException ();
541 public string ProjectFileOfTaskNode {
543 throw new NotImplementedException ();
547 public bool BuildProjectFile (string projectFileName, string[] targetNames, System.Collections.IDictionary globalProperties, System.Collections.IDictionary targetOutputs)
549 throw new NotImplementedException ();
552 public void LogCustomEvent (CustomBuildEventArgs e)
554 LogEvents.Add (null);
557 public void LogErrorEvent (BuildErrorEventArgs e)
559 LogEvents.Add (new LogEvent {
561 Subcategory = e.Subcategory,
564 EndLine = e.EndLineNumber,
565 Column = e.ColumnNumber,
566 EndColumn = e.EndColumnNumber,
572 public void LogMessageEvent (BuildMessageEventArgs e)
574 LogEvents.Add (null);
577 public void LogWarningEvent (BuildWarningEventArgs e)
579 LogEvents.Add (new LogEvent {
580 Subcategory = e.Subcategory,
583 EndLine = e.EndLineNumber,
584 Column = e.ColumnNumber,
585 EndColumn = e.EndColumnNumber,
592 class TestToolTask : ToolTask {
594 protected override string ToolName {
595 get { return "TestTool.exe"; }
598 protected override string GenerateFullPathToTool ()
604 class MockBuildEngine : IBuildEngine
606 public int ColumnNumberOfTaskNode {
612 public bool ContinueOnError {
614 throw new NotImplementedException ();
618 public int LineNumberOfTaskNode {
624 public string ProjectFileOfTaskNode {
626 return "ProjectFileOfTaskNode";
630 public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
632 throw new NotImplementedException ();
635 public void LogCustomEvent (CustomBuildEventArgs e)
639 public void LogErrorEvent (BuildErrorEventArgs e)
641 Console.WriteLine (e.Message);
644 public void LogMessageEvent (BuildMessageEventArgs e)
648 public void LogWarningEvent (BuildWarningEventArgs e)
653 class TestExecuteToolTask : ToolTask
655 public Action<string, string, string> OnExecuteTool;
656 public string FullPathToTool;
658 protected override string ToolName {
659 get { return "TestTool.exe"; }
662 protected override bool CallHostObjectToExecute ()
664 return base.CallHostObjectToExecute ();
667 protected override string GenerateFullPathToTool ()
669 return FullPathToTool;
672 protected override int ExecuteTool (string pathToTool, string responseFileCommands, string commandLineCommands)
674 if (OnExecuteTool != null)
675 OnExecuteTool (pathToTool, responseFileCommands, commandLineCommands);