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