1 /*******************************************************************************
2  * Copyright (c) 2006, 2018 Brad Reynolds.
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  *     Brad Reynolds - initial API and implementation
13  *     Brad Reynolds - bug 116920
14  *     Matthew Hall - bugs 208332, 213145, 255734
15  *     Ovidio Mallo - bug 247741
16  ******************************************************************************/
17 
18 package org.eclipse.core.tests.databinding.observable;
19 
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertSame;
23 import static org.junit.Assert.assertTrue;
24 
25 import org.eclipse.core.databinding.observable.AbstractObservable;
26 import org.eclipse.core.databinding.observable.IDisposeListener;
27 import org.eclipse.core.databinding.observable.IObservable;
28 import org.eclipse.core.databinding.observable.IStaleListener;
29 import org.eclipse.core.databinding.observable.ObservableTracker;
30 import org.eclipse.core.databinding.observable.Realm;
31 import org.eclipse.jface.databinding.conformance.ObservableContractTest;
32 import org.eclipse.jface.databinding.conformance.ObservableStaleContractTest;
33 import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableContractDelegate;
34 import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
35 import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
36 import org.eclipse.jface.databinding.conformance.util.RealmTester;
37 import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
38 import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
39 import org.junit.Before;
40 import org.junit.Test;
41 
42 import junit.framework.TestSuite;
43 
44 /**
45  * Tests for AbstractObservable.
46  *
47  * @since 1.1
48  */
49 public class AbstractObservableTest extends AbstractDefaultRealmTestCase {
50 	private ObservableStub observable;
51 
52 	@Override
53 	@Before
setUp()54 	public void setUp() throws Exception {
55 		super.setUp();
56 		observable = new ObservableStub(Realm.getDefault());
57 	}
58 
59 	@Test
testStaleListener()60 	public void testStaleListener() throws Exception {
61 		assertFalse(observable.hasListeners());
62 
63 		StaleEventTracker listener1 = new StaleEventTracker();
64 
65 		assertFalse(observable.firstListenerAdded);
66 		observable.addStaleListener(listener1);
67 		assertTrue(observable.firstListenerAdded);
68 		observable.firstListenerAdded = false; // reset
69 
70 		assertTrue(observable.hasListeners());
71 		assertEquals(0, listener1.count);
72 
73 		observable.fireStale();
74 
75 		assertEquals(1, listener1.count);
76 		assertSame(observable, listener1.event.getObservable());
77 
78 		// Add a second stale listener as 1 vs. 2 listener code is different.
79 		StaleEventTracker listener2 = new StaleEventTracker();
80 		assertEquals(0, listener2.count);
81 		observable.addStaleListener(listener2);
82 		observable.fireStale();
83 
84 		assertEquals(2, listener1.count);
85 		assertEquals(1, listener2.count);
86 
87 		// Add a third stale listener as 2 vs. 3 or greater code is different.
88 		StaleEventTracker listener3 = new StaleEventTracker();
89 		observable.addStaleListener(listener3);
90 		assertEquals(0, listener3.count);
91 
92 		observable.fireStale();
93 
94 		assertEquals(3, listener1.count);
95 		assertEquals(2, listener2.count);
96 		assertEquals(1, listener3.count);
97 
98 		assertFalse(observable.lastListenerRemoved);
99 		observable.removeStaleListener(listener1);
100 		observable.removeStaleListener(listener2);
101 		observable.removeStaleListener(listener3);
102 		assertTrue(observable.lastListenerRemoved);
103 
104 		assertFalse(observable.hasListeners());
105 	}
106 
107 	@Test
testChangeListener()108 	public void testChangeListener() throws Exception {
109 		assertFalse(observable.hasListeners());
110 
111 		ChangeEventTracker listener1 = new ChangeEventTracker();
112 
113 		assertFalse(observable.firstListenerAdded);
114 		observable.addChangeListener(listener1);
115 		assertTrue(observable.firstListenerAdded);
116 		observable.firstListenerAdded = false;
117 
118 		assertTrue(observable.hasListeners());
119 		assertEquals(0, listener1.count);
120 
121 		observable.fireChange();
122 
123 		assertEquals(1, listener1.count);
124 		assertSame(observable, listener1.event.getSource());
125 
126 		// Add a second listener as the 1 vs. 2 listener code is different.
127 		ChangeEventTracker listener2 = new ChangeEventTracker();
128 		observable.addChangeListener(listener2);
129 		assertEquals(0, listener2.count);
130 
131 		observable.fireChange();
132 		assertEquals(2, listener1.count);
133 		assertEquals(1, listener2.count);
134 
135 		// Add a third listener as the 2 vs. 3 or greater code is different.
136 		ChangeEventTracker listener3 = new ChangeEventTracker();
137 		observable.addChangeListener(listener3);
138 		assertEquals(0, listener3.count);
139 
140 		observable.fireChange();
141 
142 		assertEquals(3, listener1.count);
143 		assertEquals(2, listener2.count);
144 		assertEquals(1, listener3.count);
145 
146 		assertFalse(observable.lastListenerRemoved);
147 		observable.removeChangeListener(listener1);
148 		observable.removeChangeListener(listener2);
149 		observable.removeChangeListener(listener3);
150 		assertTrue(observable.lastListenerRemoved);
151 
152 		assertFalse(observable.hasListeners());
153 	}
154 
155 	@Test
testHasListenersWithChangeAndStaleListeners()156 	public void testHasListenersWithChangeAndStaleListeners() throws Exception {
157 		ChangeEventTracker changeListener = new ChangeEventTracker();
158 		StaleEventTracker staleListener = new StaleEventTracker();
159 
160 		assertFalse(observable.hasListeners());
161 		assertFalse(observable.firstListenerAdded);
162 		assertFalse(observable.lastListenerRemoved);
163 
164 		observable.addChangeListener(changeListener);
165 		assertTrue(observable.hasListeners());
166 		assertTrue(observable.firstListenerAdded);
167 		assertFalse(observable.lastListenerRemoved);
168 
169 		// reset
170 		observable.firstListenerAdded = false;
171 		observable.lastListenerRemoved = false;
172 
173 		observable.addStaleListener(staleListener);
174 		assertTrue(observable.hasListeners());
175 		assertFalse(observable.firstListenerAdded);
176 		assertFalse(observable.lastListenerRemoved);
177 
178 		observable.removeChangeListener(changeListener);
179 		assertTrue(observable.hasListeners());
180 		assertFalse(observable.firstListenerAdded);
181 		assertFalse(observable.lastListenerRemoved);
182 
183 		observable.removeStaleListener(staleListener);
184 		assertFalse(observable.hasListeners());
185 		assertFalse(observable.firstListenerAdded);
186 		assertTrue(observable.lastListenerRemoved);
187 	}
188 
189 	@Test
testFireStaleRealmChecks()190 	public void testFireStaleRealmChecks() throws Exception {
191 		RealmTester.setDefault(new CurrentRealm(true));
192 
193 		RealmTester.exerciseCurrent(() -> {
194 			observable = new ObservableStub();
195 			observable.fireStale();
196 		});
197 	}
198 
199 	@Test
testFireChangeRealmChecks()200 	public void testFireChangeRealmChecks() throws Exception {
201 		RealmTester.setDefault(new CurrentRealm(true));
202 
203 		RealmTester.exerciseCurrent(() -> {
204 			observable = new ObservableStub();
205 			observable.fireChange();
206 		});
207 	}
208 
209 	@Test
testAddDisposeListener_HasListenersFalse()210 	public void testAddDisposeListener_HasListenersFalse() {
211 		IDisposeListener disposeListener = staleEvent -> {
212 		};
213 		IStaleListener staleListener = staleEvent -> {
214 		};
215 
216 		assertFalse(observable.hasListeners());
217 
218 		observable.addDisposeListener(disposeListener);
219 		assertFalse(observable.hasListeners());
220 		assertFalse(observable.firstListenerAdded);
221 		assertFalse(observable.lastListenerRemoved);
222 
223 		observable.addStaleListener(staleListener);
224 		assertTrue(observable.hasListeners());
225 		assertTrue(observable.firstListenerAdded);
226 		assertFalse(observable.lastListenerRemoved);
227 
228 		observable.removeDisposeListener(disposeListener);
229 		assertTrue(observable.hasListeners());
230 		assertTrue(observable.firstListenerAdded);
231 		assertFalse(observable.lastListenerRemoved);
232 
233 		observable.removeStaleListener(staleListener);
234 		assertFalse(observable.hasListeners());
235 		assertTrue(observable.firstListenerAdded);
236 		assertTrue(observable.lastListenerRemoved);
237 	}
238 
addConformanceTest(TestSuite suite)239 	public static void addConformanceTest(TestSuite suite) {
240 		Delegate delegate = new Delegate();
241 		suite.addTest(ObservableContractTest.suite(delegate));
242 		suite.addTest(ObservableStaleContractTest.suite(delegate));
243 	}
244 
245 	/* package */static class Delegate extends
246 			AbstractObservableContractDelegate {
247 
248 		@Override
change(IObservable observable)249 		public void change(IObservable observable) {
250 			((ObservableStub) observable).fireChange();
251 		}
252 
253 		@Override
setStale(IObservable observable, boolean stale)254 		public void setStale(IObservable observable, boolean stale) {
255 			((ObservableStub) observable).setStale(stale);
256 		}
257 
258 		@Override
createObservable(Realm realm)259 		public IObservable createObservable(Realm realm) {
260 			return new ObservableStub(realm);
261 		}
262 	}
263 
264 	private static class ObservableStub extends AbstractObservable {
265 		private boolean stale;
266 
ObservableStub()267 		public ObservableStub() {
268 			this(Realm.getDefault());
269 		}
270 
271 		/**
272 		 * @param realm
273 		 */
ObservableStub(Realm realm)274 		public ObservableStub(Realm realm) {
275 			super(realm);
276 		}
277 
278 		private boolean firstListenerAdded;
279 
280 		private boolean lastListenerRemoved;
281 
282 		@Override
fireStale()283 		protected void fireStale() {
284 			super.fireStale();
285 		}
286 
287 		@Override
fireChange()288 		protected void fireChange() {
289 			super.fireChange();
290 		}
291 
292 		@Override
isStale()293 		public boolean isStale() {
294 			getterCalled();
295 			return stale;
296 		}
297 
getterCalled()298 		private void getterCalled() {
299 			ObservableTracker.getterCalled(this);
300 		}
301 
setStale(boolean stale)302 		public void setStale(boolean stale) {
303 			boolean old = this.stale;
304 			this.stale = stale;
305 
306 			if (stale && !old) {
307 				fireStale();
308 			}
309 		}
310 
311 		@Override
hasListeners()312 		protected boolean hasListeners() {
313 			return super.hasListeners();
314 		}
315 
316 		@Override
firstListenerAdded()317 		protected void firstListenerAdded() {
318 			firstListenerAdded = true;
319 		}
320 
321 		@Override
lastListenerRemoved()322 		protected void lastListenerRemoved() {
323 			lastListenerRemoved = true;
324 		}
325 	}
326 }
327