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.reloading;
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  import static org.mockito.ArgumentMatchers.any;
25  import static org.mockito.Mockito.doAnswer;
26  import static org.mockito.Mockito.mock;
27  import static org.mockito.Mockito.verify;
28  import static org.mockito.Mockito.verifyNoMoreInteractions;
29  import static org.mockito.Mockito.when;
30  
31  import org.apache.commons.configuration2.event.Event;
32  import org.apache.commons.configuration2.event.EventListener;
33  import org.apache.commons.lang3.mutable.MutableObject;
34  import org.junit.jupiter.api.BeforeEach;
35  import org.junit.jupiter.api.Test;
36  
37  /**
38   * Test class for {@code ReloadingController}.
39   */
40  public class TestReloadingController {
41      /**
42       * Creates a mock event listener.
43       *
44       * @return the mock listener
45       */
46      @SuppressWarnings("unchecked")
47      private static EventListener<ReloadingEvent> createListenerMock() {
48          return mock(EventListener.class);
49      }
50  
51      /** A mock for the detector. */
52      private ReloadingDetector detector;
53  
54      /**
55       * Creates a default test instance.
56       *
57       * @return the test instance
58       */
59      private ReloadingController createController() {
60          return new ReloadingController(detector);
61      }
62  
63      /**
64       * Prepares the given event listener mock to expect an event notification. The event received is stored in the given
65       * mutable object.
66       *
67       * @param l the listener mock
68       * @param evRef the reference where to store the event
69       */
70      private void setupEvent(final EventListener<ReloadingEvent> l, final MutableObject<ReloadingEvent> evRef) {
71          doAnswer(invocation -> {
72              evRef.setValue(invocation.getArgument(0, ReloadingEvent.class));
73              return null;
74          }).when(l).onEvent(any());
75      }
76  
77      /**
78       * Verifies that an invocation has occurred on the given event listener for an event notification.
79       *
80       * @param l the listener mock
81       */
82      private void verifyEvent(final EventListener<ReloadingEvent> l) {
83          verify(l).onEvent(any());
84      }
85  
86      @BeforeEach
87      public void setUp() throws Exception {
88          detector = mock(ReloadingDetector.class);
89      }
90  
91      /**
92       * Tests a reloading check with a negative result.
93       */
94      @Test
95      public void testCheckForReloadingFalse() {
96          final EventListener<ReloadingEvent> l = createListenerMock();
97  
98          when(detector.isReloadingRequired()).thenReturn(Boolean.FALSE);
99  
100         final ReloadingController ctrl = createController();
101         ctrl.addEventListener(ReloadingEvent.ANY, l);
102         assertFalse(ctrl.checkForReloading(null));
103         assertFalse(ctrl.isInReloadingState());
104 
105         verify(detector).isReloadingRequired();
106         verifyNoMoreInteractions(detector, l);
107     }
108 
109     /**
110      * Tests that no further checks are performed when already in reloading state.
111      */
112     @Test
113     public void testCheckForReloadingInReloadingState() {
114         final EventListener<ReloadingEvent> l = createListenerMock();
115 
116         when(detector.isReloadingRequired()).thenReturn(Boolean.TRUE);
117         // No need to setup the event; the event is not captured
118 
119         final ReloadingController ctrl = createController();
120         ctrl.addEventListener(ReloadingEvent.ANY, l);
121         assertTrue(ctrl.checkForReloading(1));
122         assertTrue(ctrl.checkForReloading(2));
123 
124         verify(detector).isReloadingRequired();
125         verifyEvent(l);
126         verifyNoMoreInteractions(detector, l);
127     }
128 
129     /**
130      * Tests a reloading check with a positive result.
131      */
132     @Test
133     public void testCheckForReloadingTrue() {
134         final EventListener<ReloadingEvent> l = createListenerMock();
135         final EventListener<ReloadingEvent> lRemoved = createListenerMock();
136         final MutableObject<ReloadingEvent> evRef = new MutableObject<>();
137 
138         setupEvent(l, evRef);
139         when(detector.isReloadingRequired()).thenReturn(Boolean.TRUE);
140 
141         final ReloadingController ctrl = createController();
142         ctrl.addEventListener(ReloadingEvent.ANY, lRemoved);
143         ctrl.addEventListener(ReloadingEvent.ANY, l);
144         assertTrue(ctrl.removeEventListener(ReloadingEvent.ANY, lRemoved));
145         final Object testData = "Some test data";
146         assertTrue(ctrl.checkForReloading(testData));
147         assertTrue(ctrl.isInReloadingState());
148         assertSame(ctrl, evRef.getValue().getSource());
149         assertSame(ctrl, evRef.getValue().getController());
150         assertEquals(testData, evRef.getValue().getData());
151 
152         verifyEvent(l);
153         verify(detector).isReloadingRequired();
154         verifyNoMoreInteractions(l, lRemoved, detector);
155     }
156 
157     /**
158      * Tries to create an instance without a detector.
159      */
160     @Test
161     public void testInitNoDetector() {
162         assertThrows(IllegalArgumentException.class, () -> new ReloadingController(null));
163     }
164 
165     /**
166      * Tests the event type of the reloading event.
167      */
168     @Test
169     public void testReloadingEventType() {
170         assertEquals(Event.ANY, ReloadingEvent.ANY.getSuperType());
171     }
172 
173     /**
174      * Tests that a newly created instance is not in reloading state.
175      */
176     @Test
177     public void testReloadingStateAfterInit() {
178         assertFalse(createController().isInReloadingState());
179     }
180 
181     /**
182      * Tests that resetReloadingState() has no effect if the controller is not in reloading state.
183      */
184     @Test
185     public void testResetReloadingNotInReloadingState() {
186         createController().resetReloadingState();
187     }
188 
189     /**
190      * Tests that the reloading state can be reset.
191      */
192     @Test
193     public void testResetReloadingState() {
194         when(detector.isReloadingRequired()).thenReturn(Boolean.TRUE);
195 
196         final ReloadingController ctrl = createController();
197         ctrl.checkForReloading(null);
198         ctrl.resetReloadingState();
199         assertFalse(ctrl.isInReloadingState());
200 
201         verify(detector).isReloadingRequired();
202         verify(detector).reloadingPerformed();
203         verifyNoMoreInteractions(detector);
204     }
205 }