[SRE] Improved token fixups processing.
[mono.git] / mcs / class / System / Test / System.Collections.ObjectModel / ObservableCollectionTest.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
21 // Copyright 2011 Xamarin Inc.
22 //
23 // Authors:
24 //      Chris Toshok (toshok@ximian.com)
25 //      Brian O'Keefe (zer0keefie@gmail.com)
26 //      Marek Safar (marek.safar@gmail.com)
27 //
28
29 #if NET_4_0
30
31 using System.Collections.ObjectModel;
32 using System.Collections.Specialized;
33 using NUnit.Framework;
34 using System.ComponentModel;
35 using System.Collections.Generic;
36 using System;
37 using System.Collections;
38 using MonoTests.System.Collections.Specialized;
39
40 namespace MonoTests.System.Collections.ObjectModel {
41
42         [TestFixture]
43         public class ObservableCollectionTest
44         {
45                 [Test]
46                 public void Constructor ()
47                 {
48                         var list = new List<int> { 3 };
49                         var col = new ObservableCollection<int> (list);
50                         col.Add (5);
51                         Assert.AreEqual (1, list.Count, "#1");
52
53                         col = new ObservableCollection<int> ((IEnumerable<int>) list);
54                         col.Add (5);
55                         Assert.AreEqual (1, list.Count, "#2");
56                 }
57
58                 [Test]
59                 public void Constructor_Invalid ()
60                 {
61                         try {
62                                 new ObservableCollection<int> ((List<int>) null);
63                                 Assert.Fail ("#1");
64                         } catch (ArgumentNullException) {
65                         }
66
67                         try {
68                                 new ObservableCollection<int> ((IEnumerable<int>) null);
69                                 Assert.Fail ("#2");
70                         } catch (ArgumentNullException) {
71                         }
72                 }
73
74                 [Test]
75                 public void Insert()
76                 {
77                         bool reached = false;
78                         ObservableCollection<int> col = new ObservableCollection<int> ();
79                         col.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
80                                 reached = true;
81                                 Assert.AreEqual (NotifyCollectionChangedAction.Add, e.Action, "INS_1");
82                                 Assert.AreEqual (0, e.NewStartingIndex, "INS_2");
83                                 Assert.AreEqual (-1, e.OldStartingIndex, "INS_3");
84                                 Assert.AreEqual (1, e.NewItems.Count, "INS_4");
85                                 Assert.AreEqual (5, (int)e.NewItems [0], "INS_5");
86                                 Assert.AreEqual (null, e.OldItems, "INS_6");
87                         };
88                         col.Insert (0, 5);
89                         Assert.IsTrue (reached, "INS_5");
90                 }
91
92                 [Test]
93                 public void RemoveAt()
94                 {
95                         bool reached = false;
96                         ObservableCollection<int> col = new ObservableCollection<int> ();
97                         col.Insert (0, 5);
98                         col.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
99                                 reached = true;
100                                 Assert.AreEqual (NotifyCollectionChangedAction.Remove, e.Action, "REMAT_1");
101                                 Assert.AreEqual (-1, e.NewStartingIndex, "REMAT_2");
102                                 Assert.AreEqual (0, e.OldStartingIndex, "REMAT_3");
103                                 Assert.AreEqual (null, e.NewItems, "REMAT_4");
104                                 Assert.AreEqual (1, e.OldItems.Count, "REMAT_5");
105                                 Assert.AreEqual (5, (int)e.OldItems [0], "REMAT_6");
106                         };
107                         col.RemoveAt (0);
108                         Assert.IsTrue (reached, "REMAT_7");
109                 }
110
111                 [Test]
112                 public void Move()
113                 {
114                         bool reached = false;
115                         ObservableCollection<int> col = new ObservableCollection<int> ();
116                         col.Insert (0, 0);
117                         col.Insert (1, 1);
118                         col.Insert (2, 2);
119                         col.Insert (3, 3);
120                         col.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
121                                 reached = true;
122                                 Assert.AreEqual (NotifyCollectionChangedAction.Move, e.Action, "MOVE_1");
123                                 Assert.AreEqual (3, e.NewStartingIndex, "MOVE_2");
124                                 Assert.AreEqual (1, e.OldStartingIndex, "MOVE_3");
125                                 Assert.AreEqual (1, e.NewItems.Count, "MOVE_4");
126                                 Assert.AreEqual (1, e.NewItems [0], "MOVE_5");
127                                 Assert.AreEqual (1, e.OldItems.Count, "MOVE_6");
128                                 Assert.AreEqual (1, e.OldItems [0], "MOVE_7");
129                         };
130                         col.Move (1, 3);
131                         Assert.IsTrue (reached, "MOVE_8");
132                 }
133
134                 [Test]
135                 public void Add()
136                 {
137                         ObservableCollection<char> collection = new ObservableCollection<char> ();
138                         bool propertyChanged = false;
139                         List<string> changedProps = new List<string> ();
140                         NotifyCollectionChangedEventArgs args = null;
141
142                         ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
143                                 propertyChanged = true;
144                                 changedProps.Add (e.PropertyName);
145                         };
146
147                         collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
148                                 args = e;
149                         };
150
151                         collection.Add ('A');
152
153                         Assert.IsTrue (propertyChanged, "ADD_1");
154                         Assert.IsTrue (changedProps.Contains ("Count"), "ADD_2");
155                         Assert.IsTrue (changedProps.Contains ("Item[]"), "ADD_3");
156
157                         CollectionChangedEventValidators.ValidateAddOperation (args, new char [] { 'A' }, 0, "ADD_4");
158                 }
159
160                 [Test]
161                 public void Remove()
162                 {
163                         ObservableCollection<char> collection = new ObservableCollection<char> ();
164                         bool propertyChanged = false;
165                         List<string> changedProps = new List<string> ();
166                         NotifyCollectionChangedEventArgs args = null;
167
168                         collection.Add ('A');
169                         collection.Add ('B');
170                         collection.Add ('C');
171
172                         ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
173                                 propertyChanged = true;
174                                 changedProps.Add (e.PropertyName);
175                         };
176
177                         collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
178                                 args = e;
179                         };
180
181                         collection.Remove ('B');
182
183                         Assert.IsTrue (propertyChanged, "REM_1");
184                         Assert.IsTrue (changedProps.Contains ("Count"), "REM_2");
185                         Assert.IsTrue (changedProps.Contains ("Item[]"), "REM_3");
186
187                         CollectionChangedEventValidators.ValidateRemoveOperation (args, new char [] { 'B' }, 1, "REM_4");
188                 }
189
190                 [Test]
191                 public void Set()
192                 {
193                         ObservableCollection<char> collection = new ObservableCollection<char> ();
194                         bool propertyChanged = false;
195                         List<string> changedProps = new List<string> ();
196                         NotifyCollectionChangedEventArgs args = null;
197
198                         collection.Add ('A');
199                         collection.Add ('B');
200                         collection.Add ('C');
201
202                         ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
203                                 propertyChanged = true;
204                                 changedProps.Add (e.PropertyName);
205                         };
206
207                         collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
208                                 args = e;
209                         };
210
211                         collection [2] = 'I';
212
213                         Assert.IsTrue (propertyChanged, "SET_1");
214                         Assert.IsTrue (changedProps.Contains ("Item[]"), "SET_2");
215
216                         CollectionChangedEventValidators.ValidateReplaceOperation (args, new char [] { 'C' }, new char [] { 'I' }, 2, "SET_3");
217                 }
218
219                 [Test]
220                 public void Reentrant()
221                 {
222                         ObservableCollection<char> collection = new ObservableCollection<char> ();
223                         bool propertyChanged = false;
224                         List<string> changedProps = new List<string> ();
225                         NotifyCollectionChangedEventArgs args = null;
226
227                         collection.Add ('A');
228                         collection.Add ('B');
229                         collection.Add ('C');
230
231                         PropertyChangedEventHandler pceh = delegate (object sender, PropertyChangedEventArgs e) {
232                                 propertyChanged = true;
233                                 changedProps.Add (e.PropertyName);
234                         };
235
236                         // Adding a PropertyChanged event handler
237                         ((INotifyPropertyChanged)collection).PropertyChanged += pceh;
238
239                         collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
240                                 args = e;
241                         };
242
243                         collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
244                                 // This one will attempt to break reentrancy
245                                 try {
246                                         collection.Add ('X');
247                                         Assert.Fail ("Reentrancy should not be allowed.");
248                                 } catch (InvalidOperationException) {
249                                 }
250                         };
251
252                         collection [2] = 'I';
253
254                         Assert.IsTrue (propertyChanged, "REENT_1");
255                         Assert.IsTrue (changedProps.Contains ("Item[]"), "REENT_2");
256
257                         CollectionChangedEventValidators.ValidateReplaceOperation (args, new char [] { 'C' }, new char [] { 'I' }, 2, "REENT_3");
258
259                         // Removing the PropertyChanged event handler should work as well:
260                         ((INotifyPropertyChanged)collection).PropertyChanged -= pceh;
261                 }
262
263                 //Private test class for protected members of ObservableCollection
264                 private class ObservableCollectionTestHelper : ObservableCollection<char> {
265                         internal void DoubleEnterReentrant()
266                         {
267                                 IDisposable object1 = BlockReentrancy ();
268                                 IDisposable object2 = BlockReentrancy ();
269
270                                 Assert.AreSame (object1, object2);
271
272                                 //With double block, try the reentrant:
273                                 NotifyCollectionChangedEventArgs args = null;
274
275                                 CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
276                                         args = e;
277                                 };
278
279                                 // We need a second callback for reentrancy to matter
280                                 CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
281                                         // Doesn't need to do anything; just needs more than one callback registered.
282                                 };
283
284                                 // Try adding - this should cause reentrancy, and fail
285                                 try {
286                                         Add ('I');
287                                         Assert.Fail ("Reentrancy should not be allowed. -- #2");
288                                 } catch (InvalidOperationException) {
289                                 }
290                                 
291                                 // Release the first reentrant
292                                 object1.Dispose ();
293
294                                 // Try adding again - this should cause reentrancy, and fail again
295                                 try {
296                                         Add ('J');
297                                         Assert.Fail ("Reentrancy should not be allowed. -- #3");
298                                 } catch (InvalidOperationException) {
299                                 }
300
301                                 // Release the reentrant a second time
302                                 object1.Dispose ();
303
304                                 // This last add should work fine.
305                                 Add ('K');
306                                 CollectionChangedEventValidators.ValidateAddOperation (args, new char [] { 'K' }, 0, "REENTHELP_1");
307                         }
308                 }
309
310                 [Test]
311                 public void ReentrantReuseObject()
312                 {
313                         ObservableCollectionTestHelper helper = new ObservableCollectionTestHelper ();
314
315                         helper.DoubleEnterReentrant ();
316                 }
317
318                 [Test]
319                 public void Clear()
320                 {
321                         List<char> initial = new List<char> ();
322
323                         initial.Add ('A');
324                         initial.Add ('B');
325                         initial.Add ('C');
326
327                         ObservableCollection<char> collection = new ObservableCollection<char> (initial);
328                         bool propertyChanged = false;
329                         List<string> changedProps = new List<string> ();
330                         NotifyCollectionChangedEventArgs args = null;
331
332                         ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
333                                 propertyChanged = true;
334                                 changedProps.Add (e.PropertyName);
335                         };
336
337                         collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
338                                 args = e;
339                         };
340
341                         collection.Clear ();
342
343                         Assert.IsTrue (propertyChanged, "CLEAR_1");
344                         Assert.IsTrue (changedProps.Contains ("Count"), "CLEAR_2");
345                         Assert.IsTrue (changedProps.Contains ("Item[]"), "CLEAR_3");
346
347                         CollectionChangedEventValidators.ValidateResetOperation (args, "CLEAR_4");
348                 }
349         }
350 }
351
352 #endif