Index: test/functional/NodeTest.java
===================================================================
--- test/functional/NodeTest.java	(revision 284)
+++ test/functional/NodeTest.java	(revision 284)
@@ -0,0 +1,109 @@
+import java.util.Iterator;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.data.osm.Way;
+
+import framework.FunctionalTestCase;
+
+public class NodeTest extends FunctionalTestCase {
+
+	public void test() throws Exception {
+		key("ctrl-n");
+		assertNotNull(Main.map);
+
+		key("n");
+		click(100,500);
+		assertEquals(1, Main.ds.nodes.size());
+		assertEquals(1, Main.ds.getSelected().size());
+		assertEquals(Main.ds.nodes.iterator().next(), Main.ds.getSelected().iterator().next());
+		
+		key("d");
+		assertEquals(0, Main.ds.getSelected().size());
+		assertEquals(0, Main.ds.allNonDeletedPrimitives().size());
+		
+		key("ctrl-z", "n");
+		click(200,500);
+		key("g");
+		drag(200,500,100,500);
+		key("n","shift-n");
+		click(150,500);
+		assertEquals(3, Main.ds.nodes.size());
+		assertEquals(2, Main.ds.segments.size());
+		assertEquals(1, Main.ds.getSelected().size());
+		
+		Node n = (Node)Main.ds.getSelected().iterator().next();
+		Iterator<Segment> segIt = Main.ds.segments.iterator();
+		Segment s1 = segIt.next();
+		Segment s2 = segIt.next();
+		if (s1.from == n)
+			assertSame(n, s2.to);
+		else
+			assertSame(n, s2.from);
+		
+		key("shift-n");
+		click(150,550);
+		checkSegments(n);
+		
+		key("ctrl-z", "w");
+		click(125,500);
+		click(175,500);
+		assertEquals(1, Main.ds.ways.size());
+		key("s");
+		click(150,500);
+		key("n");
+		click(150,550);
+		assertEquals(1, Main.ds.ways.size());
+		Way way = Main.ds.ways.iterator().next();
+		assertEquals("segment not added to middle of way", 2, way.segments.size());
+		checkSegments(n);
+		
+		key("ctrl-z", "s");
+		assertEquals(2, Main.ds.segments.size());
+		segIt = Main.ds.segments.iterator();
+		s1 = segIt.next();
+		s2 = segIt.next();
+		click(100,500);
+		key("n");
+		click(100,550);
+		assertEquals(1, Main.ds.ways.size());
+		assertSame(way, Main.ds.ways.iterator().next());
+		assertEquals(3, way.segments.size());
+		segIt = way.segments.iterator();
+		assertSame(s1, segIt.next());
+		assertSame(s2, segIt.next());
+		assertSame(s2.to, segIt.next().from);
+		
+		key("ctrl-z", "s");
+		click(200,500);
+		key("n");
+		click(200,550);
+		assertEquals(1, Main.ds.ways.size());
+		assertSame(way, Main.ds.ways.iterator().next());
+		segIt = way.segments.iterator();
+		assertSame(s1.from, segIt.next().to);
+		assertSame(s1, segIt.next());
+		assertSame(s2, segIt.next());
+    }
+
+	private void checkSegments(Node n) {
+		assertEquals(3, Main.ds.segments.size());
+		assertEquals(4, Main.ds.nodes.size());
+
+		Iterator<Segment> segIt = Main.ds.segments.iterator();
+		Segment s1 = segIt.next();
+		Segment s2 = segIt.next();
+		Segment s3 = segIt.next();
+		if (s1.to == n) {
+			assertSame(n, s2.from);
+			assertSame(n, s3.from);
+		} else if (s2.to == n) {
+			assertSame(n, s1.from);
+			assertSame(n, s3.from);
+		} else {
+			assertSame(n, s1.from);
+			assertSame(n, s2.from);
+		}
+    }
+}
Index: test/functional/SegmentTest.java
===================================================================
--- test/functional/SegmentTest.java	(revision 284)
+++ test/functional/SegmentTest.java	(revision 284)
@@ -0,0 +1,23 @@
+import org.openstreetmap.josm.Main;
+
+import framework.FunctionalTestCase;
+
+
+public class SegmentTest extends FunctionalTestCase {
+
+	public void test() throws Exception {
+	    key("ctrl-n", "n");
+		click(100,500);
+		click(200,500);
+		key("g");
+		drag(200,500,100,500);
+		assertEquals(1, Main.ds.segments.size());
+		key("s");
+		click(150,500);
+		assertEquals(1, Main.ds.getSelected().size());
+		assertEquals(Main.ds.segments.iterator().next(), Main.ds.getSelected().iterator().next());
+		key("d");
+		assertEquals(0, Main.ds.getSelected().size());
+		assertTrue(Main.ds.segments.iterator().next().deleted);
+    }
+}
Index: test/functional/WayTest.java
===================================================================
--- test/functional/WayTest.java	(revision 284)
+++ test/functional/WayTest.java	(revision 284)
@@ -0,0 +1,32 @@
+import org.openstreetmap.josm.Main;
+
+import framework.FunctionalTestCase;
+
+
+public class WayTest extends FunctionalTestCase {
+
+	public void test() throws Exception {
+	    key("ctrl-n", "n");
+	    click(100,400);
+	    click(150,400);
+	    click(200,400);
+	    key("g");
+	    drag(200,400,150,400);
+	    drag(150,400,100,400);
+	    key("s", "ctrl-a", "w");
+	    assertPopup();
+	    assertEquals(1, Main.ds.ways.size());
+	    
+	    key("ctrl-a", "s");
+	    click(125,400,"ctrl");
+	    key("w");
+	    assertPopup();
+	    assertEquals(2, Main.ds.ways.size());
+	    
+	    key("ctrl-z", "ctrl-z", "ctrl-shift-a");
+	    click(125,400);
+	    assertEquals(1, Main.ds.ways.size());
+	    click(175,400);
+	    assertEquals(1, Main.ds.ways.size());
+    }
+}
Index: test/functional/framework/FunctionalTestCase.java
===================================================================
--- test/functional/framework/FunctionalTestCase.java	(revision 284)
+++ test/functional/framework/FunctionalTestCase.java	(revision 284)
@@ -0,0 +1,148 @@
+package framework;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.KeyboardFocusManager;
+import java.awt.Point;
+import java.awt.Window;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+import javax.swing.AbstractButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+
+import junit.extensions.jfcunit.JFCTestCase;
+import junit.extensions.jfcunit.RobotTestHelper;
+import junit.extensions.jfcunit.eventdata.DragEventData;
+import junit.extensions.jfcunit.eventdata.KeyEventData;
+import junit.extensions.jfcunit.eventdata.MouseEventData;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MainApplication;
+
+public class FunctionalTestCase extends JFCTestCase {
+
+	private KeyStroke getKey(String s) {
+    	int key = 0;
+    	int modifier = 0;
+    	s = s.toUpperCase();
+    	if (s.startsWith("CTRL")) {
+    		modifier |= InputEvent.CTRL_MASK;
+    		s = s.substring(4);
+    	}
+    	if (s.startsWith("-"))
+    		s = s.substring(1);
+    	if (s.startsWith("SHIFT")) {
+    		modifier |= InputEvent.SHIFT_MASK;
+    		s = s.substring(5);
+    	}
+    	if (s.startsWith("-"))
+    		s = s.substring(1);
+    	if (s.startsWith("ALT")) {
+    		modifier |= InputEvent.ALT_MASK;
+    		s = s.substring(3);
+    	}
+    	if (s.startsWith("-"))
+    		s = s.substring(1);
+    	if (s.matches("^F[1-9][012]?$"))
+    		key = KeyEvent.VK_F1 + Integer.parseInt(s.substring(1)) - 1;
+    	else if (s.length() == 0)
+    		key = 0;
+    	else if (s.length() != 1)
+    		throw new RuntimeException("Illegal key description '"+s+"'.");
+    	else
+    		key = s.charAt(0);
+    	return KeyStroke.getKeyStroke(key, modifier);
+    }
+
+	@Override protected void setUp() throws Exception {
+		super.setUp();
+		setHelper(new RobotTestHelper());
+		MainApplication.main(new String[]{});
+	}
+
+	protected Component find(Component c, String search) {
+		if (c == null)
+			return null;
+		if (search.equals(c.getName()))
+			return c;
+		if (c instanceof JLabel && search.equals(((JLabel)c).getText()))
+			return c;
+		if (c instanceof AbstractButton && search.equals(((AbstractButton)c).getText()))
+			return c;
+
+		if (c instanceof Container) {
+			Container ct = (Container)c;
+			for (int i = 0; i < ct.getComponentCount(); ++i) {
+				Component result = find(ct.getComponent(i), search);
+				if (result != null)
+					return result;
+			}
+		}
+		if (c instanceof JMenu) {
+			JMenu menu = (JMenu)c;
+			for (int i = 0; i < menu.getMenuComponentCount(); ++i) {
+				Component result = find(menu.getMenuComponent(i), search);
+				if (result != null)
+					return result;
+			}
+		}
+		if (c instanceof JFrame) {
+			Component result = find(((JFrame)c).getJMenuBar(), search);
+			if (result != null)
+				return result;
+		}
+		return null;
+	}
+	
+	protected Component find(String s) {
+		Container frame = SwingUtilities.getAncestorOfClass(Window.class, KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
+		return find(frame, s);
+	}
+
+	protected void key(String... keys) {
+		for (String s : keys) {
+			KeyStroke k = getKey(s);
+			getHelper().sendKeyAction(new KeyEventData(this, Main.contentPane, k.getKeyCode(), k.getModifiers(), 0));
+		}
+	}
+
+	protected void key(int... keys) {
+		for (int i : keys) {
+			getHelper().sendKeyAction(new KeyEventData(this, Main.contentPane, i, 0, 0));
+		}
+	}
+	
+	/**
+	 * Clicks on a spot on the main map (should be open by now)
+	 */
+	protected void click(int x, int y) {
+		getHelper().enterClickAndLeave(new MouseEventData(this, Main.map, 1, MouseEvent.BUTTON1_MASK, false, 0, new Point(x,y)));
+	}
+
+	protected void click(int x, int y, String modifier) {
+		getHelper().enterClickAndLeave(new MouseEventData(this, Main.map, 1, MouseEvent.BUTTON1_MASK + getKey(modifier).getModifiers(), false, 0, new Point(x,y)));
+	}
+	
+	
+	protected void drag(int xfrom, int yfrom, int xto, int yto) {
+		getHelper().enterDragAndLeave(new DragEventData(
+				this,
+				new MouseEventData(this, Main.map, 1, MouseEvent.BUTTON1_MASK, false, 0, new Point(xfrom, yfrom)),
+				new MouseEventData(this, Main.map, 1, MouseEvent.BUTTON1_MASK, false, 0, new Point(xto, yto))));
+	}
+	
+
+	protected void assertPopup() {
+		Component focus = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
+		Container dlg = SwingUtilities.getAncestorOfClass(JDialog.class, focus);
+		assertNotNull("Popup dialog found", dlg);
+		key(KeyEvent.VK_ENTER);
+	}
+}
Index: test/unit/org/openstreetmap/josm/actions/CombineWayActionTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/actions/CombineWayActionTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/actions/CombineWayActionTest.java	(revision 284)
@@ -0,0 +1,83 @@
+package org.openstreetmap.josm.actions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.openstreetmap.josm.testframework.MotherObject.createSegment;
+import static org.openstreetmap.josm.testframework.MotherObject.createWay;
+
+import javax.swing.Action;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.testframework.MainMock;
+import org.openstreetmap.josm.testframework.MotherObject;
+
+
+public class CombineWayActionTest extends MainMock {
+
+	private Action action = Main.main.menu.combineWay;
+	private Way way1;
+	private Way way2;
+	private Segment segment1;
+	private Segment segment2;
+
+	@Before public void createTwoWays() {
+		Main.ds = new DataSet();
+		MotherObject.dataSet = Main.ds;
+		segment1 = createSegment();
+		segment2 = createSegment();
+		way1 = createWay(segment1);
+		way2 = createWay(segment2);
+		Main.ds.setSelected(way1, way2);
+	}
+	
+	@Test public void noSelectionBreaks() throws Exception {
+		Main.ds.setSelected();
+		action.actionPerformed(null);
+		assertPopup();
+	}
+	
+	@Test public void oneSelectedWayBreaks() throws Exception {
+		Main.ds.setSelected(way1);
+		action.actionPerformed(null);
+		assertPopup();
+	}
+	
+	@Test public void segmentsAreMergedInNewWay() throws Exception {
+	    action.actionPerformed(null);
+	    Way w = way1.deleted ? way2 : way1;
+	    assertFalse(w.deleted);
+	    assertEquals(2, w.segments.size());
+    }
+	
+	@Test public void nonConflictingPropertiesAreMerged() throws Exception {
+	    way1.put("foo", "bar");
+	    way2.put("baz", "imi");
+	    
+	    action.actionPerformed(null);
+	    Way w = way1.deleted ? way2 : way1;
+
+	    assertEquals(2, w.keys.size());
+	    assertEquals("bar", w.get("foo"));
+	    assertEquals("imi", w.get("baz"));
+    }
+	
+	@Test public void conflictingPropertiesOpenResolveDialog() throws Exception {
+	    way1.put("foo", "bar");
+	    way2.put("foo", "baz");
+	    way2.put("imi", "ada");
+	    
+	    action.actionPerformed(null);
+	    assertPopup();
+	    Way w = way1.deleted ? way2 : way1;
+
+	    assertEquals(2, w.keys.size());
+	    assertTrue(w.get("foo").equals("bar") || w.get("foo").equals("bar"));
+	    assertEquals("ada", w.get("imi"));
+	}
+}
Index: test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java	(revision 284)
@@ -0,0 +1,108 @@
+package org.openstreetmap.josm.data.osm;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.testframework.MotherObject;
+
+public class DataSetTest extends MotherObject {
+
+	private final class TestSelectionChangeListener implements SelectionChangedListener {
+	    public Collection<? extends OsmPrimitive> called;
+		public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
+	    	called = newSelection;
+	    }
+    }
+
+	private DataSet ds;
+	private Node node1;
+	private Node node2;
+	private Node node3;
+	private Segment segment;
+	private Way way;
+
+	@Override protected void setUp() throws Exception {
+		super.setUp();
+		ds = createDataSet();
+		Iterator<Node> it = ds.nodes.iterator();
+		node1 = it.next();
+		node2 = it.next();
+		node3 = it.next();
+		segment = ds.segments.iterator().next();
+		way = ds.ways.iterator().next();
+	}
+
+	public void testAllPrimitives() {
+		Collection<OsmPrimitive> all = ds.allPrimitives();
+		assertContainsSame(all, node1, node2, node3, segment, way);
+	}
+
+	public void testAllNonDeletedPrimitives() {
+		assertEquals(5, ds.allNonDeletedPrimitives().size());
+		node1.deleted = true;
+		assertEquals(4, ds.allNonDeletedPrimitives().size());
+	}
+
+	public void testClearSelection() {
+		node1.selected = true;
+		ds.clearSelection();
+		assertFalse(node1.selected);
+	}
+
+	public void testGetSelected() {
+		node1.selected = true;
+		segment.selected = true;
+		Collection<OsmPrimitive> sel = ds.getSelected();
+		assertContainsSame(sel, node1, segment);
+	}
+
+	public void testSetSelected() {
+		Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
+		sel.add(node1);
+		sel.add(way);
+		ds.setSelected(sel);
+		assertTrue(node1.selected);
+		assertFalse(node2.selected);
+		assertTrue(way.selected);
+	}
+
+	public void testSetSelectedOsmPrimitive() {
+		ds.setSelected(node3);
+		assertTrue(node3.selected);
+		assertFalse(node2.selected);
+
+		ds.setSelected();
+		assertFalse(node3.selected || node2.selected);
+		
+		ds.setSelected(node1, way);
+		assertTrue(node1.selected && way.selected);
+		assertFalse(node3.selected);
+		
+		ds.setSelected((OsmPrimitive)null);
+		assertFalse(node1.selected || node2.selected || node3.selected || way.selected);
+	}
+
+	public void testFireSelectionChanged() {
+		TestSelectionChangeListener l = new TestSelectionChangeListener();
+		ds.listeners.add(l);
+		ds.setSelected(segment);
+		assertNotNull(l.called);
+		assertEquals(1, l.called.size());
+		assertSame(segment, l.called.iterator().next());
+	}
+
+	public void testAddAllSelectionListener() {
+		DataSet ds2 = new DataSet();
+		TestSelectionChangeListener l1 = new TestSelectionChangeListener();
+		TestSelectionChangeListener l2 = new TestSelectionChangeListener();
+		ds2.listeners.add(l1);
+		ds2.listeners.add(l2);
+		ds.listeners.addAll(ds2.listeners);
+		ds2.listeners.remove(l1);
+		ds.setSelected(node2);
+		assertNotNull(l1.called);
+		assertNotNull(l2.called);
+	}
+}
Index: test/unit/org/openstreetmap/josm/data/osm/NodeTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/NodeTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/data/osm/NodeTest.java	(revision 284)
@@ -0,0 +1,48 @@
+package org.openstreetmap.josm.data.osm;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.testframework.MotherObject;
+
+public class NodeTest extends MotherObject {
+
+	private Node node;
+
+	@Override protected void setUp() throws Exception {
+	    super.setUp();
+	    node = createNode();
+    }
+
+	public void testVisit() {
+		OsmPrimitiveTest.TestCalledVisitor v = new OsmPrimitiveTest.TestCalledVisitor();
+		node.visit(v);
+		assertEquals("Node", v.called);
+	}
+
+	public void testCloneFromRealEqual() {
+		Node node2 = createNode(23,3,4);
+		assertFalse(node2.realEqual(node, false));
+		assertFalse(node.realEqual(node2, false));
+		node.cloneFrom(node2);
+		assertTrue(node2.realEqual(node, false));
+		assertTrue(node.realEqual(node2, false));
+	}
+
+	public void testNodeNode() {
+		Node node2 = new Node(node);
+		assertTrue(node2.realEqual(node, false));
+	}
+
+	public void testNodeLatLon() {
+		LatLon latLon = new LatLon(1,2);
+		node = new Node(latLon);
+		assertEquals(node.coor, latLon);
+	}
+
+	public void testCompareToNodeTypeBiggestOrComparedAfterId() {
+		assertEquals(1, node.compareTo(createSegment()));
+		assertEquals(1, node.compareTo(createWay()));
+		Node node2 = createNode(23,1,2);
+		assertEquals(-1, node.compareTo(node2));
+		assertEquals(1, node2.compareTo(node));
+	}
+}
Index: test/unit/org/openstreetmap/josm/data/osm/OsmPrimitiveTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/OsmPrimitiveTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/data/osm/OsmPrimitiveTest.java	(revision 284)
@@ -0,0 +1,87 @@
+package org.openstreetmap.josm.data.osm;
+
+import java.util.Date;
+
+import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.testframework.MotherObject;
+
+public class OsmPrimitiveTest extends MotherObject {
+
+	public final static class TestCalledVisitor implements Visitor {
+        public String called;
+    	
+    	public void visit(Node n) {
+    		assertNull(called);
+    		called = "Node";
+        }
+    
+        public void visit(Segment s) {
+        	assertNull(called);
+        	called = "Segment";
+        }
+    
+        public void visit(Way w) {
+        	assertNull(called);
+        	called = "Way";
+        }
+    }
+
+	private OsmPrimitive osm;
+	private boolean visited;
+
+	@Override protected void setUp() throws Exception {
+		super.setUp();
+		osm = new OsmPrimitive() {
+			@Override public void visit(Visitor visitor) {
+				visited = true;
+			}
+
+			public int compareTo(OsmPrimitive o) {
+				return 0;
+			}
+		};
+		visited = false;
+	}
+
+	public void testVisit() {
+		osm.visit(new Visitor(){
+			public void visit(Node n) {}
+			public void visit(Segment s) {}
+			public void visit(Way w) {}});
+		assertTrue(visited);
+	}
+
+	public void testEqualsIsEqualOnlyIfIdAndClassMatchesAndIdIsNotZero() {
+		Node node = createNode(1,23,42);
+		Node node2 = createNode(1);
+		assertTrue(node.equals(node2));
+		Segment segment = createSegment(1);
+		assertFalse(node.equals(segment));
+		node2.id = 2;
+		assertFalse(node.equals(node2));
+		node2.id = 0;
+		node.id = 0;
+		assertFalse(node.equals(node2));
+	}
+
+	public void testKeysPutRemoveGet() {
+		assertTrue(osm.keySet().isEmpty());
+		osm.put("foo", "bar");
+		assertEquals(1, osm.keySet().size());
+		assertEquals("bar", osm.get("foo"));
+		assertEquals(1, osm.entrySet().size());
+		assertEquals("foo", osm.entrySet().iterator().next().getKey());
+		assertEquals("bar", osm.entrySet().iterator().next().getValue());
+		
+		osm.remove("asd");
+		assertEquals(1, osm.keySet().size());
+		osm.remove("foo");
+		assertEquals(0, osm.keySet().size());
+	}
+
+	public void testGetTimeStr() {
+		assertNull(osm.getTimeStr());
+		osm.timestamp = new Date(1);
+		assertEquals("1970-01-01 01:00:00", osm.getTimeStr());
+	}
+}
Index: test/unit/org/openstreetmap/josm/data/osm/SegmentTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/SegmentTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/data/osm/SegmentTest.java	(revision 284)
@@ -0,0 +1,66 @@
+package org.openstreetmap.josm.data.osm;
+
+import org.openstreetmap.josm.testframework.MotherObject;
+
+public class SegmentTest extends MotherObject {
+
+	private Segment segment;
+
+	@Override protected void setUp() throws Exception {
+		super.setUp();
+		segment = createSegment();
+	}
+
+	public void testVisit() {
+		OsmPrimitiveTest.TestCalledVisitor v = new OsmPrimitiveTest.TestCalledVisitor();
+		segment.visit(v);
+		assertEquals("Segment", v.called);
+	}
+
+	public void testCloneFromRealEqual() {
+		Segment s2 = createSegment(23, createNode(1,2,3), createNode(2,3,4));
+		segment.cloneFrom(s2);
+		assertTrue(segment.realEqual(s2, false));
+		assertTrue(s2.realEqual(segment, false));
+		assertSame(segment.from, s2.from);
+		assertSame(segment.to, s2.to);
+	}
+
+	public void testSegmentSegment() {
+		Segment s = new Segment(segment);
+		assertTrue(s.realEqual(segment, false));
+	}
+
+	public void testSegmentNodeNode() {
+		Segment s = new Segment(createNode(1,2,3), createNode(4,5,6));
+		assertEquals(2.0, s.from.coor.lat());
+		assertEquals(6.0, s.to.coor.lon());
+	}
+
+	public void testSegmentLong() {
+		Segment s = new Segment(23);
+		assertEquals(23, s.id);
+	}
+
+	public void testEqualPlace() {
+		Segment s = createSegment();
+		assertFalse(s.equalPlace(segment));
+		assertFalse(segment.equalPlace(s));
+		s.from.coor = segment.to.coor;
+		s.to.coor = segment.from.coor;
+		assertTrue(s.equalPlace(segment));
+		assertTrue(segment.equalPlace(s));
+	}
+
+	public void testCompareToSegmentSmallerThanNodeBiggerThanWayOrCompareAfterId() {
+		Segment s = createSegment(23);
+		assertEquals(-1, s.compareTo(createNode()));
+		assertEquals(1, s.compareTo(createWay()));
+		
+		assertEquals(1, s.compareTo(segment));
+		assertEquals(-1, segment.compareTo(s));
+		segment.id = s.id;
+		assertEquals(0, segment.compareTo(s));
+		assertEquals(0, s.compareTo(segment));
+	}
+}
Index: test/unit/org/openstreetmap/josm/data/osm/WayTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/WayTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/data/osm/WayTest.java	(revision 284)
@@ -0,0 +1,53 @@
+package org.openstreetmap.josm.data.osm;
+
+import org.openstreetmap.josm.testframework.MotherObject;
+
+public class WayTest extends MotherObject {
+
+	private Way way;
+
+	@Override protected void setUp() throws Exception {
+		super.setUp();
+		way = createWay();
+	}
+
+	public void testVisit() {
+		OsmPrimitiveTest.TestCalledVisitor v = new OsmPrimitiveTest.TestCalledVisitor();
+		way.visit(v);
+		assertEquals("Way", v.called);
+	}
+
+	public void testCloneFromRealEqual() {
+		Way w2 = createWay(42);
+		way.cloneFrom(w2);
+		assertTrue(way.realEqual(w2, false));
+		assertEquals(w2.segments.size(), way.segments.size());
+	}
+
+	public void testWayWay() {
+		Way w = new Way(way);
+		assertEquals(way.id, w.id);
+		assertTrue(way.realEqual(w, false));
+	}
+
+	public void testWay() {
+		Way w = new Way();
+		assertEquals(0, w.id);
+		assertEquals(0, w.segments.size());
+	}
+
+	public void testCompareToWaySmallestOrCompareAfterId() {
+		Way w = createWay(23);
+		assertEquals(-1, w.compareTo(createNode()));
+		assertEquals(-1, w.compareTo(createSegment()));
+		
+		assertEquals(1, w.compareTo(way));
+		assertEquals(-1, way.compareTo(w));
+	}
+
+	public void testIsIncomplete() {
+		way.segments.add(new Segment(23));
+		assertTrue(way.isIncomplete());
+	}
+
+}
Index: test/unit/org/openstreetmap/josm/data/osm/visitor/AddVisitorTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/visitor/AddVisitorTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/data/osm/visitor/AddVisitorTest.java	(revision 284)
@@ -0,0 +1,42 @@
+package org.openstreetmap.josm.data.osm.visitor;
+
+import java.lang.reflect.Field;
+
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.testframework.MotherObject;
+
+public class AddVisitorTest extends MotherObject {
+
+	private AddVisitor v;
+	private DataSet ds;
+
+	@Override protected void setUp() throws Exception {
+		super.setUp();
+		ds = new DataSet();
+		v = new AddVisitor(ds);
+	}
+
+	public void testAddVisitorDataSet() throws Exception {
+		AddVisitor v = new AddVisitor(ds);
+		Field vDs = AddVisitor.class.getDeclaredField("ds");
+		vDs.setAccessible(true);
+		assertSame(ds, vDs.get(v));
+	}
+	
+	public void testVisitNode() {
+		createNode(23).visit(v);
+		assertEquals(1, ds.nodes.size());
+		assertEquals(23, ds.nodes.iterator().next().id);
+	}
+
+	public void testVisitSegment() {
+		createSegment().visit(v);
+		assertEquals(1, ds.segments.size());
+	}
+
+	public void testVisitWay() {
+		createWay().visit(v);
+		assertEquals(1, ds.ways.size());
+	}
+
+}
Index: test/unit/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitorTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitorTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitorTest.java	(revision 284)
@@ -0,0 +1,56 @@
+package org.openstreetmap.josm.data.osm.visitor;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.testframework.MotherObject;
+
+public class AllNodesVisitorTest extends MotherObject {
+
+	private AllNodesVisitor v;
+
+	@Override protected void setUp() throws Exception {
+		super.setUp();
+		v = new AllNodesVisitor();
+	}
+
+	public void testVisitNode() {
+		Node node = createNode();
+		node.visit(v);
+		assertEquals(1, v.nodes.size());
+		assertSame(node, v.nodes.iterator().next());
+	}
+
+	public void testVisitSegment() {
+		Segment s = createSegment();
+		s.visit(v);
+		assertEquals(2, v.nodes.size());
+		assertTrue(v.nodes.contains(s.from));
+		assertTrue(v.nodes.contains(s.to));
+	}
+
+	public void testVisitWay() {
+		Way w = createWay(createSegment());
+		w.visit(v);
+		int numberOfNodes = 2*w.segments.size();
+		assertEquals(numberOfNodes, v.nodes.size());
+		for (Segment s : w.segments) {
+			assertTrue(v.nodes.contains(s.from));
+			assertTrue(v.nodes.contains(s.to));
+		}
+	}
+
+	public void testGetAllNodes() {
+		Collection<OsmPrimitive> all = new LinkedList<OsmPrimitive>();
+		all.add(createNode());
+		all.add(createSegment());
+		Collection<Node> nodes = AllNodesVisitor.getAllNodes(all);
+		
+		assertEquals(3, nodes.size());
+	}
+
+}
Index: test/unit/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitorTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitorTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitorTest.java	(revision 284)
@@ -0,0 +1,61 @@
+package org.openstreetmap.josm.data.osm.visitor;
+
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.testframework.MotherObject;
+
+public class BoundingXYVisitorTest extends MotherObject {
+
+	private BoundingXYVisitor v;
+
+	private void assertVisitorFilled() {
+        assertNotNull(v.max);
+    	assertNotNull(v.min);
+    }
+
+	@Override protected void setUp() throws Exception {
+		super.setUp();
+		v = new BoundingXYVisitor();
+	}
+
+	public void testVisitNode() {
+		Node node = createNode();
+		node.visit(v);
+		assertVisitorFilled();
+		assertEquals(node.eastNorth.east(), v.min.east());
+		assertEquals(node.eastNorth.north(), v.min.north());
+		assertEquals(v.max, v.min);
+	}
+
+	public void testVisitSegment() {
+		createSegment().visit(v);
+		assertVisitorFilled();
+		assertFalse(v.max.equals(v.min));
+	}
+
+	public void testVisitWay() {
+		createWay(createSegment()).visit(v);
+		assertVisitorFilled();
+		assertFalse(v.max.equals(v.min));
+	}
+
+	public void testVisitEastNorth() {
+		v.visit(new EastNorth(123,321));
+		v.visit(new EastNorth(124,322));
+		assertEquals(123.0, v.min.east());
+		assertEquals(124.0, v.max.east());
+		assertEquals(321.0, v.min.north());
+		assertEquals(322.0, v.max.north());
+	}
+
+	public void testGetBounds() {
+		Node node = createNode();
+		v.visit(node);
+		Bounds b = v.getBounds();
+		assertNotNull(b);
+		assertEquals(node.coor.lat(), b.min.lat());
+		assertEquals(node.coor.lon(), b.min.lon());
+	}
+
+}
Index: test/unit/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitorTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitorTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitorTest.java	(revision 284)
@@ -0,0 +1,42 @@
+package org.openstreetmap.josm.data.osm.visitor;
+
+import java.lang.reflect.Field;
+
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.testframework.MotherObject;
+
+public class CollectBackReferencesVisitorTest extends MotherObject {
+
+	private CollectBackReferencesVisitor v;
+	private DataSet ds;
+
+	@Override protected void setUp() throws Exception {
+		super.setUp();
+		ds = createDataSet();
+		v = new CollectBackReferencesVisitor(ds);
+	}
+
+	public void testCollectBackReferencesVisitor() throws Exception {
+		DataSet dataSet = new DataSet();
+		CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(dataSet);
+		Field f = v.getClass().getDeclaredField("ds");
+		f.setAccessible(true);
+		assertSame(dataSet, f.get(v));
+	}
+
+	public void testVisitNode() {
+		ds.nodes.iterator().next().visit(v);
+		assertContainsSame(v.data, ds.segments.iterator().next(), ds.ways.iterator().next());
+	}
+
+	public void testVisitSegment() {
+		ds.segments.iterator().next().visit(v);
+		assertContainsSame(v.data, ds.ways.iterator().next());
+	}
+
+	public void testVisitWay() {
+		ds.ways.iterator().next().visit(v);
+		assertEquals(0, v.data.size());
+	}
+
+}
Index: test/unit/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java	(revision 284)
@@ -0,0 +1,362 @@
+package org.openstreetmap.josm.data.osm.visitor;
+
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+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.Segment;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.testframework.Bug;
+import org.openstreetmap.josm.testframework.DataSetTestCaseHelper;
+
+public class MergeVisitorTest extends TestCase {
+
+
+	private DataSet ds;
+	private Node dsNode;
+	private Node n;
+	private MergeVisitor v;
+
+	@Override protected void setUp() throws Exception {
+		ds = new DataSet();
+		dsNode = DataSetTestCaseHelper.createNode(ds);
+		v = new MergeVisitor(ds, null);
+		n = DataSetTestCaseHelper.createNode(null);
+	}
+
+
+	private Segment createSegment(DataSet ds, boolean incomplete, boolean deleted, int id) {
+    	Node n1 = DataSetTestCaseHelper.createNode(ds);
+    	Node n2 = DataSetTestCaseHelper.createNode(ds);
+    	Segment s = DataSetTestCaseHelper.createSegment(ds, n1, n2);
+    	s.incomplete = incomplete;
+    	s.id = id;
+    	s.deleted = deleted;
+    	s.timestamp = new Date();
+    	return s;
+    }
+
+	/**
+     * Create that amount of nodes and add them to the dataset. The id will be 1,2,3,4...
+     * @param amount Number of nodes to create.
+     * @return The created nodes.
+     */
+    private Node[] createNodes(DataSet ds, int amount) {
+    	Node[] nodes = new Node[amount];
+    	for (int i = 0; i < amount; ++i) {
+    		nodes[i] = DataSetTestCaseHelper.createNode(ds);
+    		nodes[i].id = i+1;
+    	}
+    	return nodes;
+    }
+
+
+	public void testNodesMergeUpdate() {
+		dsNode.id = 1;
+		n.id = 1;
+		n.timestamp = new Date();
+		v.visit(n);
+		assertEquals(dsNode, n);
+	}
+	public void testNodesMergeModified() {
+		dsNode.id = 1;
+		n.id = 1;
+		n.modified = true;
+		v.visit(n);
+		assertEquals(dsNode, n);
+	}
+	public void testNodesConflictBothModified() {
+		n.modified = true;
+		dsNode.modified = true;
+		n.id = 1;
+		dsNode.id = 1;
+		v.visit(n);
+		assertEquals(1, v.conflicts.size());
+	}
+	public void testNodesConflict() {
+		dsNode.id = 1;
+		dsNode.timestamp = new Date();
+		n.id = 1;
+		n.modified = true;
+		n.timestamp = new Date(dsNode.timestamp.getTime()-1);
+		v.visit(n);
+		assertEquals(1, v.conflicts.size());
+		assertSame(dsNode, v.conflicts.keySet().iterator().next());
+		assertSame(n, v.conflicts.values().iterator().next());
+	}
+	public void testNodesConflict2() {
+		dsNode.id = 1;
+		dsNode.timestamp = new Date();
+		dsNode.modified = true;
+		n.id = 1;
+		n.timestamp = new Date(dsNode.timestamp.getTime()+1);
+		v.visit(n);
+		assertEquals(1, v.conflicts.size());
+	}
+	public void testNodesConflictModifyDelete() {
+		dsNode.id = 1;
+		dsNode.modified = true;
+		n.id = 1;
+		n.delete(true);
+		v.visit(n);
+		assertEquals(1, v.conflicts.size());
+	}
+	public void testNodesMergeSamePosition() {
+		n.id = 1; // new node comes from server
+		dsNode.modified = true; // our node is modified
+		dsNode.coor = new LatLon(n.coor.lat(), n.coor.lon());
+		v.visit(n);
+		v.fixReferences();
+		assertEquals(0, v.conflicts.size());
+		assertEquals(1, dsNode.id);
+		assertFalse("updating a new node clear the modified state", dsNode.modified);
+	}
+
+	public void testNoConflictNewNodesMerged() {
+		assertEquals(0, n.id);
+		assertEquals(0, dsNode.id);
+		v.visit(n);
+		v.fixReferences();
+		assertEquals(0,v.conflicts.size());
+		assertTrue(ds.nodes.contains(n));
+		assertEquals(2, ds.nodes.size());
+	}
+
+	/**
+	 * Test that two new segments that have different from/to are not merged
+	 */
+	@Bug(101)
+	public void testNewSegmentNotMerged() {
+		v.visit(n);
+		Segment s1 = new Segment(n, dsNode);
+		v.visit(s1);
+		Segment s2 = new Segment(dsNode, n);
+		v.visit(s2);
+		assertEquals(2, ds.segments.size());
+	}
+	
+	public void testFixReferencesConflicts() {
+		// make two nodes mergable
+		dsNode.id = 1;
+		n.id = 1;
+		n.timestamp = new Date();
+		// have an old segment with the old node
+		Segment sold = new Segment(dsNode, dsNode);
+		sold.id = 23;
+		sold.modified = true;
+		ds.segments.add(sold);
+		// have a conflicting segment point to the new node
+		Segment s = new Segment(n,DataSetTestCaseHelper.createNode(null));
+		s.id = 23;
+		s.modified = true;
+
+		v.visit(n); // merge
+		assertEquals(n.timestamp, dsNode.timestamp);
+		v.visit(s);
+		assertEquals(1, v.conflicts.size());
+		v.fixReferences();
+		assertSame(s.from, dsNode);
+	}
+
+	public void testNoConflictForSame() {
+		dsNode.id = 1;
+		dsNode.modified = true;
+		n.cloneFrom(dsNode);
+		v.visit(n);
+		assertEquals(0, v.conflicts.size());
+	}
+
+	/**
+	 * Merge of an old segment with a new one. This should
+	 * be mergable (if the nodes matches).
+	 */
+	public void testMergeOldSegmentsWithNew() {
+		Node[] n = createNodes(ds, 2);
+		Segment ls1 = DataSetTestCaseHelper.createSegment(ds, n[0], n[1]);
+		ls1.id = 3;
+
+		Node newnode = new Node(new LatLon(n[1].coor.lat(), n[1].coor.lon()));
+		Segment newls = new Segment(n[0], newnode);
+
+		v.visit(newls);
+		assertEquals("segment should have been merged.", 1, ds.segments.size());
+	}
+
+	/**
+	 * Incomplete segments should always loose.
+	 */
+	public void testImportIncomplete() throws Exception {
+		Segment s1 = DataSetTestCaseHelper.createSegment(ds, dsNode, dsNode);
+		s1.id = 1;
+		Segment s2 = new Segment(s1);
+		s1.incomplete = true;
+		s2.timestamp = new Date();
+		v.visit(s2);
+		assertTrue(s1.realEqual(s2, false));
+	}
+	/**
+	 * Incomplete segments should extend existing ways.
+	 */
+	public void testImportIncompleteExtendWays() throws Exception {
+		Segment s1 = DataSetTestCaseHelper.createSegment(ds, dsNode, dsNode);
+		Way w = DataSetTestCaseHelper.createWay(ds, new Segment[]{s1});
+		s1.id = 1;
+		Segment s2 = new Segment(s1);
+		s1.incomplete = true;
+		v.visit(s2);
+		v.fixReferences();
+		assertEquals(1, w.segments.size());
+		assertEquals(s2, w.segments.get(0));
+		assertFalse(s2.incomplete);
+	}
+
+
+	/**
+	 * Nodes beeing merged are equal but should be the same.
+	 */
+	@Bug(54)
+	public void testEqualNotSame() {
+		ds = new DataSet();
+		// create a dataset with segment a-b
+		Node n[] = createNodes(ds, 2);
+		Segment ls1 = DataSetTestCaseHelper.createSegment(ds, n[0], n[1]);
+		ls1.id = 1;
+
+		// create an other dataset with segment a'-c (a' is equal, but not same to a)
+		DataSet ds2 = new DataSet();
+		Node n2[] = createNodes(ds2, 2);
+		n2[0].coor = new LatLon(n[0].coor.lat(), n[0].coor.lon());
+		n2[0].id = 0;
+		n2[1].id = 42;
+
+		Segment ls2 = DataSetTestCaseHelper.createSegment(ds, n2[0], n2[1]);
+		v = new MergeVisitor(ds, null);
+		for (OsmPrimitive osm : ds2.allPrimitives())
+			osm.visit(v);
+		v.fixReferences();
+
+		assertSame(ls1.from, ls2.from);
+	}
+
+
+	public void testCloneWayNotIncomplete() {
+		DataSet ds = new DataSet();
+		Node[] n = createNodes(ds, 2);
+		Segment s = DataSetTestCaseHelper.createSegment(ds, n[0], n[1]);
+		Way w = DataSetTestCaseHelper.createWay(ds, s);
+		MergeVisitor v = new MergeVisitor(ds, null);
+		v.visit(n[0]);
+		v.visit(n[1]);
+		v.visit(s);
+		v.visit(w);
+		Way w2 = new Way(w);
+		w2.timestamp = new Date();
+		Segment s2 = new Segment(s);
+		s2.incomplete = true;
+		w2.segments.clear();
+		w2.segments.add(s2);
+		v.visit(w2);
+		assertSame("Do not import incomplete segments when merging ways.", s, w.segments.iterator().next());
+	}
+
+	/**
+	 * When merging an incomplete way over a dataset that contain already all
+	 * necessary segments, the way must be completed.
+	 */
+	@Bug(117)
+	public void testMergeIncompleteOnExistingDoesNotComplete() {
+		// create a dataset with an segment (as base for the later incomplete way)
+		DataSet ds = new DataSet();
+		Node[] n = createNodes(ds, 2);
+		Segment s = DataSetTestCaseHelper.createSegment(ds, n[0], n[1]);
+		s.id = 23;
+		// create an incomplete way which references the former segment
+		Way w = new Way();
+		Segment incompleteSegment = new Segment(s.id);
+		w.segments.add(incompleteSegment);
+		w.id = 42;
+		// merge both
+		MergeVisitor v = new MergeVisitor(ds, null);
+		v.visit(w);
+		v.fixReferences();
+		
+		assertTrue(ds.ways.contains(w));
+		assertEquals(1, w.segments.size());
+		assertFalse(w.segments.get(0).incomplete);
+	}
+	
+	/**
+	 * Deleted segments should raise an conflict when merged over changed segments. 
+	 */
+	public void testMergeDeletedOverChangedConflict() {
+		DataSet ds = new DataSet();
+		createSegment(ds, false, false, 23).modified = true;
+		Segment s = createSegment(null, false, true, 23);
+		s.timestamp = new Date(new Date().getTime()+1);
+		
+		MergeVisitor v = new MergeVisitor(ds, null);
+		v.visit(s);
+		v.fixReferences();
+		
+		assertEquals(1, v.conflicts.size());
+	}
+	
+	public void testMergeIncompleteSegmentsAddToDataSet() throws Exception {
+		DataSet ds = new DataSet();
+		MergeVisitor v = new MergeVisitor(ds, null);
+		v.visit(createSegment(null, true, false, 1));
+		assertEquals(1, ds.segments.size());
+    }
+
+	/**
+	 * The merger should auto-resolve items, that have not changed but are marked as
+	 * changed. In the case where an unmodified newer item is merged over an modified
+	 * older, the modified-flag should be removed and the newer timestamp is used.
+	 */
+	public void testMergeModifiedWithOlderTimestampOverUnmodifiedNewerDoesNotConflict() throws Exception {
+		DataSet ds = new DataSet();
+
+		Node oldNode = createNodes(ds, 1)[0];
+		oldNode.modified = true;
+		oldNode.timestamp = new Date();
+		
+		Node newNode = new Node(oldNode);
+		Date date = new Date(oldNode.timestamp.getTime()+10000);
+		newNode.modified = false;
+		newNode.timestamp = new Date(date.getTime());
+		
+		MergeVisitor v = new MergeVisitor(ds, null);
+		v.visit(newNode);
+
+		assertEquals(0, v.conflicts.size());
+		assertEquals(date, ds.nodes.iterator().next().timestamp);
+		assertFalse(ds.nodes.iterator().next().modified);
+	}
+	
+	public void testMergeTwoIncompleteWaysMergesSecondAsWell() throws Exception {
+		DataSet ds = new DataSet();
+		Segment s1 = new Segment(23);
+		Segment s2 = new Segment(42);
+		
+		MergeVisitor v = new MergeVisitor(ds, null);
+		v.visit(s1);
+		v.visit(s2);
+		
+		assertEquals(2, ds.segments.size());
+		assertEquals(23, ds.segments.iterator().next().id);
+	}
+	
+	public void testDoNotMergeEqualObjectsWithDifferentNonZeroId() throws Exception {
+		DataSet ds = new DataSet();
+		Segment s1 = createSegment(ds, false, false, 23);
+		Segment s2 = new Segment(s1);
+		s2.id = 42;
+		MergeVisitor v = new MergeVisitor(ds, null);
+		v.visit(s2);
+		assertEquals(2, ds.segments.size());
+	}
+}
Index: test/unit/org/openstreetmap/josm/gui/annotation/AnnotationPresetTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/annotation/AnnotationPresetTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/gui/annotation/AnnotationPresetTest.java	(revision 284)
@@ -0,0 +1,71 @@
+package org.openstreetmap.josm.gui.annotation;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.util.List;
+
+import javax.swing.Action;
+import javax.swing.Icon;
+
+import junit.framework.TestCase;
+
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.Check;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.Combo;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.Key;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.Label;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.Text;
+
+public class AnnotationPresetTest extends TestCase {
+
+	public void testAnnotationPresetLoads() throws Exception {
+		InputStream in = getClass().getResourceAsStream("annotation-test.xml");
+		List<AnnotationPreset> all = AnnotationPreset.readAll(in);
+
+		assertEquals(1, all.size());
+		AnnotationPreset a = all.get(0);
+		assertEquals("Highway", a.getValue(Action.NAME));
+		Field dataField = a.getClass().getDeclaredField("data");
+		dataField.setAccessible(true);
+		List<?> data = (List<?>)dataField.get(a);
+		assertEquals(5, data.size());
+
+		Label label = (Label)data.get(0);
+		assertEquals("Inserting a highway in UK", label.text);
+
+		Text text = (Text)data.get(1);
+		assertEquals("name", text.key);
+		assertEquals("Highway (e.g. M3)", text.text);
+		assertFalse(text.delete_if_empty);
+		assertNull(text.default_);
+
+		Combo combo = (Combo)data.get(2);
+		assertEquals("highway", combo.key);
+		assertEquals("Type", combo.text);
+		assertEquals("major,minor", combo.values);
+		assertTrue(combo.delete_if_empty);
+		assertTrue(combo.editable);
+		assertNull(combo.default_);
+
+		Check check = (Check)data.get(3);
+		assertEquals("oneway", check.key);
+		assertEquals("Oneway", check.text);
+		assertTrue(check.default_);
+
+		Key key = (Key)data.get(4);
+		assertEquals("class", key.key);
+		assertEquals("highway", key.value);
+	}
+	
+	public void testIconLoadsFromClasspath() throws Exception {
+		String xml = "<annotations><item icon='logo'></item></annotations>";
+		List<AnnotationPreset> all = AnnotationPreset.readAll(new ByteArrayInputStream(xml.getBytes()));
+
+		assertEquals(1, all.size());
+
+		Icon icon = (Icon)all.get(0).getValue(Action.SMALL_ICON);
+		assertNotNull(icon);
+		assertEquals("Icon loaded and of correct size", 
+				24, Math.max(icon.getIconHeight(), icon.getIconWidth()));
+    }
+}
Index: test/unit/org/openstreetmap/josm/gui/annotation/annotation-test.xml
===================================================================
--- test/unit/org/openstreetmap/josm/gui/annotation/annotation-test.xml	(revision 284)
+++ test/unit/org/openstreetmap/josm/gui/annotation/annotation-test.xml	(revision 284)
@@ -0,0 +1,14 @@
+<annotations>
+  <item name="Highway">
+    <label text="Inserting a highway in UK" />
+
+    <text key="name" text="Highway (e.g. M3)" />
+    <combo key="highway" text="Type" values="major,minor" delete_if_empty="true" />
+
+    <!-- Highways are usually oneway -->
+    <check key="oneway" text="Oneway" default="on" />
+
+    <!-- Always setting class=highway -->
+    <key key="class" value="highway" />
+  </item>
+</annotations>
Index: test/unit/org/openstreetmap/josm/gui/download/BoundingBoxSelectionTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/download/BoundingBoxSelectionTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/gui/download/BoundingBoxSelectionTest.java	(revision 284)
@@ -0,0 +1,19 @@
+package org.openstreetmap.josm.gui.download;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.openstreetmap.josm.data.Bounds;
+
+public class BoundingBoxSelectionTest {
+
+	private Bounds bounds;
+
+	@Test public void osmurl2boundsDoesWorkWithAnyDomain() throws Exception {
+		bounds = BoundingBoxSelection.osmurl2bounds("http://foobar?mlat=123&mlon=234&zoom=1");
+		assertNotNull(bounds);
+		
+		bounds = BoundingBoxSelection.osmurl2bounds("http://www.openstreetmap.org?mlat=123&mlon=234&zoom=1");
+		assertNotNull(bounds);
+	}
+}
Index: test/unit/org/openstreetmap/josm/io/BoundingBoxDownloaderTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/io/BoundingBoxDownloaderTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/io/BoundingBoxDownloaderTest.java	(revision 284)
@@ -0,0 +1,133 @@
+package org.openstreetmap.josm.io;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+
+import org.junit.Test;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.PleaseWaitDialog;
+import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+import org.openstreetmap.josm.testframework.MainMock;
+import org.xml.sax.SAXException;
+
+public class BoundingBoxDownloaderTest extends MainMock {
+
+	private class StringDownloader extends BoundingBoxDownloader {
+	    ByteArrayInputStream stream = in == null ? null : new ByteArrayInputStream(in.getBytes());
+	    private StringDownloader() {super(1,2,3,4);}
+	    @Override protected InputStream getInputStream(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws IOException {
+	    	ByteArrayInputStream oldStream = stream;
+	    	stream = null;
+	    	return oldStream;
+	    }
+    }
+
+	private BoundingBoxDownloader bbox;
+	private String in;
+	private Collection<Collection<GpsPoint>> gps;
+	private Collection<GpsPoint> trk;
+	private DataSet ds;
+
+	private void parseRaw() throws IOException, SAXException {
+		bbox = new StringDownloader();
+		gps = bbox.parseRawGps();
+		if (gps != null && gps.size() > 0)
+			trk = gps.iterator().next();
+	}
+
+	private void parseOsm() throws IOException, SAXException {
+		bbox = new StringDownloader();
+		ds = bbox.parseOsm();
+	}
+
+	
+	@Test public void parseRawGpsEmptyDataReturnsEmptyList() throws Exception {
+		in = "<gpx></gpx>";
+		parseRaw();
+		assertEquals(0, gps.size());
+	}
+	
+	@Test public void parseRawGpsOneTrack() throws Exception {
+	    in = "<gpx><trk><trkseg><trkpt lat='1' lon='2'/></trkseg></trk></gpx>";
+		parseRaw();
+		assertEquals(1, gps.size());
+		assertEquals(1, trk.size());
+		assertEquals(1.0, trk.iterator().next().latlon.lat());
+		assertEquals(2.0, trk.iterator().next().latlon.lon());
+    }
+	
+	@Test public void parseRawGpsMultipleTracksReturnStillOneTrack() throws Exception {
+	    in = "<gpx>" +
+	    		"<trk>" +
+	    		"<trkseg><trkpt lat='23' lon='42'/></trkseg>" +
+	    		"<trkseg><trkpt lat='12' lon='34'/></trkseg>" +
+	    		"</trk>" +
+	    		"<trk><trkseg><trkpt lat='3' lon='4'/></trkseg></trk>" +
+	    		"</gpx>";
+	    parseRaw();
+	    assertEquals(1, gps.size());
+	    assertEquals(3, trk.size());
+		assertEquals(42.0, trk.iterator().next().latlon.lon());
+    }
+
+	@Test public void parseOsmReturnNullIfNullInputStream() throws Exception {
+	    in = null;
+	    parseOsm();
+	    assertNull(ds);
+    }
+	
+	@Test public void parseOsmEmpty() throws Exception {
+	    in = "<osm version='0.4'></osm>";
+	    parseOsm();
+	    assertEquals(0, ds.nodes.size());
+	    assertEquals(0, ds.segments.size());
+	    assertEquals(0, ds.ways.size());
+    }
+	
+	@Test public void parseOsmSimpleNode() throws Exception {
+	    in = "<osm version='0.4'><node id='123' lat='12' lon='23'/></osm>";
+	    parseOsm();
+	    assertEquals(1, ds.nodes.size());
+	    Node node = ds.nodes.iterator().next();
+		assertEquals(123, node.id);
+	    assertEquals(12.0, node.coor.lat());
+	    assertEquals(23.0, node.coor.lon());
+	    assertNull(node.keys);
+    }
+	
+	@Test public void parseOsmComplexWay() throws Exception {
+	    in = "<osm version='0.4'>" +
+	    		"<way id='1'>" +
+	    		"<seg id='2' />" +
+	    		"</way>" +
+	    		"<segment id='2' from='3' to='3' />" +
+	    		"<node id='3' lat='1' lon='2'><tag k='foo' v='bar' /></node>" +
+	    		"</osm>";
+	    
+	    parseOsm();
+	    
+	    assertEquals(1, ds.nodes.size());
+	    assertEquals(1, ds.segments.size());
+	    assertEquals(1, ds.ways.size());
+	    
+	    Node node = ds.nodes.iterator().next();
+	    Segment segment = ds.segments.iterator().next();
+	    Way way = ds.ways.iterator().next();
+
+	    assertNotNull(node.keys);
+	    assertEquals("bar", node.get("foo"));
+	    assertEquals(1.0, node.coor.lat());
+		assertEquals(node, segment.from);
+		assertEquals(1, way.segments.size());
+		assertEquals(segment, way.segments.iterator().next());
+    }
+}
Index: test/unit/org/openstreetmap/josm/io/IncompleteDownloaderTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/io/IncompleteDownloaderTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/io/IncompleteDownloaderTest.java	(revision 284)
@@ -0,0 +1,38 @@
+package org.openstreetmap.josm.io;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.LinkedList;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.PleaseWaitDialog;
+import org.openstreetmap.josm.testframework.Bug;
+import org.openstreetmap.josm.testframework.MotherObject;
+
+public class IncompleteDownloaderTest extends MotherObject {
+
+
+	@Bug(174)
+	public void testDownloadDoesNotWriteToMainDataDirectly() throws Exception {
+		LinkedList<Way> l = new LinkedList<Way>();
+		Way w = new Way();
+		w.segments.add(new Segment(23)); // incomplete segment
+		Main.ds.nodes.add(createNode(1));
+		Main.ds.nodes.add(createNode(2));
+		l.add(w);
+		IncompleteDownloader downloader = new IncompleteDownloader(l) {
+			@Override protected InputStream getInputStream(String urlStr, PleaseWaitDialog pleaseWaitDlg) {
+				String xml = "<osm version='0.4'><segment id='23' from='1' to='2'/></osm>";
+	            return new ByteArrayInputStream(xml.getBytes());
+            }
+		};
+
+		Main.pleaseWaitDlg = new PleaseWaitDialog(null);
+
+		downloader.parse();
+
+		assertEquals("Does not directly write to main data", 0, Main.ds.segments.size());
+	}
+}
Index: test/unit/org/openstreetmap/josm/plugins/PluginExceptionTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/plugins/PluginExceptionTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/plugins/PluginExceptionTest.java	(revision 284)
@@ -0,0 +1,18 @@
+package org.openstreetmap.josm.plugins;
+
+import junit.framework.TestCase;
+
+public class PluginExceptionTest extends TestCase {
+
+	public void testConstructorPassesExceptionParameterAndSetPluginName() {
+		RuntimeException barEx = new RuntimeException("bar");
+		PluginException e = new PluginException(new PluginProxy(new String(), null), "42", barEx);
+		assertEquals(barEx, e.getCause());
+		assertEquals("42", e.name);
+	}
+
+	public void testMessageContainsThePluginName() {
+		PluginException e = new PluginException(new PluginProxy(new String(), null), "42", new RuntimeException());
+		assertTrue(e.getMessage().contains("42"));
+	}
+}
Index: test/unit/org/openstreetmap/josm/plugins/PluginInformationTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/plugins/PluginInformationTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/plugins/PluginInformationTest.java	(revision 284)
@@ -0,0 +1,101 @@
+package org.openstreetmap.josm.plugins;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.jar.JarInputStream;
+
+import junit.framework.TestCase;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Preferences;
+
+public class PluginInformationTest extends TestCase {
+
+	@Override protected void setUp() throws Exception {
+	    super.setUp();
+	    Main.pref = new Preferences(){
+        	@Override public Collection<String> getAllPossiblePreferenceDirs() {
+        		return Arrays.asList(new String[]{getClass().getResource("..").getFile()});
+            }
+        };
+	}
+
+	public void testConstructorExtractsAttributesFromManifest() throws Exception {
+		PluginInformation info = new PluginInformation(new File(getClass().getResource("simple.jar").getFile()));
+		String s = getClass().getResource(".").getFile();
+        assertEquals(4, info.libraries.size());
+        assertEquals(s+"foo", info.libraries.get(1).getFile());
+        assertEquals(s+"bar", info.libraries.get(2).getFile());
+        assertEquals(s+"C:/Foo%20and%20Bar", info.libraries.get(3).getFile());
+        
+        assertEquals("imi", info.author);
+        assertEquals("Simple", info.className);
+        assertEquals("Simpler", info.description);
+        assertEquals(true, info.early);
+    }
+
+	public void testConstructorRequiresJarWithManifest() throws Exception {
+		try {
+	        new PluginInformation(new File(getClass().getResource("no_manifest.jar").getFile()));
+	        fail("Exception because missing manifest excpected");
+        } catch (PluginException e) {
+        }
+    }
+	
+	public void testConstructorWithInputStream() throws Exception {
+		JarInputStream f = new JarInputStream(getClass().getResourceAsStream("simple.jar"));
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		f.getManifest().write(out);
+
+		PluginInformation info = new PluginInformation(null, "simple", new ByteArrayInputStream(out.toByteArray()));
+        assertEquals("Only the 3 external classpaths are added (as we are using bootstrap classpath for plugin",
+        		3, info.libraries.size());
+    }
+	
+	public void testLoadClassInstantiatePlugin() throws Exception {
+		PluginInformation info = new PluginInformation(new File(getClass().getResource("working.jar").getFile()));
+		ClassLoader cl = new URLClassLoader(new URL[]{getClass().getResource("working.jar")});
+		assertNotNull(info.load(info.loadClass(cl)));
+    }
+	
+	// This is so the bugtracker always detect coding problems as "plugin problems"
+	public void testLoadThrowsPluginExceptionOnRuntimeException() throws Exception {
+		PluginInformation info = new PluginInformation(new File(getClass().getResource("working.jar").getFile()));
+		try {
+	        info.load(null);
+	        fail("Exception excpected because null-Class");
+        } catch (PluginException e) {
+        }
+        try {
+        	info.loadClass(null);
+        	fail("Exception excpected because null-ClassLoader");
+        } catch (PluginException e) {
+        }
+    }
+	
+	public void testFindPluginReturnsInformationFromBootstrapClasspath() throws Exception {
+	    PluginInformation info = PluginInformation.findPlugin("test_simple");
+	    assertEquals("Simpler", info.description);
+    }
+	
+	public void testFindPluginReturnsFromPreferencesDirs() throws Exception {
+	    PluginInformation info = PluginInformation.findPlugin("simple");
+	    assertEquals("Simpler", info.description);
+    }
+	
+	public void testFindPluginForUnknownReturnsNull() throws Exception {
+		assertNull(PluginInformation.findPlugin("asdf"));
+	}
+
+	public void testPluginLocationsReturnModifiedPreferenceLocations() throws Exception {
+	    setUp();
+	    Collection<String> locations = PluginInformation.getPluginLocations();
+	    assertEquals(1, locations.size());
+	    assertTrue(locations.iterator().next().endsWith("/plugins"));
+    }
+}
Index: test/unit/org/openstreetmap/josm/plugins/test_simple/MANIFEST.MF
===================================================================
--- test/unit/org/openstreetmap/josm/plugins/test_simple/MANIFEST.MF	(revision 284)
+++ test/unit/org/openstreetmap/josm/plugins/test_simple/MANIFEST.MF	(revision 284)
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Created-By: 1.5.0_07 (Sun Microsystems Inc.)
+Plugin-Class: Simple
+Plugin-Description: Simpler
+
Index: test/unit/org/openstreetmap/josm/testframework/Bug.java
===================================================================
--- test/unit/org/openstreetmap/josm/testframework/Bug.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/testframework/Bug.java	(revision 284)
@@ -0,0 +1,13 @@
+package org.openstreetmap.josm.testframework;
+
+/**
+ * Annotation that indicate that a specific test case function was a bug.
+ * @author Imi
+ */
+public @interface Bug {
+	/**
+	 * The revision this bug was detected. (Can be later than the actual first occourence.
+	 * This number is just to have a revision where the bug actually happen.)
+	 */
+	int value();
+}
Index: test/unit/org/openstreetmap/josm/testframework/DataSetTestCaseHelper.java
===================================================================
--- test/unit/org/openstreetmap/josm/testframework/DataSetTestCaseHelper.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/testframework/DataSetTestCaseHelper.java	(revision 284)
@@ -0,0 +1,73 @@
+package org.openstreetmap.josm.testframework;
+
+import java.util.Arrays;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.projection.Mercator;
+
+
+/**
+ * Test cases that need to manupulate a data set can use this helper.
+ *  
+ * @author Imi
+ */
+public class DataSetTestCaseHelper {
+
+	/**
+	 * Create a common dataset consisting of:
+	 * - 5 random nodes
+	 * - ls between node 0 and 1
+	 * - ls between node 1 and 2
+	 * - ls between node 3 and 4
+	 * - a way with ls 0 and 1
+	 */
+	public static DataSet createCommon() {
+		DataSet ds = new DataSet();
+		Node n1 = createNode(ds);
+		Node n2 = createNode(ds);
+		Node n3 = createNode(ds);
+		Node n4 = createNode(ds);
+		Node n5 = createNode(ds);
+		Segment ls1 = createSegment(ds, n1, n2);
+		Segment ls2 = createSegment(ds, n2, n3);
+		createSegment(ds, n4, n5);
+		createWay(ds, ls1, ls2);
+		return ds;
+	}
+
+	public static Way createWay(DataSet ds, Segment... segments) {
+		Way w = new Way();
+		w.segments.addAll(Arrays.asList(segments));
+		if (ds != null)
+			ds.ways.add(w);
+		return w;
+	}
+	
+	/**
+	 * Create a segment with out of the given nodes.
+	 */
+	public static Segment createSegment(DataSet ds, Node n1, Node n2) {
+		Segment ls = new Segment(n1, n2);
+		if (ds != null)
+			ds.segments.add(ls);
+		return ls;
+	}
+
+	/**
+	 * Add a random node.
+	 */
+	public static Node createNode(DataSet ds) {
+		if (Main.proj == null)
+			Main.proj = new Mercator();
+		Node node = new Node(new LatLon(Math.random(), Math.random()));
+		if (ds != null)
+			ds.nodes.add(node);
+		return node;
+	}
+
+}
Index: test/unit/org/openstreetmap/josm/testframework/MainMock.java
===================================================================
--- test/unit/org/openstreetmap/josm/testframework/MainMock.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/testframework/MainMock.java	(revision 284)
@@ -0,0 +1,72 @@
+package org.openstreetmap.josm.testframework;
+
+import java.awt.AWTEvent;
+import java.awt.AWTException;
+import java.awt.Toolkit;
+import java.awt.event.AWTEventListener;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.projection.Epsg4326;
+import org.openstreetmap.josm.gui.PleaseWaitDialog;
+
+public class MainMock {
+
+	private static JDialog lastPopup;
+
+	@Before public void clearFoundPopup() {
+		lastPopup = null;
+	}
+	
+	@BeforeClass public static void mockMain() throws Exception {
+		Main.pref = new Preferences(){
+			@Override protected void save() {}
+			@Override public void load() throws IOException {}
+			@Override public Collection<Bookmark> loadBookmarks() throws IOException {return Collections.emptyList();}
+			@Override public void saveBookmarks(Collection<Bookmark> bookmarks) throws IOException {}
+		};
+		Main.parent = new JFrame();
+		Main.proj = new Epsg4326();
+		Main.pleaseWaitDlg = new PleaseWaitDialog(Main.parent);
+		Main.main = new Main(){};
+	}
+
+	@BeforeClass public static void startPopupKiller() throws AWTException {
+		Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener(){
+			public void eventDispatched(AWTEvent event) {
+				if (event.getSource() instanceof JButton) {
+					JButton b = (JButton)event.getSource();
+					if (b.getParent().getParent() instanceof JOptionPane) {
+						lastPopup = (JDialog)SwingUtilities.getRoot(b);
+						b.doClick();
+					}
+				}
+            }
+		}, AWTEvent.FOCUS_EVENT_MASK);
+    }
+
+	public void assertPopup() {
+		waitForPopup();
+		lastPopup = null;
+	}
+
+	public JDialog waitForPopup() {
+	    for (int i = 0; i < 100; ++i) {
+			if (lastPopup != null)
+				return lastPopup;
+			try {Thread.sleep(10);} catch (InterruptedException e) {}
+		}
+		throw new AssertionError("Expected Popup dialog");
+    }
+}
Index: test/unit/org/openstreetmap/josm/testframework/MotherObject.java
===================================================================
--- test/unit/org/openstreetmap/josm/testframework/MotherObject.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/testframework/MotherObject.java	(revision 284)
@@ -0,0 +1,102 @@
+package org.openstreetmap.josm.testframework;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import junit.framework.TestCase;
+
+import org.openstreetmap.josm.Main;
+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.Segment;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.projection.Epsg4326;
+
+abstract public class MotherObject extends TestCase {
+
+	public static DataSet dataSet;
+	
+	@Override protected void setUp() throws Exception {
+	    super.setUp();
+	    Main.proj = new Epsg4326();
+    }
+
+	public static Node createNode(int id) {
+		return createNode(id, 0, 0);
+	}
+	
+	public static Node createNode(int id, double lat, double lon) {
+		Node n = createNode(lat, lon);
+		n.id = id;
+		return n;
+	}
+
+	public static Node createNode() {
+		return createNode(Math.random()*360-180, Math.random()*180-90);
+	}
+
+	public static Node createNode(double lat, double lon) {
+	    Node node = new Node(new LatLon(lat,lon));
+	    if (dataSet != null)
+	    	dataSet.nodes.add(node);
+		return node;
+    }
+	
+	
+	public static Segment createSegment(long id) {
+		Segment s = createSegment();
+		s.id = id;
+		return s;
+	}
+	public static Segment createSegment(long id, Node from, Node to) {
+		Segment s = new Segment(from, to);
+		s.id = id;
+		return s;
+	}
+	public static Segment createSegment() {
+		Segment segment = new Segment(createNode(), createNode());
+		if (dataSet != null)
+			dataSet.segments.add(segment);
+		return segment;
+	}
+	
+	
+	public static Way createWay() {
+		return createWay(0);
+	}
+	public static Way createWay(Segment... segments) {
+		return createWay(0, segments);
+	}
+	public static Way createWay(long id, Segment... segments) {
+		Way way = new Way();
+		way.segments.addAll(Arrays.asList(segments));
+		way.id = id;
+		if (dataSet != null)
+			dataSet.ways.add(way);
+		return way;
+	}
+	
+	public static DataSet createDataSet() {
+	    DataSet ds = new DataSet();
+		Node node1 = createNode();
+		Node node2 = createNode();
+		Node node3 = createNode();
+		Segment segment = createSegment(23, node1, node2);
+		Way way = createWay(42, segment);
+		ds.nodes.add(node1);
+		ds.nodes.add(node2);
+		ds.nodes.add(node3);
+		ds.segments.add(segment);
+		ds.ways.add(way);
+		return ds;
+    }
+
+	public static void assertContainsSame(Collection<OsmPrimitive> data, OsmPrimitive... all) {
+		Collection<OsmPrimitive> copy = new LinkedList<OsmPrimitive>(data);
+		copy.removeAll(Arrays.asList(all));
+		assertEquals(0, copy.size());
+    }
+}
Index: test/unit/org/openstreetmap/josm/tools/DateParserTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/tools/DateParserTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/tools/DateParserTest.java	(revision 284)
@@ -0,0 +1,31 @@
+package org.openstreetmap.josm.tools;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+import org.openstreetmap.josm.tools.DateParser;
+
+public class DateParserTest extends TestCase {
+
+	public void testWrong() throws Exception {
+	    try {
+	    	DateParser.parse("imi");
+	    	assertTrue(false);
+	    } catch (ParseException pe) {
+	    }
+    }
+	
+	public void testRight() throws Exception {
+		Date d = new SimpleDateFormat("dd MM yyyy HH mm ss SSS Z").parse("23 11 2001 23 05 42 123 +0100");
+		Date d2 = new Date(d.getTime()-123);
+		assertEquals(d2, DateParser.parse("11/23/2001 23:05:42"));
+		assertEquals(d2, DateParser.parse("11/23/2001T23:05:42"));
+		assertEquals(d2, DateParser.parse("11/23/2001T23:05:42+001"));
+		assertEquals(d2, DateParser.parse("2001-11-23T23:05:42+01:00"));
+        assertEquals(d, DateParser.parse("11/23/2001T23:05:42.123"));
+		assertEquals(d, DateParser.parse("11/23/2001T23:05:42.123+001"));
+    }
+}
Index: test/unit/org/openstreetmap/josm/tools/XmlObjectParserTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/tools/XmlObjectParserTest.java	(revision 284)
+++ test/unit/org/openstreetmap/josm/tools/XmlObjectParserTest.java	(revision 284)
@@ -0,0 +1,101 @@
+package org.openstreetmap.josm.tools;
+
+import java.io.StringReader;
+import java.util.NoSuchElementException;
+
+import org.openstreetmap.josm.tools.XmlObjectParser.Uniform;
+
+import junit.framework.TestCase;
+
+public class XmlObjectParserTest extends TestCase {
+
+	private XmlObjectParser parser;
+
+	public static class Foo {
+		public String bar;
+	}
+	public static class Bar {
+		private String ada;
+		public void setAda(String value) {
+			ada = value;
+		}
+	}
+
+	@Override protected void setUp() throws Exception {
+		super.setUp();
+		parser = new XmlObjectParser();
+	}
+
+	private XmlObjectParser createParser(String string) {
+		XmlObjectParser parser = new XmlObjectParser();
+		parser.map("foo", Foo.class);
+		parser.start(new StringReader(string));
+		return parser;
+	}
+
+	public void testSimpleStructWithAttributes() throws Exception {
+		parser = createParser("<xml><foo bar='foobar'/><foo bar='baz'/></xml>");
+
+		assertEquals("foobar", ((Foo)parser.next()).bar);
+		assertEquals("baz", ((Foo)parser.next()).bar);
+		assertFalse(parser.hasNext());
+		try {
+			parser.next();
+			fail();
+		} catch (NoSuchElementException e) {
+		}
+	}
+
+	public void testSubtagsWithCharacters() throws Exception {
+		parser = createParser("<foo><bar>asd</bar></foo>");
+		assertEquals("asd", ((Foo)parser.next()).bar);
+	}
+
+	public void testIterable() throws Exception {
+		parser = createParser("<xml><foo bar='yo'/><foo bar='yo'/><foo bar='yo'/></xml>");
+		for (Object o : parser)
+			assertEquals("yo", ((Foo)o).bar);
+	}
+
+	public void testUniformIterable() throws Exception {
+		XmlObjectParser.Uniform<Foo> p = new Uniform<Foo>(new StringReader("<xml><foo bar='sdf'/><foo bar='sdf'/></xml>"), "foo", Foo.class);
+		for (Foo foo : p)
+			assertEquals("sdf", foo.bar);
+	}
+
+
+	public void testObjectIntersection() throws Exception {
+		parser.map("foo", Foo.class);
+		parser.map("imi", Bar.class);
+		parser.start(new StringReader("<xml><foo bar='yo'><imi ada='123'/></foo></xml>"));
+
+		Object imi = parser.next();
+		Object foo = parser.next();
+		assertTrue(imi instanceof Bar);
+		assertTrue(foo instanceof Foo);
+		assertEquals("yo", ((Foo)foo).bar);
+		assertEquals("123", ((Bar)imi).ada);
+	}
+
+	public void testObjectIntersectionWithMapOnStart() throws Exception {
+		parser.mapOnStart("foo", Foo.class);
+		parser.map("imi", Bar.class);
+		parser.start(new StringReader("<xml><foo><imi/></foo></xml>"));
+
+		Object foo = parser.next();
+		Object imi = parser.next();
+		assertTrue(imi instanceof Bar);
+		assertTrue(foo instanceof Foo);
+	}
+
+	public void testMapReportsObjectsAndDoNotFillAttributes() throws Exception {
+		parser.map("foo", Foo.class);
+		parser.map("bar", Bar.class);
+		parser.start(new StringReader("<xml><foo><bar/></foo></xml>"));
+
+		assertTrue(parser.next() instanceof Bar);
+		Object foo = parser.next();
+		assertTrue(foo instanceof Foo);
+		assertNull(((Foo)foo).bar);
+	}
+}
