Преглед изворни кода

2010-06-18 Marek Habersack <[email protected]>

	* TestRunner.cs, TestWorkerRequest.cs: added support for POST
	requests.

	* StandaloneTest.cs: added support for POST requests. Run items
	are executed in sequence and their results are preserved for the
	next run. If the next run is a POST one, form data is extracted
	from the previous response and used to generate POST data for the
	current one.
	Added a new property, FailedUrlCallbackName, which contains the
	fully qualified method name of the test callback which failed.
	Callback is no longer required to be present.

2010-06-18  Marek Habersack  <[email protected]>

	* FormViewUpdateParameters_Bug607722.cs: added two POST phases -
	Edit and Update.

2010-06-18  Marek Habersack  <[email protected]>

	* standalone-runner.cs: added new command line parameter, --test,
	which selects a single test to run instead of the entire suite. It
	should be passed a fully qualified (without assembly name) type
	name of the test class.

svn path=/trunk/mcs/; revision=159114
Marek Habersack пре 15 година
родитељ
комит
25838dee95

+ 3 - 0
mcs/class/System.Web/Makefile

@@ -366,6 +366,9 @@ endif
 
 STANDALONE_TEST_RUNNER = Test/tools/standalone-runner.exe
 RUN_STANDALONE = $(TEST_RUNTIME) $(STANDALONE_TEST_RUNNER)
+ifdef TESTNAME
+RUN_STANDALONE += --test=$(TESTNAME)
+endif
 
 $(build_lib): $(RESX_RES) $(RESOURCE_FILES_2) $(RESOURCE_FILES_1)
 

+ 14 - 0
mcs/class/System.Web/Test/standalone-runner-support/ChangeLog

@@ -1,3 +1,17 @@
+2010-06-18  Marek Habersack  <[email protected]>
+
+	* TestRunner.cs, TestWorkerRequest.cs: added support for POST
+	requests.
+
+	* StandaloneTest.cs: added support for POST requests. Run items
+	are executed in sequence and their results are preserved for the
+	next run. If the next run is a POST one, form data is extracted
+	from the previous response and used to generate POST data for the
+	current one.
+	Added a new property, FailedUrlCallbackName, which contains the
+	fully qualified method name of the test callback which failed.
+	Callback is no longer required to be present.
+
 2010-03-06  Marek Habersack  <[email protected]>
 
 	* TestWorkerRequest.cs: added overloads of GetRawUrl and

+ 125 - 3
mcs/class/System.Web/Test/standalone-runner-support/StandaloneTest.cs

@@ -27,8 +27,12 @@
 //
 using System;
 using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Text;
 using System.Web;
 using System.Web.Hosting;
+using System.Xml;
 
 using MonoTests.SystemWeb.Framework;
 using NUnit.Framework;
@@ -37,6 +41,8 @@ namespace StandAloneRunnerSupport
 {
 	public sealed class StandaloneTest
 	{
+		const string HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
+		
 		string failureDetails;
 		
 		public TestCaseFailureException Exception {
@@ -67,6 +73,10 @@ namespace StandAloneRunnerSupport
 		public string FailedUrlDescription {
 			get; private set;
 		}
+
+		public string FailedUrlCallbackName {
+			get; private set;
+		}
 		
 		public TestCaseAttribute Info {
 			get; private set;
@@ -122,8 +132,10 @@ namespace StandAloneRunnerSupport
 				return;
 			}
 			
-			Response response;
+			Response response, previousResponse = null;
 			TestRunner runner;
+			string[] formValues;
+			
 			try {
 				Console.Write ('[');
 				foreach (var tri in runItems) {
@@ -131,22 +143,35 @@ namespace StandAloneRunnerSupport
 						continue;
 
 					runner = null;
+					response = null;
 					try {
 						runner = appMan.CreateObject (Info.Name, typeof (TestRunner), test.VirtualPath, test.PhysicalPath, true) as TestRunner;
 						if (runner == null) {
 							Success = false;
 							throw new InvalidOperationException ("runner must not be null.");
 						}
-						response = runner.Run (tri.Url, tri.PathInfo, tri.PostValues);
+						
+						if (tri.PostValues != null && previousResponse != null)
+							formValues = ExtractFormAndHiddenControls (previousResponse);
+						else
+							formValues = null;
+						
+						response = runner.Run (tri.Url, tri.PathInfo, tri.PostValues, formValues);
 						if (tri.Callback == null)
 							continue;
 
 						tri.TestRunData = runner.TestRunData;
-						tri.Callback (response.Body, tri);
+						if (tri.Callback != null)
+							tri.Callback (response.Body, tri);
 						Console.Write ('.');
 					} catch (Exception) {
 						FailedUrl = tri.Url;
 						FailedUrlDescription = tri.UrlDescription;
+
+						if (tri.Callback != null) {
+							MethodInfo mi = tri.Callback.Method;
+							FailedUrlCallbackName = FormatMethodName (mi);
+						}
 						Console.Write ('F');
 						throw;
 					} finally {
@@ -155,6 +180,7 @@ namespace StandAloneRunnerSupport
 							AppDomain.Unload (runner.Domain);
 						}
 						runner = null;
+						previousResponse = response;
 					}
 				}
 			} catch (AssertionException ex) {
@@ -163,5 +189,101 @@ namespace StandAloneRunnerSupport
 				Console.Write (']');
 			}
 		}
+
+		string[] ExtractFormAndHiddenControls (Response response)
+                {
+                        HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument ();
+                        htmlDoc.LoadHtml (response.Body);
+
+                        var tempxml = new StringBuilder ();
+                        var tsw = new StringWriter (tempxml);
+                        htmlDoc.OptionOutputAsXml = true;
+                        htmlDoc.Save (tsw);
+
+                        var doc = new XmlDocument ();
+                        doc.LoadXml (tempxml.ToString ());
+
+                        XmlNamespaceManager nsmgr = new XmlNamespaceManager (doc.NameTable);
+                        nsmgr.AddNamespace ("html", HTML_NAMESPACE);
+
+                        XmlNode formNode = doc.SelectSingleNode ("//html:form", nsmgr);
+                        if (formNode == null)
+                                throw new ArgumentException ("Form was not found in document: " + response.Body);
+
+                        string actionUrl = formNode.Attributes ["action"].Value;
+                        XmlNode method = formNode.Attributes ["method"];
+			var data = new List <string> ();
+			string name, value;
+			
+                        foreach (XmlNode inputNode in doc.SelectNodes ("//html:input[@type='hidden']", nsmgr)) {
+				name = inputNode.Attributes["name"].Value;
+                                if (String.IsNullOrEmpty (name))
+                                        continue;
+
+				XmlAttribute attr = inputNode.Attributes["value"];
+                                if (attr != null)
+                                        value = attr.Value;
+                                else
+                                        value = String.Empty;
+
+				data.Add (name);
+				data.Add (value);
+                        }
+
+			return data.ToArray ();
+                }
+		
+		static bool ShouldPrintFullName (Type type)
+		{
+                        return type.IsClass && (!type.IsPointer || (!type.GetElementType ().IsPrimitive && !type.GetElementType ().IsNested));
+                }
+
+                string FormatMethodName (MethodInfo mi)
+		{
+                        var sb = new StringBuilder ();
+                        Type retType = mi.ReturnType;
+                        if (ShouldPrintFullName (retType))
+                                sb.Append (retType.ToString ());
+                        else
+                                sb.Append (retType.Name);
+                        sb.Append (" ");
+			sb.Append (mi.DeclaringType.FullName);
+			sb.Append ('.');
+                        sb.Append (mi.Name);
+                        if (mi.IsGenericMethod) {
+                                Type[] gen_params = mi.GetGenericArguments ();
+                                sb.Append ("<");
+                                for (int j = 0; j < gen_params.Length; j++) {
+                                        if (j > 0)
+                                                sb.Append (",");
+                                        sb.Append (gen_params [j].Name);
+                                }
+                                sb.Append (">");
+                        }
+                        sb.Append ("(");
+                        ParameterInfo[] p = mi.GetParameters ();
+                        for (int i = 0; i < p.Length; ++i) {
+                                if (i > 0)
+                                        sb.Append (", ");
+                                Type pt = p[i].ParameterType;
+                                bool byref = pt.IsByRef;
+                                if (byref)
+                                        pt = pt.GetElementType ();
+                                if (ShouldPrintFullName (pt))
+                                        sb.Append (pt.ToString ());
+                                else
+                                        sb.Append (pt.Name);
+                                if (byref)
+                                        sb.Append (" ref");
+                        }
+                        if ((mi.CallingConvention & CallingConventions.VarArgs) != 0) {
+                                if (p.Length > 0)
+                                        sb.Append (", ");
+                                sb.Append ("...");
+                        }
+                        
+                        sb.Append (")");
+                        return sb.ToString ();
+                }
 	}
 }

+ 7 - 3
mcs/class/System.Web/Test/standalone-runner-support/TestRunItem.cs

@@ -53,11 +53,15 @@ namespace StandAloneRunnerSupport
 		public string UrlDescription {
 			get; set;
 		}
-
-		public SerializableDictionary <string, string> PostValues {
+#if BUG_IN_THE_RUNTIME_IS_FIXED
+		public SerializedDictionary <string, string> PostValues {
 			get; set;
 		}
-
+#else
+		public string[] PostValues {
+			get; set;
+		}
+#endif
 		public TestRunItem ()
 		: this (null, null, null)
 		{}

+ 8 - 1
mcs/class/System.Web/Test/standalone-runner-support/TestRunner.cs

@@ -50,8 +50,11 @@ namespace StandAloneRunnerSupport
 		public TestRunner ()
 		{
 		}
-		
+#if BUG_IN_THE_RUNTIME_IS_FIXED
 		public Response Run (string url, string pathInfo, SerializableDictionary <string, string> postValues)
+#else
+		public Response Run (string url, string pathInfo, string[] postValues, string[] formValues)
+#endif
 		{
 			if (String.IsNullOrEmpty (url))
 				throw new ArgumentNullException ("url");
@@ -83,6 +86,10 @@ namespace StandAloneRunnerSupport
 				else
 					wr = new TestWorkerRequest (uri.AbsolutePath, query, output);
 				wr.IsPost = isPost;
+				if (isPost) {
+					wr.AppendPostData (formValues, true);
+					wr.AppendPostData (postValues, false);
+				}
 				
 				HttpRuntime.ProcessRequest (wr);
 				return new Response {

+ 77 - 3
mcs/class/System.Web/Test/standalone-runner-support/TestWorkerRequest.cs

@@ -26,8 +26,10 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.Net;
+using System.Text;
 using System.Web;
 using System.Web.Hosting;
 
@@ -35,17 +37,20 @@ namespace StandAloneRunnerSupport
 {
 	sealed class TestWorkerRequest : SimpleWorkerRequest
 	{
+		const string POST_CONTENT_TYPE = "application/x-www-form-urlencoded";
 		static readonly char[] vpathTrimChars = { '/' };
 		
 		string page;
 		string query;
 		string appVirtualDir;
 		string pathInfo;
-
+		byte[] entityBody;
+		SortedDictionary <string, string> originalPostData;
+		
 		public bool IsPost { get; set; }
 		public HttpStatusCode StatusCode { get; set; }
 		public string StatusDescription { get; set; }
-			
+		
 		public TestWorkerRequest (string page, string query, TextWriter output)
 			: this (page, query, null, output)
 		{
@@ -59,7 +64,7 @@ namespace StandAloneRunnerSupport
 			this.appVirtualDir = GetAppPath ();
 			this.pathInfo = pathInfo;
 		}
-
+		
 		public override string GetFilePath ()
 		{
 			return page;
@@ -72,6 +77,32 @@ namespace StandAloneRunnerSupport
 
 			return base.GetHttpVerbName ();
 		}
+
+		public override string GetKnownRequestHeader (int index)
+		{
+			if (!IsPost || entityBody == null)
+				return base.GetKnownRequestHeader (index);
+
+
+			switch (index) {
+				case HttpWorkerRequest.HeaderContentLength:
+					return entityBody.Length.ToString ();
+
+				case HttpWorkerRequest.HeaderContentType:
+					return POST_CONTENT_TYPE;
+
+				default:
+					return base.GetKnownRequestHeader (index);
+			}
+		}
+
+		public override byte[] GetPreloadedEntityBody ()
+		{
+			if (!IsPost || entityBody == null)
+				return base.GetPreloadedEntityBody ();
+
+			return entityBody;
+		}
 		
 		public override string GetPathInfo ()
 		{
@@ -98,6 +129,49 @@ namespace StandAloneRunnerSupport
 
 			base.SendStatus (code, description);
 		}
+
+		public void AppendPostData (string[] postData, bool isEncoded)
+		{
+			int len = postData != null ? postData.Length : 0;
+			if (len == 0)
+				return;
+
+			if (len % 2 != 0)
+				throw new InvalidOperationException ("POST data array must have an even number of elements.");
+
+			if (originalPostData == null)
+				originalPostData = new SortedDictionary <string, string> ();
+
+			string key, value;
+			for (int i = 0; i < len; i += 2) {
+				key = postData [i];
+				value = postData [i + 1];
+
+				if (originalPostData.ContainsKey (key))
+					originalPostData [key] = value;
+				else
+					originalPostData.Add (key, value);
+			}
+			
+			len = originalPostData.Count;
+			var sb = new StringBuilder ();
+			bool first = true;
+			
+			foreach (var de in originalPostData) {
+				if (first)
+					first = false;
+				else
+					sb.Append ('&');
+				key = de.Key;
+				value = de.Value;
+				sb.Append (isEncoded ? key : HttpUtility.UrlEncode (key));
+				sb.Append ('=');
+				if (!String.IsNullOrEmpty (value))
+					sb.Append (isEncoded ? value : HttpUtility.UrlEncode (value));
+			}
+
+			entityBody = Encoding.ASCII.GetBytes (sb.ToString ());
+		}
 		
 		static string TrimLeadingSlash (string input)
 		{

+ 5 - 0
mcs/class/System.Web/Test/standalone-tests/ChangeLog

@@ -1,3 +1,8 @@
+2010-06-18  Marek Habersack  <[email protected]>
+
+	* FormViewUpdateParameters_Bug607722.cs: added two POST phases -
+	Edit and Update.
+
 2010-05-07  Marek Habersack  <[email protected]>
 
 	* BuildManagerCacheFiles.cs: added - tests for

+ 39 - 6
mcs/class/System.Web/Test/standalone-tests/FormViewUpdateParameters_Bug607722.cs

@@ -35,10 +35,10 @@ using StandAloneTests;
 
 using NUnit.Framework;
 
-namespace StandAloneTests.Control_GetUniqueIDRelativeTo
+namespace StandAloneTests.FormViewUpdateParameters_Bug607722
 {
 	[TestCase ("FormViewUpdateParameters_Bug607722", "FormView update parameters should include keys")]
-	public sealed class FormViewUpdateParameters_Bug607722 : ITestCase
+	public sealed class Test_01 : ITestCase
 	{
 		public string PhysicalPath {
 			get { return Path.Combine (Consts.BasePhysicalDir, "FormViewUpdateParameters_Bug607722"); }
@@ -51,12 +51,45 @@ namespace StandAloneTests.Control_GetUniqueIDRelativeTo
 		public bool SetUp (List <TestRunItem> runItems)
 		{
 			runItems.Add (new TestRunItem ("Default.aspx", Default_Aspx));
-#if BUG_IN_THE_RUNTIME
+			
+#if BUG_IN_THE_RUNTIME_IS_FIXED
+			// With this version of code, the runtime segfaults. Until this is fixed,
+			// we'll be using an alternative version of the code
+			runItems.Add (new TestRunItem ("Default.aspx", null) {
+					PostValues = new SerializableDictionary <string, string> {
+						{"__EVENTTARGET", "FormView1$EditButton"},
+						{"__EVENTARGUMENT", String.Empty}
+					},
+					UrlDescription = "Edit phase"
+				}
+			);
 			runItems.Add (new TestRunItem ("Default.aspx", Default_Aspx_POST) {
 					PostValues = new SerializableDictionary <string, string> {
+						{"__EVENTTARGET", "FormView1$UpdateButton"},
+						{"__EVENTARGUMENT", String.Empty},
 						{"FormView1$M1TextBox", "12"},
 						{"FormView1$M2TextBox", "12"}
-					}
+					},
+					UrlDescription = "Update phase"
+				}
+			);
+#else
+			runItems.Add (new TestRunItem ("Default.aspx", null) {
+					PostValues = new string[] {
+						"__EVENTTARGET", "FormView1$EditButton",
+						"__EVENTARGUMENT", String.Empty
+					},
+					UrlDescription = "Edit phase"
+				}
+			);
+			runItems.Add (new TestRunItem ("Default.aspx", Default_Aspx_Update) {
+					PostValues = new string[] {
+						"__EVENTTARGET", "FormView1$UpdateButton",
+						"__EVENTARGUMENT", String.Empty,
+						"FormView1$M1TextBox", "12",
+						"FormView1$M2TextBox", "12"
+					},
+					UrlDescription = "Update phase"
 				}
 			);
 #endif
@@ -69,8 +102,8 @@ namespace StandAloneTests.Control_GetUniqueIDRelativeTo
 			string originalHtml = @"M1: <span id=""FormView1_M1Label"">0</span><br />M2: <span id=""FormView1_M2Label"">0</span>";
 			Helpers.ExtractAndCompareCodeFromHtml (result, originalHtml, "#A1");
 		}
-
-		void Default_Aspx_POST (string result, TestRunItem runItem)
+		
+		void Default_Aspx_Update (string result, TestRunItem runItem)
 		{
 			string originalHtml = @"M1: <span id=""FormView1_M1Label"">12</span><br />M2: <span id=""FormView1_M2Label"">12</span>";
 			Helpers.ExtractAndCompareCodeFromHtml (result, originalHtml, "#A1");

+ 7 - 0
mcs/class/System.Web/Test/tools/ChangeLog

@@ -1,3 +1,10 @@
+2010-06-18  Marek Habersack  <[email protected]>
+
+	* standalone-runner.cs: added new command line parameter, --test,
+	which selects a single test to run instead of the entire suite. It
+	should be passed a fully qualified (without assembly name) type
+	name of the test class.
+
 2010-02-03  Marek Habersack  <[email protected]>
 
 	* Makefile: added targets to compile cache priority queue tests

+ 19 - 8
mcs/class/System.Web/Test/tools/standalone-runner.cs

@@ -82,12 +82,14 @@ namespace StandAloneRunner
 		static void Run (string[] args)
 		{
 			bool showHelp = false;
+			string testName = null;
+			
 			var options = new OptionSet () {
 				{"?|h|help", "Show short usage screen.", v => showHelp = true},
-			};
-
+				{"t=|test=", "Run this test only (full type name)", (string s) => testName = s},
+			}; 
+			
 			List <string> extra = options.Parse (args);
-
 			int extraCount = extra.Count;
 
 			if (showHelp || extraCount < 1)
@@ -109,13 +111,18 @@ namespace StandAloneRunner
 			int runCounter = 0;
 			int failedCounter = 0;
 			var reports = new List <string> ();
-			DateTime start = DateTime.Now;
-			DateTime end;
-
+			
 			Console.WriteLine ("Running tests:");
+			DateTime start = DateTime.Now;
+			
 			foreach (StandaloneTest test in tests) {
 				if (test.Info.Disabled)
 					continue;
+
+				if (!String.IsNullOrEmpty (testName)) {
+					if (String.Compare (test.TestType.FullName, testName) != 0)
+						continue;
+				}
 				
 				test.Run (appMan);
 				runCounter++;
@@ -124,8 +131,9 @@ namespace StandAloneRunner
 					reports.Add (FormatReport (test));
 				}
 			}
+			
+			DateTime end = DateTime.Now;
 			Console.WriteLine ();
-			end = DateTime.Now;
 
 			if (reports.Count > 0) {
 				int repCounter = 0;
@@ -151,7 +159,7 @@ namespace StandAloneRunner
 			string newline = Environment.NewLine;
 			
 			sb.AppendFormat ("{0}{1}", test.Info.Name, newline);			
-			sb.AppendFormat ("{0,16}: {1}{2}", "Type", test.TestType, newline);
+			sb.AppendFormat ("{0,16}: {1}{2}", "Test", test.TestType, newline);
 
 			if (!String.IsNullOrEmpty (test.Info.Description))
 				sb.AppendFormat ("{0,16}: {1}{2}", "Description", test.Info.Description, newline);
@@ -159,6 +167,9 @@ namespace StandAloneRunner
 			if (!String.IsNullOrEmpty (test.FailedUrl))
 				sb.AppendFormat ("{0,16}: {1}{2}", "Failed URL", test.FailedUrl, newline);
 
+			if (!String.IsNullOrEmpty (test.FailedUrlCallbackName))
+				sb.AppendFormat ("{0,16}: {1}{2}", "Callback method", test.FailedUrlCallbackName, newline);
+			
 			if (!String.IsNullOrEmpty (test.FailedUrlDescription))
 				sb.AppendFormat ("{0,16}: {1}{2}", "URL description", test.FailedUrlDescription, newline);