| | 1 | // License: GPL. For details, see LICENSE file. |
| | 2 | package org.openstreetmap.josm.io.importexport; |
| | 3 | |
| | 4 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; |
| | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | 6 | import static org.junit.jupiter.api.Assertions.assertFalse; |
| | 7 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | 8 | |
| | 9 | import java.io.File; |
| | 10 | import java.io.IOException; |
| | 11 | import java.nio.file.Path; |
| | 12 | import java.util.ArrayList; |
| | 13 | import java.util.Collections; |
| | 14 | import java.util.List; |
| | 15 | import java.util.stream.Collectors; |
| | 16 | |
| | 17 | import org.junit.jupiter.api.Test; |
| | 18 | import org.junit.jupiter.api.io.TempDir; |
| | 19 | import org.openstreetmap.josm.gui.MainApplication; |
| | 20 | import org.openstreetmap.josm.gui.io.importexport.FileExporter; |
| | 21 | import org.openstreetmap.josm.gui.io.importexport.FileImporter; |
| | 22 | import org.openstreetmap.josm.gui.layer.Layer; |
| | 23 | import org.openstreetmap.josm.gui.layer.LayerManager; |
| | 24 | import org.openstreetmap.josm.gui.progress.NullProgressMonitor; |
| | 25 | import org.openstreetmap.josm.gui.util.GuiHelper; |
| | 26 | import org.openstreetmap.josm.io.IllegalDataException; |
| | 27 | import org.openstreetmap.josm.testutils.annotations.BasicPreferences; |
| | 28 | |
| | 29 | /** |
| | 30 | * A test class for file exporters |
| | 31 | * @param <E> The file exporter class |
| | 32 | * @param <I> The <i>original</i> importer class |
| | 33 | * @param <J> The importer to class to use <i>after</i> we have exported the original data |
| | 34 | */ |
| | 35 | @BasicPreferences |
| | 36 | public interface FileExporterTest<E extends FileExporter, I extends FileImporter, J extends FileImporter> { |
| | 37 | /** |
| | 38 | * Get the exporter instance for tests |
| | 39 | * @return The exporter to test |
| | 40 | */ |
| | 41 | E getExporter(); |
| | 42 | |
| | 43 | /** |
| | 44 | * Get a file extension for the exporter |
| | 45 | * @return The expected file extension |
| | 46 | */ |
| | 47 | String getExtension(); |
| | 48 | |
| | 49 | /** |
| | 50 | * Get the original importer instance for tests |
| | 51 | * @return The importer to use |
| | 52 | */ |
| | 53 | I getOriginalImporter(); |
| | 54 | |
| | 55 | /** |
| | 56 | * Get the importer instance for verifying that the data was written correctly |
| | 57 | */ |
| | 58 | J getSavedDataImporter(); |
| | 59 | |
| | 60 | /** |
| | 61 | * Verify that the importers have generated the same data |
| | 62 | */ |
| | 63 | default void verifyData(File original, File saved) throws IOException, IllegalDataException { |
| | 64 | assertTrue(MainApplication.getLayerManager().getLayers().isEmpty()); |
| | 65 | final LayerManager layerManager = MainApplication.getLayerManager(); |
| | 66 | I originalImporter = getOriginalImporter(); |
| | 67 | assertTrue(originalImporter.acceptFile(original)); |
| | 68 | if (originalImporter.isBatchImporter()) { |
| | 69 | originalImporter.importData(Collections.singletonList(original), NullProgressMonitor.INSTANCE); |
| | 70 | } else { |
| | 71 | originalImporter.importData(original, NullProgressMonitor.INSTANCE); |
| | 72 | } |
| | 73 | syncThreads(); |
| | 74 | final List<Layer> originalLayers = new ArrayList<>(layerManager.getLayers()); |
| | 75 | originalLayers.forEach(layerManager::removeLayer); |
| | 76 | assertFalse(originalLayers.isEmpty()); |
| | 77 | |
| | 78 | assertTrue(MainApplication.getLayerManager().getLayers().isEmpty()); |
| | 79 | J savedImporter = getSavedDataImporter(); |
| | 80 | assertTrue(savedImporter.acceptFile(saved)); |
| | 81 | if (savedImporter.isBatchImporter()) { |
| | 82 | savedImporter.importData(Collections.singletonList(saved), NullProgressMonitor.INSTANCE); |
| | 83 | } else { |
| | 84 | savedImporter.importData(saved, NullProgressMonitor.INSTANCE); |
| | 85 | } |
| | 86 | syncThreads(); |
| | 87 | final List<Layer> savedLayers = new ArrayList<>(layerManager.getLayers()); |
| | 88 | savedLayers.forEach(layerManager::removeLayer); |
| | 89 | assertFalse(savedLayers.isEmpty()); |
| | 90 | |
| | 91 | verifyLayers(originalLayers, savedLayers); |
| | 92 | } |
| | 93 | |
| | 94 | /** |
| | 95 | * Verify that two layer lists are equal |
| | 96 | * @param original The original layers (from the original data) |
| | 97 | * @param saved The saved layers (from the exporter) |
| | 98 | */ |
| | 99 | void verifyLayers(List<Layer> original, List<Layer> saved); |
| | 100 | |
| | 101 | /** |
| | 102 | * Load data that the exporter can save |
| | 103 | * @return The data to save via the exporter |
| | 104 | */ |
| | 105 | File goodData(); |
| | 106 | |
| | 107 | /** |
| | 108 | * Data that the exporter should not save |
| | 109 | * @return The data to try and save via the exporter. Will be loaded via an importer. |
| | 110 | */ |
| | 111 | File badData(); |
| | 112 | |
| | 113 | /** |
| | 114 | * Check that the exporter enablement methods work properly |
| | 115 | */ |
| | 116 | @Test |
| | 117 | default void testEnabled() { |
| | 118 | E exporter = getExporter(); |
| | 119 | assertTrue(exporter.isEnabled(), "Exporters default to enabled"); |
| | 120 | exporter.setEnabled(false); |
| | 121 | assertFalse(exporter.isEnabled(), "Exporters should be able to be enabled/disabled"); |
| | 122 | exporter.setEnabled(true); |
| | 123 | assertTrue(exporter.isEnabled(), "Exporters should be able to be enabled/disabled"); |
| | 124 | } |
| | 125 | |
| | 126 | /** |
| | 127 | * Check that the exporter (at least) sets that it is cancelled |
| | 128 | */ |
| | 129 | @Test |
| | 130 | default void testCancelled() { |
| | 131 | E exporter = getExporter(); |
| | 132 | assertFalse(exporter.isCanceled()); |
| | 133 | exporter.setCanceled(true); |
| | 134 | assertTrue(exporter.isCanceled()); |
| | 135 | } |
| | 136 | |
| | 137 | /** |
| | 138 | * Test whether or not a file is accepted |
| | 139 | */ |
| | 140 | @Test |
| | 141 | default void testAcceptFileGood() { |
| | 142 | assertTrue(getOriginalImporter().acceptFile(goodData())); |
| | 143 | assertTrue(getOriginalImporter().acceptFile(badData())); |
| | 144 | } |
| | 145 | |
| | 146 | /** |
| | 147 | * Test a file that probably shouldn't be exported to |
| | 148 | */ |
| | 149 | @Test |
| | 150 | default void testAcceptFileBad() { |
| | 151 | E exporter = getExporter(); |
| | 152 | assertFalse(exporter.acceptFile(new File("foo.bazzzz"), null)); |
| | 153 | } |
| | 154 | |
| | 155 | /** |
| | 156 | * Test quietly exporting data |
| | 157 | * @param directory The directory in which we are exporting data |
| | 158 | * @throws IOException If we cannot read/write files |
| | 159 | * @throws IllegalDataException If the data was not good |
| | 160 | */ |
| | 161 | @Test |
| | 162 | default void testExportDataQuiet(@TempDir Path directory) throws IOException, IllegalDataException { |
| | 163 | final E exporter = getExporter(); |
| | 164 | final LayerManager layerManager = MainApplication.getLayerManager(); |
| | 165 | getOriginalImporter().importData(goodData(), NullProgressMonitor.INSTANCE); |
| | 166 | syncThreads(); |
| | 167 | final File testFile = directory.resolve("exportDataQuiet." + getExtension()).toFile(); |
| | 168 | List<Layer> exportableLayers = layerManager.getLayers() |
| | 169 | .stream().filter(layer -> exporter.acceptFile(testFile, layer)).collect(Collectors.toList()); |
| | 170 | assertEquals(1, exportableLayers.size()); |
| | 171 | exporter.exportDataQuiet(testFile, exportableLayers.get(0)); |
| | 172 | syncThreads(); |
| | 173 | for (Layer layer : new ArrayList<>(layerManager.getLayers())) { |
| | 174 | layerManager.removeLayer(layer); |
| | 175 | } |
| | 176 | verifyData(goodData(), testFile); |
| | 177 | } |
| | 178 | |
| | 179 | /** |
| | 180 | * Test overwrite behavior |
| | 181 | * @param directory The root directory |
| | 182 | */ |
| | 183 | @Test |
| | 184 | void testExportDataOverwrite(@TempDir Path directory); |
| | 185 | |
| | 186 | /** |
| | 187 | * Force a thread sync |
| | 188 | */ |
| | 189 | default void syncThreads() { |
| | 190 | GuiHelper.runInEDTAndWait(() -> { /* Sync thread */ }); |
| | 191 | assertDoesNotThrow(() -> MainApplication.worker.submit(() -> { /* Sync thread */ }).get()); |
| | 192 | } |
| | 193 | } |