View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration2.event;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertSame;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.util.Collection;
26  import java.util.List;
27  
28  import org.junit.jupiter.api.BeforeEach;
29  import org.junit.jupiter.api.Test;
30  
31  /**
32   * Test class for BaseEventSource.
33   */
34  public class TestEventSource {
35      /**
36       * A specialized event source implementation that counts the number of created event objects. It is used to test whether
37       * the {@code fireEvent()} methods only creates event objects if necessary. It also allows testing the clone()
38       * operation.
39       */
40      private static final class CountingEventSource extends BaseEventSource implements Cloneable {
41          int eventCount;
42  
43          int errorCount;
44  
45          @Override
46          protected ConfigurationErrorEvent createErrorEvent(final EventType<? extends ConfigurationErrorEvent> type, final EventType<?> opType,
47              final String propName, final Object propValue, final Throwable ex) {
48              errorCount++;
49              return super.createErrorEvent(type, opType, propName, propValue, ex);
50          }
51  
52          @Override
53          protected <T extends ConfigurationEvent> ConfigurationEvent createEvent(final EventType<T> eventType, final String propName, final Object propValue,
54              final boolean before) {
55              eventCount++;
56              return super.createEvent(eventType, propName, propValue, before);
57          }
58      }
59  
60      /** Constant for the event property value. */
61      private static final Object TEST_PROPVALUE = "a test property value";
62  
63      /** Constant for the event property name. */
64      private final String TEST_PROPNAME = "test.property.name";
65  
66      /** The object under test. */
67      private CountingEventSource source;
68  
69      @BeforeEach
70      public void setUp() throws Exception {
71          source = new CountingEventSource();
72      }
73  
74      /**
75       * Tests registering a new listener.
76       */
77      @Test
78      public void testAddEventListener() {
79          final EventListenerTestImpl l = new EventListenerTestImpl(this);
80          source.addEventListener(ConfigurationEvent.ANY, l);
81          final Collection<EventListener<? super ConfigurationEvent>> listeners = source.getEventListeners(ConfigurationEvent.ANY);
82          assertEquals(1, listeners.size());
83          assertTrue(listeners.contains(l));
84      }
85  
86      /**
87       * Tests adding an undefined configuration listener. This should cause an exception.
88       */
89      @Test
90      public void testAddNullEventListener() {
91          assertThrows(IllegalArgumentException.class, () -> source.addEventListener(ConfigurationEvent.ANY, null));
92      }
93  
94      /**
95       * Tests whether all error listeners can be cleared.
96       */
97      @Test
98      public void testClearErrorListeners() {
99          final EventListener<ConfigurationEvent> cl = new EventListenerTestImpl(null);
100         final ErrorListenerTestImpl el1 = new ErrorListenerTestImpl(null);
101         final ErrorListenerTestImpl el2 = new ErrorListenerTestImpl(null);
102         final ErrorListenerTestImpl el3 = new ErrorListenerTestImpl(null);
103         source.addEventListener(ConfigurationErrorEvent.READ, el1);
104         source.addEventListener(ConfigurationErrorEvent.ANY, el2);
105         source.addEventListener(ConfigurationEvent.ANY, cl);
106         source.addEventListener(ConfigurationErrorEvent.WRITE, el3);
107 
108         source.clearErrorListeners();
109         final List<EventListenerRegistrationData<?>> regs = source.getEventListenerRegistrations();
110         assertEquals(1, regs.size());
111         assertSame(cl, regs.get(0).getListener());
112     }
113 
114     /**
115      * Tests whether all event listeners can be removed.
116      */
117     @Test
118     public void testClearEventListeners() {
119         source.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(source));
120         source.addEventListener(ConfigurationEvent.ANY_HIERARCHICAL, new EventListenerTestImpl(source));
121 
122         source.clearEventListeners();
123         assertTrue(source.getEventListeners(ConfigurationEvent.ANY).isEmpty());
124         assertTrue(source.getEventListeners(ConfigurationEvent.ANY_HIERARCHICAL).isEmpty());
125     }
126 
127     /**
128      * Tests cloning an event source object. The registered listeners should not be registered at the clone.
129      */
130     @Test
131     public void testClone() throws CloneNotSupportedException {
132         source.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(source));
133         final BaseEventSource copy = (BaseEventSource) source.clone();
134         assertTrue(copy.getEventListenerRegistrations().isEmpty());
135     }
136 
137     /**
138      * Tests whether event listeners can be copied to another source.
139      */
140     @Test
141     public void testCopyEventListeners() {
142         final EventListenerTestImpl l1 = new EventListenerTestImpl(source);
143         final EventListenerTestImpl l2 = new EventListenerTestImpl(source);
144         source.addEventListener(ConfigurationEvent.ANY, l1);
145         source.addEventListener(ConfigurationEvent.ANY_HIERARCHICAL, l2);
146 
147         final BaseEventSource source2 = new BaseEventSource();
148         source.copyEventListeners(source2);
149         Collection<EventListener<? super ConfigurationEvent>> listeners = source2.getEventListeners(ConfigurationEvent.ANY_HIERARCHICAL);
150         assertEquals(2, listeners.size());
151         assertTrue(listeners.contains(l1));
152         assertTrue(listeners.contains(l2));
153         listeners = source2.getEventListeners(ConfigurationEvent.ANY);
154         assertEquals(1, listeners.size());
155         assertTrue(listeners.contains(l1));
156     }
157 
158     /**
159      * Tries to copy event listeners to a null source.
160      */
161     @Test
162     public void testCopyEventListenersNullSource() {
163         assertThrows(IllegalArgumentException.class, () -> source.copyEventListeners(null));
164     }
165 
166     /**
167      * Tests delivering an error event to a listener.
168      */
169     @Test
170     public void testFireError() {
171         final ErrorListenerTestImpl lstRead = new ErrorListenerTestImpl(source);
172         final ErrorListenerTestImpl lstWrite = new ErrorListenerTestImpl(source);
173         final ErrorListenerTestImpl lstAll = new ErrorListenerTestImpl(source);
174         source.addEventListener(ConfigurationErrorEvent.READ, lstRead);
175         source.addEventListener(ConfigurationErrorEvent.WRITE, lstWrite);
176         source.addEventListener(ConfigurationErrorEvent.ANY, lstAll);
177         final Exception testException = new Exception("A test");
178 
179         source.fireError(ConfigurationErrorEvent.WRITE, ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, testException);
180         lstRead.done();
181         assertEquals(testException,
182                 lstWrite.checkEvent(ConfigurationErrorEvent.WRITE, ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE));
183         lstWrite.done();
184         assertEquals(testException,
185                 lstAll.checkEvent(ConfigurationErrorEvent.WRITE, ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE));
186         lstAll.done();
187         assertEquals(1, source.errorCount);
188     }
189 
190     /**
191      * Tests firing an error event if there are no error listeners.
192      */
193     @Test
194     public void testFireErrorNoListeners() {
195         source.fireError(ConfigurationErrorEvent.ANY, ConfigurationEvent.ANY, TEST_PROPNAME, TEST_PROPVALUE, new Exception());
196         assertEquals(0, source.errorCount);
197     }
198 
199     /**
200      * Tests delivering an event to a listener.
201      */
202     @Test
203     public void testFireEvent() {
204         final EventListenerTestImpl l = new EventListenerTestImpl(source);
205         source.addEventListener(ConfigurationEvent.ANY, l);
206         source.fireEvent(ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, true);
207         l.checkEvent(ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, true);
208         l.done();
209     }
210 
211     /**
212      * Tests generating a detail event if detail events are not allowed.
213      */
214     @Test
215     public void testFireEventNoDetails() {
216         final EventListenerTestImpl l = new EventListenerTestImpl(source);
217         source.addEventListener(ConfigurationEvent.ANY, l);
218         source.setDetailEvents(false);
219         source.fireEvent(ConfigurationEvent.SET_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, false);
220         assertEquals(0, source.eventCount);
221         l.done();
222     }
223 
224     /**
225      * Tests firing an event if there are no listeners.
226      */
227     @Test
228     public void testFireEventNoListeners() {
229         source.fireEvent(ConfigurationEvent.ADD_NODES, TEST_PROPNAME, TEST_PROPVALUE, false);
230         assertEquals(0, source.eventCount);
231     }
232 
233     /**
234      * Tests that the collection returned by getEventListeners() is really a snapshot. A later added listener must not be
235      * visible.
236      */
237     @Test
238     public void testGetEventListenersAddNew() {
239         final Collection<EventListener<? super ConfigurationEvent>> list = source.getEventListeners(ConfigurationEvent.ANY);
240         source.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null));
241         assertTrue(list.isEmpty());
242     }
243 
244     /**
245      * Tests whether the listeners list is read only.
246      */
247     @Test
248     public void testGetEventListenersUpdate() {
249         source.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null));
250         final Collection<EventListener<? super ConfigurationEvent>> list = source.getEventListeners(ConfigurationEvent.ANY);
251         assertThrows(UnsupportedOperationException.class, list::clear);
252     }
253 
254     /**
255      * Tests a newly created source object.
256      */
257     @Test
258     public void testInit() {
259         assertTrue(source.getEventListenerRegistrations().isEmpty());
260         assertFalse(source.removeEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null)));
261         assertFalse(source.isDetailEvents());
262     }
263 
264     /**
265      * Tests removing a listener.
266      */
267     @Test
268     public void testRemoveEventListener() {
269         final EventListenerTestImpl l = new EventListenerTestImpl(this);
270         assertFalse(source.removeEventListener(ConfigurationEvent.ANY, l));
271         source.addEventListener(ConfigurationEvent.ADD_NODES, new EventListenerTestImpl(this));
272         source.addEventListener(ConfigurationEvent.ANY, l);
273         assertFalse(source.removeEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null)));
274         assertTrue(source.removeEventListener(ConfigurationEvent.ANY, l));
275         assertFalse(source.getEventListeners(ConfigurationEvent.ANY).contains(l));
276     }
277 
278     /**
279      * Tests whether an event listener can deregister itself in reaction of a delivered event.
280      */
281     @Test
282     public void testRemoveListenerInFireEvent() {
283         final EventListener<ConfigurationEvent> lstRemove = new EventListener<ConfigurationEvent>() {
284             @Override
285             public void onEvent(final ConfigurationEvent event) {
286                 source.removeEventListener(ConfigurationEvent.ANY, this);
287             }
288         };
289 
290         source.addEventListener(ConfigurationEvent.ANY, lstRemove);
291         final EventListenerTestImpl l = new EventListenerTestImpl(source);
292         source.addEventListener(ConfigurationEvent.ANY, l);
293         source.fireEvent(ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, false);
294         l.checkEvent(ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, false);
295         assertEquals(1, source.getEventListeners(ConfigurationEvent.ANY).size());
296     }
297 
298     /**
299      * Tests if a null listener can be removed. This should be a no-op.
300      */
301     @Test
302     public void testRemoveNullEventListener() {
303         source.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null));
304         assertFalse(source.removeEventListener(ConfigurationEvent.ANY, null));
305         assertEquals(1, source.getEventListeners(ConfigurationEvent.ANY).size());
306     }
307 
308     /**
309      * Tests enabling and disabling the detail events flag.
310      */
311     @Test
312     public void testSetDetailEvents() {
313         source.setDetailEvents(true);
314         assertTrue(source.isDetailEvents());
315         source.setDetailEvents(true);
316         source.setDetailEvents(false);
317         assertTrue(source.isDetailEvents());
318         source.setDetailEvents(false);
319         assertFalse(source.isDetailEvents());
320     }
321 }