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.apache.commons.configuration2.TempDirUtils.newFile;
20  import static org.apache.commons.configuration2.TempDirUtils.newFolder;
21  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertFalse;
24  import static org.junit.jupiter.api.Assertions.assertNotNull;
25  import static org.junit.jupiter.api.Assertions.assertNull;
26  import static org.junit.jupiter.api.Assertions.assertSame;
27  import static org.junit.jupiter.api.Assertions.assertThrows;
28  import static org.junit.jupiter.api.Assertions.assertTrue;
29  import static org.mockito.Mockito.doThrow;
30  import static org.mockito.Mockito.mock;
31  import static org.mockito.Mockito.verify;
32  import static org.mockito.Mockito.verifyNoMoreInteractions;
33  import static org.mockito.Mockito.when;
34  
35  import java.io.ByteArrayInputStream;
36  import java.io.ByteArrayOutputStream;
37  import java.io.File;
38  import java.io.FileOutputStream;
39  import java.io.FileReader;
40  import java.io.FileWriter;
41  import java.io.IOException;
42  import java.io.InputStream;
43  import java.io.Reader;
44  import java.io.StringReader;
45  import java.io.StringWriter;
46  import java.io.Writer;
47  import java.net.MalformedURLException;
48  import java.net.URI;
49  import java.net.URL;
50  import java.nio.charset.StandardCharsets;
51  import java.nio.file.Files;
52  import java.nio.file.Path;
53  import java.util.Arrays;
54  import java.util.HashMap;
55  import java.util.List;
56  import java.util.Map;
57  
58  import org.apache.commons.configuration2.ConfigurationAssert;
59  import org.apache.commons.configuration2.PropertiesConfiguration;
60  import org.apache.commons.configuration2.SynchronizerTestImpl;
61  import org.apache.commons.configuration2.SynchronizerTestImpl.Methods;
62  import org.apache.commons.configuration2.ex.ConfigurationException;
63  import org.junit.jupiter.api.Test;
64  import org.junit.jupiter.api.io.TempDir;
65  
66  /**
67   * Test class for {@code FileHandler}.
68   */
69  public class TestFileHandler {
70      /**
71       * A FileBased implementation which also implements FileLocatorAware. This class adds information about the current file
72       * locator to the content read and written.
73       */
74      private static final class FileBasedFileLocatorAwareTestImpl extends FileBasedTestImpl implements FileLocatorAware {
75          /** Stores the passed in file locator. */
76          private FileLocator locator;
77  
78          /**
79           * Returns the locator.
80           *
81           * @return the file locator
82           */
83          public FileLocator getLocator() {
84              return locator;
85          }
86  
87          @Override
88          public void initFileLocator(final FileLocator loc) {
89              this.locator = loc;
90          }
91  
92          @Override
93          public void read(final Reader in) throws ConfigurationException, IOException {
94              super.read(in);
95              setContent(String.valueOf(locator.getSourceURL()) + ": " + getContent());
96          }
97  
98          @Override
99          public void write(final Writer out) throws ConfigurationException, IOException {
100             out.write(String.valueOf(locator.getSourceURL()) + ": ");
101             super.write(out);
102         }
103     }
104 
105     /**
106      * A test implementation of FileBased which can also read from input streams.
107      * <p>
108      * Cannot be final for Mockito.
109      * </p>
110      */
111     private static class FileBasedInputStreamSupportTestImpl extends FileBasedTestImpl implements InputStreamSupport {
112         @Override
113         public void read(final InputStream in) throws ConfigurationException, IOException {
114             final ByteArrayOutputStream bos = new ByteArrayOutputStream();
115             int c;
116             while ((c = in.read()) != -1) {
117                 bos.write(c);
118             }
119             setContent("InputStream = " + bos.toString());
120         }
121     }
122 
123     /**
124      * An implementation of the FileBased interface used for test purposes.
125      */
126     private static class FileBasedTestImpl implements FileBased {
127         /** The content read from a reader. */
128         private String content = CONTENT;
129 
130         /**
131          * Returns the content read from a reader.
132          *
133          * @return the read content
134          */
135         public String getContent() {
136             return content;
137         }
138 
139         @Override
140         public void read(final Reader in) throws ConfigurationException, IOException {
141             content = readReader(in);
142         }
143 
144         /**
145          * Allows setting the content.
146          *
147          * @param content the content
148          */
149         public void setContent(final String content) {
150             this.content = content;
151         }
152 
153         @Override
154         public void write(final Writer out) throws ConfigurationException, IOException {
155             out.write(getContent());
156             out.flush();
157         }
158     }
159 
160     /**
161      * A test listener implementation.
162      */
163     private static final class FileHandlerListenerTestImpl extends FileHandlerListenerAdapter {
164         /** The expected file handler. */
165         private final FileHandler expHandler;
166 
167         /** A buffer for recording method invocations. */
168         private final StringBuilder methods;
169 
170         public FileHandlerListenerTestImpl(final FileHandler fh) {
171             expHandler = fh;
172             methods = new StringBuilder();
173         }
174 
175         /**
176          * Tests whether the expected listener methods have been called.
177          *
178          * @param expMethods the expected methods as plain string
179          */
180         public void checkMethods(final String expMethods) {
181             assertEquals(expMethods, methods.toString());
182         }
183 
184         @Override
185         public void loaded(final FileHandler handler) {
186             super.loaded(handler);
187             methodCalled(handler, "loaded");
188         }
189 
190         @Override
191         public void loading(final FileHandler handler) {
192             super.loading(handler);
193             methodCalled(handler, "loading");
194         }
195 
196         @Override
197         public void locationChanged(final FileHandler handler) {
198             super.locationChanged(handler);
199             methodCalled(handler, "locationChanged");
200         }
201 
202         /**
203          * One of the listener methods was called. Records this invocation.
204          *
205          * @param handler the file handler
206          * @param method the called method
207          */
208         private void methodCalled(final FileHandler handler, final String method) {
209             assertEquals(expHandler, handler);
210             methods.append(method);
211         }
212 
213         @Override
214         public void saved(final FileHandler handler) {
215             super.saved(handler);
216             methodCalled(handler, "saved");
217         }
218 
219         @Override
220         public void saving(final FileHandler handler) {
221             super.saving(handler);
222             methodCalled(handler, "saving");
223         }
224     }
225 
226     /** Constant for the name of a test file. */
227     private static final String TEST_FILENAME = "test.properties";
228 
229     /** Constant for content of the test file. */
230     private static final String CONTENT = "TestFileHandler: This is test content.";
231 
232     /**
233      * Checks a FileLocator which is expected to contain no data.
234      *
235      * @param content the data object which was passed the locator
236      */
237     private static void checkEmptyLocator(final FileBasedFileLocatorAwareTestImpl content) {
238         assertNull(content.getLocator().getSourceURL());
239         assertNull(content.getLocator().getBasePath());
240         assertNull(content.getLocator().getFileName());
241     }
242 
243     /**
244      * Reads the content of the specified file into a string
245      *
246      * @param f the file to be read
247      * @return the content of this file
248      */
249     private static String readFile(final File f) {
250         return assertDoesNotThrow(() -> {
251             try (Reader in = new FileReader(f)) {
252                 return readReader(in);
253             }
254         });
255     }
256 
257     /**
258      * Reads the content of the specified reader into a string.
259      *
260      * @param in the reader
261      * @return the read content
262      * @throws IOException if an error occurs
263      */
264     private static String readReader(final Reader in) throws IOException {
265         final StringBuilder buf = new StringBuilder();
266         int c;
267         while ((c = in.read()) != -1) {
268             buf.append((char) c);
269         }
270         return buf.toString();
271     }
272 
273     /** A folder for temporary files. */
274     @TempDir
275     public File tempFolder;
276 
277     /**
278      * Creates a test file with the test content.
279      *
280      * @return the File object pointing to the test file
281      */
282     private File createTestFile() {
283         return createTestFile(null);
284     }
285 
286     /**
287      * Creates a test file with test content and allows specifying a file name.
288      *
289      * @param f the file to be created (may be <strong>null</strong>)
290      * @return the File object pointing to the test file
291      */
292     private File createTestFile(final File f) {
293         return assertDoesNotThrow(() -> {
294             File file = f;
295             if (file == null) {
296                 file = newFile(tempFolder);
297             }
298             try (Writer out = new FileWriter(file)) {
299                 out.write(CONTENT);
300             }
301             return file;
302         });
303     }
304 
305     private Path createTestPath() {
306         return createTestFile().toPath();
307     }
308 
309     private URI createTestURI() {
310         return createTestFile().toURI();
311     }
312 
313     private URL createTestURL() throws MalformedURLException {
314         return createTestURI().toURL();
315     }
316 
317     /**
318      * Tries to add a null listener.
319      */
320     @Test
321     void testAddFileHandlerListenerNull() {
322         final FileHandler fileHandler = new FileHandler();
323         assertThrows(IllegalArgumentException.class, () -> fileHandler.addFileHandlerListener(null));
324     }
325 
326     /**
327      * Tries to invoke the assignment constructor with a null handler.
328      */
329     @Test
330     void testAssignNullHandler() {
331         final FileBased obj = new FileBasedTestImpl();
332         assertThrows(IllegalArgumentException.class, () -> new FileHandler(obj, null));
333     }
334 
335     /**
336      * Tests whether a FileHandler object can be used to specify a location and later be assigned to a FileBased object.
337      */
338     @Test
339     void testAssignWithFileBased() {
340         final FileHandler h1 = new FileHandler();
341         final File f = new File("testfile.txt");
342         h1.setFile(f);
343         final FileBased content = new FileBasedTestImpl();
344         final FileHandler h2 = new FileHandler(content, h1);
345         h1.setFileName("someOtherFile.txt");
346         assertSame(content, h2.getContent());
347         assertEquals(f, h2.getFile());
348     }
349 
350     /**
351      * Tests whether the location can be cleared.
352      */
353     @Test
354     void testClearLocation() {
355         final FileHandler handler = new FileHandler();
356         handler.setFile(createTestFile());
357         handler.clearLocation();
358         assertFalse(handler.isLocationDefined());
359         assertNull(handler.getFile());
360         assertNull(handler.getURL());
361         assertNull(handler.getBasePath());
362         assertNull(handler.getPath());
363     }
364 
365     /**
366      * Tests getBasePath() if no information is available.
367      */
368     @Test
369     void testGetBasePathUndefined() {
370         assertNull(new FileHandler().getBasePath());
371     }
372 
373     /**
374      * Tests getFileName() if no information is set.
375      */
376     @Test
377     void testGetFileNameUndefined() {
378         assertNull(new FileHandler().getFileName());
379     }
380 
381     /**
382      * Tests whether a newly created instance has a default file system.
383      */
384     @Test
385     void testGetFileSystemDefault() {
386         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
387         assertEquals(FileLocatorUtils.DEFAULT_FILE_SYSTEM, handler.getFileSystem());
388     }
389 
390     /**
391      * Tests whether a newly created instance uses the default location strategy.
392      */
393     @Test
394     void testGetLocationStrategyDefault() {
395         final FileHandler handler = new FileHandler();
396         assertNull(handler.getFileLocator().getLocationStrategy());
397         assertSame(FileLocatorUtils.DEFAULT_LOCATION_STRATEGY, handler.getLocationStrategy());
398     }
399 
400     /**
401      * Tests whether an instance can be created from a map with the properties of a FileLocator.
402      */
403     @Test
404     void testInitFromMap() {
405         final FileLocator locator = FileLocatorUtils.fileLocator().fileName(TEST_FILENAME).basePath("someBasePath").encoding("someEncoding").create();
406         final Map<String, Object> map = new HashMap<>();
407         FileLocatorUtils.put(locator, map);
408         final FileHandler handler = FileHandler.fromMap(map);
409         assertEquals(locator, handler.getFileLocator());
410     }
411 
412     /**
413      * Tests whether the initialization of properties is safe even if performed in multiple threads.
414      */
415     @Test
416     void testInitPropertiesMultiThreaded() throws InterruptedException {
417         final String encoding = "TestEncoding";
418         final FileSystem fileSystem = new DefaultFileSystem();
419         final FileLocationStrategy locationStrategy = new ProvidedURLLocationStrategy();
420         final int loops = 8;
421 
422         for (int i = 0; i < loops; i++) {
423             final FileHandler handler = new FileHandler();
424             final Thread t1 = new Thread(() -> handler.setFileSystem(fileSystem));
425             final Thread t2 = new Thread(() -> handler.setFileName(TEST_FILENAME));
426             final Thread t3 = new Thread(() -> handler.setEncoding(encoding));
427             final Thread t4 = new Thread(() -> handler.setLocationStrategy(locationStrategy));
428             final List<Thread> threads = Arrays.asList(t1, t2, t3, t4);
429             for (final Thread t : threads) {
430                 t.start();
431             }
432             for (final Thread t : threads) {
433                 t.join();
434             }
435             final FileLocator locator = handler.getFileLocator();
436             assertEquals(TEST_FILENAME, locator.getFileName());
437             assertNull(locator.getSourceURL());
438             assertEquals(encoding, locator.getEncoding());
439             assertSame(fileSystem, locator.getFileSystem());
440             assertSame(locationStrategy, locator.getLocationStrategy());
441         }
442     }
443 
444     /**
445      * Tests isLocationDefined() if only a base path is set.
446      */
447     @Test
448     void testIsLocationDefinedBasePathOnly() {
449         final FileHandler handler = new FileHandler();
450         handler.setBasePath(createTestFile().getParent());
451         assertFalse(handler.isLocationDefined());
452     }
453 
454     /**
455      * Tests whether an undefined location can be queried.
456      */
457     @Test
458     void testIsLocationDefinedFalse() {
459         final FileHandler handler = new FileHandler();
460         assertFalse(handler.isLocationDefined());
461     }
462 
463     /**
464      * Tests isLocationDefined() if a File has been set.
465      */
466     @Test
467     void testIsLocationDefinedFile() {
468         final FileHandler handler = new FileHandler();
469         handler.setFile(createTestFile());
470         assertTrue(handler.isLocationDefined());
471     }
472 
473     /**
474      * Tests isLocationDefined() if a file name has been set.
475      */
476     @Test
477     void testIsLocationDefinedFileName() {
478         final FileHandler handler = new FileHandler();
479         handler.setFileName(createTestFile().getName());
480         assertTrue(handler.isLocationDefined());
481     }
482 
483     /**
484      * Tests isLocationDefined() if a path has been set.
485      */
486     @Test
487     void testIsLocationDefinedPath() {
488         final FileHandler handler = new FileHandler();
489         handler.setPath(createTestFile().getAbsolutePath());
490         assertTrue(handler.isLocationDefined());
491     }
492 
493     /**
494      * Tests isLocationDefined() if a URL has been set.
495      */
496     @Test
497     void testIsLocationDefinedURL() throws IOException {
498         final FileHandler handler = new FileHandler();
499         handler.setURL(createTestURL());
500         assertTrue(handler.isLocationDefined());
501     }
502 
503     /**
504      * Tests that it is not possible to load a directory using the load() method which expects a File.
505      */
506     @Test
507     void testLoadDirectoryFile() {
508         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
509         assertThrows(ConfigurationException.class, () -> handler.load(ConfigurationAssert.TEST_DIR));
510     }
511 
512     /**
513      * Checks that loading a directory instead of a file throws an exception.
514      */
515     @Test
516     void testLoadDirectoryString() {
517         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
518         final String fileName = ConfigurationAssert.TEST_DIR.getAbsolutePath();
519         assertThrows(ConfigurationException.class, () -> handler.load(fileName));
520     }
521 
522     /**
523      * Tests notifications about load operations.
524      */
525     @Test
526     void testLoadEvents() throws ConfigurationException {
527         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
528         final FileHandlerListenerTestImpl listener = new FileHandlerListenerTestImpl(handler);
529         handler.addFileHandlerListener(listener);
530         handler.load(createTestFile());
531         listener.checkMethods("loadingloaded");
532     }
533 
534     /**
535      * Tests whether a FileLocatorAware object is initialized correctly when loading data.
536      */
537     @Test
538     void testLoadFileLocatorAware() throws IOException, ConfigurationException {
539         final File file = createTestFile();
540         final FileBasedFileLocatorAwareTestImpl content = new FileBasedFileLocatorAwareTestImpl();
541         final FileHandler handler = new FileHandler(content);
542         handler.setFile(file);
543         handler.load();
544         assertEquals(file.toURI().toURL().toString() + ": " + CONTENT, content.getContent());
545     }
546 
547     /**
548      * Tests a load operation with a FileLocatorAware object if data is loaded from a reader.
549      */
550     @Test
551     void testLoadFileLocatorAwareReader() throws ConfigurationException {
552         final FileBasedFileLocatorAwareTestImpl content = new FileBasedFileLocatorAwareTestImpl();
553         final FileHandler handler = new FileHandler(content);
554         handler.load(new StringReader(CONTENT));
555         checkEmptyLocator(content);
556     }
557 
558     /**
559      * Tests loading with a FileLocatorAware object if data is loaded from a stream.
560      */
561     @Test
562     void testLoadFileLocatorAwareStream() throws ConfigurationException {
563         final FileBasedFileLocatorAwareTestImpl content = new FileBasedFileLocatorAwareTestImpl();
564         final FileHandler handler = new FileHandler(content);
565         final ByteArrayInputStream bos = new ByteArrayInputStream(CONTENT.getBytes());
566         handler.load(bos);
567         checkEmptyLocator(content);
568     }
569 
570     /**
571      * Tests whether whether data can be loaded from class path.
572      */
573     @Test
574     void testLoadFromClassPath() throws ConfigurationException {
575         final FileBasedTestImpl content = new FileBasedTestImpl();
576         final FileHandler config1 = new FileHandler(content);
577         config1.setFileName("config/deep/deeptest.properties");
578         config1.load();
579         assertFalse(content.getContent().isEmpty());
580     }
581 
582     /**
583      * Tests whether data from a File can be loaded.
584      */
585     @Test
586     void testLoadFromFile() throws ConfigurationException {
587         final FileBasedTestImpl content = new FileBasedTestImpl();
588         final File file = createTestFile();
589         final FileHandler handler = new FileHandler(content);
590         handler.load(file);
591         assertEquals(CONTENT, content.getContent());
592     }
593 
594     /**
595      * Tests a load operation using the current location which is a file name.
596      */
597     @Test
598     void testLoadFromFileNameLocation() throws ConfigurationException {
599         final File file = createTestFile();
600         final FileBasedTestImpl content = new FileBasedTestImpl();
601         final FileHandler handler = new FileHandler(content);
602         handler.setBasePath(file.getParentFile().getAbsolutePath());
603         handler.setFileName(file.getName());
604         handler.load();
605         assertEquals(CONTENT, content.getContent());
606     }
607 
608     /**
609      * Tries to load data from a File if no content object was set.
610      */
611     @Test
612     void testLoadFromFileNoContent() {
613         final FileHandler handler = new FileHandler();
614         final File file = createTestFile();
615         final ConfigurationException cex = assertThrows(ConfigurationException.class, () -> handler.load(file));
616         assertEquals("No content available!", cex.getMessage());
617     }
618 
619     /**
620      * Tests whether data from an absolute path can be loaded.
621      */
622     @Test
623     void testLoadFromFilePath() throws ConfigurationException {
624         final File file = createTestFile();
625         final FileBasedTestImpl content = new FileBasedTestImpl();
626         final FileHandler handler = new FileHandler(content);
627         handler.load(file.getAbsolutePath());
628         assertEquals(CONTENT, content.getContent());
629     }
630 
631     /**
632      * Tests that a load() operation with a file path overrides a URL which might have been set.
633      */
634     @Test
635     void testLoadFromFilePathWithURLDefined() throws ConfigurationException {
636         final File file = createTestFile();
637         final FileBasedTestImpl content = new FileBasedTestImpl();
638         final FileHandler handler = new FileHandler(content);
639         handler.setURL(ConfigurationAssert.getTestURL("test.xml"));
640         handler.load(file.getAbsolutePath());
641         assertEquals(CONTENT, content.getContent());
642     }
643 
644     /**
645      * Tests whether data from a reader can be read.
646      */
647     @Test
648     void testLoadFromReader() throws Exception {
649         final FileBasedTestImpl content = new FileBasedTestImpl();
650         final FileHandler handler = new FileHandler(content);
651         try (Reader in = new FileReader(createTestFile())) {
652             handler.load(in);
653         }
654         assertEquals(CONTENT, content.getContent());
655     }
656 
657     /**
658      * Tests whether an IOException is handled when loading data from a reader.
659      */
660     @Test
661     void testLoadFromReaderIOException() throws IOException, ConfigurationException {
662         final FileBased content = mock(FileBased.class);
663         final Reader in = new StringReader(CONTENT);
664         final IOException ioex = new IOException("Test exception");
665 
666         doThrow(ioex).when(content).read(in);
667 
668         final FileHandler handler = new FileHandler(content);
669         final ConfigurationException cex = assertThrows(ConfigurationException.class, () -> handler.load(in));
670         assertEquals(ioex, cex.getCause());
671 
672         verify(content).read(in);
673         verifyNoMoreInteractions(content);
674     }
675 
676     /**
677      * Tests whether data from an input stream can be read.
678      */
679     @Test
680     void testLoadFromStream() throws Exception {
681         final FileBasedTestImpl content = new FileBasedTestImpl();
682         final FileHandler handler = new FileHandler(content);
683         try (InputStream in = Files.newInputStream(createTestPath())) {
684             handler.load(in);
685         }
686         assertEquals(CONTENT, content.getContent());
687     }
688 
689     /**
690      * Tests whether data from a URL can be loaded.
691      */
692     @Test
693     void testLoadFromURL() throws Exception {
694         final FileBasedTestImpl content = new FileBasedTestImpl();
695         final FileHandler handler = new FileHandler(content);
696         handler.load(createTestURL());
697         assertEquals(CONTENT, content.getContent());
698     }
699 
700     /**
701      * Tests a load operation using the current location which is a URL.
702      */
703     @Test
704     void testLoadFromURLLocation() throws Exception {
705         final FileBasedTestImpl content = new FileBasedTestImpl();
706         final FileHandler handler = new FileHandler(content);
707         handler.setURL(createTestURL());
708         handler.load();
709         assertEquals(CONTENT, content.getContent());
710     }
711 
712     /**
713      * Tests whether data can be read from an input stream.
714      */
715     @Test
716     void testLoadInputStreamSupport() throws ConfigurationException {
717         final FileBasedInputStreamSupportTestImpl content = new FileBasedInputStreamSupportTestImpl();
718         final FileHandler handler = new FileHandler(content);
719         final ByteArrayInputStream bin = new ByteArrayInputStream(CONTENT.getBytes());
720         handler.load(bin);
721         assertEquals("InputStream = " + CONTENT, content.getContent());
722     }
723 
724     /**
725      * Tests whether an IOException is handled when reading from an input stream.
726      */
727     @Test
728     void testLoadInputStreamSupportIOException() throws ConfigurationException, IOException {
729         final FileBasedInputStreamSupportTestImpl content = mock(FileBasedInputStreamSupportTestImpl.class);
730         final ByteArrayInputStream bin = new ByteArrayInputStream(CONTENT.getBytes());
731         final IOException ioex = new IOException();
732 
733         doThrow(ioex).when(content).read(bin);
734 
735         final FileHandler handler = new FileHandler(content);
736         final ConfigurationException cex = assertThrows(ConfigurationException.class, () -> handler.load(bin));
737         assertEquals(ioex, cex.getCause());
738 
739         verify(content).read(bin);
740         verifyNoMoreInteractions(content);
741     }
742 
743     /**
744      * Tries to call a load() method if no content object is available.
745      */
746     @Test
747     void testLoadNoContent() {
748         final FileHandler handler = new FileHandler();
749         final StringReader reader = new StringReader(CONTENT);
750         assertThrows(ConfigurationException.class, () -> handler.load(reader));
751     }
752 
753     /**
754      * Tries to load data if no location has been set.
755      */
756     @Test
757     void testLoadNoLocation() {
758         final FileBasedTestImpl content = new FileBasedTestImpl();
759         final FileHandler handler = new FileHandler(content);
760         assertThrows(ConfigurationException.class, handler::load);
761     }
762 
763     /**
764      * Tests whether a load() operation is correctly synchronized.
765      */
766     @Test
767     void testLoadSynchronized() throws ConfigurationException {
768         final PropertiesConfiguration config = new PropertiesConfiguration();
769         final SynchronizerTestImpl sync = new SynchronizerTestImpl();
770         config.setSynchronizer(sync);
771         final FileHandler handler = new FileHandler(config);
772         handler.load(ConfigurationAssert.getTestFile("test.properties"));
773         sync.verifyStart(Methods.BEGIN_WRITE);
774         sync.verifyEnd(Methods.END_WRITE);
775     }
776 
777     /**
778      * Tests a successful locate() operation.
779      */
780     @Test
781     void testLocateSuccess() throws ConfigurationException {
782         final FileHandler handler = new FileHandler();
783         handler.setFileName(TEST_FILENAME);
784         assertTrue(handler.locate());
785         final FileLocator locator = handler.getFileLocator();
786         assertNotNull(locator.getSourceURL());
787         assertNotNull(locator.getBasePath());
788         assertEquals(TEST_FILENAME, locator.getFileName());
789 
790         // check whether the correct URL was obtained
791         final PropertiesConfiguration config = new PropertiesConfiguration();
792         final FileHandler h2 = new FileHandler(config);
793         h2.setURL(locator.getSourceURL());
794         h2.load();
795         assertTrue(config.getBoolean("configuration.loaded"));
796     }
797 
798     /**
799      * Tests a locate() operation if there is not enough information.
800      */
801     @Test
802     void testLocateUndefinedLocator() {
803         final FileHandler handler = new FileHandler();
804         handler.setBasePath("only/a/base/path");
805         final FileLocator locator = handler.getFileLocator();
806         assertFalse(handler.locate());
807         assertSame(locator, handler.getFileLocator());
808     }
809 
810     /**
811      * Tests a locate() operation if the specified file cannot be resolved.
812      */
813     @Test
814     void testLocateUnknownFile() {
815         final FileHandler handler = new FileHandler();
816         handler.setFileName("unknown file");
817         final FileLocator locator = handler.getFileLocator();
818         assertFalse(handler.locate());
819         assertSame(locator, handler.getFileLocator());
820     }
821 
822     /**
823      * Tests a notification about a changed base path.
824      */
825     @Test
826     void testLocationChangedBasePath() {
827         final FileHandler handler = new FileHandler();
828         final FileHandlerListenerTestImpl listener = new FileHandlerListenerTestImpl(handler);
829         handler.addFileHandlerListener(listener);
830         handler.setBasePath(TEST_FILENAME);
831         listener.checkMethods("locationChanged");
832     }
833 
834     /**
835      * Tests a notification about a changed encoding.
836      */
837     @Test
838     void testLocationChangedEncoding() {
839         final FileHandler handler = new FileHandler();
840         final FileHandlerListenerTestImpl listener = new FileHandlerListenerTestImpl(handler);
841         handler.addFileHandlerListener(listener);
842         handler.setEncoding(StandardCharsets.UTF_8.name());
843         listener.checkMethods("locationChanged");
844     }
845 
846     /**
847      * Tests a notification about a changed file.
848      */
849     @Test
850     void testLocationChangedFile() throws IOException {
851         final FileHandler handler = new FileHandler();
852         final FileHandlerListenerTestImpl listener = new FileHandlerListenerTestImpl(handler);
853         handler.addFileHandlerListener(listener);
854         handler.setFile(newFile(tempFolder));
855         listener.checkMethods("locationChanged");
856     }
857 
858     /**
859      * Tests a notification about a changed file name.
860      */
861     @Test
862     void testLocationChangedFileName() {
863         final FileHandler handler = new FileHandler();
864         final FileHandlerListenerTestImpl listener = new FileHandlerListenerTestImpl(handler);
865         handler.addFileHandlerListener(listener);
866         handler.setFileName(TEST_FILENAME);
867         listener.checkMethods("locationChanged");
868     }
869 
870     /**
871      * Tests a notification about a changed file system.
872      */
873     @Test
874     void testLocationChangedFileSystem() {
875         final FileSystem fs = mock(FileSystem.class);
876         final FileHandler handler = new FileHandler();
877         final FileHandlerListenerTestImpl listener = new FileHandlerListenerTestImpl(handler);
878         handler.addFileHandlerListener(listener);
879         handler.setFileSystem(fs);
880         listener.checkMethods("locationChanged");
881     }
882 
883     /**
884      * Tests whether a notification is sent if the whole locator was changed.
885      */
886     @Test
887     void testLocationChangedLocator() {
888         final FileHandler handler = new FileHandler();
889         final FileHandlerListenerTestImpl listener = new FileHandlerListenerTestImpl(handler);
890         handler.addFileHandlerListener(listener);
891         handler.setFileLocator(FileLocatorUtils.fileLocator().fileName(TEST_FILENAME).create());
892         listener.checkMethods("locationChanged");
893     }
894 
895     /**
896      * Tests a notification about a changed path.
897      */
898     @Test
899     void testLocationChangedPath() {
900         final FileHandler handler = new FileHandler();
901         final FileHandlerListenerTestImpl listener = new FileHandlerListenerTestImpl(handler);
902         handler.addFileHandlerListener(listener);
903         handler.setPath(TEST_FILENAME);
904         listener.checkMethods("locationChanged");
905     }
906 
907     /**
908      * Tests a notification about a changed URL.
909      */
910     @Test
911     void testLocationChangedURL() throws IOException {
912         final FileHandler handler = new FileHandler();
913         final FileHandlerListenerTestImpl listener = new FileHandlerListenerTestImpl(handler);
914         handler.addFileHandlerListener(listener);
915         final URL url = newFile(tempFolder).toURI().toURL();
916         handler.setURL(url);
917         listener.checkMethods("locationChanged");
918     }
919 
920     /**
921      * Tests that the locator injected into the content object has an encoding set.
922      */
923     @Test
924     void testLocatorAwareEncoding() throws ConfigurationException {
925         final FileBasedFileLocatorAwareTestImpl content = new FileBasedFileLocatorAwareTestImpl();
926         final FileHandler handler = new FileHandler(content);
927         final String encoding = "testEncoding";
928         handler.setEncoding(encoding);
929         handler.save(new StringWriter());
930         assertEquals(encoding, content.getLocator().getEncoding());
931     }
932 
933     /**
934      * Tests whether file names containing a "+" character are handled correctly. This test is related to CONFIGURATION-415.
935      */
936     @Test
937     void testPathWithPlus() throws ConfigurationException, IOException {
938         final File saveFile = newFile("test+config.properties", tempFolder);
939         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
940         handler.setFile(saveFile);
941         handler.save();
942         assertEquals(CONTENT, readFile(saveFile));
943     }
944 
945     /**
946      * Tests loading and saving a configuration file with a complicated path name including spaces. (related to issue 35210)
947      */
948     @Test
949     void testPathWithSpaces() throws ConfigurationException, IOException {
950         final File path = newFolder("path with spaces", tempFolder);
951         final File confFile = new File(path, "config-test.properties");
952         final File testFile = createTestFile(confFile);
953         final URL url = testFile.toURI().toURL();
954         final FileBasedTestImpl content = new FileBasedTestImpl();
955         final FileHandler handler = new FileHandler(content);
956         handler.setURL(url);
957         handler.load();
958         assertEquals(CONTENT, content.getContent());
959         final File out = new File(path, "out.txt");
960         handler.save(out);
961         assertEquals(CONTENT, readFile(out));
962     }
963 
964     /**
965      * Tests whether the file system can be reset.
966      */
967     @Test
968     void testResetFileSystem() {
969         final FileSystem sys = mock(FileSystem.class);
970         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
971         handler.setFileSystem(sys);
972         handler.resetFileSystem();
973         assertEquals(FileLocatorUtils.DEFAULT_FILE_SYSTEM, handler.getFileSystem());
974     }
975 
976     /**
977      * Tests notifications about save operations.
978      */
979     @Test
980     void testSaveEvents() throws IOException, ConfigurationException {
981         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
982         final FileHandlerListenerTestImpl listener = new FileHandlerListenerTestImpl(handler);
983         handler.addFileHandlerListener(listener);
984         final File f = newFile(tempFolder);
985         handler.save(f);
986         listener.checkMethods("savingsaved");
987     }
988 
989     /**
990      * Tests whether a FileLocatorAware is correctly handled when saving data.
991      */
992     @Test
993     void testSaveFileLocatorAware() throws ConfigurationException, IOException {
994         final File file = newFile(tempFolder);
995         final FileBasedFileLocatorAwareTestImpl content = new FileBasedFileLocatorAwareTestImpl();
996         final FileHandler handler = new FileHandler(content);
997         handler.save(file);
998         assertEquals(file.toURI().toURL() + ": " + CONTENT, readFile(file));
999     }
1000 
1001     /**
1002      * Tests a save operation with a FileLocatorAware object if the target is a stream.
1003      */
1004     @Test
1005     void testSaveFileLocatorAwareToStream() throws ConfigurationException {
1006         final FileBasedFileLocatorAwareTestImpl content = new FileBasedFileLocatorAwareTestImpl();
1007         final FileHandler handler = new FileHandler(content);
1008         handler.save(new ByteArrayOutputStream());
1009         checkEmptyLocator(content);
1010     }
1011 
1012     /**
1013      * Tests a save operation with a FileLocatorAware object if the target is a writer.
1014      */
1015     @Test
1016     void testSaveFileLocatorAwareToWriter() throws ConfigurationException {
1017         final FileBasedFileLocatorAwareTestImpl content = new FileBasedFileLocatorAwareTestImpl();
1018         final FileHandler handler = new FileHandler(content);
1019         handler.save(new StringWriter());
1020         checkEmptyLocator(content);
1021     }
1022 
1023     /**
1024      * Tries to save the locator if no location has been set.
1025      */
1026     @Test
1027     void testSaveNoLocation() {
1028         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
1029         assertThrows(ConfigurationException.class, handler::save);
1030     }
1031 
1032     /**
1033      * Tests whether a save() operation is correctly synchronized.
1034      */
1035     @Test
1036     void testSaveSynchronized() throws ConfigurationException, IOException {
1037         final PropertiesConfiguration config = new PropertiesConfiguration();
1038         config.addProperty("test.synchronized", Boolean.TRUE);
1039         final SynchronizerTestImpl sync = new SynchronizerTestImpl();
1040         config.setSynchronizer(sync);
1041         final FileHandler handler = new FileHandler(config);
1042         final File f = newFile(tempFolder);
1043         handler.save(f);
1044         sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
1045     }
1046 
1047     /**
1048      * Tests whether data can be saved to a file.
1049      */
1050     @Test
1051     void testSaveToFile() throws ConfigurationException, IOException {
1052         final File file = newFile(tempFolder);
1053         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
1054         handler.save(file);
1055         assertEquals(CONTENT, readFile(file));
1056     }
1057 
1058     /**
1059      * Tests whether data can be saved to a file name.
1060      */
1061     @Test
1062     void testSaveToFileName() throws ConfigurationException, IOException {
1063         final File file = newFile(tempFolder);
1064         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
1065         handler.save(file.getAbsolutePath());
1066         assertEquals(CONTENT, readFile(file));
1067     }
1068 
1069     /**
1070      * Tests whether data can be saved to the internal location if it is a file name.
1071      */
1072     @Test
1073     void testSaveToFileNameLocation() throws ConfigurationException, IOException {
1074         final File file = newFile(tempFolder);
1075         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
1076         handler.setFileName(file.getAbsolutePath());
1077         handler.save();
1078         assertEquals(CONTENT, readFile(file));
1079     }
1080 
1081     /**
1082      * Tests whether a URL exception is handled when saving a file to a file name.
1083      */
1084     @Test
1085     void testSaveToFileNameURLException() throws IOException {
1086         final FileSystem fs = mock(FileSystem.class);
1087         final File file = newFile(tempFolder);
1088         final String basePath = "some base path";
1089         final MalformedURLException urlex = new MalformedURLException("Test exception");
1090         final String fileName = file.getName();
1091 
1092         when(fs.getURL(basePath, fileName)).thenThrow(urlex);
1093 
1094         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
1095         handler.setBasePath(basePath);
1096         handler.setFileSystem(fs);
1097         final ConfigurationException cex = assertThrows(ConfigurationException.class, () -> handler.save(fileName));
1098         assertEquals(urlex, cex.getCause());
1099 
1100         verify(fs).getURL(basePath, fileName);
1101         verifyNoMoreInteractions(fs);
1102     }
1103 
1104     /**
1105      * Tries to save data to a file name if the name cannot be located.
1106      */
1107     @Test
1108     void testSaveToFileNameURLNotResolved() throws IOException {
1109         final FileSystem fs = mock(FileSystem.class);
1110         final File file = newFile(tempFolder);
1111         final String fileName = file.getName();
1112 
1113         when(fs.getURL(null, fileName)).thenReturn(null);
1114 
1115         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
1116         handler.setFileSystem(fs);
1117         assertThrows(ConfigurationException.class, () -> handler.save(fileName));
1118 
1119         verify(fs).getURL(null, fileName);
1120         verifyNoMoreInteractions(fs);
1121     }
1122 
1123     /**
1124      * Tests whether data can be saved to a stream.
1125      */
1126     @Test
1127     void testSaveToStream() throws ConfigurationException, IOException {
1128         final File file = newFile(tempFolder);
1129         try (FileOutputStream out = new FileOutputStream(file)) {
1130             final FileHandler handler = new FileHandler(new FileBasedTestImpl());
1131             handler.save(out);
1132         }
1133         assertEquals(CONTENT, readFile(file));
1134     }
1135 
1136     /**
1137      * Tests whether data can be saved to a URL.
1138      */
1139     @Test
1140     void testSaveToURL() throws Exception {
1141         final File file = newFile(tempFolder);
1142         final URL url = file.toURI().toURL();
1143         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
1144         handler.save(url);
1145         assertEquals(CONTENT, readFile(file));
1146     }
1147 
1148     /**
1149      * Tests whether data can be saved to the internal location if it is a URL.
1150      */
1151     @Test
1152     void testSaveToURLLocation() throws ConfigurationException, IOException {
1153         final File file = newFile(tempFolder);
1154         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
1155         handler.setURL(file.toURI().toURL());
1156         handler.save();
1157         assertEquals(CONTENT, readFile(file));
1158     }
1159 
1160     /**
1161      * Tests whether data can be saved into a Writer.
1162      */
1163     @Test
1164     void testSaveToWriter() throws ConfigurationException {
1165         final FileBasedTestImpl content = new FileBasedTestImpl();
1166         final FileHandler handler = new FileHandler(content);
1167         final StringWriter out = new StringWriter();
1168         handler.save(out);
1169         assertEquals(CONTENT, out.toString());
1170     }
1171 
1172     /**
1173      * Tests whether an I/O exception during a save operation to a Writer is handled correctly.
1174      */
1175     @Test
1176     void testSaveToWriterIOException() throws ConfigurationException, IOException {
1177         final FileBased content = mock(FileBased.class);
1178         final StringWriter out = new StringWriter();
1179         final IOException ioex = new IOException("Test exception!");
1180 
1181         doThrow(ioex).when(content).write(out);
1182 
1183         final FileHandler handler = new FileHandler(content);
1184         final ConfigurationException cex = assertThrows(ConfigurationException.class, () -> handler.save(out));
1185         assertEquals(ioex, cex.getCause());
1186 
1187         verify(content).write(out);
1188         verifyNoMoreInteractions(content);
1189     }
1190 
1191     /**
1192      * Tries to save something to a Writer if no content is set.
1193      */
1194     @Test
1195     void testSaveToWriterNoContent() {
1196         final FileHandler handler = new FileHandler();
1197         final StringWriter writer = new StringWriter();
1198         assertThrows(ConfigurationException.class, () -> handler.save(writer));
1199     }
1200 
1201     /**
1202      * Tests whether a base path can be set and whether this removes an already set URL.
1203      */
1204     @Test
1205     void testSetBasePath() {
1206         final FileHandler handler = new FileHandler();
1207         handler.setURL(ConfigurationAssert.getTestURL(TEST_FILENAME));
1208         final String basePath = ConfigurationAssert.TEST_DIR_NAME;
1209         handler.setBasePath(basePath);
1210         final FileLocator locator = handler.getFileLocator();
1211         assertEquals(basePath, locator.getBasePath());
1212         assertNull(locator.getSourceURL());
1213         assertNull(locator.getFileName());
1214     }
1215 
1216     /**
1217      * Tests whether the file scheme is corrected when setting the base path.
1218      */
1219     @Test
1220     void testSetBasePathFileScheme() {
1221         final FileHandler handler = new FileHandler();
1222         handler.setBasePath("file:/test/path/");
1223         assertEquals("file:///test/path/", handler.getFileLocator().getBasePath());
1224     }
1225 
1226     /**
1227      * Tests whether the location can be set as a file.
1228      */
1229     @Test
1230     void testSetFile() {
1231         final FileHandler handler = new FileHandler();
1232         final File directory = ConfigurationAssert.TEST_DIR;
1233         final File file = ConfigurationAssert.getTestFile(TEST_FILENAME);
1234         handler.setFile(file);
1235         assertEquals(directory.getAbsolutePath(), handler.getBasePath());
1236         assertEquals(TEST_FILENAME, handler.getFileName());
1237         assertEquals(file.getAbsolutePath(), handler.getPath());
1238     }
1239 
1240     /**
1241      * Tests whether the handler can be initialized using a FileLocator.
1242      */
1243     @Test
1244     void testSetFileLocator() {
1245         final FileLocator locator = FileLocatorUtils.fileLocator().fileName(TEST_FILENAME).create();
1246         final FileHandler handler = new FileHandler();
1247         handler.setFileLocator(locator);
1248         assertEquals(TEST_FILENAME, handler.getFileName());
1249     }
1250 
1251     /**
1252      * Tries to set the FileLocator to null.
1253      */
1254     @Test
1255     void testSetFileLocatorNull() {
1256         final FileHandler handler = new FileHandler();
1257         assertThrows(IllegalArgumentException.class, () -> handler.setFileLocator(null));
1258     }
1259 
1260     /**
1261      * Tests whether the location can be set using file name and base path.
1262      */
1263     @Test
1264     void testSetFileName() {
1265         final FileHandler handler = new FileHandler();
1266         handler.setURL(ConfigurationAssert.getTestURL(TEST_FILENAME));
1267         handler.setFileName(TEST_FILENAME);
1268         assertNull(handler.getBasePath());
1269         assertEquals(TEST_FILENAME, handler.getFileName());
1270         assertEquals(TEST_FILENAME, handler.getFileLocator().getFileName());
1271         assertNull(handler.getFileLocator().getSourceURL());
1272     }
1273 
1274     /**
1275      * Tests whether the file scheme is corrected when setting the file name.
1276      */
1277     @Test
1278     void testSetFileNameFileScheme() {
1279         final FileHandler handler = new FileHandler();
1280         handler.setFileName("file:/test/path/test.txt");
1281         assertEquals("file:///test/path/test.txt", handler.getFileLocator().getFileName());
1282     }
1283 
1284     /**
1285      * Tests whether a null file system can be set to reset this property.
1286      */
1287     @Test
1288     void testSetFileSystemNull() {
1289         final FileSystem sys = mock(FileSystem.class);
1290         final FileHandler handler = new FileHandler(new FileBasedTestImpl());
1291         handler.setFileSystem(sys);
1292         assertSame(sys, handler.getFileSystem());
1293         handler.setFileSystem(null);
1294         assertEquals(FileLocatorUtils.DEFAULT_FILE_SYSTEM, handler.getFileSystem());
1295     }
1296 
1297     /**
1298      * Tests whether the location strategy can be changed.
1299      */
1300     @Test
1301     void testSetLocationStrategy() {
1302         final FileLocationStrategy strategy = mock(FileLocationStrategy.class);
1303         final FileHandler handler = new FileHandler();
1304         handler.setLocationStrategy(strategy);
1305         assertSame(strategy, handler.getFileLocator().getLocationStrategy());
1306         assertSame(strategy, handler.getLocationStrategy());
1307     }
1308 
1309     /**
1310      * Tests whether the location can be set as a file.
1311      */
1312     @Test
1313     void testSetPath() throws MalformedURLException {
1314         final FileHandler handler = new FileHandler();
1315         handler.setPath(ConfigurationAssert.TEST_DIR_NAME + File.separator + TEST_FILENAME);
1316         assertEquals(TEST_FILENAME, handler.getFileName());
1317         assertEquals(ConfigurationAssert.TEST_DIR.getAbsolutePath(), handler.getBasePath());
1318         final File file = ConfigurationAssert.getTestFile(TEST_FILENAME);
1319         assertEquals(file.getAbsolutePath(), handler.getPath());
1320         assertEquals(file.toURI().toURL(), handler.getURL());
1321         assertNull(handler.getFileLocator().getSourceURL());
1322     }
1323 
1324     /**
1325      * Additional tests for setting file names in various ways. (Copied from the test for XMLConfiguration)
1326      */
1327     @Test
1328     void testSettingFileNames() {
1329         final String testProperties = ConfigurationAssert.getTestFile("test.xml").getAbsolutePath();
1330         final String testBasePath = ConfigurationAssert.TEST_DIR.getAbsolutePath();
1331 
1332         final FileHandler handler = new FileHandler();
1333         handler.setFileName(testProperties);
1334         assertEquals(testProperties.toString(), handler.getFileName());
1335 
1336         handler.setBasePath(testBasePath);
1337         handler.setFileName("hello.xml");
1338         assertEquals("hello.xml", handler.getFileName());
1339         assertEquals(testBasePath.toString(), handler.getBasePath());
1340         assertEquals(new File(testBasePath, "hello.xml"), handler.getFile());
1341 
1342         handler.setBasePath(testBasePath);
1343         handler.setFileName("subdir/hello.xml");
1344         assertEquals("subdir/hello.xml", handler.getFileName());
1345         assertEquals(testBasePath.toString(), handler.getBasePath());
1346         assertEquals(new File(testBasePath, "subdir/hello.xml"), handler.getFile());
1347     }
1348 
1349     /**
1350      * Tests whether a URL can be set.
1351      */
1352     @Test
1353     void testSetURL() throws Exception {
1354         final FileHandler handler = new FileHandler();
1355         handler.setURL(new URL("https://commons.apache.org/configuration/index.html"));
1356 
1357         assertEquals("https://commons.apache.org/configuration/", handler.getBasePath());
1358         assertEquals("index.html", handler.getFileName());
1359         assertNull(handler.getFileLocator().getFileName());
1360     }
1361 
1362     /**
1363      * Tests whether the correct file scheme is applied.
1364      */
1365     @Test
1366     void testSetURLFileScheme() throws MalformedURLException {
1367         final FileHandler handler = new FileHandler();
1368         // file URL - This url is invalid, a valid url would be
1369         // file:///temp/test.properties.
1370         handler.setURL(new URL("file:/temp/test.properties"));
1371         assertEquals("file:///temp/", handler.getBasePath());
1372         assertEquals(TEST_FILENAME, handler.getFileName());
1373     }
1374 
1375     /**
1376      * Tests whether a null URL can be set.
1377      */
1378     @Test
1379     void testSetURLNull() {
1380         final FileHandler handler = new FileHandler();
1381         handler.setURL(ConfigurationAssert.getTestURL(TEST_FILENAME));
1382         handler.setURL(null);
1383         final FileLocator locator = handler.getFileLocator();
1384         assertNull(locator.getBasePath());
1385         assertNull(locator.getFileName());
1386         assertNull(locator.getSourceURL());
1387     }
1388 
1389     /**
1390      * Tests whether a URL with parameters can be set.
1391      */
1392     @Test
1393     void testSetURLWithParams() throws Exception {
1394         final FileHandler handler = new FileHandler();
1395         final URL url = new URL("https://issues.apache.org/bugzilla/show_bug.cgi?id=37886");
1396         handler.setURL(url);
1397         assertEquals("https://issues.apache.org/bugzilla/", handler.getBasePath());
1398         assertEquals("show_bug.cgi", handler.getFileName());
1399         assertEquals(url, handler.getURL());
1400     }
1401 }