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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
21 // Copyright 2011 Xamarin Inc.
24 // Chris Toshok (toshok@ximian.com)
25 // Brian O'Keefe (zer0keefie@gmail.com)
26 // Marek Safar (marek.safar@gmail.com)
31 using System.Collections.ObjectModel;
32 using System.Collections.Specialized;
33 using NUnit.Framework;
34 using System.ComponentModel;
35 using System.Collections.Generic;
37 using System.Collections;
38 using MonoTests.System.Collections.Specialized;
40 namespace MonoTests.System.Collections.ObjectModel {
43 public class ObservableCollectionTest
46 public void Constructor ()
48 var list = new List<int> { 3 };
49 var col = new ObservableCollection<int> (list);
51 Assert.AreEqual (1, list.Count, "#1");
53 col = new ObservableCollection<int> ((IEnumerable<int>) list);
55 Assert.AreEqual (1, list.Count, "#2");
59 public void Constructor_Invalid ()
62 new ObservableCollection<int> ((List<int>) null);
64 } catch (ArgumentNullException) {
68 new ObservableCollection<int> ((IEnumerable<int>) null);
70 } catch (ArgumentNullException) {
78 ObservableCollection<int> col = new ObservableCollection<int> ();
79 col.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
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");
89 Assert.IsTrue (reached, "INS_5");
93 public void RemoveAt()
96 ObservableCollection<int> col = new ObservableCollection<int> ();
98 col.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
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");
108 Assert.IsTrue (reached, "REMAT_7");
114 bool reached = false;
115 ObservableCollection<int> col = new ObservableCollection<int> ();
120 col.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
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");
131 Assert.IsTrue (reached, "MOVE_8");
137 ObservableCollection<char> collection = new ObservableCollection<char> ();
138 bool propertyChanged = false;
139 List<string> changedProps = new List<string> ();
140 NotifyCollectionChangedEventArgs args = null;
142 ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
143 propertyChanged = true;
144 changedProps.Add (e.PropertyName);
147 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
151 collection.Add ('A');
153 Assert.IsTrue (propertyChanged, "ADD_1");
154 Assert.IsTrue (changedProps.Contains ("Count"), "ADD_2");
155 Assert.IsTrue (changedProps.Contains ("Item[]"), "ADD_3");
157 CollectionChangedEventValidators.ValidateAddOperation (args, new char [] { 'A' }, 0, "ADD_4");
163 ObservableCollection<char> collection = new ObservableCollection<char> ();
164 bool propertyChanged = false;
165 List<string> changedProps = new List<string> ();
166 NotifyCollectionChangedEventArgs args = null;
168 collection.Add ('A');
169 collection.Add ('B');
170 collection.Add ('C');
172 ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
173 propertyChanged = true;
174 changedProps.Add (e.PropertyName);
177 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
181 collection.Remove ('B');
183 Assert.IsTrue (propertyChanged, "REM_1");
184 Assert.IsTrue (changedProps.Contains ("Count"), "REM_2");
185 Assert.IsTrue (changedProps.Contains ("Item[]"), "REM_3");
187 CollectionChangedEventValidators.ValidateRemoveOperation (args, new char [] { 'B' }, 1, "REM_4");
193 ObservableCollection<char> collection = new ObservableCollection<char> ();
194 bool propertyChanged = false;
195 List<string> changedProps = new List<string> ();
196 NotifyCollectionChangedEventArgs args = null;
198 collection.Add ('A');
199 collection.Add ('B');
200 collection.Add ('C');
202 ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
203 propertyChanged = true;
204 changedProps.Add (e.PropertyName);
207 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
211 collection [2] = 'I';
213 Assert.IsTrue (propertyChanged, "SET_1");
214 Assert.IsTrue (changedProps.Contains ("Item[]"), "SET_2");
216 CollectionChangedEventValidators.ValidateReplaceOperation (args, new char [] { 'C' }, new char [] { 'I' }, 2, "SET_3");
220 public void Reentrant()
222 ObservableCollection<char> collection = new ObservableCollection<char> ();
223 bool propertyChanged = false;
224 List<string> changedProps = new List<string> ();
225 NotifyCollectionChangedEventArgs args = null;
227 collection.Add ('A');
228 collection.Add ('B');
229 collection.Add ('C');
231 PropertyChangedEventHandler pceh = delegate (object sender, PropertyChangedEventArgs e) {
232 propertyChanged = true;
233 changedProps.Add (e.PropertyName);
236 // Adding a PropertyChanged event handler
237 ((INotifyPropertyChanged)collection).PropertyChanged += pceh;
239 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
243 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
244 // This one will attempt to break reentrancy
246 collection.Add ('X');
247 Assert.Fail ("Reentrancy should not be allowed.");
248 } catch (InvalidOperationException) {
252 collection [2] = 'I';
254 Assert.IsTrue (propertyChanged, "REENT_1");
255 Assert.IsTrue (changedProps.Contains ("Item[]"), "REENT_2");
257 CollectionChangedEventValidators.ValidateReplaceOperation (args, new char [] { 'C' }, new char [] { 'I' }, 2, "REENT_3");
259 // Removing the PropertyChanged event handler should work as well:
260 ((INotifyPropertyChanged)collection).PropertyChanged -= pceh;
263 //Private test class for protected members of ObservableCollection
264 private class ObservableCollectionTestHelper : ObservableCollection<char> {
265 internal void DoubleEnterReentrant()
267 IDisposable object1 = BlockReentrancy ();
268 IDisposable object2 = BlockReentrancy ();
270 Assert.AreSame (object1, object2);
272 //With double block, try the reentrant:
273 NotifyCollectionChangedEventArgs args = null;
275 CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
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.
284 // Try adding - this should cause reentrancy, and fail
287 Assert.Fail ("Reentrancy should not be allowed. -- #2");
288 } catch (InvalidOperationException) {
291 // Release the first reentrant
294 // Try adding again - this should cause reentrancy, and fail again
297 Assert.Fail ("Reentrancy should not be allowed. -- #3");
298 } catch (InvalidOperationException) {
301 // Release the reentrant a second time
304 // This last add should work fine.
306 CollectionChangedEventValidators.ValidateAddOperation (args, new char [] { 'K' }, 0, "REENTHELP_1");
311 public void ReentrantReuseObject()
313 ObservableCollectionTestHelper helper = new ObservableCollectionTestHelper ();
315 helper.DoubleEnterReentrant ();
321 List<char> initial = new List<char> ();
327 ObservableCollection<char> collection = new ObservableCollection<char> (initial);
328 bool propertyChanged = false;
329 List<string> changedProps = new List<string> ();
330 NotifyCollectionChangedEventArgs args = null;
332 ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
333 propertyChanged = true;
334 changedProps.Add (e.PropertyName);
337 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
343 Assert.IsTrue (propertyChanged, "CLEAR_1");
344 Assert.IsTrue (changedProps.Contains ("Count"), "CLEAR_2");
345 Assert.IsTrue (changedProps.Contains ("Item[]"), "CLEAR_3");
347 CollectionChangedEventValidators.ValidateResetOperation (args, "CLEAR_4");