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  
42          private int eventCount;
43  
44          private int errorCount;
45  
46          @Override
47          protected ConfigurationErrorEvent createErrorEvent(final EventType<? extends ConfigurationErrorEvent> type, final EventType<?> opType,
48              final String propName, final Object propValue, final Throwable ex) {
49              errorCount++;
50              return super.createErrorEvent(type, opType, propName, propValue, ex);
51          }
52  
53          @Override
54          protected <T extends ConfigurationEvent> ConfigurationEvent createEvent(final EventType<T> eventType, final String propName, final Object propValue,
55              final boolean before) {
56              eventCount++;
57              return super.createEvent(eventType, propName, propValue, before);
58          }
59      }
60  
61      /** Constant for the event property value. */
62      private static final Object TEST_PROPVALUE = "a test property value";
63  
64      /** Constant for the event property name. */
65      private static final String TEST_PROPNAME = "test.property.name";
66  
67      /** The object under test. */
68      private CountingEventSource source;
69  
70      @BeforeEach
71      public void setUp() throws Exception {
72          source = new CountingEventSource();
73      }
74  
75      /**
76       * Tests registering a new listener.
77       */
78      @Test
79      public void testAddEventListener() {
80          final EventListenerTestImpl l = new EventListenerTestImpl(this);
81          source.addEventListener(ConfigurationEvent.ANY, l);
82          final Collection<EventListener<? super ConfigurationEvent>> listeners = source.getEventListeners(ConfigurationEvent.ANY);
83          assertEquals(1, listeners.size());
84          assertTrue(listeners.contains(l));
85      }
86  
87      /**
88       * Tests adding an undefined configuration listener. This should cause an exception.
89       */
90      @Test
91      public void testAddNullEventListener() {
92          assertThrows(IllegalArgumentException.class, () -> source.addEventListener(ConfigurationEvent.ANY, null));
93      }
94  
95      /**
96       * Tests whether all error listeners can be cleared.
97       */
98      @Test
99      public void testClearErrorListeners() {
100         final EventListener<ConfigurationEvent> cl = new EventListenerTestImpl(null);
101         final ErrorListenerTestImpl el1 = new ErrorListenerTestImpl(null);
102         final ErrorListenerTestImpl el2 = new ErrorListenerTestImpl(null);
103         final ErrorListenerTestImpl el3 = new ErrorListenerTestImpl(null);
104         source.addEventListener(ConfigurationErrorEvent.READ, el1);
105         source.addEventListener(ConfigurationErrorEvent.ANY, el2);
106         source.addEventListener(ConfigurationEvent.ANY, cl);
107         source.addEventListener(ConfigurationErrorEvent.WRITE, el3);
108 
109         source.clearErrorListeners();
110         final List<EventListenerRegistrationData<?>> regs = source.getEventListenerRegistrations();
111         assertEquals(1, regs.size());
112         assertSame(cl, regs.get(0).getListener());
113     }
114 
115     /**
116      * Tests whether all event listeners can be removed.
117      */
118     @Test
119     public void testClearEventListeners() {
120         source.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(source));
121         source.addEventListener(ConfigurationEvent.ANY_HIERARCHICAL, new EventListenerTestImpl(source));
122 
123         source.clearEventListeners();
124         assertTrue(source.getEventListeners(ConfigurationEvent.ANY).isEmpty());
125         assertTrue(source.getEventListeners(ConfigurationEvent.ANY_HIERARCHICAL).isEmpty());
126     }
127 
128     /**
129      * Tests cloning an event source object. The registered listeners should not be registered at the clone.
130      */
131     @Test
132     public void testClone() throws CloneNotSupportedException {
133         source.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(source));
134         final BaseEventSource copy = (BaseEventSource) source.clone();
135         assertTrue(copy.getEventListenerRegistrations().isEmpty());
136     }
137 
138     /**
139      * Tests whether event listeners can be copied to another source.
140      */
141     @Test
142     public void testCopyEventListeners() {
143         final EventListenerTestImpl l1 = new EventListenerTestImpl(source);
144         final EventListenerTestImpl l2 = new EventListenerTestImpl(source);
145         source.addEventListener(ConfigurationEvent.ANY, l1);
146         source.addEventListener(ConfigurationEvent.ANY_HIERARCHICAL, l2);
147 
148         final BaseEventSource source2 = new BaseEventSource();
149         source.copyEventListeners(source2);
150         Collection<EventListener<? super ConfigurationEvent>> listeners = source2.getEventListeners(ConfigurationEvent.ANY_HIERARCHICAL);
151         assertEquals(2, listeners.size());
152         assertTrue(listeners.contains(l1));
153         assertTrue(listeners.contains(l2));
154         listeners = source2.getEventListeners(ConfigurationEvent.ANY);
155         assertEquals(1, listeners.size());
156         assertTrue(listeners.contains(l1));
157     }
158 
159     /**
160      * Tries to copy event listeners to a null source.
161      */
162     @Test
163     public void testCopyEventListenersNullSource() {
164         assertThrows(IllegalArgumentException.class, () -> source.copyEventListeners(null));
165     }
166 
167     /**
168      * Tests delivering an error event to a listener.
169      */
170     @Test
171     public void testFireError() {
172         final ErrorListenerTestImpl lstRead = new ErrorListenerTestImpl(source);
173         final ErrorListenerTestImpl lstWrite = new ErrorListenerTestImpl(source);
174         final ErrorListenerTestImpl lstAll = new ErrorListenerTestImpl(source);
175         source.addEventListener(ConfigurationErrorEvent.READ, lstRead);
176         source.addEventListener(ConfigurationErrorEvent.WRITE, lstWrite);
177         source.addEventListener(ConfigurationErrorEvent.ANY, lstAll);
178         final Exception testException = new Exception("A test");
179 
180         source.fireError(ConfigurationErrorEvent.WRITE, ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, testException);
181         lstRead.done();
182         assertEquals(testException,
183                 lstWrite.checkEvent(ConfigurationErrorEvent.WRITE, ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE));
184         lstWrite.done();
185         assertEquals(testException,
186                 lstAll.checkEvent(ConfigurationErrorEvent.WRITE, ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE));
187         lstAll.done();
188         assertEquals(1, source.errorCount);
189     }
190 
191     /**
192      * Tests firing an error event if there are no error listeners.
193      */
194     @Test
195     public void testFireErrorNoListeners() {
196         source.fireError(ConfigurationErrorEvent.ANY, ConfigurationEvent.ANY, TEST_PROPNAME, TEST_PROPVALUE, new Exception());
197         assertEquals(0, source.errorCount);
198     }
199 
200     /**
201      * Tests delivering an event to a listener.
202      */
203     @Test
204     public void testFireEvent() {
205         final EventListenerTestImpl l = new EventListenerTestImpl(source);
206         source.addEventListener(ConfigurationEvent.ANY, l);
207         source.fireEvent(ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, true);
208         l.checkEvent(ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, true);
209         l.done();
210     }
211 
212     /**
213      * Tests generating a detail event if detail events are not allowed.
214      */
215     @Test
216     public void testFireEventNoDetails() {
217         final EventListenerTestImpl l = new EventListenerTestImpl(source);
218         source.addEventListener(ConfigurationEvent.ANY, l);
219         source.setDetailEvents(false);
220         source.fireEvent(ConfigurationEvent.SET_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, false);
221         assertEquals(0, source.eventCount);
222         l.done();
223     }
224 
225     /**
226      * Tests firing an event if there are no listeners.
227      */
228     @Test
229     public void testFireEventNoListeners() {
230         source.fireEvent(ConfigurationEvent.ADD_NODES, TEST_PROPNAME, TEST_PROPVALUE, false);
231         assertEquals(0, source.eventCount);
232     }
233 
234     /**
235      * Tests that the collection returned by getEventListeners() is really a snapshot. A later added listener must not be
236      * visible.
237      */
238     @Test
239     public void testGetEventListenersAddNew() {
240         final Collection<EventListener<? super ConfigurationEvent>> list = source.getEventListeners(ConfigurationEvent.ANY);
241         source.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null));
242         assertTrue(list.isEmpty());
243     }
244 
245     /**
246      * Tests whether the listeners list is read only.
247      */
248     @Test
249     public void testGetEventListenersUpdate() {
250         source.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null));
251         final Collection<EventListener<? super ConfigurationEvent>> list = source.getEventListeners(ConfigurationEvent.ANY);
252         assertThrows(UnsupportedOperationException.class, list::clear);
253     }
254 
255     /**
256      * Tests a newly created source object.
257      */
258     @Test
259     public void testInit() {
260         assertTrue(source.getEventListenerRegistrations().isEmpty());
261         assertFalse(source.removeEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null)));
262         assertFalse(source.isDetailEvents());
263     }
264 
265     /**
266      * Tests removing a listener.
267      */
268     @Test
269     public void testRemoveEventListener() {
270         final EventListenerTestImpl l = new EventListenerTestImpl(this);
271         assertFalse(source.removeEventListener(ConfigurationEvent.ANY, l));
272         source.addEventListener(ConfigurationEvent.ADD_NODES, new EventListenerTestImpl(this));
273         source.addEventListener(ConfigurationEvent.ANY, l);
274         assertFalse(source.removeEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null)));
275         assertTrue(source.removeEventListener(ConfigurationEvent.ANY, l));
276         assertFalse(source.getEventListeners(ConfigurationEvent.ANY).contains(l));
277     }
278 
279     /**
280      * Tests whether an event listener can deregister itself in reaction of a delivered event.
281      */
282     @Test
283     public void testRemoveListenerInFireEvent() {
284         final EventListener<ConfigurationEvent> lstRemove = new EventListener<ConfigurationEvent>() {
285             @Override
286             public void onEvent(final ConfigurationEvent event) {
287                 source.removeEventListener(ConfigurationEvent.ANY, this);
288             }
289         };
290 
291         source.addEventListener(ConfigurationEvent.ANY, lstRemove);
292         final EventListenerTestImpl l = new EventListenerTestImpl(source);
293         source.addEventListener(ConfigurationEvent.ANY, l);
294         source.fireEvent(ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, false);
295         l.checkEvent(ConfigurationEvent.ADD_PROPERTY, TEST_PROPNAME, TEST_PROPVALUE, false);
296         assertEquals(1, source.getEventListeners(ConfigurationEvent.ANY).size());
297     }
298 
299     /**
300      * Tests if a null listener can be removed. This should be a no-op.
301      */
302     @Test
303     public void testRemoveNullEventListener() {
304         source.addEventListener(ConfigurationEvent.ANY, new EventListenerTestImpl(null));
305         assertFalse(source.removeEventListener(ConfigurationEvent.ANY, null));
306         assertEquals(1, source.getEventListeners(ConfigurationEvent.ANY).size());
307     }
308 
309     /**
310      * Tests enabling and disabling the detail events flag.
311      */
312     @Test
313     public void testSetDetailEvents() {
314         source.setDetailEvents(true);
315         assertTrue(source.isDetailEvents());
316         source.setDetailEvents(true);
317         source.setDetailEvents(false);
318         assertTrue(source.isDetailEvents());
319         source.setDetailEvents(false);
320         assertFalse(source.isDetailEvents());
321     }
322 }