Index: trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/nodes/NodeListMergeModelTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/nodes/NodeListMergeModelTest.java	(revision 9546)
+++ trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/nodes/NodeListMergeModelTest.java	(revision 9546)
@@ -0,0 +1,728 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.conflict.pair.nodes;
+
+import static org.fest.reflect.core.Reflection.field;
+import static org.fest.reflect.core.Reflection.method;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.DefaultListSelectionModel;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openstreetmap.josm.JOSMFixture;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DatasetFactory;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.conflict.pair.nodes.NodeListMergeModel;
+
+public class NodeListMergeModelTest {
+
+    private DatasetFactory my = new DatasetFactory();
+    private DatasetFactory their = new DatasetFactory();
+
+    /**
+     * Setup test.
+     */
+    @BeforeClass
+    public static void init() {
+        JOSMFixture.createUnitTestFixture().init();
+    }
+
+    @SuppressWarnings("unchecked")
+    protected List<Node> inspectNodeList(NodeListMergeModel model, String name) {
+        return method("get" + name + "Entries")
+        .withReturnType(List.class)
+        .in(model)
+        .invoke();
+    }
+
+    protected DefaultListSelectionModel inspectListSelectionModel(NodeListMergeModel model, String name) {
+        return field(name).ofType(DefaultListSelectionModel.class)
+        .in(model)
+        .get();
+    }
+
+    protected void ensureSelected(DefaultListSelectionModel model, Object... idx) {
+        if (idx == null) return;
+        for (int i = 0; i < idx.length; i++) {
+            if (idx[i] instanceof Integer) {
+                int j = (Integer) idx[i];
+                assertTrue("expected row " + j + " to be selected", model.isSelectedIndex(j));
+                break;
+            }
+            try {
+                int[] rows = (int[]) idx[i];
+                if (rows.length != 2) {
+                    fail("illegal selection range. Either null or not length 2: " + Arrays.toString(rows));
+                }
+                if (rows[0] > rows[1]) {
+                    fail("illegal selection range. lower bound > upper bound ");
+                }
+                for (int j = rows[0]; j <= rows[1]; j++) {
+                    assertTrue("expected row " + j + " to be selected", model.isSelectedIndex(j));
+                }
+            } catch (ClassCastException e) {
+                fail("illegal selection range:" + idx[i]);
+            }
+        }
+    }
+
+    @Test
+    public void test_copyMyNodesToTop_1() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(2));
+        myWay.addNode(my.addNode(3));
+
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+        model.copyMyToTop(new int[]{0});
+
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+
+        assertEquals(1, mergedNodes.size());
+        assertEquals(2, mergedNodes.get(0).getId());
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 0);
+    }
+
+    @Test
+    public void test_copyMyNodesToTop_2() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(2));
+        myWay.addNode(my.addNode(3));
+
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(1));
+
+        model.copyMyToTop(new int[]{0});
+
+        mergedNodes = inspectNodeList(model, "Merged");
+        assertEquals(2, mergedNodes.size());
+        assertEquals(2, mergedNodes.get(0).getId());
+        assertEquals(1, mergedNodes.get(1).getId());
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 0);
+    }
+
+    @Test
+    public void test_copyMyNodesToTop_3() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(2));
+        myWay.addNode(my.addNode(3));
+        Way theirWay = their.addWay(1);
+
+
+        model.populate(myWay, theirWay, null);
+
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(1));
+
+        model.copyMyToTop(new int[]{1}); // copy node 3
+
+        mergedNodes = inspectNodeList(model, "Merged");
+        assertEquals(2, mergedNodes.size());
+        assertEquals(3, mergedNodes.get(0).getId()); // my node 3 at position 0
+        assertEquals(1, mergedNodes.get(1).getId()); // already merged node 1 at position 1
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 0);
+    }
+
+    @Test
+    public void test_copyMyNodesToTop_4() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(2));
+        myWay.addNode(my.addNode(3));
+        myWay.addNode(my.addNode(4));
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(1));
+
+        model.copyMyToTop(new int[]{1, 2}); // copy node 3 and 4
+
+        mergedNodes = inspectNodeList(model, "Merged");
+        assertEquals(3, mergedNodes.size());
+        assertEquals(3, mergedNodes.get(0).getId()); // my node 3 at position 0
+        assertEquals(4, mergedNodes.get(1).getId()); // my node 4 at position 1
+        assertEquals(1, mergedNodes.get(2).getId()); // already merged node 1 at position 2
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 0, 1); // first two rows selected
+    }
+
+    @Test
+    public void test_copyMyNodesToEnd_1() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(2));
+        myWay.addNode(my.addNode(3));
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+        model.copyMyToEnd(new int[]{0});
+
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+
+        assertEquals(1, mergedNodes.size());
+        assertEquals(2, mergedNodes.get(0).getId());
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 0);
+    }
+
+    @Test
+    public void test_copyMyNodesToEnd_2() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(2));
+        myWay.addNode(my.addNode(3));
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(1));
+
+        model.copyMyToEnd(new int[]{0});
+
+        mergedNodes = inspectNodeList(model, "Merged");
+        assertEquals(2, mergedNodes.size());
+        assertEquals(1, mergedNodes.get(0).getId()); // already merged node 1 at position 0
+        assertEquals(2, mergedNodes.get(1).getId()); // copied node 2 at position 1
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 1);
+    }
+
+    @Test
+    public void test_copyMyNodesToEnd_3() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(2));
+        myWay.addNode(my.addNode(3));
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(1));
+
+        model.copyMyToEnd(new int[]{1}); // copy node 3
+
+        mergedNodes = inspectNodeList(model, "Merged");
+        assertEquals(2, mergedNodes.size());
+        assertEquals(1, mergedNodes.get(0).getId()); // already merged node 1 at position 0
+        assertEquals(3, mergedNodes.get(1).getId()); // my node 3 at position 1
+
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 1);
+    }
+
+    @Test
+    public void test_copyMyNodesToEnd_4() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(2));
+        myWay.addNode(my.addNode(3));
+        myWay.addNode(my.addNode(4));
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(1));
+
+        model.copyMyToEnd(new int[]{1, 2}); // copy node 3 and 4
+
+        mergedNodes = inspectNodeList(model, "Merged");
+        assertEquals(3, mergedNodes.size());
+        assertEquals(1, mergedNodes.get(0).getId()); // already merged node 1 at position 0
+        assertEquals(3, mergedNodes.get(1).getId()); // my node 3 at position 1
+        assertEquals(4, mergedNodes.get(2).getId()); // my node 4 at position 2
+
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 1, 2); // last two rows selected
+    }
+
+    /* ----------------------------------------------------------------------------- */
+    /* copyMyNodesBeforeCurrent                                                      */
+    /* ----------------------------------------------------------------------------- */
+
+    @Test
+    public void test_copyMyNodesBeforeCurrent_1() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(1));
+        myWay.addNode(my.addNode(2));
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+
+        model.copyMyBeforeCurrent(new int[]{0}, 1);
+
+        assertEquals(4, mergedNodes.size());
+        assertEquals(10, mergedNodes.get(0).getId()); // already merged node
+        assertEquals(1, mergedNodes.get(1).getId());  // copied node 1 at position 1
+        assertEquals(11, mergedNodes.get(2).getId()); // already merged node
+        assertEquals(12, mergedNodes.get(3).getId()); // already merged node
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 1); // position 1 selected
+    }
+
+    @Test
+    public void test_copyMyNodesBeforeCurrent_2() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(1));
+        myWay.addNode(my.addNode(2));
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+
+        model.copyMyBeforeCurrent(new int[]{0, 1}, 0);
+
+        assertEquals(5, mergedNodes.size());
+        assertEquals(1, mergedNodes.get(0).getId());  // copied node 1 at position 0
+        assertEquals(2, mergedNodes.get(1).getId());  // copied node 2 at position 1
+        assertEquals(10, mergedNodes.get(2).getId()); // already merged node
+        assertEquals(11, mergedNodes.get(3).getId()); // already merged node
+        assertEquals(12, mergedNodes.get(4).getId()); // already merged node
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 0, 1); // position 0 and 1 selected
+    }
+
+    @Test
+    public void test_copyMyNodesBeforeCurrent_3() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(1));
+        myWay.addNode(my.addNode(2));
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+
+        try {
+            model.copyMyBeforeCurrent(new int[]{0, 1}, -1);
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // OK
+            if (Main.isTraceEnabled()) {
+                Main.trace(e.getMessage());
+            }
+        }
+
+        try {
+            model.copyMyBeforeCurrent(new int[]{0, 1}, 4);
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // OK
+            if (Main.isTraceEnabled()) {
+                Main.trace(e.getMessage());
+            }
+        }
+    }
+
+    /* ----------------------------------------------------------------------------- */
+    /* copyMyNodesAfterCurrent                                                       */
+    /* ----------------------------------------------------------------------------- */
+    @Test
+    public void test_copyMyNodesAfterCurrent_1() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(1));
+        myWay.addNode(my.addNode(2));
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+
+        model.copyMyAfterCurrent(new int[]{0}, 1);
+
+        assertEquals(4, mergedNodes.size());
+        assertEquals(10, mergedNodes.get(0).getId()); // already merged node
+        assertEquals(11, mergedNodes.get(1).getId()); // already merged node
+        assertEquals(1, mergedNodes.get(2).getId());  // copied node 1 at position 2
+        assertEquals(12, mergedNodes.get(3).getId()); // already merged node
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 2); // position 1 selected
+    }
+
+    @Test
+    public void test_copyMyNodesAfterCurrent_2() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(1));
+        myWay.addNode(my.addNode(2));
+        myWay.addNode(my.addNode(3));
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+
+        model.copyMyAfterCurrent(new int[]{0, 1}, 2);
+
+        assertEquals(5, mergedNodes.size());
+        assertEquals(10, mergedNodes.get(0).getId()); // already merged node
+        assertEquals(11, mergedNodes.get(1).getId()); // already merged node
+        assertEquals(12, mergedNodes.get(2).getId()); // already merged node
+        assertEquals(1, mergedNodes.get(3).getId());  // copied node 1 at position 3
+        assertEquals(2, mergedNodes.get(4).getId());  // copied node 2 at position 4
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 3, 4); // position 3,4 selected
+    }
+
+    @Test
+    public void test_copyMyNodesAfterCurrent_3() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = my.addWay(1, 1);
+        myWay.addNode(my.addNode(1));
+        myWay.addNode(my.addNode(2));
+        myWay.addNode(my.addNode(3));
+        Way theirWay = their.addWay(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+
+        model.copyMyAfterCurrent(new int[]{0, 2}, 0);
+
+        assertEquals(5, mergedNodes.size());
+        assertEquals(10, mergedNodes.get(0).getId()); // already merged node
+        assertEquals(1, mergedNodes.get(1).getId());  // copied node 1 at position 1
+        assertEquals(3, mergedNodes.get(2).getId());  // copied node 3 at position 2
+        assertEquals(11, mergedNodes.get(3).getId()); // already merged node
+        assertEquals(12, mergedNodes.get(4).getId()); // already merged node
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 1, 2); // position 1,2 selected
+    }
+
+
+    /* ----------------------------------------------------------------------------- */
+    /* moveUpMergedNodes                                                       */
+    /* ----------------------------------------------------------------------------- */
+    @Test
+    public void test_moveUpMergedNodes_1() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = new Way(1, 1);
+        myWay.addNode(new Node(1));
+        myWay.addNode(new Node(2));
+        Way theirWay = new Way(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+
+        model.moveUpMerged(new int[]{1});
+
+        assertEquals(3, mergedNodes.size());
+        assertEquals(11, mergedNodes.get(0).getId());
+        assertEquals(10, mergedNodes.get(1).getId());
+        assertEquals(12, mergedNodes.get(2).getId());
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 0); // position 1 selecte0
+    }
+
+    @Test
+    public void test_moveUpMergedNodes_2() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = new Way(1, 1);
+        myWay.addNode(new Node(1));
+        myWay.addNode(new Node(2));
+        Way theirWay = new Way(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+        mergedNodes.add(new Node(13));
+        mergedNodes.add(new Node(14));
+
+        model.moveUpMerged(new int[]{1, 4});
+
+        assertEquals(5, mergedNodes.size());
+        assertEquals(11, mergedNodes.get(0).getId());
+        assertEquals(10, mergedNodes.get(1).getId());
+        assertEquals(12, mergedNodes.get(2).getId());
+        assertEquals(14, mergedNodes.get(3).getId());
+        assertEquals(13, mergedNodes.get(4).getId());
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 0, 3); // position 0 and 3 selecte0
+    }
+
+    @Test
+    public void test_moveUpMergedNodes_3() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = new Way(1, 1);
+        myWay.addNode(new Node(1));
+        myWay.addNode(new Node(2));
+        Way theirWay = new Way(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+        mergedNodes.add(new Node(13));
+        mergedNodes.add(new Node(14));
+
+        model.moveUpMerged(new int[]{1, 2, 3, 4});
+
+        assertEquals(5, mergedNodes.size());
+        assertEquals(11, mergedNodes.get(0).getId());
+        assertEquals(12, mergedNodes.get(1).getId());
+        assertEquals(13, mergedNodes.get(2).getId());
+        assertEquals(14, mergedNodes.get(3).getId());
+        assertEquals(10, mergedNodes.get(4).getId());
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 0, 1, 2, 3);
+    }
+
+    /* ----------------------------------------------------------------------------- */
+    /* moveDownMergedNodes                                                       */
+    /* ----------------------------------------------------------------------------- */
+    @Test
+    public void test_moveDownMergedNodes_1() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = new Way(1, 1);
+        myWay.addNode(new Node(1));
+        myWay.addNode(new Node(2));
+        Way theirWay = new Way(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+
+        model.moveDownMerged(new int[]{1});
+
+        assertEquals(3, mergedNodes.size());
+        assertEquals(10, mergedNodes.get(0).getId());
+        assertEquals(12, mergedNodes.get(1).getId());
+        assertEquals(11, mergedNodes.get(2).getId());
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 2);
+    }
+
+    @Test
+    public void test_moveDownMergedNodes_2() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = new Way(1, 1);
+        myWay.addNode(new Node(1));
+        myWay.addNode(new Node(2));
+        Way theirWay = new Way(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+        mergedNodes.add(new Node(13));
+        mergedNodes.add(new Node(14));
+
+        model.moveDownMerged(new int[]{1, 3});
+
+        assertEquals(5, mergedNodes.size());
+        assertEquals(10, mergedNodes.get(0).getId());
+        assertEquals(12, mergedNodes.get(1).getId());
+        assertEquals(11, mergedNodes.get(2).getId());
+        assertEquals(14, mergedNodes.get(3).getId());
+        assertEquals(13, mergedNodes.get(4).getId());
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 2, 4);
+    }
+
+    @Test
+    public void test_moveDownMergedNodes_3() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        Way myWay = new Way(1, 1);
+        myWay.addNode(new Node(1));
+        myWay.addNode(new Node(2));
+        Way theirWay = new Way(1);
+
+        model.populate(myWay, theirWay, null);
+        List<Node> mergedNodes = inspectNodeList(model, "Merged");
+        mergedNodes.add(new Node(10));
+        mergedNodes.add(new Node(11));
+        mergedNodes.add(new Node(12));
+        mergedNodes.add(new Node(13));
+        mergedNodes.add(new Node(14));
+
+        model.moveDownMerged(new int[]{1, 2, 3});
+
+        assertEquals(5, mergedNodes.size());
+        assertEquals(10, mergedNodes.get(0).getId());
+        assertEquals(14, mergedNodes.get(1).getId());
+        assertEquals(11, mergedNodes.get(2).getId());
+        assertEquals(12, mergedNodes.get(3).getId());
+        assertEquals(13, mergedNodes.get(4).getId());
+
+        DefaultListSelectionModel mergedSelection = inspectListSelectionModel(model, "mergedEntriesSelectionModel");
+        ensureSelected(mergedSelection, 2, 3, 4);
+    }
+
+    /* ----------------------------------------------------------------------------- */
+    /* PropertyChangeListener                                                        */
+    /* ----------------------------------------------------------------------------- */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void addPropertyChangeListener() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        PropertyChangeListener listener = new PropertyChangeListener() {
+
+            public void propertyChange(PropertyChangeEvent evt) {
+            }
+        };
+
+        model.addPropertyChangeListener(listener);
+
+        Set<PropertyChangeListener> listeners = field("listeners")
+        .ofType(Set.class)
+        .in(model)
+        .get();
+
+        assertEquals(1, listeners.size());
+        assertEquals(listener, listeners.iterator().next());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void removePropertyChangeListener() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        PropertyChangeListener listener = new PropertyChangeListener() {
+
+            public void propertyChange(PropertyChangeEvent evt) {
+            }
+        };
+
+        model.addPropertyChangeListener(listener);
+        model.removePropertyChangeListener(listener);
+
+        Set<PropertyChangeListener> listeners = field("listeners")
+        .ofType(Set.class)
+        .in(model)
+        .get();
+
+        assertEquals(0, listeners.size());
+    }
+
+    /* ----------------------------------------------------------------------------- */
+    /* property frozen                                                               */
+    /* ----------------------------------------------------------------------------- */
+    @Test
+    public void setFrozen() {
+        NodeListMergeModel model = new NodeListMergeModel();
+        model.setFrozen(true);
+        assertTrue(model.isFrozen());
+
+        model.setFrozen(false);
+        assertFalse(model.isFrozen());
+    }
+
+    @Test
+    public void setFrozenWithPropertyChangeNotification() {
+        NodeListMergeModel model = new NodeListMergeModel();
+
+        class MyListener implements PropertyChangeListener {
+            public ArrayList<PropertyChangeEvent> events = new ArrayList<>();
+
+            public void propertyChange(PropertyChangeEvent evt) {
+                events.add(evt);
+            }
+        }
+
+        MyListener listener = new MyListener();
+        model.addPropertyChangeListener(listener);
+        boolean oldValue = model.isFrozen();
+        model.setFrozen(!oldValue);
+        assertEquals(!oldValue, model.isFrozen());
+
+        assertEquals(1, listener.events.size());
+        assertEquals(oldValue, listener.events.get(0).getOldValue());
+        assertEquals(!oldValue, listener.events.get(0).getNewValue());
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/nodes/NodeListMergerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/nodes/NodeListMergerTest.java	(revision 9546)
+++ trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/nodes/NodeListMergerTest.java	(revision 9546)
@@ -0,0 +1,30 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.conflict.pair.nodes;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openstreetmap.josm.JOSMFixture;
+
+/**
+ * Unit tests of {@link NodeListMerger} class.
+ */
+public class NodeListMergerTest {
+
+    /**
+     * Setup test.
+     */
+    @BeforeClass
+    public static void setUpBeforeClass() {
+        JOSMFixture.createUnitTestFixture().init();
+    }
+
+    /**
+     * Unit test of {@link NodeListMerger#NodeListMerger}.
+     */
+    @Test
+    public void testNodeListMerger() {
+        assertNotNull(new NodeListMerger());
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMergeModelTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMergeModelTest.java	(revision 9546)
+++ trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMergeModelTest.java	(revision 9546)
@@ -0,0 +1,145 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.conflict.pair.properties;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openstreetmap.josm.JOSMFixture;
+import org.openstreetmap.josm.data.conflict.Conflict;
+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.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType;
+import org.openstreetmap.josm.gui.conflict.pair.properties.PropertiesMergeModel;
+
+public class PropertiesMergeModelTest {
+
+    public abstract static class TestObserver implements Observer {
+        public int numInvocations;
+
+        public void update(Observable o, Object arg) {
+            numInvocations++;
+            test();
+        }
+
+        public abstract void test();
+
+        public void assertNumInvocations(int count) {
+            assertEquals(count, numInvocations);
+        }
+    }
+
+    PropertiesMergeModel model;
+
+    /**
+     * Setup test.
+     */
+    @BeforeClass
+    public static void init() {
+        JOSMFixture.createUnitTestFixture().init();
+    }
+
+    /**
+     * Setup test.
+     */
+    @Before
+    public void setUp() {
+        model = new PropertiesMergeModel();
+    }
+
+    private void populate(OsmPrimitive my, OsmPrimitive their) {
+        model.populate(new Conflict<>(my, their));
+    }
+
+    @Test
+    public void populate() {
+        DataSet d1 = new DataSet();
+        DataSet d2 = new DataSet();
+        Node n1 = new Node(1);
+        Node n2 = new Node(1);
+        d1.addPrimitive(n1);
+        d2.addPrimitive(n2);
+        populate(n1, n2);
+
+        Way w1 = new Way(1);
+        Way w2 = new Way(1);
+        d1.addPrimitive(w1);
+        d2.addPrimitive(w2);
+        populate(w2, w2);
+
+        Relation r1 = new Relation(1);
+        Relation r2 = new Relation(1);
+        d1.addPrimitive(r1);
+        d2.addPrimitive(r2);
+        populate(r1, r2);
+    }
+
+    @Test
+    public void decidingAboutCoords() {
+        DataSet d1 = new DataSet();
+        DataSet d2 = new DataSet();
+
+        Node n1 = new Node(1);
+        Node n2 = new Node(1);
+        d1.addPrimitive(n1);
+        d2.addPrimitive(n2);
+        populate(n1, n2);
+        assertFalse(model.hasCoordConflict());
+
+        n1.setCoor(new LatLon(1, 1));
+        populate(n1, n2);
+        assertTrue(model.hasCoordConflict());
+
+
+        n1.cloneFrom(new Node(1));
+        n2.setCoor(new LatLon(2, 2));
+        populate(n1, n2);
+        assertTrue(model.hasCoordConflict());
+
+        n1.setCoor(new LatLon(1, 1));
+        n2.setCoor(new LatLon(2, 2));
+        populate(n1, n2);
+        assertTrue(model.hasCoordConflict());
+
+        // decide KEEP_MINE  and ensure notification via Observable
+        //
+        TestObserver observerTest;
+        model.addObserver(
+                observerTest = new TestObserver() {
+                    @Override
+                    public void test() {
+                        assertTrue(model.isCoordMergeDecision(MergeDecisionType.KEEP_MINE));
+                    }
+                }
+        );
+        model.decideCoordsConflict(MergeDecisionType.KEEP_MINE);
+        assertTrue(model.isCoordMergeDecision(MergeDecisionType.KEEP_MINE));
+        observerTest.assertNumInvocations(1);
+
+        // decide KEEP_THEIR and  ensure notification via Observable
+        //
+        model.deleteObserver(observerTest);
+        model.addObserver(
+                observerTest = new TestObserver() {
+                    @Override
+                    public void test() {
+                        assertTrue(model.isCoordMergeDecision(MergeDecisionType.KEEP_THEIR));
+                    }
+                }
+        );
+        model.decideCoordsConflict(MergeDecisionType.KEEP_THEIR);
+        assertTrue(model.isCoordMergeDecision(MergeDecisionType.KEEP_THEIR));
+        observerTest.assertNumInvocations(1);
+        model.deleteObserver(observerTest);
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMergerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMergerTest.java	(revision 9546)
+++ trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMergerTest.java	(revision 9546)
@@ -0,0 +1,30 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.conflict.pair.properties;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openstreetmap.josm.JOSMFixture;
+
+/**
+ * Unit tests of {@link PropertiesMerger} class.
+ */
+public class PropertiesMergerTest {
+
+    /**
+     * Setup test.
+     */
+    @BeforeClass
+    public static void setUpBeforeClass() {
+        JOSMFixture.createUnitTestFixture().init();
+    }
+
+    /**
+     * Unit test of {@link PropertiesMerger#PropertiesMerger}.
+     */
+    @Test
+    public void testPropertiesMerger() {
+        assertNotNull(new PropertiesMerger());
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/relation/RelationMemberMergerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/relation/RelationMemberMergerTest.java	(revision 9546)
+++ trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/relation/RelationMemberMergerTest.java	(revision 9546)
@@ -0,0 +1,30 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.conflict.pair.relation;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openstreetmap.josm.JOSMFixture;
+
+/**
+ * Unit tests of {@link RelationMemberMerger} class.
+ */
+public class RelationMemberMergerTest {
+
+    /**
+     * Setup test.
+     */
+    @BeforeClass
+    public static void setUpBeforeClass() {
+        JOSMFixture.createUnitTestFixture().init();
+    }
+
+    /**
+     * Unit test of {@link RelationMemberMerger#RelationMemberMerger}.
+     */
+    @Test
+    public void testRelationMemberMerger() {
+        assertNotNull(new RelationMemberMerger());
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/tags/TagMergeItemTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/tags/TagMergeItemTest.java	(revision 9546)
+++ trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/tags/TagMergeItemTest.java	(revision 9546)
@@ -0,0 +1,165 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.conflict.pair.tags;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openstreetmap.josm.JOSMFixture;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType;
+import org.openstreetmap.josm.gui.conflict.pair.tags.TagMergeItem;
+
+/**
+ * Unit tests of {@link TagMergeItem} class.
+ */
+public class TagMergeItemTest {
+
+    /**
+     * Setup test.
+     */
+    @BeforeClass
+    public static void init() {
+        JOSMFixture.createUnitTestFixture().init();
+    }
+
+    @Test
+    public void test_TagMergeItem() {
+        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
+        assertEquals("key", item.getKey());
+        assertEquals("myvalue", item.getMyTagValue());
+        assertEquals("theirvalue", item.getTheirTagValue());
+        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
+    }
+
+    @Test
+    public void test_TagMergeItem_2() {
+        Node n1 = new Node(1);
+        Node n2 = new Node(1);
+        n1.put("key", "myvalue");
+        n2.put("key", "theirvalue");
+
+        TagMergeItem item = new TagMergeItem("key", n1, n2);
+        assertEquals("key", item.getKey());
+        assertEquals("myvalue", item.getMyTagValue());
+        assertEquals("theirvalue", item.getTheirTagValue());
+        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
+    }
+
+    @Test
+    public void test_TagMergeItem_3() {
+        Node n1 = new Node(1);
+        Node n2 = new Node(1);
+        n1.put("key", "myvalue");
+        // n2 does not have this key
+
+        TagMergeItem item = new TagMergeItem("key", n1, n2);
+        assertEquals("key", item.getKey());
+        assertEquals("myvalue", item.getMyTagValue());
+        assertNull(item.getTheirTagValue());
+        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
+    }
+
+    @Test
+    public void test_TagMergeItem_4() {
+        Node n1 = new Node(1);
+        Node n2 = new Node(1);
+        // n1 does not have this key
+        // n1.put("key", "myvalue");
+        n2.put("key", "theirvalue");
+
+        TagMergeItem item = new TagMergeItem("key", n1, n2);
+        assertEquals("key", item.getKey());
+        assertNull(item.getMyTagValue());
+        assertEquals("theirvalue", item.getTheirTagValue());
+        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
+    }
+
+    @Test
+    public void test_decide() {
+        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
+        item.decide(MergeDecisionType.KEEP_MINE);
+        assertEquals(MergeDecisionType.KEEP_MINE, item.getMergeDecision());
+    }
+
+    @Test
+    public void test_decide_1() {
+        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
+        try {
+            item.decide(null);
+            fail("expected IllegalArgumentException not thrown");
+        } catch (IllegalArgumentException e) {
+            // OK
+            if (Main.isTraceEnabled()) {
+                Main.trace(e.getMessage());
+            }
+        }
+    }
+
+    @Test
+    public void test_applyToMyPrimitive() {
+        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
+        item.decide(MergeDecisionType.KEEP_MINE);
+
+        Node n1 = new Node(1);
+        n1.put("key", "oldvalue");
+        item.applyToMyPrimitive(n1);
+        assertEquals("myvalue", n1.get("key"));
+
+        n1 = new Node(1);
+        item.applyToMyPrimitive(n1);
+        assertEquals("myvalue", n1.get("key"));
+    }
+
+    @Test
+    public void test_applyToMyPrimitive_2() {
+        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
+        item.decide(MergeDecisionType.KEEP_THEIR);
+
+        Node n1 = new Node(1);
+        n1.put("key", "oldvalue");
+        item.applyToMyPrimitive(n1);
+        assertEquals("theirvalue", n1.get("key"));
+
+        n1 = new Node(1);
+        item.applyToMyPrimitive(n1);
+        assertEquals("theirvalue", n1.get("key"));
+    }
+
+    @Test
+    public void test_applyToMyPrimitive_3() {
+        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
+        // item is undecided
+        // item.decide(MergeDecisionType.KEEP_THEIR);
+
+        Node n1 = new Node(1);
+        n1.put("key", "oldvalue");
+        try {
+            item.applyToMyPrimitive(n1);
+            fail("expected IllegalStateException");
+        } catch (IllegalStateException e) {
+            // OK
+            if (Main.isTraceEnabled()) {
+                Main.trace(e.getMessage());
+            }
+        }
+    }
+
+    @Test
+    public void test_applyToMyPrimitive_4() {
+        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
+
+        try {
+            item.applyToMyPrimitive(null);
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // OK
+            if (Main.isTraceEnabled()) {
+                Main.trace(e.getMessage());
+            }
+        }
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/tags/TagMergeModelTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/tags/TagMergeModelTest.java	(revision 9546)
+++ trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/tags/TagMergeModelTest.java	(revision 9546)
@@ -0,0 +1,239 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.conflict.pair.tags;
+
+import static org.fest.reflect.core.Reflection.field;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openstreetmap.josm.JOSMFixture;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType;
+import org.openstreetmap.josm.gui.conflict.pair.tags.TagMergeItem;
+import org.openstreetmap.josm.gui.conflict.pair.tags.TagMergeModel;
+
+/**
+ * Unit tests of {@link TagMergeModel} class.
+ */
+@SuppressWarnings("unchecked")
+public class TagMergeModelTest {
+
+    /**
+     * Setup test.
+     */
+    @BeforeClass
+    public static void init() {
+        JOSMFixture.createUnitTestFixture().init();
+    }
+
+    @Test
+    public void addPropertyChangeListener() {
+        TagMergeModel model = new TagMergeModel();
+        PropertyChangeListener listener = new PropertyChangeListener() {
+
+            public void propertyChange(PropertyChangeEvent evt) {
+            }
+        };
+        model.addPropertyChangeListener(listener);
+
+        Set<?> list = field("listeners").ofType(Set.class)
+        .in(model)
+        .get();
+
+        assertEquals(1, list.size());
+        assertEquals(listener, list.iterator().next());
+    }
+
+    @Test
+    public void removePropertyChangeListener() {
+        TagMergeModel model = new TagMergeModel();
+        PropertyChangeListener listener = new PropertyChangeListener() {
+
+            public void propertyChange(PropertyChangeEvent evt) {
+            }
+        };
+        model.addPropertyChangeListener(listener);
+        model.removePropertyChangeListener(listener);
+
+        Set<?> list = field("listeners")
+        .ofType(Set.class)
+        .in(model)
+        .get();
+
+        assertEquals(0, list.size());
+    }
+
+    @Test
+    public void populateNoConflichts() {
+        Node my = new Node(1);
+        Node their = new Node(1);
+        TagMergeModel model = new TagMergeModel();
+        model.populate(my, their);
+
+        List<TagMergeItem> list = field("tagMergeItems")
+        .ofType(List.class)
+        .in(model)
+        .get();
+
+        assertEquals(0, list.size());
+    }
+
+    @Test
+    public void populateNoConflicts1() {
+        Node my = new Node(1);
+        my.put("key", "value");
+        Node their = new Node(1);
+        their.put("key", "value");
+        TagMergeModel model = new TagMergeModel();
+        model.populate(my, their);
+
+        List<TagMergeItem> list = field("tagMergeItems")
+        .ofType(List.class)
+        .in(model)
+        .get();
+
+        assertEquals(0, list.size());
+    }
+
+    @Test
+    public void populateMissingKeyMine() {
+        Node my = new Node(1);
+        Node their = new Node(1);
+        their.put("key", "value");
+        TagMergeModel model = new TagMergeModel();
+        model.populate(my, their);
+
+        List<TagMergeItem> list = field("tagMergeItems")
+        .ofType(List.class)
+        .in(model)
+        .get();
+
+        assertEquals(1, list.size());
+        TagMergeItem item = list.get(0);
+        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
+        assertEquals("key", item.getKey());
+        assertNull(item.getMyTagValue());
+        assertEquals("value", item.getTheirTagValue());
+    }
+
+    @Test
+    public void populateMissingKeyTheir() {
+        Node my = new Node(1);
+        my.put("key", "value");
+        Node their = new Node(1);
+        TagMergeModel model = new TagMergeModel();
+        model.populate(my, their);
+
+        List<TagMergeItem> list = field("tagMergeItems")
+        .ofType(List.class)
+        .in(model)
+        .get();
+
+        assertEquals(1, list.size());
+        TagMergeItem item = list.get(0);
+        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
+        assertEquals("key", item.getKey());
+        assertNull(item.getTheirTagValue());
+        assertEquals("value", item.getMyTagValue());
+    }
+
+    @Test
+    public void populateConflictingValues() {
+        Node my = new Node(1);
+        my.put("key", "myvalue");
+        Node their = new Node(1);
+        their.put("key", "theirvalue");
+        TagMergeModel model = new TagMergeModel();
+        model.populate(my, their);
+
+        List<TagMergeItem> list = field("tagMergeItems")
+        .ofType(List.class)
+        .in(model)
+        .get();
+
+        assertEquals(1, list.size());
+        TagMergeItem item = list.get(0);
+        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
+        assertEquals("key", item.getKey());
+        assertEquals("myvalue", item.getMyTagValue());
+        assertEquals("theirvalue", item.getTheirTagValue());
+    }
+
+    @Test
+    public void addItem() {
+        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
+        TagMergeModel model = new TagMergeModel();
+        model.addItem(item);
+
+        List<TagMergeItem> list = field("tagMergeItems")
+        .ofType(List.class)
+        .in(model)
+        .get();
+
+        assertEquals(1, list.size());
+        item = list.get(0);
+        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
+        assertEquals("key", item.getKey());
+        assertEquals("myvalue", item.getMyTagValue());
+        assertEquals("theirvalue", item.getTheirTagValue());
+    }
+
+    @Test
+    public void decide() {
+        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
+        TagMergeModel model = new TagMergeModel();
+        model.addItem(item);
+
+        List<TagMergeItem> list = field("tagMergeItems")
+        .ofType(List.class)
+        .in(model)
+        .get();
+
+        model.decide(0, MergeDecisionType.KEEP_MINE);
+        assertEquals(1, list.size());
+        item = list.get(0);
+        assertEquals(MergeDecisionType.KEEP_MINE, item.getMergeDecision());
+
+        model.decide(0, MergeDecisionType.KEEP_THEIR);
+        assertEquals(1, list.size());
+        item = list.get(0);
+        assertEquals(MergeDecisionType.KEEP_THEIR, item.getMergeDecision());
+
+        model.decide(0, MergeDecisionType.UNDECIDED);
+        assertEquals(1, list.size());
+        item = list.get(0);
+        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
+    }
+
+    @Test
+    public void decideMultiple() {
+
+        TagMergeModel model = new TagMergeModel();
+        for (int i = 0; i < 10; i++) {
+            model.addItem(new TagMergeItem("key-" + i, "myvalue-" + i, "theirvalue-" +i));
+        }
+
+        List<TagMergeItem> list = field("tagMergeItems")
+        .ofType(List.class)
+        .in(model)
+        .get();
+
+        assertEquals(10, list.size());
+
+        model.decide(new int[] {0, 3, 5}, MergeDecisionType.KEEP_MINE);
+        for (int i = 0; i < 10; i++) {
+            TagMergeItem item = list.get(i);
+            if (i == 0 || i == 3 || i == 5) {
+                assertEquals(MergeDecisionType.KEEP_MINE, item.getMergeDecision());
+            } else {
+                assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
+            }
+        }
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/tags/TagMergerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/tags/TagMergerTest.java	(revision 9546)
+++ trunk/test/unit/org/openstreetmap/josm/gui/conflict/pair/tags/TagMergerTest.java	(revision 9546)
@@ -0,0 +1,30 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.conflict.pair.tags;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openstreetmap.josm.JOSMFixture;
+
+/**
+ * Unit tests of {@link TagMerger} class.
+ */
+public class TagMergerTest {
+
+    /**
+     * Setup test.
+     */
+    @BeforeClass
+    public static void setUpBeforeClass() {
+        JOSMFixture.createUnitTestFixture().init();
+    }
+
+    /**
+     * Unit test of {@link TagMerger#TagMerger}.
+     */
+    @Test
+    public void testTagMerger() {
+        assertNotNull(new TagMerger());
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/gui/conflict/tags/TagMergeItemTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/conflict/tags/TagMergeItemTest.java	(revision 9544)
+++ 	(revision )
@@ -1,165 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.conflict.tags;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.openstreetmap.josm.JOSMFixture;
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType;
-import org.openstreetmap.josm.gui.conflict.pair.tags.TagMergeItem;
-
-/**
- * Unit tests of {@link TagMergeItem} class.
- */
-public class TagMergeItemTest {
-
-    /**
-     * Setup test.
-     */
-    @BeforeClass
-    public static void init() {
-        JOSMFixture.createUnitTestFixture().init();
-    }
-
-    @Test
-    public void test_TagMergeItem() {
-        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
-        assertEquals("key", item.getKey());
-        assertEquals("myvalue", item.getMyTagValue());
-        assertEquals("theirvalue", item.getTheirTagValue());
-        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
-    }
-
-    @Test
-    public void test_TagMergeItem_2() {
-        Node n1 = new Node(1);
-        Node n2 = new Node(1);
-        n1.put("key", "myvalue");
-        n2.put("key", "theirvalue");
-
-        TagMergeItem item = new TagMergeItem("key", n1, n2);
-        assertEquals("key", item.getKey());
-        assertEquals("myvalue", item.getMyTagValue());
-        assertEquals("theirvalue", item.getTheirTagValue());
-        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
-    }
-
-    @Test
-    public void test_TagMergeItem_3() {
-        Node n1 = new Node(1);
-        Node n2 = new Node(1);
-        n1.put("key", "myvalue");
-        // n2 does not have this key
-
-        TagMergeItem item = new TagMergeItem("key", n1, n2);
-        assertEquals("key", item.getKey());
-        assertEquals("myvalue", item.getMyTagValue());
-        assertNull(item.getTheirTagValue());
-        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
-    }
-
-    @Test
-    public void test_TagMergeItem_4() {
-        Node n1 = new Node(1);
-        Node n2 = new Node(1);
-        // n1 does not have this key
-        // n1.put("key", "myvalue");
-        n2.put("key", "theirvalue");
-
-        TagMergeItem item = new TagMergeItem("key", n1, n2);
-        assertEquals("key", item.getKey());
-        assertNull(item.getMyTagValue());
-        assertEquals("theirvalue", item.getTheirTagValue());
-        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
-    }
-
-    @Test
-    public void test_decide() {
-        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
-        item.decide(MergeDecisionType.KEEP_MINE);
-        assertEquals(MergeDecisionType.KEEP_MINE, item.getMergeDecision());
-    }
-
-    @Test
-    public void test_decide_1() {
-        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
-        try {
-            item.decide(null);
-            fail("expected IllegalArgumentException not thrown");
-        } catch (IllegalArgumentException e) {
-            // OK
-            if (Main.isTraceEnabled()) {
-                Main.trace(e.getMessage());
-            }
-        }
-    }
-
-    @Test
-    public void test_applyToMyPrimitive() {
-        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
-        item.decide(MergeDecisionType.KEEP_MINE);
-
-        Node n1 = new Node(1);
-        n1.put("key", "oldvalue");
-        item.applyToMyPrimitive(n1);
-        assertEquals("myvalue", n1.get("key"));
-
-        n1 = new Node(1);
-        item.applyToMyPrimitive(n1);
-        assertEquals("myvalue", n1.get("key"));
-    }
-
-    @Test
-    public void test_applyToMyPrimitive_2() {
-        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
-        item.decide(MergeDecisionType.KEEP_THEIR);
-
-        Node n1 = new Node(1);
-        n1.put("key", "oldvalue");
-        item.applyToMyPrimitive(n1);
-        assertEquals("theirvalue", n1.get("key"));
-
-        n1 = new Node(1);
-        item.applyToMyPrimitive(n1);
-        assertEquals("theirvalue", n1.get("key"));
-    }
-
-    @Test
-    public void test_applyToMyPrimitive_3() {
-        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
-        // item is undecided
-        // item.decide(MergeDecisionType.KEEP_THEIR);
-
-        Node n1 = new Node(1);
-        n1.put("key", "oldvalue");
-        try {
-            item.applyToMyPrimitive(n1);
-            fail("expected IllegalStateException");
-        } catch (IllegalStateException e) {
-            // OK
-            if (Main.isTraceEnabled()) {
-                Main.trace(e.getMessage());
-            }
-        }
-    }
-
-    @Test
-    public void test_applyToMyPrimitive_4() {
-        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
-
-        try {
-            item.applyToMyPrimitive(null);
-            fail("expected IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // OK
-            if (Main.isTraceEnabled()) {
-                Main.trace(e.getMessage());
-            }
-        }
-    }
-}
Index: trunk/test/unit/org/openstreetmap/josm/gui/conflict/tags/TagMergeModelTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/conflict/tags/TagMergeModelTest.java	(revision 9544)
+++ 	(revision )
@@ -1,239 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.conflict.tags;
-
-import static org.fest.reflect.core.Reflection.field;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.util.List;
-import java.util.Set;
-
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.openstreetmap.josm.JOSMFixture;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType;
-import org.openstreetmap.josm.gui.conflict.pair.tags.TagMergeItem;
-import org.openstreetmap.josm.gui.conflict.pair.tags.TagMergeModel;
-
-/**
- * Unit tests of {@link TagMergeModel} class.
- */
-@SuppressWarnings("unchecked")
-public class TagMergeModelTest {
-
-    /**
-     * Setup test.
-     */
-    @BeforeClass
-    public static void init() {
-        JOSMFixture.createUnitTestFixture().init();
-    }
-
-    @Test
-    public void addPropertyChangeListener() {
-        TagMergeModel model = new TagMergeModel();
-        PropertyChangeListener listener = new PropertyChangeListener() {
-
-            public void propertyChange(PropertyChangeEvent evt) {
-            }
-        };
-        model.addPropertyChangeListener(listener);
-
-        Set<?> list = field("listeners").ofType(Set.class)
-        .in(model)
-        .get();
-
-        assertEquals(1, list.size());
-        assertEquals(listener, list.iterator().next());
-    }
-
-    @Test
-    public void removePropertyChangeListener() {
-        TagMergeModel model = new TagMergeModel();
-        PropertyChangeListener listener = new PropertyChangeListener() {
-
-            public void propertyChange(PropertyChangeEvent evt) {
-            }
-        };
-        model.addPropertyChangeListener(listener);
-        model.removePropertyChangeListener(listener);
-
-        Set<?> list = field("listeners")
-        .ofType(Set.class)
-        .in(model)
-        .get();
-
-        assertEquals(0, list.size());
-    }
-
-    @Test
-    public void populateNoConflichts() {
-        Node my = new Node(1);
-        Node their = new Node(1);
-        TagMergeModel model = new TagMergeModel();
-        model.populate(my, their);
-
-        List<TagMergeItem> list = field("tagMergeItems")
-        .ofType(List.class)
-        .in(model)
-        .get();
-
-        assertEquals(0, list.size());
-    }
-
-    @Test
-    public void populateNoConflicts1() {
-        Node my = new Node(1);
-        my.put("key", "value");
-        Node their = new Node(1);
-        their.put("key", "value");
-        TagMergeModel model = new TagMergeModel();
-        model.populate(my, their);
-
-        List<TagMergeItem> list = field("tagMergeItems")
-        .ofType(List.class)
-        .in(model)
-        .get();
-
-        assertEquals(0, list.size());
-    }
-
-    @Test
-    public void populateMissingKeyMine() {
-        Node my = new Node(1);
-        Node their = new Node(1);
-        their.put("key", "value");
-        TagMergeModel model = new TagMergeModel();
-        model.populate(my, their);
-
-        List<TagMergeItem> list = field("tagMergeItems")
-        .ofType(List.class)
-        .in(model)
-        .get();
-
-        assertEquals(1, list.size());
-        TagMergeItem item = list.get(0);
-        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
-        assertEquals("key", item.getKey());
-        assertNull(item.getMyTagValue());
-        assertEquals("value", item.getTheirTagValue());
-    }
-
-    @Test
-    public void populateMissingKeyTheir() {
-        Node my = new Node(1);
-        my.put("key", "value");
-        Node their = new Node(1);
-        TagMergeModel model = new TagMergeModel();
-        model.populate(my, their);
-
-        List<TagMergeItem> list = field("tagMergeItems")
-        .ofType(List.class)
-        .in(model)
-        .get();
-
-        assertEquals(1, list.size());
-        TagMergeItem item = list.get(0);
-        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
-        assertEquals("key", item.getKey());
-        assertNull(item.getTheirTagValue());
-        assertEquals("value", item.getMyTagValue());
-    }
-
-    @Test
-    public void populateConflictingValues() {
-        Node my = new Node(1);
-        my.put("key", "myvalue");
-        Node their = new Node(1);
-        their.put("key", "theirvalue");
-        TagMergeModel model = new TagMergeModel();
-        model.populate(my, their);
-
-        List<TagMergeItem> list = field("tagMergeItems")
-        .ofType(List.class)
-        .in(model)
-        .get();
-
-        assertEquals(1, list.size());
-        TagMergeItem item = list.get(0);
-        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
-        assertEquals("key", item.getKey());
-        assertEquals("myvalue", item.getMyTagValue());
-        assertEquals("theirvalue", item.getTheirTagValue());
-    }
-
-    @Test
-    public void addItem() {
-        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
-        TagMergeModel model = new TagMergeModel();
-        model.addItem(item);
-
-        List<TagMergeItem> list = field("tagMergeItems")
-        .ofType(List.class)
-        .in(model)
-        .get();
-
-        assertEquals(1, list.size());
-        item = list.get(0);
-        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
-        assertEquals("key", item.getKey());
-        assertEquals("myvalue", item.getMyTagValue());
-        assertEquals("theirvalue", item.getTheirTagValue());
-    }
-
-    @Test
-    public void decide() {
-        TagMergeItem item = new TagMergeItem("key", "myvalue", "theirvalue");
-        TagMergeModel model = new TagMergeModel();
-        model.addItem(item);
-
-        List<TagMergeItem> list = field("tagMergeItems")
-        .ofType(List.class)
-        .in(model)
-        .get();
-
-        model.decide(0, MergeDecisionType.KEEP_MINE);
-        assertEquals(1, list.size());
-        item = list.get(0);
-        assertEquals(MergeDecisionType.KEEP_MINE, item.getMergeDecision());
-
-        model.decide(0, MergeDecisionType.KEEP_THEIR);
-        assertEquals(1, list.size());
-        item = list.get(0);
-        assertEquals(MergeDecisionType.KEEP_THEIR, item.getMergeDecision());
-
-        model.decide(0, MergeDecisionType.UNDECIDED);
-        assertEquals(1, list.size());
-        item = list.get(0);
-        assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
-    }
-
-    @Test
-    public void decideMultiple() {
-
-        TagMergeModel model = new TagMergeModel();
-        for (int i = 0; i < 10; i++) {
-            model.addItem(new TagMergeItem("key-" + i, "myvalue-" + i, "theirvalue-" +i));
-        }
-
-        List<TagMergeItem> list = field("tagMergeItems")
-        .ofType(List.class)
-        .in(model)
-        .get();
-
-        assertEquals(10, list.size());
-
-        model.decide(new int[] {0, 3, 5}, MergeDecisionType.KEEP_MINE);
-        for (int i = 0; i < 10; i++) {
-            TagMergeItem item = list.get(i);
-            if (i == 0 || i == 3 || i == 5) {
-                assertEquals(MergeDecisionType.KEEP_MINE, item.getMergeDecision());
-            } else {
-                assertEquals(MergeDecisionType.UNDECIDED, item.getMergeDecision());
-            }
-        }
-    }
-}
Index: trunk/test/unit/org/openstreetmap/josm/tools/PairTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/tools/PairTest.java	(revision 9546)
+++ trunk/test/unit/org/openstreetmap/josm/tools/PairTest.java	(revision 9546)
@@ -0,0 +1,21 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import nl.jqno.equalsverifier.Warning;
+
+/**
+ * Unit tests of {@link Pair} class.
+ */
+public class PairTest {
+
+    /**
+     * Unit test of methods {@link Pair#equals} and {@link Pair#hashCode}.
+     */
+    @Test
+    public void equalsContract() {
+        EqualsVerifier.forClass(Pair.class).suppress(Warning.NONFINAL_FIELDS).verify();
+    }
+}
