// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.data;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.AutosaveTask.AutosaveLayerInfo;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.testutils.JOSMTestRules;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Unit tests for class {@link AutosaveTask}.
*/
public class AutosaveTaskTest {
/**
* We need preferences and a home directory for this.
*/
@Rule
@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
public JOSMTestRules test = new JOSMTestRules().preferences().platform().projection();
private AutosaveTask task;
/**
* Setup test.
* @throws IOException if autosave directory cannot be created
*/
@Before
public void setUp() throws IOException {
task = new AutosaveTask();
}
/**
* Unit test to {@link AutosaveTask#getUnsavedLayersFiles} - empty case
*/
@Test
public void testGetUnsavedLayersFilesEmpty() {
assertTrue(task.getUnsavedLayersFiles().isEmpty());
}
/**
* Unit test to {@link AutosaveTask#getUnsavedLayersFiles} - non empty case
* @throws IOException in case of I/O error
*/
@Test
public void testGetUnsavedLayersFilesNotEmpty() throws IOException {
Files.createDirectories(task.getAutosaveDir());
String autodir = task.getAutosaveDir().toString();
File layer1 = Files.createFile(Paths.get(autodir, "layer1.osm")).toFile();
File layer2 = Files.createFile(Paths.get(autodir, "layer2.osm")).toFile();
File dir = Files.createDirectory(Paths.get(autodir, "dir.osm")).toFile();
List<File> files = task.getUnsavedLayersFiles();
assertEquals(2, files.size());
assertTrue(files.contains(layer1));
assertTrue(files.contains(layer2));
assertFalse(files.contains(dir));
}
/**
* Unit test to {@link AutosaveTask#getNewLayerFile}
* @throws IOException in case of I/O error
*/
@Test
public void testGetNewLayerFile() throws IOException {
Files.createDirectories(task.getAutosaveDir());
AutosaveLayerInfo info = new AutosaveLayerInfo(new OsmDataLayer(new DataSet(), "layer", null));
Date fixed = Date.from(ZonedDateTime.of(2016, 1, 1, 1, 2, 3, 456_000_000, ZoneId.systemDefault()).toInstant());
AutosaveTask.PROP_INDEX_LIMIT.put(5);
for (int i = 0; i <= AutosaveTask.PROP_INDEX_LIMIT.get() + 2; i++) {
// Only retry 2 indexes to avoid 1000*1000 disk operations
File f = task.getNewLayerFile(info, fixed, Math.max(0, i - 2));
if (i > AutosaveTask.PROP_INDEX_LIMIT.get()) {
assertNull(f);
} else {
assertNotNull(f);
File pid = task.getPidFile(f);
assertTrue(pid.exists());
assertTrue(f.exists());
if (i == 0) {
assertEquals("null_20160101_010203456.osm", f.getName());
assertEquals("null_20160101_010203456.pid", pid.getName());
} else {
assertEquals("null_20160101_010203456_" + i + ".osm", f.getName());
assertEquals("null_20160101_010203456_" + i + ".pid", pid.getName());
}
}
}
}
/**
* Tests if {@link AutosaveTask#schedule()} creates the directories.
*/
@Test
public void testScheduleCreatesDirectories() {
try {
task.schedule();
assertTrue(task.getAutosaveDir().toFile().isDirectory());
} finally {
task.cancel();
}
}
/**
* Tests that {@link AutosaveTask#run()} saves every layer
*/
@Test
public void testAutosaveIgnoresUnmodifiedLayer() {
OsmDataLayer layer = new OsmDataLayer(new DataSet(), "OsmData", null);
Main.getLayerManager().addLayer(layer);
try {
task.schedule();
assertEquals(0, countFiles());
task.run();
assertEquals(0, countFiles());
} finally {
task.cancel();
}
}
private int countFiles() {
String[] files = task.getAutosaveDir().toFile().list((dir, name) -> name.endsWith(".osm"));
return files != null ? files.length : 0;
}
/**
* Tests that {@link AutosaveTask#run()} saves every layer.
*/
@Test
public void testAutosaveSavesLayer() {
runAutosaveTaskSeveralTimes(1);
}
/**
* Tests that {@link AutosaveTask#run()} saves every layer.
*/
@Test
public void testAutosaveSavesLayerMultipleTimes() {
AutosaveTask.PROP_FILES_PER_LAYER.put(3);
runAutosaveTaskSeveralTimes(5);
}
private void runAutosaveTaskSeveralTimes(int times) {
DataSet data = new DataSet();
OsmDataLayer layer = new OsmDataLayer(data, "OsmData", null);
Main.getLayerManager().addLayer(layer);
try {
task.schedule();
assertEquals(0, countFiles());
for (int i = 0; i < times; i++) {
data.addPrimitive(new Node(new LatLon(10, 10)));
task.run();
assertEquals(Math.min(i + 1, 3), countFiles());
}
} finally {
task.cancel();
}
}
/**
* Tests that {@link AutosaveTask#discardUnsavedLayers()} ignores layers from the current instance
* @throws IOException in case of I/O error
*/
@Test
public void testDiscardUnsavedLayersIgnoresCurrentInstance() throws IOException {
runAutosaveTaskSeveralTimes(1);
try (BufferedWriter file = Files.newBufferedWriter(
new File(task.getAutosaveDir().toFile(), "any_other_file.osm").toPath(), StandardCharsets.UTF_8)) {
file.append("");
}
assertEquals(2, countFiles());
task.discardUnsavedLayers();
assertEquals(1, countFiles());
}
/**
* Tests that {@link AutosaveTask#run()} handles duplicate layers
*/
@Test
public void testAutosaveHandlesDupplicateNames() {
DataSet data1 = new DataSet();
OsmDataLayer layer1 = new OsmDataLayer(data1, "OsmData", null);
Main.getLayerManager().addLayer(layer1);
DataSet data2 = new DataSet();
OsmDataLayer layer2 = new OsmDataLayer(data2, "OsmData", null);
try {
task.schedule();
assertEquals(0, countFiles());
// also test adding layer later
Main.getLayerManager().addLayer(layer2);
data1.addPrimitive(new Node(new LatLon(10, 10)));
data2.addPrimitive(new Node(new LatLon(10, 10)));
task.run();
assertEquals(2, countFiles());
} finally {
task.cancel();
}
}
/**
* Test that {@link AutosaveTask#recoverUnsavedLayers()} recovers unsaved layers.
* @throws Exception in case of error
*/
@Test
public void testRecoverLayers() throws Exception {
runAutosaveTaskSeveralTimes(1);
try (BufferedWriter file = Files.newBufferedWriter(
new File(task.getAutosaveDir().toFile(), "any_other_file.osm").toPath(), StandardCharsets.UTF_8)) {
file.append("<?xml version=\"1.0\"?><osm version=\"0.6\"><node id=\"1\" lat=\"1\" lon=\"2\" version=\"1\"/></osm>");
}
assertEquals(2, countFiles());
task.recoverUnsavedLayers().get();
assertEquals(1, countFiles());
}
}