1 // Copyright (c) Microsoft. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 4 using System; 5 using System.IO; 6 using System.Xml; 7 using System.Resources; 8 using System.Globalization; 9 using System.Text; 10 using Microsoft.Build.BuildEngine; 11 using NUnit.Framework; 12 using System.Collections; 13 using System.Collections.Generic; 14 using Microsoft.Build.Framework; 15 16 17 namespace Microsoft.Build.UnitTests 18 { 19 /* 20 * Class: MockLogger 21 * Owner: jomof 22 * 23 * Mock logger class. Keeps track of errors and warnings and also builds 24 * up a raw string (fullLog) that contains all messages, warnings, errors. 25 * 26 */ 27 internal sealed class MockLogger : ILogger 28 { 29 #region Properties 30 private int errorCount = 0; 31 private int warningCount = 0; 32 private StringBuilder fullLog = new StringBuilder(); 33 private List<BuildErrorEventArgs> errors = new List<BuildErrorEventArgs>(); 34 private List<BuildWarningEventArgs> warnings = new List<BuildWarningEventArgs>(); 35 private List<ExternalProjectStartedEventArgs> externalProjectStartedEvents = new List<ExternalProjectStartedEventArgs>(); 36 private List<ExternalProjectFinishedEventArgs> externalProjectFinishedEvents = new List<ExternalProjectFinishedEventArgs>(); 37 38 /* 39 * Method: ErrorCount 40 * Owner: jomof 41 * 42 * The count of all errors seen so far. 43 * 44 */ 45 internal int ErrorCount 46 { 47 get { return this.errorCount; } 48 } 49 50 /* 51 * Method: WarningCount 52 * Owner: jomof 53 * 54 * The count of all warnings seen so far. 55 * 56 */ 57 internal int WarningCount 58 { 59 get { return this.warningCount; } 60 } 61 62 /// <summary> 63 /// Return the list of logged errors 64 /// </summary> 65 /// <owner>LukaszG</owner> 66 internal List<BuildErrorEventArgs> Errors 67 { 68 get 69 { 70 return this.errors; 71 } 72 } 73 74 /// <summary> 75 /// Returns the list of logged warnings 76 /// </summary> 77 /// <owner>LukaszG</owner> 78 internal List<BuildWarningEventArgs> Warnings 79 { 80 get 81 { 82 return this.warnings; 83 } 84 } 85 86 /// <summary> 87 /// List of ExternalProjectStarted events 88 /// </summary> 89 internal List<ExternalProjectStartedEventArgs> ExternalProjectStartedEvents 90 { 91 get { return this.externalProjectStartedEvents; } 92 } 93 94 /// <summary> 95 /// List of ExternalProjectFinished events 96 /// </summary> 97 internal List<ExternalProjectFinishedEventArgs> ExternalProjectFinishedEvents 98 { 99 get { return this.externalProjectFinishedEvents; } 100 } 101 102 /* 103 * Method: FullLog 104 * Owner: jomof 105 * 106 * The raw concatenation of all messages, errors and warnings seen so far. 107 * 108 */ 109 internal string FullLog 110 { 111 get { return this.fullLog.ToString(); } 112 } 113 #endregion 114 115 #region Minimal ILogger implementation 116 117 /* 118 * Property: Verbosity 119 * Owner: SumedhK 120 * 121 * The level of detail to show in the event log. 122 * 123 */ 124 public LoggerVerbosity Verbosity 125 { 126 get {return LoggerVerbosity.Normal;} 127 set {/* do nothing */} 128 } 129 130 /* 131 * Property: Parameters 132 * Owner: SumedhK 133 * 134 * The mock logger does not take parameters. 135 * 136 */ 137 public string Parameters 138 { 139 get 140 { 141 return null; 142 } 143 144 set 145 { 146 // do nothing 147 } 148 } 149 150 /* 151 * Method: Initialize 152 * Owner: jomof 153 * 154 * Add a new build event. 155 * 156 */ Initialize(IEventSource eventSource)157 public void Initialize(IEventSource eventSource) 158 { 159 eventSource.AnyEventRaised += 160 new AnyEventHandler(LoggerEventHandler); 161 } 162 163 /// <summary> 164 /// Clears the content of the log "file" 165 /// </summary> ClearLog()166 public void ClearLog() 167 { 168 this.fullLog = new StringBuilder(); 169 } 170 171 /* 172 * Method: Shutdown 173 * Owner: SumedhK 174 * 175 * The mock logger does not need to release any resources. 176 * 177 */ Shutdown()178 public void Shutdown() 179 { 180 // do nothing 181 } 182 #endregion 183 184 /* 185 * Method: LoggerEventHandler 186 * Owner: jomof 187 * 188 * Recieves build events and logs them the way we like. 189 * 190 */ LoggerEventHandler(object sender, BuildEventArgs eventArgs)191 internal void LoggerEventHandler(object sender, BuildEventArgs eventArgs) 192 { 193 if (eventArgs is BuildWarningEventArgs) 194 { 195 BuildWarningEventArgs w = (BuildWarningEventArgs) eventArgs; 196 197 // hack: disregard the MTA warning. 198 // need the second condition to pass on ploc builds 199 if (w.Code != "MSB4056" && !w.Message.Contains("MSB4056")) 200 { 201 fullLog.AppendFormat("{0}({1},{2}): {3} warning {4}: {5}\r\n", 202 w.File, 203 w.LineNumber, 204 w.ColumnNumber, 205 w.Subcategory, 206 w.Code, 207 w.Message); 208 209 ++warningCount; 210 this.warnings.Add(w); 211 } 212 } 213 else if (eventArgs is BuildErrorEventArgs) 214 { 215 BuildErrorEventArgs e = (BuildErrorEventArgs) eventArgs; 216 217 fullLog.AppendFormat("{0}({1},{2}): {3} error {4}: {5}\r\n", 218 e.File, 219 e.LineNumber, 220 e.ColumnNumber, 221 e.Subcategory, 222 e.Code, 223 e.Message); 224 225 ++errorCount; 226 this.errors.Add(e); 227 } 228 else 229 { 230 fullLog.Append(eventArgs.Message); 231 fullLog.Append("\r\n"); 232 } 233 234 if (eventArgs is ExternalProjectStartedEventArgs) 235 { 236 this.ExternalProjectStartedEvents.Add((ExternalProjectStartedEventArgs)eventArgs); 237 } 238 else if (eventArgs is ExternalProjectFinishedEventArgs) 239 { 240 this.ExternalProjectFinishedEvents.Add((ExternalProjectFinishedEventArgs)eventArgs); 241 } 242 243 if (eventArgs is BuildFinishedEventArgs) 244 { 245 // Console.Write in the context of a unit test is very expensive. A hundred 246 // calls to Console.Write can easily take two seconds on a fast machine. Therefore, only 247 // do the Console.Write once at the end of the build. 248 Console.Write(FullLog); 249 } 250 } 251 252 // Lazy-init property returning the MSBuild engine resource manager 253 static private ResourceManager EngineResourceManager 254 { 255 get 256 { 257 if (engineResourceManager == null) 258 { 259 engineResourceManager = new ResourceManager("Microsoft.Build.Engine.Resources.Strings", typeof(Engine).Assembly); 260 } 261 262 return engineResourceManager; 263 } 264 } 265 266 static private ResourceManager engineResourceManager = null; 267 268 // Gets the resource string given the resource ID GetString(string stringId)269 static public string GetString(string stringId) 270 { 271 return EngineResourceManager.GetString(stringId, CultureInfo.CurrentUICulture); 272 } 273 274 /// <summary> 275 /// Assert that the log file contains the given strings, in order. 276 /// </summary> 277 /// <param name="contains"></param> AssertLogContains(params string[] contains)278 internal void AssertLogContains(params string[] contains) 279 { 280 StringReader reader = new StringReader(FullLog); 281 int index = 0; 282 string currentLine = reader.ReadLine(); 283 while(currentLine != null) 284 { 285 if (currentLine.Contains(contains[index])) 286 { 287 index++; 288 if (index == contains.Length) break; 289 } 290 currentLine = reader.ReadLine(); 291 } 292 if (index != contains.Length) 293 { 294 Assertion.Fail(String.Format(CultureInfo.CurrentCulture, "Log was expected to contain '{0}', but did not.\n=======\n" + FullLog + "\n=======", contains[index])); 295 } 296 } 297 298 /// <summary> 299 /// Assert that the log file contains the given string. 300 /// </summary> 301 /// <param name="contains"></param> AssertLogDoesntContain(string contains)302 internal void AssertLogDoesntContain(string contains) 303 { 304 Assertion.Assert( 305 String.Format("Log was not expected to contain '{0}', but did.", contains), 306 !FullLog.Contains(contains)); 307 } 308 } 309 } 310