// This software is released into the Public Domain. See copying.txt for details. package org.openstreetmap.osmosis.set.v0_6; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.Assert; import org.junit.Test; import org.openstreetmap.osmosis.core.Osmosis; import org.openstreetmap.osmosis.core.OsmosisRuntimeException; import org.openstreetmap.osmosis.core.merge.common.ConflictResolutionMethod; import org.openstreetmap.osmosis.core.misc.v0_6.EmptyReader; import org.openstreetmap.osmosis.testutil.AbstractDataTest; import org.openstreetmap.osmosis.testutil.v0_6.RunTaskUtilities; import org.openstreetmap.osmosis.xml.common.CompressionMethod; import org.openstreetmap.osmosis.xml.v0_6.XmlReader; /** * Test the --merge task. * * @author Igor Podolskiy */ public class EntityMergerTest extends AbstractDataTest { /** * Tests empty + X == X. * * @throws Exception if something fails */ @Test public void firstEmpty() throws Exception { File sourceFile; File expectedOutputFile; File actualOutputFile; // Generate files. sourceFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); actualOutputFile = dataUtils.newFile(); // Run the merge. Osmosis.run( new String [] { "-q", "--read-xml-0.6", sourceFile.getPath(), "--read-empty-0.6", "--merge", "--write-xml-0.6", actualOutputFile.getPath() } ); // Validate that the output file matches the expected result. dataUtils.compareFiles(expectedOutputFile, actualOutputFile); } /** * Tests X + empty == X. * * @throws Exception if something fails */ @Test public void secondEmpty() throws Exception { File sourceFile; File expectedOutputFile; File actualOutputFile; // Generate files. sourceFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); actualOutputFile = dataUtils.newFile(); // Run the merge. Osmosis.run( new String [] { "-q", "--read-empty-0.6", "--read-xml-0.6", sourceFile.getPath(), "--merge", "--write-xml-0.6", actualOutputFile.getPath() } ); // Validate that the output file matches the expected result. dataUtils.compareFiles(expectedOutputFile, actualOutputFile); } /** * Tests X + X == X. * * @throws Exception if something fails */ @Test public void selfMerge() throws Exception { File sourceFile1; File sourceFile2; File expectedOutputFile; File actualOutputFile; // Generate files. sourceFile1 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); sourceFile2 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); actualOutputFile = dataUtils.newFile(); // Run the merge. Osmosis.run( new String [] { "-q", "--read-xml-0.6", sourceFile2.getPath(), "--read-xml-0.6", sourceFile1.getPath(), "--merge", "--write-xml-0.6", actualOutputFile.getPath() } ); // Validate that the output file matches the expected result. dataUtils.compareFiles(expectedOutputFile, actualOutputFile); } /** * Tests the timestamp conflict resolution strategy. * * @throws Exception if something fails */ @Test public void timestampConflictResolution() throws Exception { File sourceFile1; File sourceFile2; File expectedOutputFile; File actualOutputFile; // Generate files. sourceFile1 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); sourceFile2 = dataUtils.createDataFile("v0_6/merge/merge-in-2-timestamp.osm"); expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-out-timestamp.osm"); actualOutputFile = dataUtils.newFile(); // Run the merge. Osmosis.run( new String [] { "-q", "--read-xml-0.6", sourceFile2.getPath(), "--read-xml-0.6", sourceFile1.getPath(), "--merge", "conflictResolutionMethod=timestamp", "--write-xml-0.6", actualOutputFile.getPath() } ); // Validate that the output file matches the expected result. dataUtils.compareFiles(expectedOutputFile, actualOutputFile); // Timestamp conflict resolution should be commutative. // Run the merge. Osmosis.run( new String [] { "-q", "--read-xml-0.6", sourceFile1.getPath(), "--read-xml-0.6", sourceFile2.getPath(), "--merge", "conflictResolutionMethod=timestamp", "--write-xml-0.6", actualOutputFile.getPath() } ); // Validate that the output file matches the expected result. dataUtils.compareFiles(expectedOutputFile, actualOutputFile); } /** * Tests the version conflict resolution strategy. * * @throws Exception if something fails */ @Test public void versionConflictResolution() throws Exception { File sourceFile1; File sourceFile2; File expectedOutputFile; File actualOutputFile; // Generate files. sourceFile1 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); sourceFile2 = dataUtils.createDataFile("v0_6/merge/merge-in-2-version.osm"); expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-out-version.osm"); actualOutputFile = dataUtils.newFile(); // Run the merge. Osmosis.run( new String [] { "-q", "--read-xml-0.6", sourceFile2.getPath(), "--read-xml-0.6", sourceFile1.getPath(), "--merge", "conflictResolutionMethod=version", "--write-xml-0.6", actualOutputFile.getPath() } ); // Validate that the output file matches the expected result. dataUtils.compareFiles(expectedOutputFile, actualOutputFile); // Version conflict resolution should be commutative. // Run the merge. Osmosis.run( new String [] { "-q", "--read-xml-0.6", sourceFile1.getPath(), "--read-xml-0.6", sourceFile2.getPath(), "--merge", "conflictResolutionMethod=version", "--write-xml-0.6", actualOutputFile.getPath() } ); // Validate that the output file matches the expected result. dataUtils.compareFiles(expectedOutputFile, actualOutputFile); } /** * Tests the version conflict resolution strategy. * * @throws Exception if something fails */ @Test public void secondSourceConflictResolution() throws Exception { File sourceFile1; File sourceFile2; File expectedOutputFile; File actualOutputFile; // Generate files. sourceFile1 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); sourceFile2 = dataUtils.createDataFile("v0_6/merge/merge-in-2-secondSource.osm"); expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-out-secondSource.osm"); actualOutputFile = dataUtils.newFile(); // Run the merge. Osmosis.run( new String [] { "-q", "--read-xml-0.6", sourceFile2.getPath(), "--read-xml-0.6", sourceFile1.getPath(), "--merge", "conflictResolutionMethod=lastSource", "--write-xml-0.6", actualOutputFile.getPath() } ); // Validate that the output file matches the expected result. dataUtils.compareFiles(expectedOutputFile, actualOutputFile); // Timestamp conflict resolution is NOT commutative, // but it deserves testing as well. // As the mergen-in-2 input does not contain any entities that are not // in the second source, the output should be identical to the first source. expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); // Run the merge. Osmosis.run( new String [] { "-q", "--read-xml-0.6", sourceFile1.getPath(), "--read-xml-0.6", sourceFile2.getPath(), "--merge", "conflictResolutionMethod=lastSource", "--write-xml-0.6", actualOutputFile.getPath() } ); // Validate that the output file matches the expected result. dataUtils.compareFiles(expectedOutputFile, actualOutputFile); } /** * Tests merging two completely disjunct datasets (no conflicts). * * @throws Exception if something fails */ @Test public void disjunctDatasets() throws Exception { File sourceFile1; File sourceFile2; File expectedOutputFile; File actualOutputFile; // Generate files. sourceFile1 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm"); sourceFile2 = dataUtils.createDataFile("v0_6/merge/merge-in-2-disjunct.osm"); expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-out-disjunct.osm"); actualOutputFile = dataUtils.newFile(); // Run the merge. Osmosis.run( new String [] { "-q", "--read-xml-0.6", sourceFile2.getPath(), "--read-xml-0.6", sourceFile1.getPath(), "--merge", "--write-xml-0.6", actualOutputFile.getPath() } ); // Validate that the output file matches the expected result. dataUtils.compareFiles(expectedOutputFile, actualOutputFile); // Merging of disjunct datasets should be commutative. // Run the merge. Osmosis.run( new String [] { "-q", "--read-xml-0.6", sourceFile1.getPath(), "--read-xml-0.6", sourceFile2.getPath(), "--merge", "conflictResolutionMethod=version", "--write-xml-0.6", actualOutputFile.getPath() } ); // Validate that the output file matches the expected result. dataUtils.compareFiles(expectedOutputFile, actualOutputFile); } /** * Tests bad sort order in an input stream (node, way, relations not in * order). * * @throws Exception * if something fails */ @Test public void badSortOrderType() throws Exception { File sourceFile = dataUtils.createDataFile("v0_6/merge/merge-in-badorder-type.osm"); mergeAndLookForException(sourceFile, "Pipeline entities are not sorted"); } /** * Tests bad sort order in an input stream (ids not sorted). * * @throws Exception * if something fails */ @Test public void badSortOrderId() throws Exception { File sourceFile = dataUtils.createDataFile("v0_6/merge/merge-in-badorder-id.osm"); mergeAndLookForException(sourceFile, "Pipeline entities are not sorted"); } /** * Runs a merge and records the exceptions that happened during the merge. * * The test is considered passed iff at least one exception was thrown and * the exception message begins with a given string. * * This method does not use command line parsing because it is impossible * to check whether the right exception has been thrown. Also, as all * exceptions are OsmosisRuntimeExceptions, we need to check the message * so the JUnit expected exception facility is no good here. * * To add insult to injury, running a merge task involves three worker threads; * the exception we expect is thrown on one of those worker threads which brings * the pipeline down. But we want to check for that one source exception * is thrown for the correct reason, otherwise the test becomes very unspecific. * */ private void mergeAndLookForException(File sourceFile, String exceptionMessagePrefix) throws Exception { final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<Throwable>()); XmlReader reader = new XmlReader(sourceFile, false, CompressionMethod.None); EntityMerger merger = new EntityMerger( ConflictResolutionMethod.LatestSource, 1, BoundRemovedAction.Ignore); RunTaskUtilities.run(merger, reader, new EmptyReader(), new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { exceptions.add(e); } }); // At least one of those exceptions should be a "Pipeline not sorted" one boolean sortExceptionFound = false; for (Throwable t : exceptions) { if (!(t instanceof OsmosisRuntimeException)) { Assert.fail("Unexpected exception thrown: " + t); } sortExceptionFound |= t.getMessage().startsWith(exceptionMessagePrefix); } if (!sortExceptionFound) { Assert.fail("Expected exception not thrown"); } } }