Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mcs / class / System.Web / Test / standalone-runner-support / StandaloneTest.cs
1 //
2 // Authors:
3 //   Marek Habersack (mhabersack@novell.com)
4 //
5 // (C) 2010 Novell, Inc http://novell.com/
6 //
7
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.Collections.Generic;
30 using System.IO;
31 using System.Reflection;
32 using System.Text;
33 using System.Web;
34 using System.Web.Hosting;
35 using System.Xml;
36
37 using MonoTests.SystemWeb.Framework;
38 using NUnit.Framework;
39
40 namespace StandAloneRunnerSupport
41 {
42         public sealed class StandaloneTest
43         {
44                 const string HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
45                 
46                 string failureDetails;
47                 
48                 public TestCaseFailureException Exception {
49                         get; private set;
50                 }
51
52                 public string FailureDetails {
53                         get {
54                                 if (!String.IsNullOrEmpty (failureDetails))
55                                         return failureDetails;
56
57                                 TestCaseFailureException ex = Exception;
58                                 if (ex == null)
59                                         return String.Empty;
60
61                                 return ex.Details;
62                         }
63
64                         private set {
65                                 failureDetails = value;
66                         }
67                 }
68
69                 public string FailedUrl {
70                         get; private set;
71                 }
72
73                 public string FailedUrlDescription {
74                         get; private set;
75                 }
76
77                 public string FailedUrlCallbackName {
78                         get; private set;
79                 }
80                 
81                 public TestCaseAttribute Info {
82                         get; private set;
83                 }
84
85                 public bool Success {
86                         get; private set;
87                 }
88                 
89                 public Type TestType {
90                         get; private set;
91                 }
92                 
93                 public StandaloneTest (Type testType, TestCaseAttribute info)
94                 {
95                         if (testType == null)
96                                 throw new ArgumentNullException ("testType");
97                         if (info == null)
98                                 throw new ArgumentNullException ("info");
99                         
100                         TestType = testType;
101                         Info = info;
102                 }
103
104                 public void Run (ApplicationManager appMan, bool verbose)
105                 {
106                         try {
107                                 Success = true;
108                                 RunInternal (appMan, verbose);
109                         } catch (TestCaseFailureException ex) {
110                                 Exception = ex;
111                                 Success = false;
112                         } catch (Exception ex) {
113                                 FailureDetails = String.Format ("Test failed with exception of type '{0}':{1}{2}",
114                                                                 ex.GetType (), Environment.NewLine, ex.ToString ());
115                                 Success = false;
116                         }
117                 }
118                 
119                 void RunInternal (ApplicationManager appMan, bool verbose)
120                 {
121                         ITestCase test = Activator.CreateInstance (TestType) as ITestCase;
122                         var runItems = new List <TestRunItem> ();                       
123                         if (!test.SetUp (runItems)) {
124                                 Success = false;
125                                 FailureDetails = "Test aborted in setup phase.";
126                                 return;
127                         }
128
129                         if (runItems.Count == 0) {
130                                 Success = false;
131                                 FailureDetails = "No test run items returned by the test case.";
132                                 return;
133                         }
134                         
135                         Response response, previousResponse = null;
136                         TestRunner runner;
137                         string[] formValues;
138                         
139                         try {
140                                 Console.Write ('[');
141                                 if (verbose)
142                                         Console.WriteLine ("{0} ({1}: {2})]", TestType, Info.Name, Info.Description);
143                                 foreach (var tri in runItems) {
144                                         if (tri == null)
145                                                 continue;
146
147                                         if (verbose)
148                                                 Console.Write ("\t{0} ({1}) ", tri.Callback != null ? tri.Callback.Method.ToString () : "<null>", 
149                                                                !String.IsNullOrEmpty (tri.UrlDescription) ? tri.UrlDescription : tri.Url);
150                                         runner = null;
151                                         response = null;
152                                         try {
153                                                 runner = appMan.CreateObject (Info.Name, typeof (TestRunner), test.VirtualPath, test.PhysicalPath, true) as TestRunner;
154                                                 if (runner == null) {
155                                                         Success = false;
156                                                         throw new InvalidOperationException ("runner must not be null.");
157                                                 }
158                                                 
159                                                 if (tri.PostValues != null && previousResponse != null)
160                                                         formValues = ExtractFormAndHiddenControls (previousResponse);
161                                                 else
162                                                         formValues = null;
163
164                                                 SetRunnerDomainData (tri.AppDomainData, runner.Domain);
165                                                 response = runner.Run (tri.Url, tri.PathInfo, tri.PostValues, formValues);
166                                                 if (tri.Callback == null)
167                                                         continue;
168
169                                                 tri.TestRunData = runner.TestRunData;
170                                                 tri.StatusCode = runner.StatusCode;
171                                                 tri.Redirected = runner.Redirected;
172                                                 tri.RedirectLocation = runner.RedirectLocation; 
173
174                                                 if (tri.Callback != null)
175                                                         tri.Callback (response.Body, tri);
176
177                                                 Console.Write ('.');
178                                         } catch (Exception) {
179                                                 FailedUrl = tri.Url;
180                                                 FailedUrlDescription = tri.UrlDescription;
181
182                                                 if (tri.Callback != null) {
183                                                         MethodInfo mi = tri.Callback.Method;
184                                                         FailedUrlCallbackName = FormatMethodName (mi);
185                                                 }
186                                                 Console.Write ('F');
187                                                 throw;
188                                         } finally {
189                                                 if (runner != null) {
190                                                         runner.Stop (true);
191                                                         AppDomain.Unload (runner.Domain);
192                                                 }
193                                                 runner = null;
194                                                 previousResponse = response;
195                                         }
196                                 }
197                         } catch (AssertionException ex) {
198                                 throw new TestCaseFailureException ("Assertion failed.", ex.Message, ex);
199                         } finally {
200                                 if (verbose)
201                                         Console.WriteLine ();
202                                 else
203                                         Console.Write (']');
204                         }
205                 }
206
207                 void SetRunnerDomainData (object[] data, AppDomain domain)
208                 {
209                         int len = data != null ? data.Length : 0;
210                         if (len == 0)
211                                 return;
212
213                         if (len % 2 != 0)
214                                 throw new ArgumentException ("Must have an even number of elements.", "data");
215
216                         string name;
217                         for (int i = 0; i < len; i += 2) {
218                                 name = data [i] as string;
219                                 if (String.IsNullOrEmpty (name))
220                                         throw new InvalidOperationException (String.Format ("Name at index {0} must not be null or empty.", i));
221
222                                 domain.SetData (name, data [i + 1]);
223                         }
224                 }
225                 
226                 string[] ExtractFormAndHiddenControls (Response response)
227                 {
228                         HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument ();
229                         htmlDoc.LoadHtml (response.Body);
230
231                         var tempxml = new StringBuilder ();
232                         var tsw = new StringWriter (tempxml);
233                         htmlDoc.OptionOutputAsXml = true;
234                         htmlDoc.Save (tsw);
235
236                         var doc = new XmlDocument ();
237                         doc.LoadXml (tempxml.ToString ());
238
239                         XmlNamespaceManager nsmgr = new XmlNamespaceManager (doc.NameTable);
240                         nsmgr.AddNamespace ("html", HTML_NAMESPACE);
241
242                         XmlNode formNode = doc.SelectSingleNode ("//html:form", nsmgr);
243                         if (formNode == null)
244                                 throw new ArgumentException ("Form was not found in document: " + response.Body);
245
246                         string actionUrl = formNode.Attributes ["action"].Value;
247                         XmlNode method = formNode.Attributes ["method"];
248                         var data = new List <string> ();
249                         string name, value;
250                         
251                         foreach (XmlNode inputNode in doc.SelectNodes ("//html:input[@type='hidden']", nsmgr)) {
252                                 name = inputNode.Attributes["name"].Value;
253                                 if (String.IsNullOrEmpty (name))
254                                         continue;
255
256                                 XmlAttribute attr = inputNode.Attributes["value"];
257                                 if (attr != null)
258                                         value = attr.Value;
259                                 else
260                                         value = String.Empty;
261
262                                 data.Add (name);
263                                 data.Add (value);
264                         }
265
266                         return data.ToArray ();
267                 }
268                 
269                 static bool ShouldPrintFullName (Type type)
270                 {
271                         return type.IsClass && (!type.IsPointer || (!type.GetElementType ().IsPrimitive && !type.GetElementType ().IsNested));
272                 }
273
274                 string FormatMethodName (MethodInfo mi)
275                 {
276                         var sb = new StringBuilder ();
277                         Type retType = mi.ReturnType;
278                         if (ShouldPrintFullName (retType))
279                                 sb.Append (retType.ToString ());
280                         else
281                                 sb.Append (retType.Name);
282                         sb.Append (" ");
283                         sb.Append (mi.DeclaringType.FullName);
284                         sb.Append ('.');
285                         sb.Append (mi.Name);
286                         if (mi.IsGenericMethod) {
287                                 Type[] gen_params = mi.GetGenericArguments ();
288                                 sb.Append ("<");
289                                 for (int j = 0; j < gen_params.Length; j++) {
290                                         if (j > 0)
291                                                 sb.Append (",");
292                                         sb.Append (gen_params [j].Name);
293                                 }
294                                 sb.Append (">");
295                         }
296                         sb.Append ("(");
297                         ParameterInfo[] p = mi.GetParameters ();
298                         for (int i = 0; i < p.Length; ++i) {
299                                 if (i > 0)
300                                         sb.Append (", ");
301                                 Type pt = p[i].ParameterType;
302                                 bool byref = pt.IsByRef;
303                                 if (byref)
304                                         pt = pt.GetElementType ();
305                                 if (ShouldPrintFullName (pt))
306                                         sb.Append (pt.ToString ());
307                                 else
308                                         sb.Append (pt.Name);
309                                 if (byref)
310                                         sb.Append (" ref");
311                         }
312                         if ((mi.CallingConvention & CallingConventions.VarArgs) != 0) {
313                                 if (p.Length > 0)
314                                         sb.Append (", ");
315                                 sb.Append ("...");
316                         }
317                         
318                         sb.Append (")");
319                         return sb.ToString ();
320                 }
321         }
322 }