7a3834dd93c94c871a4c0cbd973d0ea053c1a6b6
[mono.git] / mcs / class / System.Runtime.Serialization / Test / System.Runtime.Serialization / CollectionSerialization.cs
1 //
2 // CollectionSerialization
3 //
4 // Authors:
5 //      Martin Baulig (martin.baulig@xamarin.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 using System;
30 using System.IO;
31 using System.Linq;
32 using System.Text;
33 using System.Reflection;
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Collections.ObjectModel;
37 using System.Runtime.Serialization;
38 using System.ServiceModel;
39 using NUnit.Framework;
40 using NUnit.Framework.Constraints;
41 #if !MOBILE
42 using NUnit.Framework.SyntaxHelpers;
43 #endif
44
45 namespace MonoTests.System.Runtime.Serialization
46 {
47         [TestFixture]
48         public class CollectionSerialization
49         {
50                 [DataContract]
51                 class Foo
52                 {
53                         [DataMember]
54                         public int Hello;
55                 }
56                 
57                 class MyList<T> : List<T>, IMyList<T>
58                 {
59                 }
60                 
61                 interface IMyList<T> : IList
62                 {
63                 }
64
65                 [Serializable]
66                 class CustomList<T> : IList<T>
67                 {
68                         List<T> list;
69
70                         public CustomList (IList<T> elements)
71                         {
72                                 list = new List<T> ();
73                                 if (elements != null)
74                                         list.AddRange (elements);
75                         }
76
77                         #region IList implementation
78                         public int IndexOf (T item)
79                         {
80                                 return list.IndexOf (item);
81                         }
82                         public void Insert (int index, T item)
83                         {
84                                 list.Insert (index, item);
85                         }
86                         public void RemoveAt (int index)
87                         {
88                                 list.RemoveAt (index);
89                         }
90                         public T this [int index] {
91                                 get {
92                                         return list [index];
93                                 }
94                                 set {
95                                         list [index] = value;
96                                 }
97                         }
98                         #endregion
99                         #region ICollection implementation
100                         public void Add (T item)
101                         {
102                                 list.Add (item);
103                         }
104                         public void Clear ()
105                         {
106                                 list.Clear ();
107                         }
108                         public bool Contains (T item)
109                         {
110                                 return list.Contains (item);
111                         }
112                         public void CopyTo (T[] array, int arrayIndex)
113                         {
114                                 list.CopyTo (array, arrayIndex);
115                         }
116                         public bool Remove (T item)
117                         {
118                                 return list.Remove (item);
119                         }
120                         #endregion
121                         #region IEnumerable implementation
122                         public IEnumerator<T> GetEnumerator ()
123                         {
124                                 return list.GetEnumerator ();
125                         }
126                         #endregion
127                         #region IEnumerable implementation
128                         IEnumerator IEnumerable.GetEnumerator ()
129                         {
130                                 return GetEnumerator ();
131                         }
132                         #endregion
133                         #region ICollection<T> implementation
134                         int ICollection<T>.Count {
135                                 get {
136                                         return list.Count;
137                                 }
138                         }
139                         bool ICollection<T>.IsReadOnly {
140                                 get {
141                                         return ((ICollection<T>)list).IsReadOnly;
142                                 }
143                         }
144                         #endregion
145
146                         public override int GetHashCode ()
147                         {
148                                 return list.GetHashCode ();
149                         }
150
151                         public override bool Equals (object obj)
152                         {
153                                 var custom = obj as CustomList<T>;
154                                 if (custom == null)
155                                         return false;
156
157                                 if (list.Count != custom.list.Count)
158                                         return false;
159
160                                 for (int i = 0; i < list.Count; i++)
161                                         if (!list [i].Equals (custom.list [i]))
162                                                 return false;
163
164                                 return true;
165                         }
166                 }
167
168                 class CustomCollection<T> : CustomList<T>
169                 {
170                         public CustomCollection ()
171                                 : base (null)
172                         {
173                         }
174
175                         public CustomCollection (IList<T> elements)
176                                 : base (elements)
177                         {
178                         }
179                 }
180
181                 static object Serialize<T> (object arg)
182                 {
183                         using (var ms = new MemoryStream ()) {
184                                 try {
185                                         var serializer = new DataContractSerializer (typeof(T));
186                                         serializer.WriteObject (ms, arg);
187                                 } catch (Exception ex) {
188                                         return ex;
189                                 }
190
191                                 return new UTF8Encoding ().GetString (ms.GetBuffer (), 0, (int)ms.Position);
192                         }
193                 }
194
195                 static T Deserialize<T> (string text)
196                 {
197                         var buffer = new UTF8Encoding ().GetBytes (text);
198                         using (var ms = new MemoryStream (buffer)) {
199                                 var serializer = new DataContractSerializer (typeof(T));
200                                 return (T)serializer.ReadObject (ms);
201                         }
202                 }
203
204                 [Test]
205                 public void CollectionInterfaceContract ()
206                 {
207                         var array = new object[3] { 1, 2, 3 };
208                         var arrayResult = (string)Serialize<object[]> (array);
209
210                         var list = new List<int> (new[] { 1, 2, 3 });
211                         
212                         Assert.That (Serialize<IList> (array), Is.EqualTo (arrayResult), "#1");
213                         Assert.That (Serialize<IList> (list), Is.EqualTo (arrayResult), "#2");
214                         Assert.That (Serialize<IEnumerable> (list), Is.EqualTo (arrayResult), "#3");
215                         Assert.That (Serialize<ICollection> (list), Is.EqualTo (arrayResult), "#4");
216
217                         var alist = new ArrayList ();
218                         alist.AddRange (array);
219
220                         Assert.That (Serialize<IList> (alist), Is.EqualTo (arrayResult), "#5");
221
222                         Assert.That (Deserialize<IList> (arrayResult), Is.EqualTo (list), "#6");
223                         Assert.That (Deserialize<IEnumerable> (arrayResult), Is.EqualTo (list), "#7");
224                         Assert.That (Deserialize<ICollection> (arrayResult), Is.EqualTo (list), "#8");
225                 }
226
227                 [Test]
228                 public void GenericCollectionInterfaceContract ()
229                 {
230                         var array = new[] { 1, 2, 3 };
231                         var arrayResult = (string)Serialize<int[]> (array);
232                         
233                         var list = new List<int> (array);
234                         var mylist = new MyList<int> ();
235                         mylist.AddRange (array);
236
237                         var custom = new CustomList<int> (array);
238
239                         Assert.That (Serialize<IList<int>> (list), Is.EqualTo (arrayResult), "#1");
240                         Assert.That (Serialize<IEnumerable<int>> (list), Is.EqualTo (arrayResult), "#2");
241                         Assert.That (Serialize<ICollection<int>> (list), Is.EqualTo (arrayResult), "#3");
242
243                         Assert.That (Serialize<IList<object>> (list),
244                                      InstanceOf (typeof (InvalidCastException)), "#4");
245
246                         Assert.That (Serialize<IList<int>> (mylist), Is.EqualTo (arrayResult), "#5");
247                         Assert.That (Serialize<IList<int>> (list.AsReadOnly ()), Is.EqualTo (arrayResult), "#6");
248                         Assert.That (Serialize<IList<int>> (custom), Is.EqualTo (arrayResult), "#7");
249
250                         Assert.That (Deserialize<IList<int>> (arrayResult), Is.EqualTo (list), "#8");
251                         Assert.That (Deserialize<List<int>> (arrayResult), Is.EqualTo (list), "#9");
252                 }
253
254                 [Test]
255                 public void CustomCollectionInterfaceContract ()
256                 {
257                         var array = new[] { 1, 2, 3 };
258                         var arrayResult = Serialize<int[]> (array);
259                         
260                         var mylist = new MyList<int> ();
261                         mylist.AddRange (array);
262
263                         Assert.That (Serialize<IList<int>> (mylist), Is.EqualTo (arrayResult), "#1");
264                         Assert.That (Serialize<List<int>> (mylist), Is.EqualTo (arrayResult), "#2");
265                         Assert.That (Serialize<IMyList<int>> (mylist),
266                                      InstanceOf (typeof (SerializationException)), "#3");
267                         Assert.That (Serialize<MyList<int>> (mylist), Is.EqualTo (arrayResult), "#4");
268                 }
269
270                 [Test]
271                 public void CustomCollectionTypeContract ()
272                 {
273                         var array = new[] { 1, 2, 3 };
274                         var arrayResult = (string)Serialize<int[]> (array);
275
276                         var custom = new CustomList<int> (array);
277
278                         var result = (string)Serialize<CustomList<int>> (custom);
279                         Assert.That (result.Contains ("CustomListOfint"), Is.True, "#1");
280                         Assert.That (Deserialize<CustomList<int>> (result), Is.EqualTo (custom), "#2");
281
282                         var ro = array.ToList ().AsReadOnly ();
283                         var result2 = (string)Serialize<ReadOnlyCollection<int>> (ro);
284                         Assert.That (result2.Contains ("ReadOnlyCollectionOfint"), Is.True, "#3");
285                         Assert.That (Deserialize<ReadOnlyCollection<int>> (result2), Is.EqualTo (ro), "#4");
286
287                         /*
288                          * CustomList<T> implements one of the collection interfaces, but does not have
289                          * a public parameterless constructor.  It is therefor treated like a normal
290                          * [Serializable] type and can not be deserialized from an array.
291                          * 
292                          * The same also applies to ReadOnlyCollection<T>.
293                          * 
294                          */
295
296                         try {
297                                 Deserialize<CustomList<int>> (arrayResult);
298                                 Assert.Fail ("#5");
299                         } catch (Exception ex) {
300                                 Assert.That (ex, InstanceOf (typeof (SerializationException)), "#6");
301                         }
302
303                         try {
304                                 Deserialize<ReadOnlyCollection<int>> (arrayResult);
305                                 Assert.Fail ("#7");
306                         } catch (Exception ex) {
307                                 Assert.That (ex, InstanceOf (typeof (SerializationException)), "#8");
308                         }
309
310                         /*
311                          * CustomCollection<T> does have the required public parameterless constructor,
312                          * so it is treated as custom collection type and serialized as array.
313                          * 
314                          */
315
316                         var collection = new CustomCollection<int> (array);
317                         var result3 = (string)Serialize<CustomCollection<int>> (collection);
318                         Assert.That (result3, Is.EqualTo (arrayResult), "#9");
319                         Assert.That (Deserialize<CustomCollection<int>> (result3), Is.EqualTo (collection), "#10");
320                 }
321
322                 [Test]
323                 public void ArrayContract ()
324                 {
325                         var array = new[] { 1, 2, 3 };
326                         var list = new List<int> (array);
327
328                         Assert.That (Serialize<int[]> (list),
329                                      InstanceOf (typeof (InvalidCastException)), "#1");
330                         Assert.That (Serialize<object[]> (array),
331                                      InstanceOf (typeof (InvalidCastException)), "#2");
332                 }
333
334                 [Test]
335                 public void ListOfArrays ()
336                 {
337                         var water = new[] { "Fish", "Mermaid" };
338                         var land = new[] { "Horse", "Human", "Lion" };
339                         var air = new[] { "Bird", "Drake" };
340                         var species = new[] { water, land, air };
341                         var serialized = (string)Serialize<string[][]> (species);
342
343                         var list = new List<string[]> (species);
344                         Assert.That (Serialize<IList<string[]>> (species), Is.EqualTo (serialized), "#1");
345                         Assert.That (Serialize<IList<string[]>> (list), Is.EqualTo (serialized), "#2");
346                 }
347
348                 [CollectionDataContract (Name = "MyCollection")]
349                 class MissingAddMethod<T> : IEnumerable<T>
350                 {
351                         #region IEnumerable implementation
352                         public IEnumerator<T> GetEnumerator ()
353                         {
354                                 throw new InvalidOperationException ();
355                         }
356 #endregion
357                         #region IEnumerable implementation
358                         IEnumerator IEnumerable.GetEnumerator ()
359                         {
360                                 throw new InvalidOperationException ();
361                         }
362 #endregion
363                 }
364                 
365                 [CollectionDataContract (Name = "MyCollection")]
366                 class MissingEnumerable<T>
367                 {
368                         public void Add (T item)
369                         {
370                                 throw new NotImplementedException ();
371                         }
372                 }
373                 
374                 [CollectionDataContract (Name = "MyCollection")]
375                 class MyDataContractCollection<T> : IEnumerable<T>
376                 {
377                         List<T> list;
378                         
379                         public MyDataContractCollection ()
380                         {
381                                 list = new List<T> ();
382                         }
383                         
384                         public MyDataContractCollection (IList<T> elements)
385                         {
386                                 list = new List<T> ();
387                                 list.AddRange (elements);
388                         }
389                         
390                         #region IEnumerable implementation
391                         public IEnumerator<T> GetEnumerator ()
392                         {
393                                 return list.GetEnumerator ();
394                         }
395 #endregion
396                         #region IEnumerable implementation
397                         IEnumerator IEnumerable.GetEnumerator ()
398                         {
399                                 return GetEnumerator ();
400                         }
401 #endregion
402                         
403                         public void Add (T item)
404                         {
405                                 list.Add (item);
406                         }
407                 }
408
409                 class MyDerivedDataContract<T> : MyDataContractCollection<T>
410                 {
411                 }
412                 
413                 [Test]
414                 public void TestCollectionDataContract ()
415                 {
416                         Assert.That (Serialize<MissingAddMethod<int>> (new MissingAddMethod<int> ()),
417                                      InstanceOf (typeof (InvalidDataContractException)), "#1");
418                         Assert.That (Serialize<MissingEnumerable<int>> (new MissingEnumerable<int> ()),
419                                      InstanceOf (typeof (InvalidDataContractException)), "#2");
420
421                         var array = new[] { 1, 2, 3 };
422                         var arrayResult = (string)Serialize<int[]> (array);
423                         var collection = new MyDataContractCollection<int> (array);
424                         
425                         var result = Serialize<MyDataContractCollection<int>> (collection);
426                         Assert.That (result, InstanceOf (typeof(string)), "#3");
427
428                         Assert.That (Serialize<MyDataContractCollection<int>> (array),
429                                      InstanceOf (typeof (SerializationException)), "#4");
430
431                         var derived = new MyDerivedDataContract<int> ();
432                         Assert.That (Serialize<MyDataContractCollection<int>> (derived),
433                                      InstanceOf (typeof (SerializationException)), "#5");
434
435                         try {
436                                 Deserialize<MyDataContractCollection<int>> (arrayResult);
437                                 Assert.Fail ("#6");
438                         } catch (Exception ex) {
439                                 Assert.That (ex, InstanceOf (typeof(SerializationException)), "#7");
440                         }
441                         
442                         var deserialized = Deserialize<MyDataContractCollection<int>> ((string)result);
443                         Assert.That (deserialized, InstanceOf (typeof (MyDataContractCollection<int>)), "#8");
444                 }
445
446                 [Test]
447                 public void Test ()
448                 {
449                         var derived = new MyDerivedDataContract<int> ();
450                         Assert.That (Serialize<MyDataContractCollection<int>> (derived),
451                                      InstanceOf (typeof (SerializationException)), "#5");
452                 }
453
454                 public static InstanceOfTypeConstraint InstanceOf (Type expectedType)
455                 {
456                         return new InstanceOfTypeConstraint (expectedType);
457                 }
458         }
459 }
460