3 // Marek Habersack (mhabersack@novell.com)
5 // (C) 2010 Novell, Inc http://novell.com/
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.Generic;
31 using System.Reflection;
34 using System.Web.Hosting;
37 using MonoTests.SystemWeb.Framework;
38 using NUnit.Framework;
40 namespace StandAloneRunnerSupport
42 public sealed class StandaloneTest
44 const string HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
46 string failureDetails;
48 public TestCaseFailureException Exception {
52 public string FailureDetails {
54 if (!String.IsNullOrEmpty (failureDetails))
55 return failureDetails;
57 TestCaseFailureException ex = Exception;
65 failureDetails = value;
69 public string FailedUrl {
73 public string FailedUrlDescription {
77 public string FailedUrlCallbackName {
81 public TestCaseAttribute Info {
89 public Type TestType {
93 public StandaloneTest (Type testType, TestCaseAttribute info)
96 throw new ArgumentNullException ("testType");
98 throw new ArgumentNullException ("info");
104 public void Run (ApplicationManager appMan, bool verbose)
108 RunInternal (appMan, verbose);
109 } catch (TestCaseFailureException ex) {
112 } catch (Exception ex) {
113 FailureDetails = String.Format ("Test failed with exception of type '{0}':{1}{2}",
114 ex.GetType (), Environment.NewLine, ex.ToString ());
119 void RunInternal (ApplicationManager appMan, bool verbose)
121 ITestCase test = Activator.CreateInstance (TestType) as ITestCase;
122 var runItems = new List <TestRunItem> ();
123 if (!test.SetUp (runItems)) {
125 FailureDetails = "Test aborted in setup phase.";
129 if (runItems.Count == 0) {
131 FailureDetails = "No test run items returned by the test case.";
135 Response response, previousResponse = null;
142 Console.WriteLine ("{0} ({1}: {2})]", TestType, Info.Name, Info.Description);
143 foreach (var tri in runItems) {
148 Console.Write ("\t{0} ({1}) ", tri.Callback != null ? tri.Callback.Method.ToString () : "<null>",
149 !String.IsNullOrEmpty (tri.UrlDescription) ? tri.UrlDescription : tri.Url);
153 runner = appMan.CreateObject (Info.Name, typeof (TestRunner), test.VirtualPath, test.PhysicalPath, true) as TestRunner;
154 if (runner == null) {
156 throw new InvalidOperationException ("runner must not be null.");
159 if (tri.PostValues != null && previousResponse != null)
160 formValues = ExtractFormAndHiddenControls (previousResponse);
164 SetRunnerDomainData (tri.AppDomainData, runner.Domain);
165 response = runner.Run (tri.Url, tri.PathInfo, tri.PostValues, formValues);
166 if (tri.Callback == null)
169 tri.TestRunData = runner.TestRunData;
170 tri.StatusCode = runner.StatusCode;
171 tri.Redirected = runner.Redirected;
172 tri.RedirectLocation = runner.RedirectLocation;
174 if (tri.Callback != null)
175 tri.Callback (response.Body, tri);
178 } catch (Exception) {
180 FailedUrlDescription = tri.UrlDescription;
182 if (tri.Callback != null) {
183 MethodInfo mi = tri.Callback.Method;
184 FailedUrlCallbackName = FormatMethodName (mi);
189 if (runner != null) {
191 AppDomain.Unload (runner.Domain);
194 previousResponse = response;
197 } catch (AssertionException ex) {
198 throw new TestCaseFailureException ("Assertion failed.", ex.Message, ex);
201 Console.WriteLine ();
207 void SetRunnerDomainData (object[] data, AppDomain domain)
209 int len = data != null ? data.Length : 0;
214 throw new ArgumentException ("Must have an even number of elements.", "data");
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));
222 domain.SetData (name, data [i + 1]);
226 string[] ExtractFormAndHiddenControls (Response response)
228 HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument ();
229 htmlDoc.LoadHtml (response.Body);
231 var tempxml = new StringBuilder ();
232 var tsw = new StringWriter (tempxml);
233 htmlDoc.OptionOutputAsXml = true;
236 var doc = new XmlDocument ();
237 doc.LoadXml (tempxml.ToString ());
239 XmlNamespaceManager nsmgr = new XmlNamespaceManager (doc.NameTable);
240 nsmgr.AddNamespace ("html", HTML_NAMESPACE);
242 XmlNode formNode = doc.SelectSingleNode ("//html:form", nsmgr);
243 if (formNode == null)
244 throw new ArgumentException ("Form was not found in document: " + response.Body);
246 string actionUrl = formNode.Attributes ["action"].Value;
247 XmlNode method = formNode.Attributes ["method"];
248 var data = new List <string> ();
251 foreach (XmlNode inputNode in doc.SelectNodes ("//html:input[@type='hidden']", nsmgr)) {
252 name = inputNode.Attributes["name"].Value;
253 if (String.IsNullOrEmpty (name))
256 XmlAttribute attr = inputNode.Attributes["value"];
260 value = String.Empty;
266 return data.ToArray ();
269 static bool ShouldPrintFullName (Type type)
271 return type.IsClass && (!type.IsPointer || (!type.GetElementType ().IsPrimitive && !type.GetElementType ().IsNested));
274 string FormatMethodName (MethodInfo mi)
276 var sb = new StringBuilder ();
277 Type retType = mi.ReturnType;
278 if (ShouldPrintFullName (retType))
279 sb.Append (retType.ToString ());
281 sb.Append (retType.Name);
283 sb.Append (mi.DeclaringType.FullName);
286 if (mi.IsGenericMethod) {
287 Type[] gen_params = mi.GetGenericArguments ();
289 for (int j = 0; j < gen_params.Length; j++) {
292 sb.Append (gen_params [j].Name);
297 ParameterInfo[] p = mi.GetParameters ();
298 for (int i = 0; i < p.Length; ++i) {
301 Type pt = p[i].ParameterType;
302 bool byref = pt.IsByRef;
304 pt = pt.GetElementType ();
305 if (ShouldPrintFullName (pt))
306 sb.Append (pt.ToString ());
312 if ((mi.CallingConvention & CallingConventions.VarArgs) != 0) {
319 return sb.ToString ();