* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / System.Web / Test / mainsoft / NunitWeb / NunitWeb / WebTest.cs
1 using System;
2 using System.Reflection;
3 using System.IO;
4 using System.Web.Hosting;
5
6 namespace MonoTests.SystemWeb.Framework
7 {
8         /// <summary>\r
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
12         /// </summary>
13         /// <seealso cref="Request"/>
14         /// <seealso cref="Response"/>
15         /// <seealso cref="Invoker"/>
16         /// <seealso cref="Run"/>
17         [Serializable]
18         public class WebTest
19         {
20                 object _userData;
21                 /// <summary>\r
22                 /// Any user-defined data. Must be serializable to pass between appdomains.\r
23                 /// </summary>\r
24                 /// <example>\r
25                 /// [Test]\r
26                 /// public void SampleTest ()\r
27                 /// {\r
28                 ///     WebTest t = new WebTest (new HandlerInvoker (MyCallback));\r
29                 ///     t.Run ();\r
30                 ///     Assert.AreEqual ("Was here", t.UserData.ToString());\r
31                 /// }\r
32                 /// \r
33                 /// static public void MyCallback ()\r
34                 /// {\r
35                 ///     WebTest.CurrentTest.UserData = "Was here";\r
36                 /// }\r
37                 /// </example>\r
38
39                 public object UserData
40                 {
41                         get { return _userData; }
42                         set { _userData = value; }
43                 }
44
45                 Response _response;
46                 /// <summary>\r
47                 /// The result of the last <see cref="Run"/>. See <see cref="MonoTests.SystemWeb.Framework.Response"/>,\r
48                 /// <see cref="FormRequest"/>.\r
49                 /// </summary>
50                 /// <seealso cref="Run"/>
51                 /// <seealso cref="MonoTests.SystemWeb.Framework.Response"/>
52                 /// <seealso cref="FormRequest"/>
53                 public Response Response
54                 {
55                         get { return _response; }
56                         set { _response = value; }
57                 }
58
59                 BaseInvoker _invoker;
60                 /// <summary>\r
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
64                 /// </summary>
65                 /// <seealso cref="Invoke"/>
66                 /// <seealso cref="PageInvoker"/>
67                 /// <seealso cref="BaseInvoker"/>
68                 /// <seealso cref="HandlerInvoker"/>
69                 public BaseInvoker Invoker
70                 {
71                         get { return _invoker; }
72                         set { _invoker = value; }
73                 }
74
75                 BaseRequest _request;
76                 /// <summary>\r
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
80                 /// </summary>
81                 /// <seealso cref="System.Web.HttpWorkerRequest"/>
82                 /// <seealso cref="BaseRequest"/>
83                 /// <seealso cref="PostableRequest"/>
84                 /// <seealso cref="FormRequest"/>
85                 public BaseRequest Request
86                 {
87                         get { return _request; }
88                         set { _request = value; }
89                 }
90
91                 static MyHost host;
92                 private static MyHost Host
93                 {
94                         get {
95                                 if (host != null)
96                                         return host;
97 #if !TARGET_JVM
98                                 host = AppDomain.CurrentDomain.GetData (HOST_INSTANCE_NAME) as MyHost;
99                                 if (host != null)
100                                         return host;
101 #endif
102                                 try {
103                                         host = new MyHost (); //Fake instance to make EnsureHosting happy
104                                         host = InitHosting ();
105                                 }
106                                 catch {
107                                         host = null; //Remove the fake instance if CreateHosting failed
108                                         throw;
109                                 }
110                                 return host;
111                         }
112                 }
113
114                 /// <summary>\r
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
117                 /// </summary>\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"/>
123                 public string Run ()
124                 {
125                         if (Request.Url == null)
126                                 Request.Url = Invoker.GetDefaultUrl ();
127                         WebTest newTestInstance = Host.Run (this);
128                         CopyFrom (newTestInstance);
129                         return _response.Body;
130                 }
131
132                 private void CopyFrom (WebTest newTestInstance)
133                 {
134                         this._invoker = newTestInstance._invoker;
135                         this._request = newTestInstance._request;
136                         this._response = newTestInstance._response;
137                         this._userData = newTestInstance._userData;
138                 }
139
140                 /// <summary>\r
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
143                 /// </summary>
144                 public static WebTest CurrentTest
145                 {
146                         get { return MyHost.GetCurrentTest (); }
147                 }
148
149                 /// <summary>\r
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
152                 /// </summary>\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)
159                 {
160                         try {
161                                 Invoker.DoInvoke (param);
162                         }
163                         catch (Exception ex) {
164                                 RegisterException (ex);
165                                 throw;
166                         }
167                 }
168
169                 /// <summary>\r
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
175                 /// </summary>\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)
181                 {
182                         Host.RegisterException (ex);
183                 }
184
185                 /// <summary>\r
186                 /// Unload the web appdomain and delete the temporary application root\r
187                 /// directory.\r
188                 /// </summary>
189                 public static void Unload ()
190                 {
191                         if (host == null)
192                                 return;
193
194                         AppDomain oldDomain = host.AppDomain;
195                         host = null;
196 #if !TARGET_JVM
197                         AppDomain.CurrentDomain.SetData (HOST_INSTANCE_NAME, null);
198                         AppDomain.Unload (oldDomain);
199 #endif
200                         Directory.Delete (baseDir, true);
201                 }
202
203                 /// <summary>\r
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
207                 /// </summary>
208                 /// <seealso cref="Invoker"/>
209                 /// <seealso cref="BaseInvoker"/>
210                 /// <seealso cref="Request"/>
211                 /// <seealso cref="BaseRequest"/>
212                 public WebTest ()
213                 {
214                         Invoker = new BaseInvoker ();
215                         Request = new BaseRequest ();
216                 }
217
218                 /// <summary>\r
219                 /// Same as <see cref="WebTest()"/>, and set <see cref="MonoTests.SystemWeb.Framework.BaseRequest.Url"/> to\r
220                 /// the specified Url.\r
221                 /// </summary>\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)
226                         : this ()
227                 {
228                         Request.Url = url;
229                 }
230
231                 /// <summary>\r
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
234                 /// </summary>\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)
240                         : this ()
241                 {
242                         Invoker = invoker;
243                 }\r
244 \r
245                 /// <summary>\r
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
248                 /// </summary>\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)
254                         : this ()
255                 {
256                         Request = request;
257                 }
258
259
260 #if !TARGET_JVM
261                 static void LoadAssemblyRecursive (Assembly ass)
262                 {
263                         if (ass.GlobalAssemblyCache)
264                                 return;
265                         foreach (AssemblyName ran in ass.GetReferencedAssemblies ()) {
266                                 bool found = false;
267                                 foreach (Assembly domain_ass in AppDomain.CurrentDomain.GetAssemblies ()) {
268                                         if (domain_ass.FullName == ran.FullName) {
269                                                 found = true;
270                                                 break;
271                                         }
272                                 }
273                                 if (found)
274                                         continue;
275                                 Assembly ra = Assembly.Load (ran, null);
276                                 LoadAssemblyRecursive (ra);
277                         }
278                 }
279
280                 private static void CopyAssembly (Assembly ass, string dir)
281                 {
282                         if (ass.GlobalAssemblyCache)
283                                 return;
284                         string oldfn = ass.Location;
285                         if (oldfn.EndsWith (".exe"))
286                                 return;
287                         string newfn = Path.Combine (dir, Path.GetFileName (oldfn));
288                         if (File.Exists (newfn))
289                                 return;
290                         File.Copy (oldfn, newfn);
291                 }
292 #endif
293
294                 private static void EnsureDirectoryExists (string directory)
295                 {
296                         if (directory == string.Empty)
297                                 return;
298                         if (Directory.Exists (directory))
299                                 return;
300                         EnsureDirectoryExists (Path.GetDirectoryName (directory));
301                         Directory.CreateDirectory (directory);
302                 }
303
304                 /// <summary>
305                 /// Copy a resource embedded in the assembly into the web application
306                 /// </summary>
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)
313                 {
314                         EnsureHosting ();
315                         EnsureDirectoryExists (Path.Combine (baseDir,
316                                 Path.GetDirectoryName (targetUrl)));
317                         using (Stream source = type.Assembly.GetManifestResourceStream (resourceName)) {
318                                 if (source == null)
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);
324                                 }
325                         }
326                 }
327
328                 private static void EnsureHosting ()
329                 {
330                         MyHost h = Host;
331                 }
332
333                 private static string baseDir;
334                 private static string binDir;
335                 const string VIRTUAL_BASE_DIR = "/NunitWeb";
336 #if !TARGET_JVM
337                 const string HOST_INSTANCE_NAME = "MonoTests/SysWeb/Framework/Host";
338 #endif
339
340                 private static MyHost InitHosting ()
341                 {
342                         string tmpFile = Path.GetTempFileName ();
343                         File.Delete (tmpFile);
344                         Directory.CreateDirectory (tmpFile);
345                         baseDir = tmpFile;
346                         binDir = Directory.CreateDirectory (Path.Combine (baseDir, "bin")).FullName;
347
348                         CopyResources ();
349                         File.Create (Path.Combine (baseDir, "page.fake"));
350 #if !TARGET_JVM
351                         foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies ())
352                                 LoadAssemblyRecursive (ass);
353
354                         foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies ())
355                                 CopyAssembly (ass, binDir);
356
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);
360 #else
361                         host = new MyHost ();
362                         AppDomain.CurrentDomain.SetData (".appVPath", VIRTUAL_BASE_DIR);\r
363                         AppDomain.CurrentDomain.SetData (".appPath", baseDir);\r
364 #endif
365                         return host;
366                 }
367
368                 private static void CopyResources ()
369                 {
370 #if VISUAL_STUDIO
371                         CopyResource (typeof (WebTest),
372                                 "MonoTests.SystemWeb.Framework.Resources.Web.config",
373                                 "Web.config");
374                         CopyResource (typeof (WebTest),
375                                 "MonoTests.SystemWeb.Framework.Resources.MyPage.aspx",
376                                 "MyPage.aspx");
377                         CopyResource (typeof (WebTest),
378                                 "MonoTests.SystemWeb.Framework.Resources.MyPage.aspx.cs",
379                                 "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",
385                                 "My.master");
386 #if TARGET_JVM
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");
405 #endif
406 #else
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");
412 #endif
413                 }
414         }
415 }