Merge pull request #3142 from henricm/fix-for-win-mono_string_to_utf8
[mono.git] / mcs / nunit24 / NUnitCore / core / TestMethod.cs
1 // ****************************************************************\r
2 // This is free software licensed under the NUnit license. You\r
3 // may obtain a copy of the license as well as information regarding\r
4 // copyright ownership at http://nunit.org/?p=license&r=2.4.\r
5 // ****************************************************************\r
6 \r
7 namespace NUnit.Core\r
8 {\r
9         using System;\r
10         using System.Text;\r
11         using System.Text.RegularExpressions;\r
12         using System.Reflection;\r
13 \r
14         /// <summary>\r
15         /// The TestMethod class represents a TestCase implemented as a method\r
16         /// call on a fixture object. At the moment, this is the only way we \r
17         /// implement a TestCase, but others are expected in the future.\r
18         /// \r
19         /// Because of how exceptions are handled internally, this class\r
20         /// must incorporate processing of expected exceptions. A change to\r
21         /// the TestCase interface might make it easier to process exceptions\r
22         /// in an object that aggregates a TestMethod in the future.\r
23         /// </summary>\r
24         public abstract class TestMethod : TestCase\r
25         {\r
26                 #region Fields\r
27                 /// <summary>\r
28                 /// The test method\r
29                 /// </summary>\r
30                 private MethodInfo method;\r
31 \r
32                 /// <summary>\r
33                 /// The SetUp method.\r
34                 /// </summary>\r
35                 protected MethodInfo setUpMethod;\r
36 \r
37                 /// <summary>\r
38                 /// The teardown method\r
39                 /// </summary>\r
40                 protected MethodInfo tearDownMethod;\r
41 \r
42                 /// <summary>\r
43                 /// The exception handler method\r
44                 /// </summary>\r
45                 internal MethodInfo exceptionHandler;\r
46 \r
47                 /// <summary>\r
48                 /// True if an exception is expected\r
49                 /// </summary>\r
50                 internal bool exceptionExpected;\r
51 \r
52                 /// <summary>\r
53                 /// The type of any expected exception\r
54                 /// </summary>\r
55                 internal Type expectedExceptionType;\r
56         \r
57                 /// <summary>\r
58                 /// The full name of any expected exception type\r
59                 /// </summary>\r
60                 internal string expectedExceptionName;\r
61         \r
62                 /// <summary>\r
63                 /// The value of any message associated with an expected exception\r
64                 /// </summary>\r
65                 internal string expectedMessage;\r
66         \r
67                 /// <summary>\r
68                 /// A string indicating how to match the expected message\r
69                 /// </summary>\r
70                 internal string matchType;\r
71 \r
72                 /// <summary>\r
73                 /// A string containing any user message specified for the expected exception\r
74                 /// </summary>\r
75                 internal string userMessage;\r
76 \r
77                 #endregion\r
78 \r
79                 #region Constructors\r
80                 public TestMethod( MethodInfo method ) \r
81                         : base( method ) \r
82                 {\r
83                         this.method = method;\r
84                 }\r
85                 #endregion\r
86 \r
87                 #region Properties\r
88                 public MethodInfo Method\r
89                 {\r
90                         get { return method; }\r
91                 }\r
92 \r
93                 public bool ExceptionExpected\r
94                 {\r
95                         get { return exceptionExpected; }\r
96                         set { exceptionExpected = value; }\r
97                 }\r
98 \r
99                 public MethodInfo ExceptionHandler\r
100                 {\r
101                         get { return exceptionHandler; }\r
102                         set { exceptionHandler = value; }\r
103                 }\r
104 \r
105                 public Type ExpectedExceptionType\r
106                 {\r
107                         get { return expectedExceptionType; }\r
108                         set \r
109                         { \r
110                                 expectedExceptionType = value;\r
111                                 expectedExceptionName = expectedExceptionType != null\r
112                                         ? expectedExceptionType.FullName\r
113                                         : null;\r
114                         }\r
115                 }\r
116 \r
117                 public string ExpectedExceptionName\r
118                 {\r
119                         get { return expectedExceptionName; }\r
120                         set\r
121                         {\r
122                                 expectedExceptionType = null;\r
123                                 expectedExceptionName = value;\r
124                         }\r
125                 }\r
126 \r
127                 public string ExpectedMessage\r
128                 {\r
129                         get { return expectedMessage; }\r
130                         set { expectedMessage = value; }\r
131                 }\r
132 \r
133                 public string MatchType\r
134                 {\r
135                         get { return matchType; }\r
136                         set { matchType = value; }\r
137                 }\r
138 \r
139                 public string UserMessage\r
140                 {\r
141                         get { return userMessage; }\r
142                         set { userMessage = value; }\r
143                 }\r
144                 #endregion\r
145 \r
146                 #region Run Methods\r
147                 public override void Run(TestCaseResult testResult)\r
148                 { \r
149                         try\r
150                         {\r
151                                 if ( this.Parent != null)\r
152                                         Fixture = this.Parent.Fixture;\r
153 \r
154                                 if (!testResult.IsFailure)\r
155                                 {\r
156                                         // Temporary... to allow for tests that directly execute a test case\r
157                                         if (Fixture == null)\r
158                                                 Fixture = Reflect.Construct(this.FixtureType);\r
159 \r
160                     if (this.Properties["_SETCULTURE"] != null)\r
161                         TestContext.CurrentCulture =\r
162                             new System.Globalization.CultureInfo((string)Properties["_SETCULTURE"]);\r
163                     \r
164                     doRun(testResult);\r
165                                 }\r
166                         }\r
167                         catch (Exception ex)\r
168                         {\r
169                                 if (ex is NUnitException)\r
170                                         ex = ex.InnerException;\r
171 \r
172                                 RecordException(ex, testResult);\r
173                         }\r
174                         finally\r
175                         {\r
176                                 Fixture = null;\r
177                         }\r
178                 }\r
179 \r
180                 /// <summary>\r
181                 /// The doRun method is used to run a test internally.\r
182                 /// It assumes that the caller is taking care of any \r
183                 /// TestFixtureSetUp and TestFixtureTearDown needed.\r
184                 /// </summary>\r
185                 /// <param name="testResult">The result in which to record success or failure</param>\r
186                 public virtual void doRun( TestCaseResult testResult )\r
187                 {\r
188                         DateTime start = DateTime.Now;\r
189 \r
190                         try \r
191                         {\r
192                                 if ( setUpMethod != null )\r
193                                         Reflect.InvokeMethod( setUpMethod, this.Fixture );\r
194 \r
195                                 doTestCase( testResult );\r
196                         }\r
197                         catch(Exception ex)\r
198                         {\r
199                                 if ( ex is NUnitException )\r
200                                         ex = ex.InnerException;\r
201 \r
202                                 RecordException( ex, testResult );\r
203                         }\r
204                         finally \r
205                         {\r
206                                 doTearDown( testResult );\r
207 \r
208                                 DateTime stop = DateTime.Now;\r
209                                 TimeSpan span = stop.Subtract(start);\r
210                                 testResult.Time = (double)span.Ticks / (double)TimeSpan.TicksPerSecond;\r
211                         }\r
212                 }\r
213                 #endregion\r
214 \r
215                 #region Invoke Methods by Reflection, Recording Errors\r
216 \r
217                 private void doTearDown( TestCaseResult testResult )\r
218                 {\r
219                         try\r
220                         {\r
221                                 if ( tearDownMethod != null )\r
222                                         tearDownMethod.Invoke( this.Fixture, new object[0] );\r
223                         }\r
224                         catch(Exception ex)\r
225                         {\r
226                                 if ( ex is NUnitException )\r
227                                         ex = ex.InnerException;\r
228                                 // TODO: What about ignore exceptions in teardown?\r
229                                 testResult.Error( ex,FailureSite.TearDown );\r
230                         }\r
231                 }\r
232 \r
233                 private void doTestCase( TestCaseResult testResult )\r
234                 {\r
235                         try\r
236                         {\r
237                                 RunTestMethod(testResult);\r
238                                 ProcessNoException(testResult);\r
239                         }\r
240                         catch( Exception ex )\r
241                         {\r
242                                 if ( ex is NUnitException )\r
243                                         ex = ex.InnerException;\r
244 \r
245                                 if ( IsIgnoreException( ex ) )\r
246                                         testResult.Ignore( ex );\r
247                                 else\r
248                                         ProcessException(ex, testResult);\r
249                         }\r
250                 }\r
251 \r
252                 public virtual void RunTestMethod(TestCaseResult testResult)\r
253                 {\r
254                         Reflect.InvokeMethod( this.method, this.Fixture );\r
255                 }\r
256 \r
257                 #endregion\r
258 \r
259                 #region Record Info About An Exception\r
260 \r
261                 protected void RecordException( Exception ex, TestResult testResult )\r
262                 {\r
263                         if ( IsIgnoreException( ex ) )\r
264                                 testResult.Ignore( ex.Message );\r
265                         else if ( IsAssertException( ex ) )\r
266                                 testResult.Failure( ex.Message, ex.StackTrace );\r
267                         else    \r
268                                 testResult.Error( ex );\r
269                 }\r
270 \r
271                 protected string GetStackTrace(Exception exception)\r
272                 {\r
273                         try\r
274                         {\r
275                                 return exception.StackTrace;\r
276                         }\r
277                         catch( Exception )\r
278                         {\r
279                                 return "No stack trace available";\r
280                         }\r
281                 }\r
282 \r
283                 #endregion\r
284 \r
285                 #region Exception Processing\r
286                 protected internal virtual void ProcessNoException(TestCaseResult testResult)\r
287                 {\r
288                         if ( ExceptionExpected )\r
289                                 testResult.Failure(NoExceptionMessage(), null);\r
290                         else\r
291                                 testResult.Success();\r
292                 }\r
293                 \r
294                 protected internal virtual void ProcessException(Exception exception, TestCaseResult testResult)\r
295                 {\r
296                         if (!ExceptionExpected)\r
297                         {\r
298                                 RecordException(exception, testResult); \r
299                                 return;\r
300                         }\r
301 \r
302                         if (IsExpectedExceptionType(exception))\r
303                         {\r
304                                 if (IsExpectedMessageMatch(exception))\r
305                                 {\r
306                                         if ( exceptionHandler != null )\r
307                                                 Reflect.InvokeMethod( exceptionHandler, this.Fixture, exception );\r
308 \r
309                                         testResult.Success();\r
310                                 }\r
311                                 else\r
312                                 {\r
313                                         testResult.Failure(WrongTextMessage(exception), GetStackTrace(exception));\r
314                                 }\r
315                         }\r
316                         else if (IsAssertException(exception))\r
317                         {\r
318                                 testResult.Failure(exception.Message, exception.StackTrace);\r
319                         }\r
320                         else\r
321                         {\r
322                                 testResult.Failure(WrongTypeMessage(exception), GetStackTrace(exception));\r
323                         }\r
324                 }\r
325                 #endregion\r
326 \r
327                 #region Abstract Methods\r
328                 protected abstract bool IsAssertException(Exception ex);\r
329 \r
330                 protected abstract bool IsIgnoreException(Exception ex);\r
331                 #endregion\r
332 \r
333                 #region Helper Methods\r
334                 protected bool IsExpectedExceptionType(Exception exception)\r
335                 {\r
336                         return expectedExceptionName == null || expectedExceptionName.Equals(exception.GetType().FullName);\r
337                 }\r
338 \r
339                 protected bool IsExpectedMessageMatch(Exception exception)\r
340                 {\r
341                         if (expectedMessage == null)\r
342                                 return true;\r
343 \r
344                         switch (matchType)\r
345                         {\r
346                                 case "Exact":\r
347                                 default:\r
348                                         return expectedMessage.Equals(exception.Message);\r
349                                 case "Contains":\r
350                                         return exception.Message.IndexOf(expectedMessage) >= 0;\r
351                                 case "Regex":\r
352                                         return Regex.IsMatch(exception.Message, expectedMessage);\r
353                         }\r
354                 }\r
355 \r
356                 protected string NoExceptionMessage()\r
357                 {\r
358                         string expectedType = expectedExceptionName == null ? "An Exception" : expectedExceptionName;\r
359                         return CombineWithUserMessage( expectedType + " was expected" );\r
360                 }\r
361 \r
362                 protected string WrongTypeMessage(Exception exception)\r
363                 {\r
364                         return CombineWithUserMessage(\r
365                                 "An unexpected exception type was thrown" + Environment.NewLine +\r
366                                 "Expected: " + expectedExceptionName + Environment.NewLine +\r
367                                 " but was: " + exception.GetType().FullName + " : " + exception.Message );\r
368                 }\r
369 \r
370                 protected string WrongTextMessage(Exception exception)\r
371                 {\r
372                         string expectedText;\r
373                         switch (matchType)\r
374                         {\r
375                                 default:\r
376                                 case "Exact":\r
377                                         expectedText = "Expected: ";\r
378                                         break;\r
379                                 case "Contains":\r
380                                         expectedText = "Expected message containing: ";\r
381                                         break;\r
382                                 case "Regex":\r
383                                         expectedText = "Expected message matching: ";\r
384                                         break;\r
385                         }\r
386 \r
387                         return CombineWithUserMessage(\r
388                                 "The exception message text was incorrect" + Environment.NewLine +\r
389                                 expectedText + expectedMessage + Environment.NewLine +\r
390                                 " but was: " + exception.Message );\r
391                 }\r
392 \r
393                 private string CombineWithUserMessage( string message )\r
394                 {\r
395                         if ( userMessage == null )\r
396                                 return message;\r
397                         return userMessage + Environment.NewLine + message;\r
398                 }\r
399         #endregion\r
400     }\r
401 }\r