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