Merge pull request #1936 from esdrubal/DotNetRelativeOrAbsolute
[mono.git] / mcs / class / System / Test / System.Net / WebClientTestAsync.cs
1 //
2 // System.Net.WebClientTestAsync
3 //
4 // Authors:
5 //      Martin Baulig (martin.baulig@googlemail.com)
6 //
7 // Copyright 2012 Xamarin Inc. (http://www.xamarin.com)
8 //
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 #if NET_4_5
30 using System;
31 using System.IO;
32 using System.Collections.Generic;
33 using System.Threading;
34 using System.Threading.Tasks;
35 using System.Reflection;
36 using System.Net;
37 using NUnit.Framework;
38
39 namespace MonoTests.System.Net
40 {
41         [TestFixture]
42         public class WebClientTestAsync
43         {
44                 [Test]
45                 [Category("Async")]
46                 [Category("AndroidNotWorking")] // Attempts to access the test dll which won't work on Android
47                 public void DownloadData ()
48                 {
49                         WebClient wc;
50                         bool progress_changed = false;
51                         bool completed = false;
52                         bool progress_changed_error = false;
53                         bool completed_error = false;
54
55                         int thread_id = Thread.CurrentThread.ManagedThreadId;
56
57                         wc = new WebClient ();
58
59                         wc.DownloadProgressChanged += delegate {
60                                 progress_changed = true;
61                                 if (Thread.CurrentThread.ManagedThreadId != thread_id)
62                                         progress_changed_error = true;
63                         };
64                         wc.DownloadDataCompleted += delegate {
65                                 completed = true;
66                                 if (Thread.CurrentThread.ManagedThreadId != thread_id)
67                                         completed_error = true;
68                         };
69
70                         MessagePumpSyncContext.Run (async () => {
71                                 var url = Assembly.GetExecutingAssembly ().CodeBase;
72                                 await wc.DownloadDataTaskAsync (url);
73                                 Assert.AreEqual (Thread.CurrentThread.ManagedThreadId, thread_id);
74                         }, () => progress_changed && completed, 10000);
75
76                         Assert.IsTrue (progress_changed, "#1");
77                         Assert.IsFalse (progress_changed_error, "#2");
78                         Assert.IsTrue (completed, "#3");
79                         Assert.IsFalse (completed_error, "#4");
80                 }
81
82                 [Test]
83                 [Category("InetAccess")]
84                 public void DownloadFileTaskAsync ()
85                 {
86                         WebClient wc = new WebClient ();
87                         string filename = Path.GetTempFileName ();
88
89                         var task = wc.DownloadFileTaskAsync ("http://www.mono-project.com/", filename);
90                         Assert.IsTrue (task.Wait (15000));
91                         Assert.IsTrue (task.IsCompleted);
92                         
93                         File.Delete (filename);
94                 }
95
96                 [Test]
97                 [Category("InetAccess")]
98                 [Category ("AndroidNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
99                 public void Cancellation ()
100                 {
101                         WebClient wc = new WebClient ();
102                         var progress = new ManualResetEvent (false);
103                         wc.DownloadProgressChanged += delegate {
104                                 progress.Set ();
105                         };
106
107                         // Try downloading some large file, so we don't finish early.
108                         var url = "http://download.mono-project.com/archive/2.10.9/macos-10-x86/11/MonoFramework-MDK-2.10.9_11.macos10.xamarin.x86.dmg";
109                         var task = wc.DownloadDataTaskAsync (url);
110                         Assert.IsTrue (progress.WaitOne (15000), "#1");
111                         wc.CancelAsync ();
112
113                         try {
114                                 task.Wait ();
115                                 Assert.Fail ("#2");
116                         } catch (Exception ex) {
117                                 if (ex is AggregateException)
118                                         ex = ((AggregateException)ex).InnerException;
119                                 Assert.That (ex is WebException || ex is OperationCanceledException, "#4");
120                                 Assert.IsTrue (task.IsCanceled || task.IsFaulted, "#5");
121                         }
122                 }
123
124                 [Test]
125                 [Category("InetAccess")]
126                 [Category ("AndroidNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
127                 public void DownloadMultiple ()
128                 {
129                         WebClient wc = new WebClient ();
130                         var t1 = wc.OpenReadTaskAsync ("http://www.google.com/");
131                         Assert.That (t1.Wait (15000));
132                         Assert.IsTrue (t1.IsCompleted, "#1");
133
134                         var t2 = wc.OpenReadTaskAsync ("http://www.mono-project.com/");
135                         Assert.That (t2.Wait (15000));
136                         Assert.IsTrue (t2.IsCompleted, "#2");
137
138                         var t3 = wc.DownloadStringTaskAsync ("http://www.google.com/");
139                         Assert.That (t3.Wait (15000));
140                         Assert.IsTrue (t3.IsCompleted, "#3");
141                 }
142
143                 [Test]
144                 [Category("InetAccess")]
145                 [Category ("AndroidNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
146                 public void DownloadMultiple2 ()
147                 {
148                         WebClient wc = new WebClient ();
149
150                         MessagePumpSyncContext.Run (async () => {
151                                 await wc.DownloadStringTaskAsync ("http://www.google.com/");
152                                 await wc.DownloadDataTaskAsync ("http://www.mono-project.com/");
153                         }, null, 15000);
154                 }
155
156                 [Test]
157                 [Category("InetAccess")]
158                 [Category ("AndroidNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
159                 public void DownloadMultiple3 ()
160                 {
161                         WebClient wc = new WebClient ();
162                         int thread_id = Thread.CurrentThread.ManagedThreadId;
163                         bool data_completed = false;
164                         bool string_completed = false;
165                         bool error = false;
166
167                         wc.DownloadDataCompleted += delegate {
168                                 if (data_completed || (Thread.CurrentThread.ManagedThreadId != thread_id))
169                                         error = true;
170                                 data_completed = true;
171                         };
172                         wc.DownloadStringCompleted += delegate {
173                                 if (string_completed || (Thread.CurrentThread.ManagedThreadId != thread_id))
174                                         error = true;
175                                 string_completed = true;
176                         };
177
178                         MessagePumpSyncContext.Run (async () => {
179                                 await wc.DownloadStringTaskAsync ("http://www.google.com/");
180                                 await wc.DownloadDataTaskAsync ("http://www.mono-project.com/");
181                         }, () => data_completed && string_completed, 15000);
182
183                         Assert.IsTrue (data_completed, "#1");
184                         Assert.IsTrue (string_completed, "#2");
185                         Assert.IsFalse (error, "#3");
186                 }
187
188                 public sealed class MessagePumpSyncContext : SynchronizationContext
189                 {
190                         private delegate void MyAction ();
191
192                         private readonly Queue<MyAction> queue = new Queue<MyAction> ();
193                         private readonly object sync = new object ();
194                         private readonly Func<bool> completed;
195                         private readonly int timeout;
196                         private bool running = true;
197
198                         MessagePumpSyncContext (Func<bool> completed, int timeout)
199                         {
200                                 this.completed = completed;
201                                 this.timeout = timeout;
202                         }
203
204                         public override void Send (SendOrPostCallback d, object state)
205                         {
206                                 throw new InvalidOperationException ();
207                         }
208
209                         public override void Post (SendOrPostCallback d, object state)
210                         {
211                                 lock (sync) {
212                                         queue.Enqueue (() => d (state));
213                                         Monitor.Pulse (sync);
214                                 }
215                         }
216
217                         bool IsCompleted {
218                                 get {
219                                         if (running)
220                                                 return false;
221                                         if (completed != null)
222                                                 return completed ();
223                                         return true;
224                                 }
225                         }
226
227                         void RunMessagePump ()
228                         {
229                                 while (running) {
230                                         MyAction action;
231                                         lock (sync) {
232                                                 while (queue.Count == 0) {
233                                                         if (IsCompleted)
234                                                                 return;
235                                                         if (!Monitor.Wait (sync, timeout))
236                                                                 throw new TimeoutException ();
237                                                 }
238                                                 action = queue.Dequeue ();
239                                         }
240                                         action ();
241                                 }
242                         }
243
244                         public void Cancel ()
245                         {
246                                 lock (sync) {
247                                         running = false;
248                                         Monitor.Pulse (sync);
249                                 }
250                         }
251
252                         public static void Run (Func<Task> action, Func<bool> completed, int timeout)
253                         {
254                                 var old_ctx = SynchronizationContext.Current;
255
256                                 var ctx = new MessagePumpSyncContext (completed, timeout);
257                                 try {
258                                         SynchronizationContext.SetSynchronizationContext (ctx);
259
260                                         var thread_id = Thread.CurrentThread.ManagedThreadId;
261
262                                         var task = action ();
263                                         task.ContinueWith ((t) => {
264                                                 ctx.running = false;
265                                         }, TaskScheduler.FromCurrentSynchronizationContext ());
266
267                                         ctx.RunMessagePump ();
268
269                                         if (task.IsFaulted)
270                                                 throw task.Exception;
271                                 } finally {
272                                         SynchronizationContext.SetSynchronizationContext (old_ctx);
273                                 }
274                         }
275
276                 }
277         }
278 }
279 #endif