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