2 using System.Reflection;
4 using System.Web.Hosting;
6 namespace MonoTests.SystemWeb.Framework
9 /// The most important class from user perspective. See <see cref="Request"/>,
\r
10 /// <see cref="Response"/>, <see cref="Invoker"/>, <see cref="Run"/> for
\r
11 /// more information.
\r
13 /// <seealso cref="Request"/>
14 /// <seealso cref="Response"/>
15 /// <seealso cref="Invoker"/>
16 /// <seealso cref="Run"/>
22 /// Any user-defined data. Must be serializable to pass between appdomains.
\r
26 /// public void SampleTest ()
\r
28 /// WebTest t = new WebTest (new HandlerInvoker (MyCallback));
\r
30 /// Assert.AreEqual ("Was here", t.UserData.ToString());
\r
33 /// static public void MyCallback ()
\r
35 /// WebTest.CurrentTest.UserData = "Was here";
\r
39 public object UserData
41 get { return _userData; }
42 set { _userData = value; }
47 /// The result of the last <see cref="Run"/>. See <see cref="MonoTests.SystemWeb.Framework.Response"/>,
\r
48 /// <see cref="FormRequest"/>.
\r
50 /// <seealso cref="Run"/>
51 /// <seealso cref="MonoTests.SystemWeb.Framework.Response"/>
52 /// <seealso cref="FormRequest"/>
53 public Response Response
55 get { return _response; }
56 set { _response = value; }
61 /// Set the invoker, which is executed in the web context by <see cref="Invoke"/>
\r
62 /// method. Most commonly used <see cref="PageInvoker"/>. See also: <see cref="BaseInvoker"/>,
\r
63 /// <see cref="HandlerInvoker"/>
\r
65 /// <seealso cref="Invoke"/>
66 /// <seealso cref="PageInvoker"/>
67 /// <seealso cref="BaseInvoker"/>
68 /// <seealso cref="HandlerInvoker"/>
69 public BaseInvoker Invoker
71 get { return _invoker; }
72 set { _invoker = value; }
77 /// Contains all the data necessary to create an <see cref="System.Web.HttpWorkerRequest"/> in
\r
78 /// the application appdomain. See also <see cref="BaseRequest"/>,
\r
79 /// <see cref="PostableRequest"/>, <see cref="FormRequest"/>.
\r
81 /// <seealso cref="System.Web.HttpWorkerRequest"/>
82 /// <seealso cref="BaseRequest"/>
83 /// <seealso cref="PostableRequest"/>
84 /// <seealso cref="FormRequest"/>
85 public BaseRequest Request
87 get { return _request; }
88 set { _request = value; }
92 private static MyHost Host
98 host = AppDomain.CurrentDomain.GetData (HOST_INSTANCE_NAME) as MyHost;
103 host = new MyHost (); //Fake instance to make EnsureHosting happy
104 host = InitHosting ();
107 host = null; //Remove the fake instance if CreateHosting failed
115 /// Run the request using <see cref="Request"/> and <see cref="Invoker"/>
\r
116 /// values. Keep the result of the request in <see cref="Response"/> property.
\r
118 /// <returns>The body of the HTTP response (<see cref="MonoTests.SystemWeb.Framework.Response.Body"/>).</returns>
119 /// <seealso cref="Request"/>
120 /// <seealso cref="Invoker"/>
121 /// <seealso cref="Response"/>
122 /// <seealso cref="MonoTests.SystemWeb.Framework.Response.Body"/>
125 if (Request.Url == null)
126 Request.Url = Invoker.GetDefaultUrl ();
127 WebTest newTestInstance = Host.Run (this);
128 CopyFrom (newTestInstance);
129 return _response.Body;
132 private void CopyFrom (WebTest newTestInstance)
134 this._invoker = newTestInstance._invoker;
135 this._request = newTestInstance._request;
136 this._response = newTestInstance._response;
137 this._userData = newTestInstance._userData;
141 /// The instance of the currently running test. Defined only in the web appdomain.
\r
142 /// In different threads this property may have different values.
\r
144 public static WebTest CurrentTest
146 get { return MyHost.GetCurrentTest (); }
150 /// This method must be called when custom <see cref="System.Web.IHttpHandler.ProcessRequest"/> or aspx code behind is used,
\r
151 /// to allow the framework to invoke all user supplied delegates.
\r
153 /// <param name="param">Parameter defined by the <see cref="BaseInvoker"/> subclass. For example,
154 /// <see cref="PageInvoker"/> expects to receive a <see cref="System.Web.UI.Page"/> instance here.</param>
155 /// <seealso cref="System.Web.IHttpHandler.ProcessRequest"/>
156 /// <seealso cref="BaseInvoker"/>
157 /// <seealso cref="PageInvoker"/>
158 public void Invoke (object param)
161 Invoker.DoInvoke (param);
163 catch (Exception ex) {
164 RegisterException (ex);
170 /// This method is intended for use from <see cref="MonoTests.SystemWeb.Framework.BaseInvoker.DoInvoke"/> when
\r
171 /// the invocation causes an exception. In such cases, the exception must be registered
\r
172 /// with this method, and then swallowed. Before returning, <see cref="WebTest.Run"/>
\r
173 /// will rethrow this exception. This is done to hide the exception from <see cref="System.Web.HttpRuntime"/>,
\r
174 /// which normally swallows the exception and returns 500 ERROR http result.
\r
176 /// <param name="ex">The exception to be registered and rethrown.</param>
177 /// <seealso cref="MonoTests.SystemWeb.Framework.BaseInvoker.DoInvoke"/>
178 /// <seealso cref="WebTest.Run"/>
179 /// <seealso cref="System.Web.HttpRuntime"/>
180 public static void RegisterException (Exception ex)
182 Host.RegisterException (ex);
186 /// Unload the web appdomain and delete the temporary application root
\r
189 public static void Unload ()
194 AppDomain oldDomain = host.AppDomain;
197 AppDomain.CurrentDomain.SetData (HOST_INSTANCE_NAME, null);
198 AppDomain.Unload (oldDomain);
200 Directory.Delete (baseDir, true);
204 /// Default constructor. Initializes <see cref="Invoker"/> with a new
\r
205 /// <see cref="BaseInvoker"/> and <see cref="Request"/> with an empty
\r
206 /// <see cref="BaseRequest"/>.
\r
208 /// <seealso cref="Invoker"/>
209 /// <seealso cref="BaseInvoker"/>
210 /// <seealso cref="Request"/>
211 /// <seealso cref="BaseRequest"/>
214 Invoker = new BaseInvoker ();
215 Request = new BaseRequest ();
219 /// Same as <see cref="WebTest()"/>, and set <see cref="MonoTests.SystemWeb.Framework.BaseRequest.Url"/> to
\r
220 /// the specified Url.
\r
222 /// <param name="url">The URL used for the next <see cref="Run"/></param>
223 /// <seealso cref="MonoTests.SystemWeb.Framework.BaseRequest.Url"/>
224 /// <seealso cref="Run"/>
225 public WebTest (string url)
232 /// Create a new instance, initializing <see cref="Invoker"/> with the given
\r
233 /// value, and the <see cref="Request"/> with <see cref="BaseRequest"/>.
\r
235 /// <param name="invoker">The invoker used for this test.</param>
236 /// <seealso cref="Invoker"/>
237 /// <seealso cref="Request"/>
238 /// <seealso cref="BaseRequest"/>
239 public WebTest (BaseInvoker invoker)
246 /// Create a new instance, initializing <see cref="Request"/> with the given
\r
247 /// value, and the <see cref="Invoker"/> with <see cref="BaseInvoker"/>.
\r
249 /// <param name="request">The request used for this test.</param>
\r
250 /// <seealso cref="Request"/>
\r
251 /// <seealso cref="Invoker"/>
\r
252 /// <seealso cref="BaseInvoker"/>
\r
253 public WebTest (BaseRequest request)
261 static void LoadAssemblyRecursive (Assembly ass)
263 if (ass.GlobalAssemblyCache)
265 foreach (AssemblyName ran in ass.GetReferencedAssemblies ()) {
267 foreach (Assembly domain_ass in AppDomain.CurrentDomain.GetAssemblies ()) {
268 if (domain_ass.FullName == ran.FullName) {
275 Assembly ra = Assembly.Load (ran, null);
276 LoadAssemblyRecursive (ra);
280 private static void CopyAssembly (Assembly ass, string dir)
282 if (ass.GlobalAssemblyCache)
284 string oldfn = ass.Location;
285 if (oldfn.EndsWith (".exe"))
287 string newfn = Path.Combine (dir, Path.GetFileName (oldfn));
288 if (File.Exists (newfn))
290 File.Copy (oldfn, newfn);
294 private static void EnsureDirectoryExists (string directory)
296 if (directory == string.Empty)
298 if (Directory.Exists (directory))
300 EnsureDirectoryExists (Path.GetDirectoryName (directory));
301 Directory.CreateDirectory (directory);
305 /// Copy a resource embedded in the assembly into the web application
307 /// <param name="type">A type in the assembly that contains the embedded resource.</param>
308 /// <param name="resourceName">The name of the resource.</param>
309 /// <param name="targetUrl">The URL where the resource will be available</param>
310 /// <exception cref="System.ArgumentException">Thrown when resource with name resourceName is not found.</exception>
311 /// <example><code>CopyResource (GetType (), "Default.skin", "App_Themes/Black/Default.skin");</code></example>
312 public static void CopyResource (Type type, string resourceName, string targetUrl)
315 EnsureDirectoryExists (Path.Combine (baseDir,
316 Path.GetDirectoryName (targetUrl)));
317 using (Stream source = type.Assembly.GetManifestResourceStream (resourceName)) {
319 throw new ArgumentException ("resource not found: " + resourceName, "resourceName");
320 using (FileStream target = new FileStream (Path.Combine (baseDir, targetUrl), FileMode.CreateNew)) {
321 byte[] array = new byte[source.Length];
322 source.Read (array, 0, array.Length);
323 target.Write (array, 0, array.Length);
328 private static void EnsureHosting ()
333 private static string baseDir;
334 private static string binDir;
335 const string VIRTUAL_BASE_DIR = "/NunitWeb";
337 const string HOST_INSTANCE_NAME = "MonoTests/SysWeb/Framework/Host";
340 private static MyHost InitHosting ()
342 string tmpFile = Path.GetTempFileName ();
343 File.Delete (tmpFile);
344 Directory.CreateDirectory (tmpFile);
346 binDir = Directory.CreateDirectory (Path.Combine (baseDir, "bin")).FullName;
349 File.Create (Path.Combine (baseDir, "page.fake"));
351 foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies ())
352 LoadAssemblyRecursive (ass);
354 foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies ())
355 CopyAssembly (ass, binDir);
357 MyHost host = (MyHost) ApplicationHost.CreateApplicationHost (typeof (MyHost), VIRTUAL_BASE_DIR, baseDir);
358 AppDomain.CurrentDomain.SetData (HOST_INSTANCE_NAME, host);
359 host.AppDomain.SetData (HOST_INSTANCE_NAME, host);
361 host = new MyHost ();
362 AppDomain.CurrentDomain.SetData (".appVPath", VIRTUAL_BASE_DIR);
\r
363 AppDomain.CurrentDomain.SetData (".appPath", baseDir);
\r
368 private static void CopyResources ()
371 CopyResource (typeof (WebTest),
372 "MonoTests.SystemWeb.Framework.Resources.Web.config",
374 CopyResource (typeof (WebTest),
375 "MonoTests.SystemWeb.Framework.Resources.MyPage.aspx",
377 CopyResource (typeof (WebTest),
378 "MonoTests.SystemWeb.Framework.Resources.MyPage.aspx.cs",
380 CopyResource (typeof (WebTest),
381 "MonoTests.SystemWeb.Framework.Resources.MyPageWithMaster.aspx",
382 "MyPageWithMaster.aspx");
383 CopyResource (typeof (WebTest),
384 "MonoTests.SystemWeb.Framework.Resources.My.master",
387 CopyResource (typeof (WebTest),
388 "MonoTests.SystemWeb.Framework.Resources.assemblies.global.asax.xml",
389 "assemblies/global.asax.xml");
390 CopyResource (typeof (WebTest),
391 "MonoTests.SystemWeb.Framework.Resources.assemblies.mypage.aspx.xml",
392 "assemblies/mypage.aspx.xml");
393 CopyResource (typeof (WebTest),
394 "MonoTests.SystemWeb.Framework.Resources.assemblies.hnnefdht.dll.ghres",
395 "assemblies/hnnefdht/dll.ghres");
396 CopyResource (typeof (WebTest),
397 "MonoTests.SystemWeb.Framework.Resources.assemblies.hnnefdht.hnnefdhtAttrib.class",
398 "assemblies/hnnefdht/hnnefdhtAttrib.class");
399 // CopyResource (typeof (WebTest),
400 // "MonoTests.SystemWeb.Framework.Resources.assemblies.hnnefdht.ASP.MyPage_aspx_MyPage_aspxAttrib.class",
401 // "assemblies/hnnefdht/ASP/MyPage_aspx$MyPage_aspxAttrib.class");
402 // CopyResource (typeof (WebTest),
403 // "MonoTests.SystemWeb.Framework.Resources.assemblies.hnnefdht.ASP.MyPage_aspx.class",
404 // "assemblies/hnnefdht/ASP/MyPage_aspx.class");
407 CopyResource (typeof (WebTest), "Web.config", "Web.config");
408 CopyResource (typeof (WebTest), "MyPage.aspx", "MyPage.aspx");
409 CopyResource (typeof (WebTest), "MyPage.aspx.cs", "MyPage.aspx.cs");
410 CopyResource (typeof (WebTest), "MyPageWithMaster.aspx", "MyPageWithMaster.aspx");
411 CopyResource (typeof (WebTest), "My.master", "My.master");