// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.io;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Random;
import java.util.logging.Logger;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import org.openstreetmap.josm.JOSMFixture;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.Changeset;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.RelationMember;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.gui.io.UploadStrategy;
import org.openstreetmap.josm.gui.io.UploadStrategySpecification;
import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Unit tests of {@link MultiFetchServerObjectReader}.
*/
@SuppressFBWarnings(value = "CRLF_INJECTION_LOGS")
public class MultiFetchServerObjectReaderTest {
private static Logger logger = Logger.getLogger(MultiFetchServerObjectReader.class.getName());
/**
* Global timeout applied to all test methods.
*/
@Rule
public Timeout globalTimeout = Timeout.seconds(60);
/**
* builds a large data set to be used later for testing MULTI FETCH on the server
*
* @return a large data set
*/
protected static DataSet buildTestDataSet() {
DataSet ds = new DataSet();
ds.setVersion("0.6");
Random rand = new SecureRandom();
int numNodes = 1000;
int numWays = 1000;
int numRelations = 1000;
ArrayList<Node> nodes = new ArrayList<>();
ArrayList<Way> ways = new ArrayList<>();
// create a set of nodes
//
for (int i = 0; i < numNodes; i++) {
Node n = new Node();
n.setCoor(new LatLon(-36.6, 47.6));
n.put("name", "node-"+i);
ds.addPrimitive(n);
nodes.add(n);
}
// create a set of ways, each with a random number of nodes
//
for (int i = 0; i < numWays; i++) {
Way w = new Way();
int numNodesInWay = 2 + (int) Math.round(rand.nextDouble() * 5);
int start = (int) Math.round(rand.nextDouble() * numNodes);
for (int j = 0; j < numNodesInWay; j++) {
int idx = (start + j) % numNodes;
Node n = nodes.get(idx);
w.addNode(n);
}
w.put("name", "way-"+i);
ds.addPrimitive(w);
ways.add(w);
}
// create a set of relations each with a random number of nodes, and ways
//
for (int i = 0; i < numRelations; i++) {
Relation r = new Relation();
r.put("name", "relation-" +i);
int numNodesInRelation = (int) Math.round(rand.nextDouble() * 10);
int start = (int) Math.round(rand.nextDouble() * numNodes);
for (int j = 0; j < numNodesInRelation; j++) {
int idx = (start + j) % 500;
Node n = nodes.get(idx);
r.addMember(new RelationMember("role-" + j, n));
}
int numWaysInRelation = (int) Math.round(rand.nextDouble() * 10);
start = (int) Math.round(rand.nextDouble() * numWays);
for (int j = 0; j < numWaysInRelation; j++) {
int idx = (start + j) % 500;
Way w = ways.get(idx);
r.addMember(new RelationMember("role-" + j, w));
}
ds.addPrimitive(r);
}
return ds;
}
private static DataSet testDataSet;
/**
* creates the dataset on the server.
*
* @param ds the data set
* @throws OsmTransferException if something goes wrong
*/
public static void createDataSetOnServer(DataSet ds) throws OsmTransferException {
logger.info("creating data set on the server ...");
ArrayList<OsmPrimitive> primitives = new ArrayList<>();
primitives.addAll(testDataSet.getNodes());
primitives.addAll(testDataSet.getWays());
primitives.addAll(testDataSet.getRelations());
OsmServerWriter writer = new OsmServerWriter();
Changeset cs = new Changeset();
writer.uploadOsm(new UploadStrategySpecification().setStrategy(UploadStrategy.SINGLE_REQUEST_STRATEGY),
primitives, cs, NullProgressMonitor.INSTANCE);
OsmApi.getOsmApi().closeChangeset(cs, NullProgressMonitor.INSTANCE);
}
/**
* Setup test.
* @throws Exception if an error occurs
*/
@BeforeClass
public static void init() throws Exception {
logger.info("initializing ...");
JOSMFixture.createFunctionalTestFixture().init();
Main.pref.put("osm-server.auth-method", "basic");
// don't use atomic upload, the test API server can't cope with large diff uploads
Main.pref.put("osm-server.atomic-upload", false);
File dataSetCacheOutputFile = new File(System.getProperty("java.io.tmpdir"),
MultiFetchServerObjectReaderTest.class.getName() + ".dataset");
String p = System.getProperties().getProperty("useCachedDataset");
if (p != null && Boolean.parseBoolean(p.trim().toLowerCase(Locale.ENGLISH))) {
logger.info(MessageFormat.format("property ''{0}'' set, using cached dataset", "useCachedDataset"));
return;
}
logger.info(MessageFormat.format(
"property ''{0}'' not set to true, creating test dataset on the server. property is ''{1}''", "useCachedDataset", p));
// build and upload the test data set
logger.info("creating test data set ....");
testDataSet = buildTestDataSet();
logger.info("uploading test data set ...");
createDataSetOnServer(testDataSet);
try (
PrintWriter pw = new PrintWriter(
new OutputStreamWriter(new FileOutputStream(dataSetCacheOutputFile), StandardCharsets.UTF_8)
)) {
logger.info(MessageFormat.format("caching test data set in ''{0}'' ...", dataSetCacheOutputFile.toString()));
try (OsmWriter w = new OsmWriter(pw, false, testDataSet.getVersion())) {
w.header();
w.writeDataSources(testDataSet);
w.writeContent(testDataSet);
w.footer();
}
}
}
private DataSet ds;
/**
* Setup test.
* @throws IOException if any I/O error occurs
* @throws IllegalDataException if an error was found while parsing the OSM data
* @throws FileNotFoundException if the dataset file cannot be found
*/
@Before
public void setUp() throws IOException, IllegalDataException, FileNotFoundException {
File f = new File(System.getProperty("java.io.tmpdir"), MultiFetchServerObjectReaderTest.class.getName() + ".dataset");
logger.info(MessageFormat.format("reading cached dataset ''{0}''", f.toString()));
ds = new DataSet();
try (FileInputStream fis = new FileInputStream(f)) {
ds = OsmReader.parseDataSet(fis, NullProgressMonitor.INSTANCE);
}
}
/**
* Test to multi-get 10 nodes.
* @throws OsmTransferException if an error occurs
*/
@Test
public void testMultiGet10Nodes() throws OsmTransferException {
MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader();
ArrayList<Node> nodes = new ArrayList<>(ds.getNodes());
for (int i = 0; i < 10; i++) {
reader.append(nodes.get(i));
}
DataSet out = reader.parseOsm(NullProgressMonitor.INSTANCE);
assertEquals(10, out.getNodes().size());
for (Node n1:out.getNodes()) {
Node n2 = (Node) ds.getPrimitiveById(n1);
assertNotNull(n2);
assertEquals(n2.get("name"), n2.get("name"));
}
assertTrue(reader.getMissingPrimitives().isEmpty());
}
/**
* Test to multi-get 10 ways.
* @throws OsmTransferException if an error occurs
*/
@Test
public void testMultiGet10Ways() throws OsmTransferException {
MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader();
ArrayList<Way> ways = new ArrayList<>(ds.getWays());
for (int i = 0; i < 10; i++) {
reader.append(ways.get(i));
}
DataSet out = reader.parseOsm(NullProgressMonitor.INSTANCE);
assertEquals(10, out.getWays().size());
for (Way w1: out.getWays()) {
Way w2 = (Way) ds.getPrimitiveById(w1);
assertNotNull(w2);
assertEquals(w2.getNodesCount(), w1.getNodesCount());
assertEquals(w2.get("name"), w1.get("name"));
}
assertTrue(reader.getMissingPrimitives().isEmpty());
}
/**
* Test to multi-get 10 relations.
* @throws OsmTransferException if an error occurs
*/
@Test
public void testMultiGet10Relations() throws OsmTransferException {
MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader();
ArrayList<Relation> relations = new ArrayList<>(ds.getRelations());
for (int i = 0; i < 10; i++) {
reader.append(relations.get(i));
}
DataSet out = reader.parseOsm(NullProgressMonitor.INSTANCE);
assertEquals(10, out.getRelations().size());
for (Relation r1: out.getRelations()) {
Relation r2 = (Relation) ds.getPrimitiveById(r1);
assertNotNull(r2);
assertEquals(r2.getMembersCount(), r1.getMembersCount());
assertEquals(r2.get("name"), r2.get("name"));
}
assertTrue(reader.getMissingPrimitives().isEmpty());
}
/**
* Test to multi-get 800 nodes.
* @throws OsmTransferException if an error occurs
*/
@Test
public void testMultiGet800Nodes() throws OsmTransferException {
MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader();
ArrayList<Node> nodes = new ArrayList<>(ds.getNodes());
for (int i = 0; i < 812; i++) {
reader.append(nodes.get(i));
}
DataSet out = reader.parseOsm(NullProgressMonitor.INSTANCE);
assertEquals(812, out.getNodes().size());
for (Node n1:out.getNodes()) {
Node n2 = (Node) ds.getPrimitiveById(n1);
assertNotNull(n2);
assertEquals(n2.get("name"), n2.get("name"));
}
assertTrue(reader.getMissingPrimitives().isEmpty());
}
/**
* Test to multi-get non-existing node.
* @throws OsmTransferException if an error occurs
*/
@Test
public void testMultiGetWithNonExistingNode() throws OsmTransferException {
MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader();
ArrayList<Node> nodes = new ArrayList<>(ds.getNodes());
for (int i = 0; i < 10; i++) {
reader.append(nodes.get(i));
}
Node n = new Node(9999999);
reader.append(n); // doesn't exist
DataSet out = reader.parseOsm(NullProgressMonitor.INSTANCE);
assertEquals(10, out.getNodes().size());
for (Node n1:out.getNodes()) {
Node n2 = (Node) ds.getPrimitiveById(n1);
assertNotNull(n2);
assertEquals(n2.get("name"), n2.get("name"));
}
assertFalse(reader.getMissingPrimitives().isEmpty());
assertEquals(1, reader.getMissingPrimitives().size());
assertEquals(9999999, reader.getMissingPrimitives().iterator().next().getUniqueId());
}
}