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.io;
18  
19  import static org.junit.jupiter.api.Assertions.assertIterableEquals;
20  import static org.junit.jupiter.api.Assertions.assertNull;
21  import static org.junit.jupiter.api.Assertions.assertSame;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.mockito.Mockito.mock;
24  import static org.mockito.Mockito.verify;
25  import static org.mockito.Mockito.verifyNoMoreInteractions;
26  import static org.mockito.Mockito.when;
27  
28  import java.net.URL;
29  import java.util.ArrayList;
30  import java.util.Arrays;
31  import java.util.Collection;
32  import java.util.LinkedList;
33  import java.util.Objects;
34  
35  import org.apache.commons.configuration2.ConfigurationAssert;
36  import org.junit.jupiter.api.BeforeAll;
37  import org.junit.jupiter.api.Test;
38  
39  /**
40   * Test class for {@code CombinedLocationStrategy}.
41   */
42  public class TestCombinedLocationStrategy {
43  
44      /** A test locator. */
45      private static FileLocator locator;
46  
47      /** A URL indicating a successful locate() operation. */
48      private static URL locateURL;
49  
50      @BeforeAll
51      public static void setUpOnce() throws Exception {
52          locator = FileLocatorUtils.fileLocator().fileName("testFile.tst").create();
53          locateURL = ConfigurationAssert.getTestURL("test.xml");
54      }
55  
56      /** A mock for the file system. */
57      private FileSystem fileSystem;
58  
59      /** An array with mock sub strategies. */
60      private FileLocationStrategy[] subStrategies;
61  
62      /**
63       * Checks whether the passed in combined strategy contains the expected sub strategies.
64       *
65       * @param strategy the combined strategy to check
66       */
67      private void checkSubStrategies(final CombinedLocationStrategy strategy) {
68          final Collection<FileLocationStrategy> subs = strategy.getSubStrategies();
69          assertIterableEquals(Arrays.asList(getSubStrategies()), subs);
70      }
71  
72      /**
73       * Helper method for creating a combined strategy with the mock sub strategies.
74       *
75       * @return the newly created combined strategy
76       */
77      private CombinedLocationStrategy createCombinedStrategy() {
78          return new CombinedLocationStrategy(Arrays.asList(getSubStrategies()));
79      }
80  
81      /**
82       * Returns the mock file system. It is created on demand.
83       *
84       * @return the mock file system
85       */
86      private FileSystem getFileSystem() {
87          if (fileSystem == null) {
88              fileSystem = mock(FileSystem.class);
89          }
90          return fileSystem;
91      }
92  
93      /**
94       * Returns an array with mock objects for sub strategies.
95       *
96       * @return the array with mock strategies
97       */
98      private FileLocationStrategy[] getSubStrategies() {
99          if (subStrategies == null) {
100             subStrategies = new FileLocationStrategy[2];
101             for (int i = 0; i < subStrategies.length; i++) {
102                 subStrategies[i] = mock(FileLocationStrategy.class);
103             }
104         }
105         return subStrategies;
106     }
107 
108     /**
109      * Tests that the collection with sub strategies cannot be modified.
110      */
111     @Test
112     void testGetSubStrategiesModify() {
113         final CombinedLocationStrategy strategy = createCombinedStrategy();
114         final Collection<FileLocationStrategy> strategies = strategy.getSubStrategies();
115         assertThrows(UnsupportedOperationException.class, strategies::clear);
116     }
117 
118     /**
119      * Tries to create an instance containing a null element.
120      */
121     @Test
122     void testInitCollectionWithNullEntries() {
123         final Collection<FileLocationStrategy> col = new LinkedList<>(Arrays.asList(getSubStrategies()));
124         col.add(null);
125         assertThrows(IllegalArgumentException.class, () -> new CombinedLocationStrategy(col));
126     }
127 
128     /**
129      * Tests that the constructor handles collections that throw NPE on contains(null) (like ImmutableList).
130      */
131     @Test
132     void testInitCollectionThrowsNPEOnContainsNull() {
133         // Create a collection that throws NPE on contains(null) like List.of() instance does
134         final Collection<FileLocationStrategy> collectionThatThrowsNPE = new ArrayList<FileLocationStrategy>(Arrays.asList(getSubStrategies())) {
135             @Override
136             public boolean contains(final Object o) {
137                 Objects.requireNonNull(o);
138                 return super.contains(o);
139             }
140         };
141 
142         // This should not throw NPE - the constructor should handle it gracefully
143         final CombinedLocationStrategy strategy = new CombinedLocationStrategy(collectionThatThrowsNPE);
144         checkSubStrategies(strategy);
145     }
146 
147     /**
148      * Tests whether a defensive copy of the collection with sub strategies is made.
149      */
150     @Test
151     void testInitDefensiveCopy() {
152         final Collection<FileLocationStrategy> col = new LinkedList<>(Arrays.asList(getSubStrategies()));
153         final CombinedLocationStrategy strategy = new CombinedLocationStrategy(col);
154         col.add(mock(FileLocationStrategy.class));
155         checkSubStrategies(strategy);
156     }
157 
158     /**
159      * Tries to create an instance with a null collection.
160      */
161     @Test
162     void testInitNullCollection() {
163         assertThrows(IllegalArgumentException.class, () -> new CombinedLocationStrategy(null));
164     }
165 
166     /**
167      * Tests a failed locate() operation.
168      */
169     @Test
170     void testLocateFailed() {
171         when(getSubStrategies()[0].locate(getFileSystem(), locator)).thenReturn(null);
172         when(getSubStrategies()[1].locate(getFileSystem(), locator)).thenReturn(null);
173 
174         final CombinedLocationStrategy strategy = createCombinedStrategy();
175         assertNull(strategy.locate(getFileSystem(), locator));
176 
177         verify(getSubStrategies()[0]).locate(getFileSystem(), locator);
178         verify(getSubStrategies()[1]).locate(getFileSystem(), locator);
179         verifyNoMoreSubCategoryInteractions();
180     }
181 
182     /**
183      * Tests a successful locate() operation if the first sub strategy can locate the file.
184      */
185     @Test
186     void testLocateSuccessFirstSubStrategy() {
187         when(getSubStrategies()[0].locate(getFileSystem(), locator)).thenReturn(locateURL);
188 
189         final CombinedLocationStrategy strategy = createCombinedStrategy();
190         assertSame(locateURL, strategy.locate(getFileSystem(), locator));
191 
192         verify(getSubStrategies()[0]).locate(getFileSystem(), locator);
193         verifyNoMoreSubCategoryInteractions();
194     }
195 
196     /**
197      * Tests a successful locate() operation if the 2nd sub strategy can locate the file.
198      */
199     @Test
200     void testLocateSuccessSecondSubStrategy() {
201         when(getSubStrategies()[0].locate(getFileSystem(), locator)).thenReturn(null);
202         when(getSubStrategies()[1].locate(getFileSystem(), locator)).thenReturn(locateURL);
203 
204         final CombinedLocationStrategy strategy = createCombinedStrategy();
205         assertSame(locateURL, strategy.locate(getFileSystem(), locator));
206 
207         verify(getSubStrategies()[0]).locate(getFileSystem(), locator);
208         verify(getSubStrategies()[1]).locate(getFileSystem(), locator);
209         verifyNoMoreSubCategoryInteractions();
210     }
211 
212     /**
213      * Helper method for verifying that no more interactions have been performed on the sub categories.
214      */
215     private void verifyNoMoreSubCategoryInteractions() {
216         verifyNoMoreInteractions((Object[]) getSubStrategies());
217     }
218 }