Merge pull request #656 from LogosBible/collection_lock
[mono.git] / mcs / class / System.Core / Test / System.Linq.Expressions / ExpressionTest_Call.cs
1 //
2 // ExpressionTest_Call.cs
3 //
4 // Author:
5 //   Federico Di Gregorio <fog@initd.org>
6 //   Jb Evain (jbevain@novell.com)
7 //
8 // (C) 2008 Novell, Inc. (http://www.novell.com)
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
30 using System;
31 using System.Reflection;
32 using System.Collections.Generic;
33 using System.Linq;
34 using System.Linq.Expressions;
35 using NUnit.Framework;
36
37 namespace MonoTests.System.Linq.Expressions {
38
39         [TestFixture]
40         public class ExpressionTest_Call {
41
42                 [Test]
43                 [ExpectedException (typeof (ArgumentNullException))]
44                 public void Arg1Null ()
45                 {
46                         Expression.Call ((Type)null, "TestMethod", null, Expression.Constant (1));
47                 }
48
49                 [Test]
50                 [ExpectedException (typeof (ArgumentNullException))]
51                 public void Arg2Null ()
52                 {
53                         Expression.Call (typeof (MemberClass), null, null, Expression.Constant (1));
54                 }
55
56                 [Test]
57                 [ExpectedException (typeof (InvalidOperationException))]
58                 public void Arg4WrongType ()
59                 {
60                         Expression.Call (typeof (MemberClass), "StaticMethod", null, Expression.Constant (true));
61                 }
62
63                 [Test]
64                 [ExpectedException (typeof (InvalidOperationException))]
65                 public void InstanceMethod ()
66                 {
67                         Expression.Call (typeof (MemberClass), "TestMethod", null, Expression.Constant (1));
68                 }
69
70                 [Test]
71                 public void StaticMethod ()
72                 {
73                         Expression.Call (typeof (MemberClass), "StaticMethod", null, Expression.Constant (1));
74                 }
75
76                 [Test]
77                 public void StaticGenericMethod ()
78                 {
79                         Expression.Call (typeof (MemberClass), "StaticGenericMethod", new [] { typeof (int) }, Expression.Constant (1));
80                 }
81
82                 [Test]
83                 [ExpectedException (typeof (ArgumentNullException))]
84                 public void ArgMethodNull ()
85                 {
86                         Expression.Call (Expression.Constant (new object ()), null);
87                 }
88
89                 [Test]
90 #if NET_4_0
91                 [ExpectedException (typeof (ArgumentException))]
92 #else
93                 [ExpectedException (typeof (ArgumentNullException))]
94 #endif
95                 public void ArgInstanceNullForNonStaticMethod ()
96                 {
97                         Expression.Call (null, typeof (object).GetMethod ("ToString"));
98                 }
99
100                 [Test]
101                 [ExpectedException (typeof (ArgumentException))]
102                 public void InstanceTypeDoesntMatchMethodDeclaringType ()
103                 {
104 #if MOBILE
105                         // ensure that String.Intern won't be removed by the linker
106                         string s = String.Intern (String.Empty);
107 #endif
108                         Expression.Call (Expression.Constant (1), typeof (string).GetMethod ("Intern"));
109                 }
110
111                 [Test]
112                 [ExpectedException (typeof (ArgumentException))]
113                 public void MethodArgumentCountDoesnMatchParameterLength ()
114                 {
115                         Expression.Call (Expression.Constant (new object ()), typeof (object).GetMethod ("ToString"), Expression.Constant (new object ()));
116                 }
117
118                 public class Foo {
119                         public void Bar (string s)
120                         {
121                         }
122                 }
123
124                 [Test]
125                 [ExpectedException (typeof (ArgumentNullException))]
126                 public void MethodHasNullArgument ()
127                 {
128                         Expression.Call (Expression.New (typeof (Foo)), typeof (Foo).GetMethod ("Bar"), null as Expression);
129                 }
130
131                 [Test]
132                 [ExpectedException (typeof (ArgumentException))]
133                 public void MethodArgumentDoesntMatchParameterType ()
134                 {
135                         Expression.Call (Expression.New (typeof (Foo)), typeof (Foo).GetMethod ("Bar"), Expression.Constant (42));
136                 }
137
138                 [Test]
139                 public void CallToString ()
140                 {
141                         var call = Expression.Call (Expression.Constant (new object ()), typeof (object).GetMethod ("ToString"));
142                         Assert.AreEqual ("value(System.Object).ToString()", call.ToString ());
143                 }
144
145                 [Test]
146                 public void CallStringIsNullOrEmpty ()
147                 {
148                         var call = Expression.Call (null, typeof (string).GetMethod ("IsNullOrEmpty"), Expression.Constant (""));
149                         Assert.AreEqual ("IsNullOrEmpty(\"\")", call.ToString ());
150                 }
151
152                 [Test]
153                 [Category ("NotDotNet")] // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=339351
154                 [ExpectedException (typeof (ArgumentException))]
155                 public void CallStaticMethodWithInstanceArgument ()
156                 {
157                         Expression.Call (
158                                 Expression.Parameter (GetType (), "t"),
159                                 GetType ().GetMethod ("Identity"),
160                                 Expression.Constant (null));
161                 }
162
163                 public static object Identity (object o)
164                 {
165                         return o;
166                 }
167
168                 [Test]
169                 public void CompileSimpleStaticCall ()
170                 {
171                         var p = Expression.Parameter (typeof (object), "o");
172                         var lambda = Expression.Lambda<Func<object, object>> (Expression.Call (GetType ().GetMethod ("Identity"), p), p);
173
174                         var i = lambda.Compile ();
175
176                         Assert.AreEqual (2, i (2));
177                         Assert.AreEqual ("Foo", i ("Foo"));
178                 }
179
180                 [Test]
181                 public void CompileSimpleInstanceCall ()
182                 {
183                         var p = Expression.Parameter (typeof (string), "p");
184                         var lambda = Expression.Lambda<Func<string, string>> (
185                                 Expression.Call (
186                                         p, typeof (string).GetMethod ("ToString", Type.EmptyTypes)),
187                                 p);
188
189                         var ts = lambda.Compile ();
190
191                         Assert.AreEqual ("foo", ts ("foo"));
192                         Assert.AreEqual ("bar", ts ("bar"));
193                 }
194
195                 [Test]
196                 [ExpectedException (typeof (InvalidOperationException))]
197                 public void CheckTypeArgsIsNotUsedForParameterLookup ()
198                 {
199                         Expression.Call (GetType (), "EineMethod", new [] { typeof (string), typeof (int) }, "foo".ToConstant (), 2.ToConstant ());
200                 }
201
202                 public static void EineGenericMethod<X, Y> (string foo, int bar)
203                 {
204                 }
205
206                 [Test]
207                 public void CheckTypeArgsIsUsedForGenericArguments ()
208                 {
209                         var m = Expression.Call (GetType (), "EineGenericMethod", new [] { typeof (string), typeof (int) }, "foo".ToConstant (), 2.ToConstant ());
210                         Assert.IsNotNull (m.Method);
211                         Assert.AreEqual ("Void EineGenericMethod[String,Int32](System.String, Int32)", m.Method.ToString ());
212                 }
213
214                 public struct EineStrukt {
215
216                         public string Foo;
217
218                         public EineStrukt (string foo)
219                         {
220                                 Foo = foo;
221                         }
222
223                         public string GimmeFoo ()
224                         {
225                                 return Foo;
226                         }
227                 }
228
229                 [Test]
230                 [Category ("NotWorkingInterpreter")]
231                 public void CallMethodOnStruct ()
232                 {
233                         var param = Expression.Parameter (typeof (EineStrukt), "s");
234                         var foo = Expression.Lambda<Func<EineStrukt, string>> (
235                                 Expression.Call (param, typeof (EineStrukt).GetMethod ("GimmeFoo")), param).Compile ();
236
237                         var s = new EineStrukt ("foo");
238                         Assert.AreEqual ("foo", foo (s));
239                 }
240
241                 public static int OneStaticMethod ()
242                 {
243                         return 42;
244                 }
245
246                 [Test]
247                 [Category ("NotDotNet")] // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=339351
248                 [ExpectedException (typeof (ArgumentException))]
249                 public void CallStaticMethodOnNonSenseInstanceExpression ()
250                 {
251                         Expression.Call (
252                                 Expression.Constant ("la la la"),
253                                 this.GetType ().GetMethod ("OneStaticMethod"));
254                 }
255
256                 public static int DoSomethingWith (ref int a)
257                 {
258                         return a + 4;
259                 }
260
261                 public static string DoAnotherThing (ref int a, string s)
262                 {
263                         return s + a;
264                 }
265
266                 [Test]
267                 [Category ("NotWorkingInterpreter")]
268                 public void CallStaticMethodWithRefParameter ()
269                 {
270                         var p = Expression.Parameter (typeof (int), "i");
271
272                         var c = Expression.Lambda<Func<int, int>> (
273                                 Expression.Call (GetType ().GetMethod ("DoSomethingWith"), p), p).Compile ();
274
275                         Assert.AreEqual (42, c (38));
276                 }
277
278                 [Test]
279                 [Category ("NotWorkingInterpreter")]
280                 public void CallStaticMethodWithRefParameterAndOtherParameter ()
281                 {
282                         var i = Expression.Parameter (typeof (int), "i");
283                         var s = Expression.Parameter (typeof (string), "s");
284
285                         var lamda = Expression.Lambda<Func<int, string, string>> (
286                                 Expression.Call (GetType ().GetMethod ("DoAnotherThing"), i, s), i, s).Compile ();
287
288                         Assert.AreEqual ("foo42", lamda (42, "foo"));
289                 }
290
291                 public static int Bang (Expression i)
292                 {
293                         return (int) (i as ConstantExpression).Value;
294                 }
295 #if !NET_4_0 // dlr bug 5875
296                 [Test]
297                 public void CallMethodWithExpressionParameter ()
298                 {
299                         var call = Expression.Call (GetType ().GetMethod ("Bang"), Expression.Constant (42));
300                         Assert.AreEqual (ExpressionType.Quote, call.Arguments [0].NodeType);
301
302                         var l = Expression.Lambda<Func<int>> (call).Compile ();
303
304                         Assert.AreEqual (42, l ());
305                 }
306 #endif
307                 static bool fout_called = false;
308
309                 public static int FooOut (out int x)
310                 {
311                         fout_called = true;
312                         return x = 0;
313                 }
314
315                 [Test]
316                 [Category ("NotWorkingInterpreter")]
317                 public void Connect282729 ()
318                 {
319                         // test from https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=282729
320
321                         var p = Expression.Parameter (typeof (int), "p");
322                         var lambda = Expression.Lambda<Func<int, int>> (
323                                 Expression.Call (
324                                         GetType ().GetMethod ("FooOut"),
325                                         Expression.ArrayIndex(
326                                                 Expression.NewArrayBounds (
327                                                         typeof(int),
328                                                         1.ToConstant ()),
329                                                 0.ToConstant ())),
330                                 p).Compile ();
331
332                         Assert.AreEqual (0, lambda (0));
333                         Assert.IsTrue (fout_called);
334                 }
335
336                 public static int FooOut2 (out int x)
337                 {
338                         x = 2;
339                         return 3;
340                 }
341
342                 [Test]
343                 [Category ("NotWorking")]
344                 [Category ("NotWorkingInterpreter")]
345                 public void Connect290278 ()
346                 {
347                         // test from https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=290278
348
349                         var p = Expression.Parameter (typeof (int [,]), "p");
350                         var lambda = Expression.Lambda<Func<int [,], int>> (
351                                 Expression.Call (
352                                         GetType ().GetMethod ("FooOut2"),
353                                         Expression.ArrayIndex (p, 0.ToConstant (), 0.ToConstant ())),
354                                 p).Compile ();
355
356                         int [,] data = { { 1 } };
357
358                         Assert.AreEqual (3, lambda (data));
359                         Assert.AreEqual (2, data [0, 0]);
360                 }
361
362                 public static void FooRef (ref string s)
363                 {
364                 }
365
366                 [Test]
367                 [Category ("NotWorkingInterpreter")]
368                 public void Connect297597 ()
369                 {
370                         // test from https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=297597
371
372                         var strings = new string [1];
373
374                         var lambda = Expression.Lambda<Action> (
375                                 Expression.Call (
376                                         GetType ().GetMethod ("FooRef"),
377                                         Expression.ArrayIndex (
378                                                 Expression.Constant (strings), 0.ToConstant ()))).Compile ();
379
380                         lambda ();
381                 }
382
383                 [Test]
384                 [Category ("NotDotNet")] // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=319190
385                 public void Connect319190 ()
386                 {
387                         var lambda = Expression.Lambda<Func<bool>> (
388                                 Expression.TypeIs (
389                                         Expression.New (typeof (TypedReference)),
390                                         typeof (object))).Compile ();
391
392                         Assert.IsTrue (lambda ());
393                 }
394
395                 public static int Truc ()
396                 {
397                         return 42;
398                 }
399
400                 [Test]
401                 [Category ("NotWorkingInterpreter")]
402                 public void Connect282702 ()
403                 {
404                         var lambda = Expression.Lambda<Func<Func<int>>> (
405                                 Expression.Convert (
406                                         Expression.Call (
407                                                 typeof (Delegate).GetMethod ("CreateDelegate", new [] { typeof (Type), typeof (object), typeof (MethodInfo) }),
408                                                 Expression.Constant (typeof (Func<int>), typeof (Type)),
409                                                 Expression.Constant (null, typeof (object)),
410                                                 Expression.Constant (GetType ().GetMethod ("Truc"))),
411                                         typeof (Func<int>))).Compile ();
412
413                         Assert.AreEqual (42, lambda ().Invoke ());
414                 }
415
416                 [Test]
417                 public void CallQueryableWhere ()
418                 {
419                         var queryable = new [] { 1, 2, 3 }.AsQueryable ();
420
421                         var parameter = Expression.Parameter (typeof (int), "i");
422                         var lambda = Expression.Lambda<Func<int, bool>> (
423                                 Expression.LessThan (parameter, Expression.Constant (2)),
424                                 parameter);
425
426                         var selector = Expression.Quote (lambda);
427
428                         var call = Expression.Call (
429                                 typeof (Queryable),
430                                 "Where",
431                                 new [] { typeof (int) },
432                                 queryable.Expression,
433                                 selector);
434
435                         Assert.IsNotNull (call);
436                         Assert.IsNotNull (call.Method);
437                 }
438
439                 [Test]
440                 public void CallAsQueryable () // #537768
441                 {
442                         var constant = Expression.Constant (
443                                 new List<string> (),
444                                 typeof (IEnumerable<string>));
445
446                         var call = Expression.Call (
447                                 typeof (Queryable),
448                                 "AsQueryable",
449                                 new [] { typeof (string) },
450                                 constant);
451
452                         Assert.IsNotNull (call);
453                         Assert.AreEqual (1, call.Arguments.Count);
454                         Assert.AreEqual (constant, call.Arguments [0]);
455
456                         var method = call.Method;
457
458                         Assert.AreEqual ("AsQueryable", method.Name);
459                         Assert.IsTrue (method.IsGenericMethod);
460                         Assert.AreEqual (typeof (string), method.GetGenericArguments () [0]);
461                 }
462
463
464                 [Test]
465                 public void CallQueryableSelect () // #536637
466                 {
467                         var parameter = Expression.Parameter (typeof (string), "s");
468                         var string_length = Expression.Property (parameter, typeof (string).GetProperty ("Length"));
469                         var lambda = Expression.Lambda (string_length, parameter);
470
471                         var strings = new [] { "1", "22", "333" };
472
473                         var call = Expression.Call (
474                                 typeof (Queryable),
475                                 "Select",
476                                 new [] { typeof (string), typeof (int) },
477                                 Expression.Constant (strings.AsQueryable ()),
478                                 lambda);
479
480                         Assert.IsNotNull (call);
481
482                         var method = call.Method;
483
484                         Assert.AreEqual ("Select", method.Name);
485                         Assert.IsTrue (method.IsGenericMethod);
486                         Assert.AreEqual (typeof (string), method.GetGenericArguments () [0]);
487                         Assert.AreEqual (typeof (int), method.GetGenericArguments () [1]);
488                 }
489
490                 [Test]
491                 [Category ("NotWorkingInterpreter")]
492                 public void CallNullableGetValueOrDefault () // #568989
493                 {
494                         var value = Expression.Parameter (typeof (int?), "value");
495                         var default_parameter = Expression.Parameter (typeof (int), "default");
496
497                         var getter = Expression.Lambda<Func<int?, int, int>> (
498                                 Expression.Call (
499                                         value,
500                                         "GetValueOrDefault",
501                                         Type.EmptyTypes,
502                                         default_parameter),
503                                 value,
504                                 default_parameter).Compile ();
505
506                         Assert.AreEqual (2, getter (null, 2));
507                         Assert.AreEqual (4, getter (4, 2));
508                 }
509
510                 [Test]
511                 public void CallToStringOnEnum () // #625367
512                 {
513                         var lambda = Expression.Lambda<Func<string>> (
514                                 Expression.Call (
515                                         Expression.Constant (TypeCode.Boolean, typeof (TypeCode)),
516                                         typeof (object).GetMethod ("ToString"))).Compile ();
517
518                         Assert.AreEqual ("Boolean", lambda ());
519                 }
520
521                 public static void AcceptsIEnumerable(IEnumerable<object> o)
522                 {
523                 }
524
525                 [Test]
526                 public void CallIQueryableMethodWithNewArrayBoundExpression () // #2304
527                 {
528                         Expression.Call (
529                                 GetType ().GetMethod ("AcceptsIEnumerable", BindingFlags.Public | BindingFlags.Static),
530                                 Expression.NewArrayBounds (typeof (object), Expression.Constant (0)));
531                 }
532         }
533 }