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)
+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
//
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;
{
public sealed class StandaloneTest
{
+ const string HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
+
string failureDetails;
public TestCaseFailureException Exception {
public string FailedUrlDescription {
get; private set;
}
+
+ public string FailedUrlCallbackName {
+ get; private set;
+ }
public TestCaseAttribute Info {
get; private set;
return;
}
- Response response;
+ Response response, previousResponse = null;
TestRunner runner;
+ string[] formValues;
+
try {
Console.Write ('[');
foreach (var tri in runItems) {
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 {
AppDomain.Unload (runner.Domain);
}
runner = null;
+ previousResponse = response;
}
}
} catch (AssertionException ex) {
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 ();
+ }
}
}
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)
{}
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");
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 {
// 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;
{
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)
{
this.appVirtualDir = GetAppPath ();
this.pathInfo = pathInfo;
}
-
+
public override string GetFilePath ()
{
return page;
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 ()
{
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)
{
+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
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"); }
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
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");
+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
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)
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++;
reports.Add (FormatReport (test));
}
}
+
+ DateTime end = DateTime.Now;
Console.WriteLine ();
- end = DateTime.Now;
if (reports.Count > 0) {
int repCounter = 0;
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);
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);