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)
30 using System.Collections.ObjectModel;
31 using System.Collections.Specialized;
32 using NUnit.Framework;
33 using System.ComponentModel;
34 using System.Collections.Generic;
36 using System.Collections;
37 using MonoTests.System.Collections.Specialized;
39 namespace MonoTests.System.Collections.ObjectModel {
42 public class ObservableCollectionTest
45 public void Constructor ()
47 var list = new List<int> { 3 };
48 var col = new ObservableCollection<int> (list);
50 Assert.AreEqual (1, list.Count, "#1");
52 col = new ObservableCollection<int> ((IEnumerable<int>) list);
54 Assert.AreEqual (1, list.Count, "#2");
58 public void Constructor_Invalid ()
61 new ObservableCollection<int> ((List<int>) null);
63 } catch (ArgumentNullException) {
67 new ObservableCollection<int> ((IEnumerable<int>) null);
69 } catch (ArgumentNullException) {
77 ObservableCollection<int> col = new ObservableCollection<int> ();
78 col.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
80 Assert.AreEqual (NotifyCollectionChangedAction.Add, e.Action, "INS_1");
81 Assert.AreEqual (0, e.NewStartingIndex, "INS_2");
82 Assert.AreEqual (-1, e.OldStartingIndex, "INS_3");
83 Assert.AreEqual (1, e.NewItems.Count, "INS_4");
84 Assert.AreEqual (5, (int)e.NewItems [0], "INS_5");
85 Assert.AreEqual (null, e.OldItems, "INS_6");
88 Assert.IsTrue (reached, "INS_5");
92 public void RemoveAt()
95 ObservableCollection<int> col = new ObservableCollection<int> ();
97 col.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
99 Assert.AreEqual (NotifyCollectionChangedAction.Remove, e.Action, "REMAT_1");
100 Assert.AreEqual (-1, e.NewStartingIndex, "REMAT_2");
101 Assert.AreEqual (0, e.OldStartingIndex, "REMAT_3");
102 Assert.AreEqual (null, e.NewItems, "REMAT_4");
103 Assert.AreEqual (1, e.OldItems.Count, "REMAT_5");
104 Assert.AreEqual (5, (int)e.OldItems [0], "REMAT_6");
107 Assert.IsTrue (reached, "REMAT_7");
113 bool reached = false;
114 ObservableCollection<int> col = new ObservableCollection<int> ();
119 col.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
121 Assert.AreEqual (NotifyCollectionChangedAction.Move, e.Action, "MOVE_1");
122 Assert.AreEqual (3, e.NewStartingIndex, "MOVE_2");
123 Assert.AreEqual (1, e.OldStartingIndex, "MOVE_3");
124 Assert.AreEqual (1, e.NewItems.Count, "MOVE_4");
125 Assert.AreEqual (1, e.NewItems [0], "MOVE_5");
126 Assert.AreEqual (1, e.OldItems.Count, "MOVE_6");
127 Assert.AreEqual (1, e.OldItems [0], "MOVE_7");
130 Assert.IsTrue (reached, "MOVE_8");
136 ObservableCollection<char> collection = new ObservableCollection<char> ();
137 bool propertyChanged = false;
138 List<string> changedProps = new List<string> ();
139 NotifyCollectionChangedEventArgs args = null;
141 ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
142 propertyChanged = true;
143 changedProps.Add (e.PropertyName);
146 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
150 collection.Add ('A');
152 Assert.IsTrue (propertyChanged, "ADD_1");
153 Assert.IsTrue (changedProps.Contains ("Count"), "ADD_2");
154 Assert.IsTrue (changedProps.Contains ("Item[]"), "ADD_3");
156 CollectionChangedEventValidators.ValidateAddOperation (args, new char [] { 'A' }, 0, "ADD_4");
162 ObservableCollection<char> collection = new ObservableCollection<char> ();
163 bool propertyChanged = false;
164 List<string> changedProps = new List<string> ();
165 NotifyCollectionChangedEventArgs args = null;
167 collection.Add ('A');
168 collection.Add ('B');
169 collection.Add ('C');
171 ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
172 propertyChanged = true;
173 changedProps.Add (e.PropertyName);
176 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
180 collection.Remove ('B');
182 Assert.IsTrue (propertyChanged, "REM_1");
183 Assert.IsTrue (changedProps.Contains ("Count"), "REM_2");
184 Assert.IsTrue (changedProps.Contains ("Item[]"), "REM_3");
186 CollectionChangedEventValidators.ValidateRemoveOperation (args, new char [] { 'B' }, 1, "REM_4");
192 ObservableCollection<char> collection = new ObservableCollection<char> ();
193 bool propertyChanged = false;
194 List<string> changedProps = new List<string> ();
195 NotifyCollectionChangedEventArgs args = null;
197 collection.Add ('A');
198 collection.Add ('B');
199 collection.Add ('C');
201 ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
202 propertyChanged = true;
203 changedProps.Add (e.PropertyName);
206 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
210 collection [2] = 'I';
212 Assert.IsTrue (propertyChanged, "SET_1");
213 Assert.IsTrue (changedProps.Contains ("Item[]"), "SET_2");
215 CollectionChangedEventValidators.ValidateReplaceOperation (args, new char [] { 'C' }, new char [] { 'I' }, 2, "SET_3");
219 public void Reentrant()
221 ObservableCollection<char> collection = new ObservableCollection<char> ();
222 bool propertyChanged = false;
223 List<string> changedProps = new List<string> ();
224 NotifyCollectionChangedEventArgs args = null;
226 collection.Add ('A');
227 collection.Add ('B');
228 collection.Add ('C');
230 PropertyChangedEventHandler pceh = delegate (object sender, PropertyChangedEventArgs e) {
231 propertyChanged = true;
232 changedProps.Add (e.PropertyName);
235 // Adding a PropertyChanged event handler
236 ((INotifyPropertyChanged)collection).PropertyChanged += pceh;
238 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
242 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
243 // This one will attempt to break reentrancy
245 collection.Add ('X');
246 Assert.Fail ("Reentrancy should not be allowed.");
247 } catch (InvalidOperationException) {
251 collection [2] = 'I';
253 Assert.IsTrue (propertyChanged, "REENT_1");
254 Assert.IsTrue (changedProps.Contains ("Item[]"), "REENT_2");
256 CollectionChangedEventValidators.ValidateReplaceOperation (args, new char [] { 'C' }, new char [] { 'I' }, 2, "REENT_3");
258 // Removing the PropertyChanged event handler should work as well:
259 ((INotifyPropertyChanged)collection).PropertyChanged -= pceh;
262 //Private test class for protected members of ObservableCollection
263 private class ObservableCollectionTestHelper : ObservableCollection<char> {
264 internal void DoubleEnterReentrant()
266 IDisposable object1 = BlockReentrancy ();
267 IDisposable object2 = BlockReentrancy ();
269 Assert.AreSame (object1, object2);
271 //With double block, try the reentrant:
272 NotifyCollectionChangedEventArgs args = null;
274 CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
278 // We need a second callback for reentrancy to matter
279 CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
280 // Doesn't need to do anything; just needs more than one callback registered.
283 // Try adding - this should cause reentrancy, and fail
286 Assert.Fail ("Reentrancy should not be allowed. -- #2");
287 } catch (InvalidOperationException) {
290 // Release the first reentrant
293 // Try adding again - this should cause reentrancy, and fail again
296 Assert.Fail ("Reentrancy should not be allowed. -- #3");
297 } catch (InvalidOperationException) {
300 // Release the reentrant a second time
303 // This last add should work fine.
305 CollectionChangedEventValidators.ValidateAddOperation (args, new char [] { 'K' }, 0, "REENTHELP_1");
310 public void ReentrantReuseObject()
312 ObservableCollectionTestHelper helper = new ObservableCollectionTestHelper ();
314 helper.DoubleEnterReentrant ();
320 List<char> initial = new List<char> ();
326 ObservableCollection<char> collection = new ObservableCollection<char> (initial);
327 bool propertyChanged = false;
328 List<string> changedProps = new List<string> ();
329 NotifyCollectionChangedEventArgs args = null;
331 ((INotifyPropertyChanged)collection).PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) {
332 propertyChanged = true;
333 changedProps.Add (e.PropertyName);
336 collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
342 Assert.IsTrue (propertyChanged, "CLEAR_1");
343 Assert.IsTrue (changedProps.Contains ("Count"), "CLEAR_2");
344 Assert.IsTrue (changedProps.Contains ("Item[]"), "CLEAR_3");
346 CollectionChangedEventValidators.ValidateResetOperation (args, "CLEAR_4");