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.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertNull;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  import static org.mockito.Mockito.mock;
26  
27  import java.util.Arrays;
28  import java.util.Iterator;
29  import java.util.LinkedList;
30  import java.util.List;
31  import java.util.NoSuchElementException;
32  
33  import org.junit.jupiter.api.BeforeAll;
34  import org.junit.jupiter.api.BeforeEach;
35  import org.junit.jupiter.api.Test;
36  
37  /**
38   * Test class for {@code EventListenerList.}
39   */
40  public class TestEventListenerList {
41      /**
42       * Test event class. For testing purposes, a small hierarchy of test event class is created. This way it can be checked
43       * whether event types are correctly evaluated and take the event hierarchy into account.
44       */
45      private static class EventBase extends Event {
46          private static final long serialVersionUID = 1L;
47  
48          /** An event message for testing pay-load. */
49          private final String message;
50  
51          public EventBase(final Object source, final EventType<? extends EventBase> type, final String msg) {
52              super(source, type);
53              message = msg;
54          }
55  
56          public String getMessage() {
57              return message;
58          }
59      }
60  
61      /**
62       * A test event class derived from the base test event class.
63       */
64      private static final class EventSub1 extends EventBase {
65          private static final long serialVersionUID = 1L;
66  
67          public EventSub1(final Object source, final EventType<? extends EventSub1> type, final String msg) {
68              super(source, type, msg);
69          }
70      }
71  
72      /**
73       * Another test event class derived from the base class.
74       */
75      private static final class EventSub2 extends EventBase {
76          private static final long serialVersionUID = 1L;
77  
78          public EventSub2(final Object source, final EventType<? extends EventSub2> type, final String msg) {
79              super(source, type, msg);
80          }
81      }
82  
83      /**
84       * A test event listener implementation. This listener class expects that it receives at most a single event. This event
85       * is stored for further evaluation.
86       */
87      private static final class ListenerTestImpl implements EventListener<EventBase> {
88          /** The event received by this object. */
89          private EventBase receivedEvent;
90  
91          /**
92           * Checks that this listener has received an event with the expected properties.
93           *
94           * @param expSource the expected source
95           * @param expType the expected type
96           * @param expMessage the expected message
97           */
98          public void assertEvent(final Object expSource, final EventType<?> expType, final String expMessage) {
99              assertNotNull(receivedEvent);
100             assertEquals(expSource, receivedEvent.getSource());
101             assertEquals(expType, receivedEvent.getEventType());
102             assertEquals(expMessage, receivedEvent.getMessage());
103         }
104 
105         /**
106          * Checks that this listener has not received any event.
107          */
108         public void assertNoEvent() {
109             assertNull(receivedEvent);
110         }
111 
112         @Override
113         public void onEvent(final EventBase event) {
114             assertNull(receivedEvent, "Too many events: " + event);
115             receivedEvent = event;
116         }
117     }
118 
119     /** Constant for a test event message. */
120     private static final String MESSAGE = "TestEventMessage";
121 
122     /** Type for the base event. */
123     private static EventType<EventBase> typeBase;
124 
125     /** Type for sub event 1. */
126     private static EventType<EventSub1> typeSub1;
127 
128     /** Type for sub event 2. */
129     private static EventType<EventSub2> typeSub2;
130 
131     /**
132      * Helper method for collecting the elements in the given iterable.
133      *
134      * @param iterable the iterable
135      * @return a list with the content of the iterable
136      */
137     private static <T> List<T> fetchElements(final Iterable<? extends T> iterable) {
138         final List<T> elems = new LinkedList<>();
139         for (final T listener : iterable) {
140             elems.add(listener);
141         }
142         return elems;
143     }
144 
145     @BeforeAll
146     public static void setUpBeforeClass() throws Exception {
147         typeBase = new EventType<>(Event.ANY, "BASE");
148         typeSub1 = new EventType<>(typeBase, "SUB1");
149         typeSub2 = new EventType<>(typeBase, "SUB2");
150     }
151 
152     /** The list to be tested. */
153     private EventListenerList list;
154 
155     /**
156      * Helper method for checking whether a specific set of event listeners is returned by getEventListeners().
157      *
158      * @param eventType the event type
159      * @param expListeners the expected listeners
160      */
161     private void checkEventListenersForType(final EventType<? extends Event> eventType, final EventListener<?>... expListeners) {
162         final List<?> listeners = fetchElements(list.getEventListeners(eventType));
163         assertEquals(Arrays.asList(expListeners), listeners);
164     }
165 
166     @BeforeEach
167     public void setUp() throws Exception {
168         list = new EventListenerList();
169     }
170 
171     /**
172      * Tests whether the content of another list can be added.
173      */
174     @Test
175     public void testAddAll() {
176         final EventListener<EventBase> l1 = new ListenerTestImpl();
177         final EventListener<EventBase> l2 = new ListenerTestImpl();
178         final EventListener<EventBase> l3 = new ListenerTestImpl();
179         list.addEventListener(typeBase, l1);
180         final EventListenerList list2 = new EventListenerList();
181         list2.addEventListener(typeSub1, l2);
182         list2.addEventListener(typeBase, l3);
183 
184         list.addAll(list2);
185         final Iterator<EventListenerRegistrationData<?>> it = list.getRegistrations().iterator();
186         EventListenerRegistrationData<?> reg = it.next();
187         assertEquals(typeBase, reg.getEventType());
188         assertEquals(l1, reg.getListener());
189         reg = it.next();
190         assertEquals(typeSub1, reg.getEventType());
191         assertEquals(l2, reg.getListener());
192         reg = it.next();
193         assertEquals(typeBase, reg.getEventType());
194         assertEquals(l3, reg.getListener());
195     }
196 
197     /**
198      * Tries to add the content of a null list.
199      */
200     @Test
201     public void testAddAllNull() {
202         assertThrows(IllegalArgumentException.class, () -> list.addAll(null));
203     }
204 
205     /**
206      * Tests whether the list can be cleared.
207      */
208     @Test
209     public void testClear() {
210         list.addEventListener(typeSub1, new ListenerTestImpl());
211         list.addEventListener(typeSub2, new ListenerTestImpl());
212 
213         list.clear();
214         assertTrue(list.getRegistrations().isEmpty());
215     }
216 
217     /**
218      * Tests that a null event is handled by the iterator.
219      */
220     @Test
221     public void testEventListenerIteratorNullEvent() {
222         list.addEventListener(typeBase, new ListenerTestImpl());
223         final EventListenerList.EventListenerIterator<EventBase> iterator = list.getEventListenerIterator(typeBase);
224         assertThrows(IllegalArgumentException.class, () -> iterator.invokeNext(null));
225     }
226 
227     /**
228      * Tests whether the event listener iterator validates the passed in event object.
229      */
230     @Test
231     public void testEventListenerIteratorWrongEvent() {
232         final EventListener<EventSub2> listener = event -> {};
233         list.addEventListener(typeSub2, listener);
234         final EventListenerList.EventListenerIterator<EventSub2> iterator = list.getEventListenerIterator(typeSub2);
235         assertTrue(iterator.hasNext());
236         final Event event = new EventBase(this, typeBase, "Test");
237         assertThrows(IllegalArgumentException.class, () -> iterator.invokeNext(event));
238     }
239 
240     /**
241      * Tests that a null event is rejected by fire().
242      */
243     @Test
244     public void testFireNullEvent() {
245         assertThrows(IllegalArgumentException.class, () -> list.fire(null));
246     }
247 
248     /**
249      * Tests whether event listener registrations derived from a super type can be queried.
250      */
251     @Test
252     public void testGetEventListenerRegistrationsForSuperType() {
253         final ListenerTestImpl l1 = new ListenerTestImpl();
254         final ListenerTestImpl l2 = new ListenerTestImpl();
255         @SuppressWarnings("unchecked")
256         final EventListener<Event> l3 = mock(EventListener.class);
257         list.addEventListener(typeSub1, l1);
258         list.addEventListener(Event.ANY, l3);
259         list.addEventListener(typeBase, l2);
260 
261         final List<EventListenerRegistrationData<? extends EventBase>> regs = list.getRegistrationsForSuperType(typeBase);
262         final Iterator<EventListenerRegistrationData<? extends EventBase>> iterator = regs.iterator();
263         assertEquals(l1, iterator.next().getListener());
264         assertEquals(l2, iterator.next().getListener());
265         assertFalse(iterator.hasNext());
266     }
267 
268     /**
269      * Tests whether the base type is taken into account when querying for event listeners.
270      */
271     @Test
272     public void testGetEventListenersBaseType() {
273         final ListenerTestImpl listener1 = new ListenerTestImpl();
274         final ListenerTestImpl listener2 = new ListenerTestImpl();
275         list.addEventListener(typeBase, listener1);
276         list.addEventListener(typeBase, listener2);
277         checkEventListenersForType(typeSub1, listener1, listener2);
278     }
279 
280     /**
281      * Tests that the iterator returned by getEventListeners() throws an exception if the iteration goes beyond the last
282      * element.
283      */
284     @Test
285     public void testGetEventListenersIteratorNextNoElement() {
286         final ListenerTestImpl listener1 = new ListenerTestImpl();
287         final ListenerTestImpl listener2 = new ListenerTestImpl();
288         list.addEventListener(typeBase, listener1);
289         list.addEventListener(typeBase, listener2);
290         final Iterator<EventListener<? super EventBase>> iterator = list.getEventListeners(typeBase).iterator();
291         for (int i = 0; i < 2; i++) {
292             iterator.next();
293         }
294         assertThrows(NoSuchElementException.class, iterator::next);
295     }
296 
297     /**
298      * Tests that the iterator returned by getEventListeners() does not support remove() operations.
299      */
300     @Test
301     public void testGetEventListenersIteratorRemove() {
302         list.addEventListener(typeBase, new ListenerTestImpl());
303         final Iterator<EventListener<? super EventBase>> iterator = list.getEventListeners(typeBase).iterator();
304         assertTrue(iterator.hasNext());
305         assertThrows(UnsupportedOperationException.class, iterator::remove);
306     }
307 
308     /**
309      * Tests whether only matching event listeners are returned by getEventListeners().
310      */
311     @Test
312     public void testGetEventListenersMatchingType() {
313         final ListenerTestImpl listener1 = new ListenerTestImpl();
314         final ListenerTestImpl listener2 = new ListenerTestImpl();
315         list.addEventListener(typeSub1, listener1);
316         list.addEventListener(typeSub2, listener2);
317         checkEventListenersForType(typeSub1, listener1);
318     }
319 
320     /**
321      * Tests that an empty result is correctly handled by getEventListeners().
322      */
323     @Test
324     public void testGetEventListenersNoMatch() {
325         list.addEventListener(typeSub1, new ListenerTestImpl());
326         checkEventListenersForType(typeSub2);
327     }
328 
329     /**
330      * Tests whether event listeners for a null type can be queried.
331      */
332     @Test
333     public void testGetEventListenersNull() {
334         assertTrue(fetchElements(list.getEventListeners(null)).isEmpty());
335     }
336 
337     /**
338      * Tests whether all event listener registrations can be queried.
339      */
340     @Test
341     public void testGetRegistrations() {
342         final EventListenerRegistrationData<EventSub1> reg1 = new EventListenerRegistrationData<>(typeSub1, new ListenerTestImpl());
343         final EventListenerRegistrationData<EventSub2> reg2 = new EventListenerRegistrationData<>(typeSub2, new ListenerTestImpl());
344         list.addEventListener(reg1);
345         list.addEventListener(reg2);
346 
347         final List<EventListenerRegistrationData<?>> registrations = list.getRegistrations();
348         assertEquals(Arrays.asList(reg1, reg2), registrations);
349     }
350 
351     /**
352      * Tests that the list with registration information cannot be modified.
353      */
354     @Test
355     public void testGetRegistrationsModify() {
356         final EventListenerRegistrationData<EventBase> registrationData = new EventListenerRegistrationData<>(typeBase, new ListenerTestImpl());
357         final List<EventListenerRegistrationData<?>> registrations = list.getRegistrations();
358         assertThrows(UnsupportedOperationException.class, () -> registrations.add(registrationData));
359     }
360 
361     /**
362      * Tests whether an event listener can be registered via a registration data object.
363      */
364     @Test
365     public void testListenerRegistrationWithListenerData() {
366         final ListenerTestImpl listener = new ListenerTestImpl();
367         final EventListenerRegistrationData<EventSub1> regData = new EventListenerRegistrationData<>(typeSub1, listener);
368         list.addEventListener(regData);
369 
370         list.fire(new EventSub1(this, typeSub1, MESSAGE));
371         listener.assertEvent(this, typeSub1, MESSAGE);
372     }
373 
374     /**
375      * Tries to register a listener with a null registration data object.
376      */
377     @Test
378     public void testListenerRegistrationWithNullListenerData() {
379         assertThrows(IllegalArgumentException.class, () -> list.addEventListener(null));
380     }
381 
382     /**
383      * Tests that a listener can be registered multiple times for different event types.
384      */
385     @Test
386     public void testMultipleListenerRegistration() {
387         final ListenerTestImpl listener = new ListenerTestImpl();
388         list.addEventListener(typeSub1, listener);
389         list.addEventListener(typeSub2, listener);
390 
391         list.fire(new EventSub2(this, typeSub2, MESSAGE));
392         list.removeEventListener(typeSub1, listener);
393         list.fire(new EventSub1(this, typeSub1, MESSAGE));
394         listener.assertEvent(this, typeSub2, MESSAGE);
395     }
396 
397     /**
398      * Tests whether the event type is taken into account when calling listeners.
399      */
400     @Test
401     public void testReceiveEventDifferentType() {
402         final ListenerTestImpl listener1 = new ListenerTestImpl();
403         final ListenerTestImpl listener2 = new ListenerTestImpl();
404         list.addEventListener(typeSub1, listener1);
405         list.addEventListener(typeSub2, listener2);
406 
407         list.fire(new EventSub1(this, typeSub1, MESSAGE));
408         listener1.assertEvent(this, typeSub1, MESSAGE);
409         listener2.assertNoEvent();
410     }
411 
412     /**
413      * Tests whether multiple event listeners can be registered.
414      */
415     @Test
416     public void testReceiveEventMultipleListeners() {
417         final ListenerTestImpl listener1 = new ListenerTestImpl();
418         final ListenerTestImpl listener2 = new ListenerTestImpl();
419         list.addEventListener(typeSub1, listener1);
420         list.addEventListener(typeSub1, listener2);
421 
422         list.fire(new EventSub1(this, typeSub1, MESSAGE));
423         listener1.assertEvent(this, typeSub1, MESSAGE);
424         listener2.assertEvent(this, typeSub1, MESSAGE);
425     }
426 
427     /**
428      * Tests whether events matching the registration type are delivered.
429      */
430     @Test
431     public void testReceiveEventOfExactType() {
432         final ListenerTestImpl listener = new ListenerTestImpl();
433         list.addEventListener(typeSub1, listener);
434 
435         list.fire(new EventSub1(this, typeSub1, MESSAGE));
436         listener.assertEvent(this, typeSub1, MESSAGE);
437     }
438 
439     /**
440      * Tests that events of a derived type are delivered to listeners registered for a base type.
441      */
442     @Test
443     public void testReceiveEventSubType() {
444         final ListenerTestImpl listener = new ListenerTestImpl();
445         list.addEventListener(typeBase, listener);
446 
447         list.fire(new EventSub1(this, typeSub1, MESSAGE));
448         listener.assertEvent(this, typeSub1, MESSAGE);
449     }
450 
451     /**
452      * Tries to register a listener for a null event type.
453      */
454     @Test
455     public void testRegisterEventTypeNull() {
456         final ListenerTestImpl listener = new ListenerTestImpl();
457         assertThrows(IllegalArgumentException.class, () -> list.addEventListener(null, listener));
458     }
459 
460     /**
461      * Tests that null event listeners cannot be registered.
462      */
463     @Test
464     public void testRegisterListenerNull() {
465         assertThrows(IllegalArgumentException.class, () -> list.addEventListener(typeBase, null));
466     }
467 
468     /**
469      * Tests whether an event listener can be removed.
470      */
471     @Test
472     public void testRemoveEventListenerExisting() {
473         final ListenerTestImpl listener = new ListenerTestImpl();
474         list.addEventListener(typeSub1, listener);
475 
476         assertTrue(list.removeEventListener(typeSub1, listener));
477         list.fire(new EventSub1(this, typeSub1, MESSAGE));
478         listener.assertNoEvent();
479     }
480 
481     /**
482      * Tests removeEventListener() if another event type is specified for an existing listener.
483      */
484     @Test
485     public void testRemoveEventListenerNonExistingEventType() {
486         final ListenerTestImpl listener = new ListenerTestImpl();
487         list.addEventListener(typeSub1, listener);
488 
489         assertFalse(list.removeEventListener(typeBase, listener));
490     }
491 
492     /**
493      * Tests removeEventListener() for a non-existing event listener.
494      */
495     @Test
496     public void testRemoveEventListenerNonExistingListener() {
497         list.addEventListener(typeBase, new ListenerTestImpl());
498         assertFalse(list.removeEventListener(typeBase, new ListenerTestImpl()));
499     }
500 
501     /**
502      * Tests that removeEventListener() can handle a null listener.
503      */
504     @Test
505     public void testRemoveEventListenerNullListener() {
506         assertFalse(list.removeEventListener(typeBase, null));
507     }
508 
509     /**
510      * Tests that removeEventListener() can handle a null registration object.
511      */
512     @Test
513     public void testRemoveEventListenerNullRegistration() {
514         assertFalse(list.removeEventListener(null));
515     }
516 
517     /**
518      * Tests that removeEventListener() can handle a null event type.
519      */
520     @Test
521     public void testRemoveEventListenerNullType() {
522         assertFalse(list.removeEventListener(null, new ListenerTestImpl()));
523     }
524 
525     /**
526      * Tests that events of a base type do not cause a listener to be invoked.
527      */
528     @Test
529     public void testSuppressEventOfSuperType() {
530         final ListenerTestImpl listener = new ListenerTestImpl();
531         list.addEventListener(typeSub1, listener);
532 
533         list.fire(new EventBase(this, typeBase, MESSAGE));
534         listener.assertNoEvent();
535     }
536 }