1 /*******************************************************************************
2  * Copyright (c) 2007, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Matthew Hall - bugs 208858, 221351, 213145, 244098
14  *     Simon Scholz <simon.scholz@vogella.com> - Bug 444829
15  ******************************************************************************/
16 
17 package org.eclipse.jface.databinding.conformance;
18 
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.List;
23 
24 import org.eclipse.core.databinding.observable.IObservablesListener;
25 import org.eclipse.core.databinding.observable.list.IObservableList;
26 import org.eclipse.core.databinding.observable.list.ListDiff;
27 import org.eclipse.jface.databinding.conformance.delegate.IObservableCollectionContractDelegate;
28 import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
29 import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
30 import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
31 import org.junit.Assert;
32 
33 import junit.framework.Test;
34 
35 /**
36  * Mutability tests for IObservableList.
37  *
38  * <p>
39  * This class is experimental and can change at any time. It is recommended to
40  * not subclass or assume the test names will not change. The only API that is
41  * guaranteed to not change are the constructors. The tests will remain public
42  * and not final in order to allow for consumers to turn off a test if needed by
43  * subclassing.
44  * </p>
45  *
46  * @since 3.2
47  */
48 public class MutableObservableListContractTest extends
49 		MutableObservableCollectionContractTest {
50 	private IObservableCollectionContractDelegate delegate;
51 
52 	private IObservableList list;
53 
54 	/**
55 	 * @param delegate
56 	 */
MutableObservableListContractTest( IObservableCollectionContractDelegate delegate)57 	public MutableObservableListContractTest(
58 			IObservableCollectionContractDelegate delegate) {
59 		super(delegate);
60 		this.delegate = delegate;
61 	}
62 
MutableObservableListContractTest(String testName, IObservableCollectionContractDelegate delegate)63 	public MutableObservableListContractTest(String testName,
64 			IObservableCollectionContractDelegate delegate) {
65 		super(testName, delegate);
66 		this.delegate = delegate;
67 	}
68 
69 	@Override
setUp()70 	protected void setUp() throws Exception {
71 		super.setUp();
72 		list = (IObservableList) getObservable();
73 	}
74 
testAdd_ListChangeEvent()75 	public void testAdd_ListChangeEvent() throws Exception {
76 		final Object element = delegate.createElement(list);
77 		assertListChangeEventFired(() -> list.add(element), "List.add(Object)", list,
78 				Collections.singletonList(element));
79 	}
80 
testAdd_ListDiffEntry()81 	public void testAdd_ListDiffEntry() throws Exception {
82 		Object element0 = delegate.createElement(list);
83 		list.add(element0);
84 		final Object element1 = delegate.createElement(list);
85 
86 		assertListChangeEventFired(() -> list.add(element1), "List.add(Object)", list,
87 				Arrays.asList(new Object[] { element0, element1 }));
88 	}
89 
testAddAtIndex_ChangeEvent()90 	public void testAddAtIndex_ChangeEvent() throws Exception {
91 		assertChangeEventFired(() -> list.add(0, delegate.createElement(list)), "List.add(int, Object)", list);
92 	}
93 
testAddAtIndex_ListChangeEvent()94 	public void testAddAtIndex_ListChangeEvent() throws Exception {
95 		final Object element = delegate.createElement(list);
96 		assertListChangeEventFired(() -> list.add(0, element), "List.add(int, Object)", list,
97 				Collections.singletonList(element));
98 	}
99 
testAddAtIndex_ChangeEventFiredAfterElementIsAdded()100 	public void testAddAtIndex_ChangeEventFiredAfterElementIsAdded()
101 			throws Exception {
102 		final Object element = delegate.createElement(list);
103 
104 		assertContainsDuringChangeEvent(() -> list.add(0, element), "List.add(int, Collection)", list, element);
105 	}
106 
testAddAtIndex_ListDiffEntry()107 	public void testAddAtIndex_ListDiffEntry() throws Exception {
108 		Object element0 = delegate.createElement(list);
109 		list.add(element0);
110 		final Object element1 = delegate.createElement(list);
111 
112 		assertListChangeEventFired(() -> list.add(1, element1), "List.add(int, Object)", list,
113 				Arrays.asList(new Object[] { element0, element1 }));
114 	}
115 
testAddAll_ListChangeEvent()116 	public void testAddAll_ListChangeEvent() throws Exception {
117 		final Object element = delegate.createElement(list);
118 		assertListChangeEventFired(() -> list.addAll(Collections.singletonList(element)), "List.addAll(Collection",
119 				list, Collections.singletonList(element));
120 	}
121 
testAddAll_ListDiffEntry()122 	public void testAddAll_ListDiffEntry() throws Exception {
123 		final Object element = delegate.createElement(list);
124 
125 		assertListChangeEventFired(() -> list.addAll(Collections.singletonList(element)), "List.addAll(Collection)",
126 				list, Collections.singletonList(element));
127 	}
128 
testAddAll_ListDiffEntry2()129 	public void testAddAll_ListDiffEntry2() throws Exception {
130 		final Object element0 = delegate.createElement(list);
131 		list.add(element0);
132 		final Object element1 = delegate.createElement(list);
133 
134 		assertListChangeEventFired(() -> list.addAll(Collections.singletonList(element1)), "List.addAll(Collection)",
135 				list,
136 				Arrays.asList(new Object[] { element0, element1 }));
137 	}
138 
testAddAllAtIndex_ChangeEvent()139 	public void testAddAllAtIndex_ChangeEvent() throws Exception {
140 		assertChangeEventFired(() -> list.addAll(0, Arrays.asList(new Object[] { delegate.createElement(list) })),
141 				"List.addAll(int, Collection)", list);
142 	}
143 
testAddAllAtIndex_ListChangeEvent()144 	public void testAddAllAtIndex_ListChangeEvent() throws Exception {
145 		final Object element = delegate.createElement(list);
146 		assertListChangeEventFired(() -> list.addAll(0, Collections.singletonList(element)),
147 				"List.addAll(int, Collection)", list,
148 				Collections.singletonList(element));
149 	}
150 
testAddAllAtIndex_ChangeEventFiredAfterElementIsAdded()151 	public void testAddAllAtIndex_ChangeEventFiredAfterElementIsAdded()
152 			throws Exception {
153 		final Object element = delegate.createElement(list);
154 
155 		assertContainsDuringChangeEvent(() -> list.addAll(0, Arrays.asList(new Object[] { element })),
156 				"List.addAll(int, Collection)", list, element);
157 	}
158 
testAddAllAtIndex_ListDiffEntry()159 	public void testAddAllAtIndex_ListDiffEntry() throws Exception {
160 		Object element0 = delegate.createElement(list);
161 		list.add(element0);
162 		final Object element1 = delegate.createElement(list);
163 
164 		assertListChangeEventFired(() -> list.addAll(1, Collections.singletonList(element1)),
165 				"List.addAll(int, Collection)", list,
166 				Arrays.asList(new Object[] { element0, element1 }));
167 	}
168 
testSet_ChangeEvent()169 	public void testSet_ChangeEvent() throws Exception {
170 		list.add(delegate.createElement(list));
171 
172 		assertChangeEventFired(() -> list.set(0, delegate.createElement(list)), "List.set(int, Object)", list);
173 	}
174 
testSet_ListChangeEvent()175 	public void testSet_ListChangeEvent() throws Exception {
176 		final Object element0 = delegate.createElement(list);
177 		list.add(element0);
178 		final Object element1 = delegate.createElement(list);
179 
180 		assertListChangeEventFired(() -> assertSame(element0, list.set(0, element1)), "List.set(int, Object)", list,
181 				Arrays.asList(new Object[] { element1 }));
182 	}
183 
testSet_ChangeEventFiredAfterElementIsSet()184 	public void testSet_ChangeEventFiredAfterElementIsSet() throws Exception {
185 		final Object element1 = delegate.createElement(list);
186 		list.add(element1);
187 		final Object element2 = delegate.createElement(list);
188 
189 		assertContainsDuringChangeEvent(() -> assertSame(element1, list.set(0, element2)), "List.set(int, Object)",
190 				list, element2);
191 	}
192 
testSet_ListChangeEvent2()193 	public void testSet_ListChangeEvent2() throws Exception {
194 		Object element0 = delegate.createElement(list);
195 		list.add(element0);
196 		Object oldElement1 = delegate.createElement(list);
197 		list.add(oldElement1);
198 		final Object newElement1 = delegate.createElement(list);
199 
200 		assertListChangeEventFired(() -> list.set(1, newElement1), "List.set(int, Object)", list,
201 				Arrays.asList(new Object[] { element0, newElement1 }));
202 	}
203 
testMove_ChangeEvent()204 	public void testMove_ChangeEvent() throws Exception {
205 		list.add(delegate.createElement(list));
206 		list.add(delegate.createElement(list));
207 
208 		assertChangeEventFired(() -> list.move(0, 1), "IObservableList.move(int, int)", list);
209 	}
210 
testMove_NoChangeEventAtSameIndex()211 	public void testMove_NoChangeEventAtSameIndex() throws Exception {
212 		Object element = delegate.createElement(list);
213 		list.add(element);
214 
215 		ListChangeEventTracker tracker = ListChangeEventTracker.observe(list);
216 
217 		final Object movedElement = list.move(0, 0);
218 
219 		assertEquals(
220 				formatFail("IObservableList.move(int,int) should return the moved element"),
221 				element, movedElement);
222 		assertEquals(
223 				formatFail("IObservableLIst.move(int,int) should not fire a change event"
224 						+ "when the old and new indices are the same"), 0,
225 				tracker.count);
226 	}
227 
testMove_ListChangeEvent()228 	public void testMove_ListChangeEvent() throws Exception {
229 		final Object element0 = delegate.createElement(list);
230 		list.add(element0);
231 		final Object element1 = delegate.createElement(list);
232 		list.add(element1);
233 
234 		assertListChangeEventFired(() -> assertSame(element0, list.move(0, 1)), "IObservableList.move(int, int)", list,
235 				Arrays.asList(new Object[] { element1, element0 }));
236 	}
237 
testMove_ChangeEventFiredAfterElementIsMoved()238 	public void testMove_ChangeEventFiredAfterElementIsMoved() throws Exception {
239 		Object element0 = delegate.createElement(list);
240 		Object element1 = delegate.createElement(list);
241 		list.add(element0);
242 		list.add(element1);
243 
244 		assertSame(element0, list.get(0));
245 		assertSame(element1, list.get(1));
246 
247 		list.move(0, 1);
248 
249 		assertSame(element1, list.get(0));
250 		assertSame(element0, list.get(1));
251 	}
252 
testMove_ListChangeEvent2()253 	public void testMove_ListChangeEvent2() {
254 		Object element0 = delegate.createElement(list);
255 		list.add(element0);
256 		Object element1 = delegate.createElement(list);
257 		list.add(element1);
258 
259 		assertListChangeEventFired(() -> list.move(0, 1), "IObservableList.move(int, int)", list,
260 				Arrays.asList(new Object[] { element1, element0 }));
261 	}
262 
testRemove_ListChangeEvent()263 	public void testRemove_ListChangeEvent() throws Exception {
264 		final Object element = delegate.createElement(list);
265 		list.add(element);
266 
267 		assertListChangeEventFired(() -> list.remove(element), "List.remove(Object)", list, Collections.EMPTY_LIST);
268 	}
269 
testRemove_ListDiffEntry()270 	public void testRemove_ListDiffEntry() throws Exception {
271 		final Object element0 = delegate.createElement(list);
272 		list.add(element0);
273 		final Object element1 = delegate.createElement(list);
274 		list.add(element1);
275 
276 		assertListChangeEventFired(() -> list.remove(element1), "List.remove(Object)", list,
277 				Collections.singletonList(element0));
278 	}
279 
testRemoveAtIndex_ChangeEvent()280 	public void testRemoveAtIndex_ChangeEvent() throws Exception {
281 		list.add(delegate.createElement(list));
282 
283 		assertChangeEventFired(() -> list.remove(0), "List.remove(int)", list);
284 	}
285 
testRemoveAtIndex_ListChangeEvent()286 	public void testRemoveAtIndex_ListChangeEvent() throws Exception {
287 		list.add(delegate.createElement(list));
288 
289 		assertListChangeEventFired(() -> list.remove(0), "List.remove(int)", list, Collections.EMPTY_LIST);
290 	}
291 
testRemoveAtIndex_ChangeEventFiredAfterElementIsRemoved()292 	public void testRemoveAtIndex_ChangeEventFiredAfterElementIsRemoved()
293 			throws Exception {
294 		final Object element = delegate.createElement(list);
295 		list.add(element);
296 
297 		assertDoesNotContainDuringChangeEvent(() -> list.remove(0), "List.remove(int)", list, element);
298 	}
299 
testRemoveAtIndex_ListDiffEntry()300 	public void testRemoveAtIndex_ListDiffEntry() throws Exception {
301 		Object element0 = delegate.createElement(list);
302 		list.add(element0);
303 		Object element1 = delegate.createElement(list);
304 		list.add(element1);
305 
306 		assertListChangeEventFired(() -> list.remove(1), "List.remove(int)", list, Collections.singletonList(element0));
307 	}
308 
testRemoveAll_ListChangeEvent()309 	public void testRemoveAll_ListChangeEvent() throws Exception {
310 		final Object element = delegate.createElement(list);
311 		list.add(element);
312 
313 		assertListChangeEventFired(() -> list.removeAll(Collections.singletonList(element)),
314 				"List.removeAll(Collection)", list, Collections.EMPTY_LIST);
315 	}
316 
testRemoveAll_ListDiffEntry()317 	public void testRemoveAll_ListDiffEntry() throws Exception {
318 		final Object element = delegate.createElement(list);
319 		list.add(element);
320 
321 		assertListChangeEventFired(() -> list.removeAll(Collections.singletonList(element)),
322 				"List.removeAll(Collection)", list, Collections.EMPTY_LIST);
323 	}
324 
testRemoveAll_ListDiffEntry2()325 	public void testRemoveAll_ListDiffEntry2() throws Exception {
326 		Object element0 = delegate.createElement(list);
327 		list.add(element0);
328 		final Object element1 = delegate.createElement(list);
329 		list.add(element1);
330 
331 		assertListChangeEventFired(() -> list.removeAll(Arrays.asList(new Object[] { element1 })),
332 				"List.removeAll(Collection)", list,
333 				Collections.singletonList(element0));
334 	}
335 
testRetainAll_ListChangeEvent()336 	public void testRetainAll_ListChangeEvent() throws Exception {
337 		final Object element0 = delegate.createElement(list);
338 		list.add(element0);
339 		list.add(delegate.createElement(list));
340 
341 		assertListChangeEventFired(() -> list.retainAll(Arrays.asList(new Object[] { element0 })),
342 				"List.retainAll(Collection", list,
343 				Collections.singletonList(element0));
344 	}
345 
testRetainAll_ListDiffEntry()346 	public void testRetainAll_ListDiffEntry() throws Exception {
347 		final Object element = delegate.createElement(list);
348 		list.add(element);
349 		list.add(delegate.createElement(list));
350 
351 		assertListChangeEventFired(() -> list.retainAll(Arrays.asList(new Object[] { element })),
352 				"List.retainAll(Collection)", list,
353 				Collections.singletonList(element));
354 	}
355 
testClear_ListChangeEvent()356 	public void testClear_ListChangeEvent() throws Exception {
357 		list.add(delegate.createElement(list));
358 
359 		assertListChangeEventFired(() -> list.clear(), "List.clear()", list, Collections.EMPTY_LIST);
360 	}
361 
testClear_ListDiffEntry()362 	public void testClear_ListDiffEntry() throws Exception {
363 		list.add(delegate.createElement(list));
364 
365 		assertListChangeEventFired(() -> list.clear(), "List.clear()", list, Collections.EMPTY_LIST);
366 	}
367 
testClear_ClearsList()368 	public void testClear_ClearsList() {
369 		Object element = delegate.createElement(list);
370 		list.add(element);
371 		Assert.assertEquals(Collections.singletonList(element), list);
372 		list.clear();
373 		Assert.assertEquals(Collections.EMPTY_LIST, list);
374 	}
375 
assertListChangeEventFired(Runnable runnable, String methodName, IObservableList list, List newList)376 	private void assertListChangeEventFired(Runnable runnable,
377 			String methodName, IObservableList list, List newList) {
378 		List oldList = new ArrayList(list);
379 
380 		List<IObservablesListener> queue = new ArrayList<>();
381 		ListChangeEventTracker listListener = new ListChangeEventTracker(queue);
382 		ChangeEventTracker changeListener = new ChangeEventTracker(queue);
383 
384 		list.addListChangeListener(listListener);
385 		list.addChangeListener(changeListener);
386 
387 		runnable.run();
388 
389 		assertEquals(formatFail(methodName
390 				+ " should fire one ListChangeEvent."), 1, listListener.count);
391 		assertEquals(formatFail(methodName
392 				+ "'s change event observable should be the created List."),
393 				list, listListener.event.getObservable());
394 
395 		assertEquals(
396 				formatFail("Two notifications should have been received."), 2,
397 				queue.size());
398 		assertEquals("ChangeEvent of " + methodName
399 				+ " should have fired before the ListChangeEvent.",
400 				changeListener, queue.get(0));
401 		assertEquals("ListChangeEvent of " + methodName
402 				+ " should have fired after the ChangeEvent.", listListener,
403 				queue.get(1));
404 
405 		assertEquals(formatFail(methodName
406 				+ " did not leave observable list with the expected contents"),
407 				newList, list);
408 
409 		ListDiff diff = listListener.event.diff;
410 		diff.applyTo(oldList);
411 		assertEquals(
412 				formatFail(methodName
413 						+ " fired a diff which does not represent the expected list change"),
414 				newList, oldList);
415 
416 	}
417 
suite(IObservableCollectionContractDelegate delegate)418 	public static Test suite(IObservableCollectionContractDelegate delegate) {
419 		return new SuiteBuilder()
420 				.addObservableContractTest(
421 						MutableObservableListContractTest.class, delegate)
422 				.addObservableContractTest(ObservableListContractTest.class,
423 						delegate).build();
424 	}
425 }
426