2010-06-18 Marek Habersack <mhabersack@novell.com>
authorMarek Habersack <grendel@twistedcode.net>
Thu, 17 Jun 2010 23:49:37 +0000 (23:49 -0000)
committerMarek Habersack <grendel@twistedcode.net>
Thu, 17 Jun 2010 23:49:37 +0000 (23:49 -0000)
* 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  <mhabersack@novell.com>

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

2010-06-18  Marek Habersack  <mhabersack@novell.com>

* 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

mcs/class/System.Web/Makefile
mcs/class/System.Web/Test/standalone-runner-support/ChangeLog
mcs/class/System.Web/Test/standalone-runner-support/StandaloneTest.cs
mcs/class/System.Web/Test/standalone-runner-support/TestRunItem.cs
mcs/class/System.Web/Test/standalone-runner-support/TestRunner.cs
mcs/class/System.Web/Test/standalone-runner-support/TestWorkerRequest.cs
mcs/class/System.Web/Test/standalone-tests/ChangeLog
mcs/class/System.Web/Test/standalone-tests/FormViewUpdateParameters_Bug607722.cs
mcs/class/System.Web/Test/tools/ChangeLog
mcs/class/System.Web/Test/tools/standalone-runner.cs

index 156608ae199c6542990530c47fc4e85fd2f1417a..52d4738d2d2e8bc986341a6d8b3992825e6ed25e 100644 (file)
@@ -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)
 
index 34398dbdd7a3ccb899b3b873f3712dee9a955fdd..b25f4365a042fed3c840a058e2034b7a8a55844a 100644 (file)
@@ -1,3 +1,17 @@
+2010-06-18  Marek Habersack  <mhabersack@novell.com>
+
+       * 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  <mhabersack@novell.com>
 
        * TestWorkerRequest.cs: added overloads of GetRawUrl and
index 1ab845b8473676a76b9b65258d44104e71de69ac..85bc1cfb6f07dab4fa99f753dbcfa67c13e55ea3 100644 (file)
 //
 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 ();
+                }
        }
 }
index 2386d574a7066419daa1e9dd05f8d57a83ec41a2..13b02f0723d76559e3243640993a7f35b26b3a51 100644 (file)
@@ -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)
                {}
index 545468096e31c0327bf67f7bd33061e765ff154a..c510bb9f977a36193a98264e8693a2da87454d7a 100644 (file)
@@ -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 {
index eec08548e362e00e79d507ba7123f15e1ba42cde..d3724abaae1312e6981d2cfcc0ea9fbb43505b56 100644 (file)
 // 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)
                {
index 320d0431afb3cf8a786cb0ec5539d81062fe41e7..cfbf69d1317386653d8bde4e5d37d90de1c9fbf1 100644 (file)
@@ -1,3 +1,8 @@
+2010-06-18  Marek Habersack  <mhabersack@novell.com>
+
+       * FormViewUpdateParameters_Bug607722.cs: added two POST phases -
+       Edit and Update.
+
 2010-05-07  Marek Habersack  <mhabersack@novell.com>
 
        * BuildManagerCacheFiles.cs: added - tests for
index 1cc3814361fc13474eac8f1c15a2692e8ff9ca6a..e0817943f5e3cd6c509ee1bd7c598763383f94b8 100644 (file)
@@ -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");
index 6f079badd530c6c51dda3a5a26aa27ebe7e35c07..e27c31e180fecb90b3123113d62580ea3910392f 100644 (file)
@@ -1,3 +1,10 @@
+2010-06-18  Marek Habersack  <mhabersack@novell.com>
+
+       * 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  <mhabersack@novell.com>
 
        * Makefile: added targets to compile cache priority queue tests
index 2633b56e43f007c33a4444a5521341ed28a9f3d4..6d523fd196e8c73bfc78f09e168e47c53a3b94f3 100644 (file)
@@ -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);