2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.Core / Test / System.Linq.Expressions / ExpressionTest_Convert.cs
1 //
2 // ExpressionTest_Convert.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //
7 // (C) 2008 Novell, Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Reflection;
31 using System.Linq;
32 using System.Linq.Expressions;
33 using NUnit.Framework;
34
35 namespace MonoTests.System.Linq.Expressions {
36
37         [TestFixture]
38         public class ExpressionTest_Convert {
39
40                 [Test]
41                 [ExpectedException (typeof (ArgumentNullException))]
42                 public void NullExpression ()
43                 {
44                         Expression.Convert (null, typeof (int));
45                 }
46
47                 [Test]
48                 [ExpectedException (typeof (ArgumentNullException))]
49                 public void NullType ()
50                 {
51                         Expression.Convert (1.ToConstant (), null);
52                 }
53
54                 [Test]
55                 [ExpectedException (typeof (InvalidOperationException))]
56                 public void ConvertIntToString ()
57                 {
58                         Expression.Convert (1.ToConstant (), typeof (string));
59                 }
60
61                 interface IFoo { }
62                 class Foo : IFoo { }
63                 class Bar : Foo { }
64                 class Baz { }
65
66                 interface ITzap { }
67
68                 [Test]
69                 public void ConvertBackwardAssignability ()
70                 {
71                         var c = Expression.Convert (
72                                 Expression.Constant (null, typeof (Bar)), typeof (Foo));
73
74                         Assert.AreEqual ("Convert(null)", c.ToString ());
75                 }
76
77                 [Test]
78                 public void ConvertInterfaces ()
79                 {
80                         var p = Expression.Parameter (typeof (IFoo), null);
81
82                         var conv = Expression.Convert (p, typeof (ITzap));
83                         Assert.AreEqual (typeof (ITzap), conv.Type);
84                         Assert.AreEqual ("Convert(<param>)", conv.ToString ());
85
86                         p = Expression.Parameter (typeof (ITzap), null);
87                         conv = Expression.Convert (p, typeof (IFoo));
88
89                         Assert.AreEqual (typeof (IFoo), conv.Type);
90                         Assert.AreEqual ("Convert(<param>)", conv.ToString ());
91                 }
92
93                 [Test]
94                 public void ConvertCheckedInt32ToInt64 ()
95                 {
96                         var c = Expression.ConvertChecked (
97                                 Expression.Constant (2, typeof (int)), typeof (long));
98
99                         Assert.AreEqual (ExpressionType.ConvertChecked, c.NodeType);
100                         Assert.AreEqual ("ConvertChecked(2)", c.ToString ());
101                 }
102
103                 [Test]
104                 public void ConvertCheckedFallbackToConvertForNonPrimitives ()
105                 {
106                         var p = Expression.ConvertChecked (
107                                 Expression.Constant (null, typeof (object)), typeof (IFoo));
108
109                         Assert.AreEqual (ExpressionType.Convert, p.NodeType);
110                 }
111
112                 [Test]
113                 [ExpectedException (typeof (InvalidOperationException))]
114                 public void ConvertBazToFoo ()
115                 {
116                         Expression.Convert (Expression.Parameter (typeof (Baz), ""), typeof (Foo));
117                 }
118
119                 struct EineStrukt { }
120
121                 [Test]
122                 [ExpectedException (typeof (InvalidOperationException))]
123                 public void ConvertStructToFoo ()
124                 {
125                         Expression.Convert (Expression.Parameter (typeof (EineStrukt), ""), typeof (Foo));
126                 }
127
128                 [Test]
129                 [ExpectedException (typeof (InvalidOperationException))]
130                 public void ConvertInt32ToBool ()
131                 {
132                         Expression.Convert (Expression.Parameter (typeof (int), ""), typeof (bool));
133                 }
134
135                 [Test]
136                 public void ConvertIFooToFoo ()
137                 {
138                         var c = Expression.Convert (Expression.Parameter (typeof (IFoo), ""), typeof (Foo));
139                         Assert.AreEqual (typeof (Foo), c.Type);
140                         Assert.IsFalse (c.IsLifted);
141                         Assert.IsFalse (c.IsLiftedToNull);
142                         Assert.IsNull (c.Method);
143                 }
144
145                 [Test]
146                 public void BoxInt32 ()
147                 {
148                         var c = Expression.Convert (Expression.Parameter (typeof (int), ""), typeof (object));
149                         Assert.AreEqual (typeof (object), c.Type);
150                         Assert.IsFalse (c.IsLifted);
151                         Assert.IsFalse (c.IsLiftedToNull);
152                         Assert.IsNull (c.Method);
153                 }
154
155                 [Test]
156                 public void UnBoxInt32 ()
157                 {
158                         var c = Expression.Convert (Expression.Parameter (typeof (object), ""), typeof (int));
159                         Assert.AreEqual (typeof (int), c.Type);
160                         Assert.IsFalse (c.IsLifted);
161                         Assert.IsFalse (c.IsLiftedToNull);
162                         Assert.IsNull (c.Method);
163                 }
164
165                 [Test]
166                 public void ConvertInt32ToInt64 ()
167                 {
168                         var c = Expression.Convert (Expression.Parameter (typeof (int), ""), typeof (long));
169                         Assert.AreEqual (typeof (long), c.Type);
170                         Assert.IsFalse (c.IsLifted);
171                         Assert.IsFalse (c.IsLiftedToNull);
172                         Assert.IsNull (c.Method);
173                 }
174
175                 [Test]
176                 public void ConvertInt64ToInt32 ()
177                 {
178                         var c = Expression.Convert (Expression.Parameter (typeof (long), ""), typeof (int));
179                         Assert.AreEqual (typeof (int), c.Type);
180                         Assert.IsFalse (c.IsLifted);
181                         Assert.IsFalse (c.IsLiftedToNull);
182                         Assert.IsNull (c.Method);
183                 }
184
185                 enum EineEnum { }
186
187                 [Test]
188                 public void ConvertEnumToInt32 ()
189                 {
190                         var c = Expression.Convert (Expression.Parameter (typeof (EineEnum), ""), typeof (int));
191                         Assert.AreEqual (typeof (int), c.Type);
192                         Assert.IsFalse (c.IsLifted);
193                         Assert.IsFalse (c.IsLiftedToNull);
194                         Assert.IsNull (c.Method);
195                 }
196
197                 [Test]
198                 public void ConvertNullableInt32ToInt32 ()
199                 {
200                         var c = Expression.Convert (Expression.Parameter (typeof (int?), ""), typeof (int));
201                         Assert.AreEqual (typeof (int), c.Type);
202                         Assert.IsTrue (c.IsLifted);
203                         Assert.IsFalse (c.IsLiftedToNull);
204                         Assert.IsNull (c.Method);
205                 }
206
207                 [Test]
208                 public void ConvertInt32ToNullableInt32 ()
209                 {
210                         var c = Expression.Convert (Expression.Parameter (typeof (int), ""), typeof (int?));
211                         Assert.AreEqual (typeof (int?), c.Type);
212                         Assert.IsTrue (c.IsLifted);
213                         Assert.IsTrue (c.IsLiftedToNull);
214                         Assert.IsNull (c.Method);
215                 }
216
217
218                 class Klang {
219                         int i;
220
221                         public Klang (int i)
222                         {
223                                 this.i = i;
224                         }
225
226                         public static explicit operator int (Klang k)
227                         {
228                                 return k.i;
229                         }
230                 }
231
232                 [Test]
233                 public void ConvertClassWithExplicitOp ()
234                 {
235                         var c = Expression.Convert (Expression.Parameter (typeof (Klang), ""), typeof (int));
236                         Assert.AreEqual (typeof (int), c.Type);
237                         Assert.IsFalse (c.IsLifted);
238                         Assert.IsFalse (c.IsLiftedToNull);
239                         Assert.IsNotNull (c.Method);
240                 }
241
242                 [Test]
243                 public void CompileConvertClassWithExplicitOp ()
244                 {
245                         var p = Expression.Parameter (typeof (Klang), "klang");
246                         var c = Expression.Lambda<Func<Klang, int>> (
247                                 Expression.Convert (p, typeof (int)), p).Compile ();
248
249                         Assert.AreEqual (42, c (new Klang (42)));
250                 }
251
252                 [Test]
253                 public void ConvertClassWithExplicitOpToNullableInt ()
254                 {
255                         var c = Expression.Convert (Expression.Parameter (typeof (Klang), ""), typeof (int?));
256                         Assert.AreEqual (typeof (int?), c.Type);
257                         Assert.IsTrue (c.IsLifted);
258                         Assert.IsTrue (c.IsLiftedToNull);
259                         Assert.IsNotNull (c.Method);
260                 }
261
262                 struct Kling {
263                         int i;
264
265                         public Kling (int i)
266                         {
267                                 this.i = i;
268                         }
269
270                         public static implicit operator int (Kling k)
271                         {
272                                 return k.i;
273                         }
274                 }
275
276                 [Test]
277                 public void ConvertStructWithImplicitOp ()
278                 {
279                         var c = Expression.Convert (Expression.Parameter (typeof (Kling), ""), typeof (int));
280                         Assert.AreEqual (typeof (int), c.Type);
281                         Assert.IsFalse (c.IsLifted);
282                         Assert.IsFalse (c.IsLiftedToNull);
283                         Assert.IsNotNull (c.Method);
284                 }
285
286                 [Test]
287                 public void CompileConvertStructWithImplicitOp ()
288                 {
289                         var p = Expression.Parameter (typeof (Kling), "kling");
290                         var c = Expression.Lambda<Func<Kling, int>> (
291                                 Expression.Convert (p, typeof (int)), p).Compile ();
292
293                         Assert.AreEqual (42, c (new Kling (42)));
294                 }
295
296                 [Test]
297                 public void ConvertStructWithImplicitOpToNullableInt ()
298                 {
299                         var c = Expression.Convert (Expression.Parameter (typeof (Kling), ""), typeof (int?));
300                         Assert.AreEqual (typeof (int?), c.Type);
301                         Assert.IsTrue (c.IsLifted);
302                         Assert.IsTrue (c.IsLiftedToNull);
303                         Assert.IsNotNull (c.Method);
304                 }
305
306                 [Test]
307                 public void ConvertNullableStructWithImplicitOpToNullableInt ()
308                 {
309                         var c = Expression.Convert (Expression.Parameter (typeof (Kling?), ""), typeof (int?));
310                         Assert.AreEqual (typeof (int?), c.Type);
311                         Assert.IsTrue (c.IsLifted);
312                         Assert.IsTrue (c.IsLiftedToNull);
313                         Assert.IsNotNull (c.Method);
314                 }
315
316                 [Test]
317                 public void CompiledBoxing ()
318                 {
319                         var b = Expression.Lambda<Func<object>> (
320                                 Expression.Convert (42.ToConstant (), typeof (object))).Compile ();
321
322                         Assert.AreEqual ((object) 42, b ());
323                 }
324
325                 [Test]
326                 public void CompiledUnBoxing ()
327                 {
328                         var p = Expression.Parameter (typeof (object), "o");
329
330                         var u = Expression.Lambda<Func<object, int>> (
331                                 Expression.Convert (p, typeof (int)), p).Compile ();
332
333                         Assert.AreEqual (42, u ((object) 42));
334                 }
335
336                 [Test]
337                 public void CompiledCast ()
338                 {
339                         var p = Expression.Parameter (typeof (IFoo), "foo");
340
341                         var c = Expression.Lambda<Func<IFoo, Bar>> (
342                                 Expression.Convert (p, typeof (Bar)), p).Compile ();
343
344                         IFoo foo = new Bar ();
345
346                         Bar b = c (foo);
347
348                         Assert.AreEqual (b, foo);
349                 }
350
351                 [Test]
352                 public void CompileNotNullableToNullable ()
353                 {
354                         var p = Expression.Parameter (typeof (int), "i");
355                         var c = Expression.Lambda<Func<int, int?>> (
356                                 Expression.Convert (p, typeof (int?)), p).Compile ();
357
358                         Assert.AreEqual ((int?) 0, c (0));
359                         Assert.AreEqual ((int?) 42, c (42));
360                 }
361
362                 [Test]
363                 public void CompileNullableToNotNullable ()
364                 {
365                         var p = Expression.Parameter (typeof (int?), "i");
366                         var c = Expression.Lambda<Func<int?, int>> (
367                                 Expression.Convert (p, typeof (int)), p).Compile ();
368
369                         Assert.AreEqual (0, c ((int?) 0));
370                         Assert.AreEqual (42, c ((int?) 42));
371
372                         Action a = () => c (null);
373
374                         a.AssertThrows (typeof (InvalidOperationException));
375                 }
376
377                 [Test]
378                 public void CompiledConvertToSameType ()
379                 {
380                         var k = new Klang (42);
381
382                         var p = Expression.Parameter (typeof (Klang), "klang");
383                         var c = Expression.Lambda<Func<Klang, Klang>> (
384                                 Expression.Convert (
385                                         p, typeof (Klang)),
386                                 p).Compile ();
387
388                         Assert.AreEqual (k, c (k));
389                 }
390
391                 [Test]
392                 public void CompiledConvertNullableToNullable ()
393                 {
394                         var p = Expression.Parameter (typeof (int?), "i");
395                         var c = Expression.Lambda<Func<int?, short?>> (
396                                 Expression.Convert (p, typeof (short?)), p).Compile ();
397
398                         Assert.AreEqual ((short?) null, c (null));
399                         Assert.AreEqual ((short?) 12, c (12));
400                 }
401
402                 [Test]
403                 public void CompiledNullableBoxing ()
404                 {
405                         var p = Expression.Parameter (typeof (int?), "i");
406                         var c = Expression.Lambda<Func<int?, object>> (
407                                 Expression.Convert (p, typeof (object)), p).Compile ();
408
409                         Assert.AreEqual (null, c (null));
410                         Assert.AreEqual ((object) (int?) 42, c (42));
411                 }
412
413                 [Test]
414                 public void CompiledNullableUnboxing ()
415                 {
416                         var p = Expression.Parameter (typeof (object), "o");
417                         var c = Expression.Lambda<Func<object, int?>> (
418                                 Expression.Convert (p, typeof (int?)), p).Compile ();
419
420                         Assert.AreEqual ((int?) null, c (null));
421                         Assert.AreEqual ((int?) 42, c ((int?) 42));
422                 }
423
424                 [Test]
425                 public void ChainedNullableConvert ()
426                 {
427                         var p = Expression.Parameter (typeof (sbyte?), "a");
428
429                         var test = Expression.Lambda<Func<sbyte?, long?>> (
430                                 Expression.Convert (
431                                         Expression.Convert (
432                                                 p,
433                                                 typeof (int?)),
434                                         typeof (long?)), p).Compile ();
435
436                         Assert.AreEqual ((long?) 3, test ((sbyte?) 3));
437                         Assert.AreEqual (null, test (null));
438                 }
439
440                 struct ImplicitToShort {
441                         short value;
442
443                         public ImplicitToShort (short v)
444                         {
445                                 value = v;
446                         }
447
448                         public static implicit operator short (ImplicitToShort i)
449                         {
450                                 return i.value;
451                         }
452                 }
453
454                 [Test]
455                 public void ConvertImplicitToShortToNullableInt ()
456                 {
457                         var a = Expression.Parameter (typeof (ImplicitToShort?), "a");
458
459                         var method = typeof (ImplicitToShort).GetMethod ("op_Implicit");
460
461                         var node = Expression.Convert (a, typeof (short), method);
462                         Assert.IsTrue (node.IsLifted);
463                         Assert.IsFalse (node.IsLiftedToNull);
464                         Assert.AreEqual (typeof (short), node.Type);
465                         Assert.AreEqual (method, node.Method);
466
467                         var conv = Expression.Lambda<Func<ImplicitToShort?, int?>> (
468                                 Expression.Convert (
469                                         node,
470                                         typeof (int?)), a).Compile ();
471
472                         Assert.AreEqual ((int?) 42, conv (new ImplicitToShort (42)));
473
474                         Action convnull = () => Assert.AreEqual (null, conv (null));
475
476                         convnull.AssertThrows (typeof (InvalidOperationException));
477                 }
478
479                 [Test]
480                 public void NullableImplicitToShort ()
481                 {
482                         var i = Expression.Parameter (typeof (ImplicitToShort?), "i");
483
484                         var method = typeof (ImplicitToShort).GetMethod ("op_Implicit");
485
486                         var node = Expression.Convert (i, typeof (short?), method);
487
488                         Assert.IsTrue (node.IsLifted);
489                         Assert.IsTrue (node.IsLiftedToNull);
490                         Assert.AreEqual (typeof (short?), node.Type);
491                         Assert.AreEqual (method, node.Method);
492
493                         var convert = Expression.Lambda<Func<ImplicitToShort?, short?>> (node, i).Compile ();
494
495                         Assert.AreEqual ((short?) 42, convert (new ImplicitToShort (42)));
496                 }
497
498                 [Test]
499                 public void ConvertLongToDecimal ()
500                 {
501                         var p = Expression.Parameter (typeof (long), "l");
502
503                         var node = Expression.Convert (p, typeof (decimal));
504                         Assert.IsFalse (node.IsLifted);
505                         Assert.IsFalse (node.IsLiftedToNull);
506                         Assert.AreEqual (typeof (decimal), node.Type);
507                         Assert.IsNotNull (node.Method);
508
509                         var convert = Expression.Lambda<Func<long, decimal>> (node, p).Compile ();
510
511                         Assert.AreEqual (42, convert (42));
512                 }
513
514                 [Test]
515                 public void ConvertNullableULongToNullableDecimal ()
516                 {
517                         var p = Expression.Parameter (typeof (ulong?), "l");
518
519                         var node = Expression.Convert (p, typeof (decimal?));
520                         Assert.IsTrue (node.IsLifted);
521                         Assert.IsTrue (node.IsLiftedToNull);
522                         Assert.AreEqual (typeof (decimal?), node.Type);
523                         Assert.IsNotNull (node.Method);
524
525                         var convert = Expression.Lambda<Func<ulong?, decimal?>> (node, p).Compile ();
526
527                         Assert.AreEqual (42, convert (42));
528                         Assert.AreEqual (null, convert (null));
529                 }
530
531                 [Test]
532                 public void ConvertCheckedNullableIntToInt ()
533                 {
534                         var p = Expression.Parameter (typeof (int?), "i");
535
536                         var node = Expression.ConvertChecked (p, typeof (int));
537                         Assert.AreEqual (ExpressionType.ConvertChecked, node.NodeType);
538                         Assert.IsTrue (node.IsLifted);
539                         Assert.IsFalse (node.IsLiftedToNull);
540                         Assert.AreEqual (typeof (int), node.Type);
541                         Assert.IsNull (node.Method);
542                 }
543
544                 struct ImplicitToInt {
545                         int Value;
546
547                         public ImplicitToInt (int v)
548                         {
549                                 Value = v;
550                         }
551
552                         public static implicit operator int (ImplicitToInt i)
553                         {
554                                 return i.Value;
555                         }
556                 }
557
558                 [Test]
559                 public void ConvertNullableImplictToIntToNullableLong ()
560                 {
561                         var i = Expression.Parameter (typeof (ImplicitToInt?), "i");
562
563                         var method = typeof (ImplicitToInt).GetMethod ("op_Implicit");
564
565                         var node = Expression.Convert (i, typeof (int), method);
566                         node = Expression.Convert (node, typeof (long?));
567                         var conv = Expression.Lambda<Func<ImplicitToInt?, long?>> (node, i).Compile ();
568
569                         Assert.AreEqual ((long?) 42, conv (new ImplicitToInt (42)));
570                         Action convnull = () => Assert.AreEqual (null, conv (null));
571                         convnull.AssertThrows (typeof (InvalidOperationException));
572                 }
573
574                 [Test]
575                 [ExpectedException (typeof (InvalidOperationException))]
576                 [Category ("NotWorking")]
577                 public void ConvertNullableIntToStringWithConvertMethod ()
578                 {
579                         Expression.Convert (
580                                 Expression.Constant ((int?) 0),
581                                 typeof (string),
582                                 typeof (Convert).GetMethod ("ToString", new [] { typeof (object) }));
583                 }
584         }
585 }