Index: src/org/openstreetmap/josm/actions/ValidateAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/ValidateAction.java	(revision 14995)
+++ src/org/openstreetmap/josm/actions/ValidateAction.java	(working copy)
@@ -116,6 +116,8 @@
         private boolean canceled;
         private List<TestError> errors;
 
+        private List<Class<? extends Test>> runTests;
+
         /**
          * Constructs a new {@code ValidationTask}
          * @param tests  the tests to run
@@ -153,32 +155,47 @@
         @Override
         protected void realRun() throws SAXException, IOException,
         OsmTransferException {
+            runTests = new ArrayList<>();
             if (tests == null || tests.isEmpty())
                 return;
             errors = new ArrayList<>(200);
             getProgressMonitor().setTicksCount(tests.size() * validatedPrimitives.size());
-            int testCounter = 0;
+            runTests(tests, 0);
+            tests = null;
+            if (ValidatorPrefHelper.PREF_USE_IGNORE.get()) {
+                getProgressMonitor().setCustomText("");
+                getProgressMonitor().subTask(tr("Updating ignored errors ..."));
+                for (TestError error : errors) {
+                    if (canceled) return;
+                    error.updateIgnored();
+                }
+            }
+        }
+
+        protected int runTests(Collection<Test> tests, int testCounter) {
+            ArrayList<Test> remaining = new ArrayList<>();
             for (Test test : tests) {
                 if (canceled)
-                    return;
+                    return testCounter;
+                if (test.getAfterClass() != null && !runTests.contains(test.getAfterClass())) {
+                    remaining.add(test);
+                    continue;
+                }
                 testCounter++;
-                getProgressMonitor().setCustomText(tr("Test {0}/{1}: Starting {2}", testCounter, tests.size(), test.getName()));
+                getProgressMonitor().setCustomText(tr("Test {0}/{1}: Starting {2}", testCounter, this.tests.size(), test.getName()));
                 test.setPartialSelection(formerValidatedPrimitives != null);
+                test.setPreviousErrors(errors);
                 test.startTest(getProgressMonitor().createSubTaskMonitor(validatedPrimitives.size(), false));
                 test.visit(validatedPrimitives);
                 test.endTest();
                 errors.addAll(test.getErrors());
                 test.clear();
+                runTests.add(test.getClass());
             }
-            tests = null;
-            if (ValidatorPrefHelper.PREF_USE_IGNORE.get()) {
-                getProgressMonitor().setCustomText("");
-                getProgressMonitor().subTask(tr("Updating ignored errors ..."));
-                for (TestError error : errors) {
-                    if (canceled) return;
-                    error.updateIgnored();
-                }
+            if (!remaining.isEmpty()) {
+                testCounter = runTests(remaining, testCounter);
             }
+            return testCounter;
         }
     }
 }
Index: src/org/openstreetmap/josm/data/validation/OsmValidator.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/OsmValidator.java	(revision 14995)
+++ src/org/openstreetmap/josm/data/validation/OsmValidator.java	(working copy)
@@ -49,6 +49,7 @@
 import org.openstreetmap.josm.data.validation.tests.DuplicatedWayNodes;
 import org.openstreetmap.josm.data.validation.tests.Highways;
 import org.openstreetmap.josm.data.validation.tests.InternetTags;
+import org.openstreetmap.josm.data.validation.tests.IntersectionIssues;
 import org.openstreetmap.josm.data.validation.tests.Lanes;
 import org.openstreetmap.josm.data.validation.tests.LongSegment;
 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
@@ -148,6 +149,7 @@
         LongSegment.class, // 3500 .. 3599
         PublicTransportRouteTest.class, // 3600 .. 3699
         RightAngleBuildingTest.class, // 3700 .. 3799
+        IntersectionIssues.class, // 3800 .. 3899
     };
 
     /**
Index: src/org/openstreetmap/josm/data/validation/Test.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/Test.java	(revision 14995)
+++ src/org/openstreetmap/josm/data/validation/Test.java	(working copy)
@@ -46,6 +46,9 @@
     /** Name of the test */
     protected final String name;
 
+    /** Test to run after */
+    protected Class<? extends Test> afterTest;
+
     /** Description of the test */
     protected final String description;
 
@@ -67,6 +70,9 @@
     /** The list of errors */
     protected List<TestError> errors = new ArrayList<>(30);
 
+    /** The list of previously found errors */
+    protected List<TestError> previousErrors;
+
     /** Whether the test is run on a partial selection data */
     protected boolean partialSelection;
 
@@ -84,8 +90,21 @@
      * @param description Description of the test
      */
     public Test(String name, String description) {
+        this(name, description, null);
+    }
+
+    /**
+     * Constructor
+     * @param name Name of the test
+     * @param description Description of the test
+     * @param afterTest Ensure the test is run after a test with this name
+     *
+     * @since xxx
+     */
+    public Test(String name, String description, Class<? extends Test> afterTest) {
         this.name = name;
         this.description = description;
+        this.afterTest = afterTest;
     }
 
     /**
@@ -178,6 +197,15 @@
     }
 
     /**
+     * Set the validation errors accumulated by other tests until this moment
+     * For validation errors accumulated by this test, use {@code getErrors()}
+     * @param errors The errors from previous tests
+     */
+    public void setPreviousErrors(List<TestError> errors) {
+        previousErrors = errors;
+    }
+
+    /**
      * Notification of the end of the test. The tester may perform additional
      * actions and destroy the used structures.
      * <p>
@@ -319,6 +347,16 @@
     }
 
     /**
+     * Get the class that the test must run after
+     * @return A class that extends {@code Test}
+     *
+     * @since xxx
+     */
+    public Class<? extends Test> getAfterClass() {
+        return afterTest;
+    }
+
+    /**
      * Determines if the test has been canceled.
      * @return {@code true} if the test has been canceled, {@code false} otherwise
      */
Index: src/org/openstreetmap/josm/data/validation/tests/IntersectionIssues.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/tests/IntersectionIssues.java	(nonexistent)
+++ src/org/openstreetmap/josm/data/validation/tests/IntersectionIssues.java	(working copy)
@@ -0,0 +1,312 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.validation.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.WaySegment;
+import org.openstreetmap.josm.data.validation.Severity;
+import org.openstreetmap.josm.data.validation.Test;
+import org.openstreetmap.josm.data.validation.TestError;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.Geometry;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * Finds issues with highway intersections
+ * @author Taylor Smock
+ * @since xxx
+ */
+public class IntersectionIssues extends Test {
+    private static final int INTERSECTIONISSUESCODE = 3800;
+    /** The code for an intersection which briefly interrupts a road */
+    public static final int SHORT_DISCONNECT = INTERSECTIONISSUESCODE + 0;
+    /** The code for a node that is almost on a way */
+    public static final int NEARBY_NODE = INTERSECTIONISSUESCODE + 1;
+    /** The distance to consider for nearby nodes/short disconnects */
+    public static final double MAX_DISTANCE = 5.0; // meters
+    /** The distance to consider for nearby nodes with tags */
+    public static final double MAX_DISTANCE_NODE_INFORMATION = MAX_DISTANCE / 5.0; // meters
+    /** The maximum angle for almost overlapping ways */
+    public static final double MAX_ANGLE = 15.0; // degrees
+    /** The maximum length to consider for almost overlapping ways */
+    public static final double MAX_LENGTH = 5.0; // meters
+
+    private HashMap<String, ArrayList<Way>> ways;
+    ArrayList<Way> allWays;
+
+    /**
+     * Construct a new {@code IntersectionIssues} object
+     */
+    public IntersectionIssues() {
+        super(tr("Intersection Issues"), tr("Check for issues at intersections"), OverlappingWays.class);
+    }
+
+    @Override
+    public void startTest(ProgressMonitor monitor) {
+        super.startTest(monitor);
+        ways = new HashMap<>();
+        allWays = new ArrayList<>();
+    }
+
+    @Override
+    public void endTest() {
+        Way pWay = null;
+        try {
+            for (ArrayList<Way> comparison : ways.values()) {
+                pWay = comparison.get(0);
+                checkNearbyEnds(comparison);
+            }
+            for (Way way : allWays) {
+                pWay = way;
+                for (Way way2 : allWays) {
+                    if (way2.equals(way)) continue;
+                    pWay = way2;
+                    if (way.getBBox().intersects(way2.getBBox())) {
+                        checkNearbyNodes(way, way2);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            if (pWay != null) {
+                Logging.debug("Way https://osm.org/way/{0} caused an error", pWay.getOsmId());
+            }
+            Logging.warn(e);
+        }
+        ways = null;
+        allWays = null;
+        super.endTest();
+    }
+
+    @Override
+    public void visit(Way way) {
+        if (!way.isUsable()) return;
+        String HIGHWAY = "highway";
+        if (way.hasKey(HIGHWAY) && !way.get(HIGHWAY).contains("_link") &&
+                !way.get(HIGHWAY).contains("proposed")) {
+            String[] identityTags = new String[] {"name", "ref"};
+            for (String tag : identityTags) {
+                if (way.hasKey(tag)) {
+                    ArrayList<Way> similar = ways.get(way.get(tag)) == null ? new ArrayList<>() : ways.get(way.get(tag));
+                    if (!similar.contains(way)) similar.add(way);
+                    ways.put(way.get(tag), similar);
+                }
+            }
+            if (!allWays.contains(way)) allWays.add(way);
+        }
+    }
+
+    /**
+     * Check for ends that are nearby but not directly connected
+     * @param comparison Ways to look at
+     */
+    public void checkNearbyEnds(List<Way> comparison) {
+        ArrayList<Way> errored = new ArrayList<>();
+        for (Way one : comparison) {
+            LatLon oneLast = one.lastNode().getCoor();
+            LatLon oneFirst = one.firstNode().getCoor();
+            for (Way two : comparison) {
+                if (one.equals(two)) continue;
+                if (one.isFirstLastNode(two.firstNode()) || one.isFirstLastNode(two.lastNode()) ||
+                        (errored.contains(one) && errored.contains(two))) continue;
+                LatLon twoLast = two.lastNode().getCoor();
+                LatLon twoFirst = two.firstNode().getCoor();
+                int nearCase = getNearCase(oneFirst, oneLast, twoFirst, twoLast);
+                if (nearCase != 0) {
+                    for (Way way : two.lastNode().getParentWays()) {
+                        if (way.equals(two)) continue;
+                        if (one.hasKey("name") && way.hasKey("name") && way.get("name").equals(one.get("name")) ||
+                                one.hasKey("ref") && way.hasKey("ref") && way.get("ref").equals(one.get("ref"))) {
+                            return;
+                        }
+                    }
+                    for (Way way : two.firstNode().getParentWays()) {
+                        if (way.equals(two)) continue;
+                        if (one.hasKey("name") && way.hasKey("name") && way.get("name").equals(one.get("name")) ||
+                                one.hasKey("ref") && way.hasKey("ref") && way.get("ref").equals(one.get("ref"))) {
+                            return;
+                        }
+                    }
+                }
+                if (nearCase > 0) {
+                    List<Way> nearby = new ArrayList<>();
+                    nearby.add(one);
+                    nearby.add(two);
+                    List<WaySegment> segments = new ArrayList<>();
+                    if ((nearCase & 1) != 0) {
+                        segments.add(new WaySegment(two, two.getNodesCount() - 2));
+                        segments.add(new WaySegment(one, one.getNodesCount() - 2));
+                    }
+                    if ((nearCase & 2) != 0) {
+                        segments.add(new WaySegment(two, two.getNodesCount() - 2));
+                        segments.add(new WaySegment(one, 0));
+                    }
+                    if ((nearCase & 4) != 0) {
+                        segments.add(new WaySegment(two, 0));
+                        segments.add(new WaySegment(one, one.getNodesCount() - 2));
+                    }
+                    if ((nearCase & 8) != 0) {
+                        segments.add(new WaySegment(two, 0));
+                        segments.add(new WaySegment(one, 0));
+                    }
+                    errored.addAll(nearby);
+                    allWays.removeAll(errored);
+                    TestError.Builder testError = TestError.builder(this, Severity.WARNING, SHORT_DISCONNECT)
+                            .primitives(nearby)
+                            .highlightWaySegments(segments)
+                            .message(tr("Disconnected road"));
+                    errors.add(testError.build());
+                }
+            }
+        }
+    }
+
+    /**
+     * Get nearby cases
+     * @param oneFirst The {@code LatLon} of the the first node of the first way
+     * @param oneLast The {@code LatLon} of the the last node of the first way
+     * @param twoFirst The {@code LatLon} of the the first node of the second way
+     * @param twoLast The {@code LatLon} of the the last node of the second way
+     * @return A bitwise int (8421 -> twoFirst/oneFirst, twoFirst/oneLast, twoLast/oneFirst, twoLast/oneLast)
+     *
+     */
+    private int getNearCase(LatLon oneFirst, LatLon oneLast, LatLon twoFirst, LatLon twoLast) {
+        int returnInt = 0;
+        if (twoLast.greatCircleDistance(oneLast) <= MAX_DISTANCE) {
+            returnInt = returnInt | 1;
+        }
+        if (twoLast.greatCircleDistance(oneFirst) <= MAX_DISTANCE) {
+            returnInt = returnInt | 2;
+        }
+        if (twoFirst.greatCircleDistance(oneLast) <= MAX_DISTANCE) {
+            returnInt = returnInt | 4;
+        }
+        if (twoFirst.greatCircleDistance(oneFirst) <= MAX_DISTANCE) {
+            returnInt = returnInt | 8;
+        }
+        return returnInt;
+    }
+
+    /**
+     * Check nearby nodes to an intersection of two ways
+     * @param way1 A way to check an almost intersection with
+     * @param way2 A way to check an almost intersection with
+     */
+    public void checkNearbyNodes(Way way1, Way way2) {
+        Collection<Node> intersectingNodes = getIntersectingNode(way1, way2);
+        if (intersectingNodes.isEmpty() ||
+                (way1.isOneway() != 0 && way2.isOneway() != 0 &&
+                (way1.hasKey("name") && way1.get("name").equals(way2.get("name")) ||
+                 way1.hasKey("ref") && way1.get("ref").equals(way2.get("ref"))))) return;
+        for (Node intersectingNode : intersectingNodes) {
+            checkNearbyNodes(way1, way2, intersectingNode);
+            checkNearbyNodes(way2, way1, intersectingNode);
+        }
+    }
+
+    private void checkNearbyNodes(Way way1, Way way2, Node nearby) {
+        for (Node node : way1.getNeighbours(nearby)) {
+            if (node.equals(nearby) || way2.containsNode(node)) continue;
+            double distance = Geometry.getDistance(way2, node);
+            double angle = getSmallestAngle(way2, nearby, node);
+            if (((distance < MAX_DISTANCE && !node.isTagged())
+                    || (distance < MAX_DISTANCE_NODE_INFORMATION && node.isTagged()))
+                    && angle < MAX_ANGLE) {
+                List<Way> primitiveIssues = new ArrayList<>();
+                primitiveIssues.add(way1);
+                primitiveIssues.add(way2);
+                List<TestError> tErrors = new ArrayList<>();
+                if (previousErrors != null) tErrors.addAll(previousErrors);
+                tErrors.addAll(getErrors());
+                for (TestError error : tErrors) {
+                    int code = error.getCode();
+                    if ((code == SHORT_DISCONNECT || code == NEARBY_NODE
+                            || code == OverlappingWays.OVERLAPPING_HIGHWAY
+                            || code == OverlappingWays.DUPLICATE_WAY_SEGMENT
+                            || code == OverlappingWays.OVERLAPPING_HIGHWAY_AREA
+                            || code == OverlappingWays.OVERLAPPING_WAY
+                            || code == OverlappingWays.OVERLAPPING_WAY_AREA
+                            || code == OverlappingWays.OVERLAPPING_RAILWAY
+                            || code == OverlappingWays.OVERLAPPING_RAILWAY_AREA)
+                            && primitiveIssues.containsAll(error.getPrimitives())) {
+                        return;
+                    }
+                }
+                List<WaySegment> waysegmentsOne = new ArrayList<>();
+                int index = way1.getNodes().indexOf(nearby);
+                if (index >= way1.getNodesCount() - 1) index--;
+                waysegmentsOne.add(new WaySegment(way1, index));
+                if (index > 0) waysegmentsOne.add(new WaySegment(way1, index - 1));
+                index = way2.getNodes().indexOf(nearby);
+                List<WaySegment> waysegmentsTwo = new ArrayList<>();
+                if (index >= way2.getNodesCount() - 1) index--;
+                waysegmentsTwo.add(new WaySegment(way2, index));
+                if (index > 0) waysegmentsTwo.add(new WaySegment(way2, index - 1));
+                List<WaySegment> waysegments = new ArrayList<>();
+                for (WaySegment twoSegment : waysegmentsTwo) {
+                    if (angle == getSmallestAngle(twoSegment.toWay(), nearby, node)) {
+                        waysegments.add(twoSegment);
+                        break;
+                    }
+                }
+                for (WaySegment oneSegment: waysegmentsOne) {
+                    if (oneSegment.toWay().containsNode(node)) {
+                        waysegments.add(oneSegment);
+                        break;
+                    }
+                }
+                TestError.Builder testError = TestError.builder(this, Severity.WARNING, NEARBY_NODE)
+                        .primitives(primitiveIssues)
+                        .highlightWaySegments(waysegments)
+                        .message(tr("Sharp angle"));
+                errors.add(testError.build());
+            }
+        }
+    }
+
+    /**
+     * Get the intersecting node of two ways
+     * @param way1 A way that (hopefully) intersects with way2
+     * @param way2 A way to find an intersection with
+     * @return A collection of nodes where the ways intersect
+     */
+    public Collection<Node> getIntersectingNode(Way way1, Way way2) {
+        HashSet<Node> nodes = new HashSet<>();
+        for (Node node : way1.getNodes()) {
+            if (way2.containsNode(node)) {
+                nodes.add(node);
+            }
+        }
+        return nodes;
+    }
+
+    /**
+     * Get the corner angle between nodes
+     * @param way The way with additional nodes
+     * @param intersection The node to get angles around
+     * @param comparison The node to get angles from
+     * @return The angle for comparison->intersection->(additional node) (normalized degrees)
+     */
+    public double getSmallestAngle(Way way, Node intersection, Node comparison) {
+        Set<Node> neighbours = way.getNeighbours(intersection);
+        double angle = Double.MAX_VALUE;
+        EastNorth eastNorthIntersection = intersection.getEastNorth();
+        EastNorth eastNorthComparison = comparison.getEastNorth();
+        for (Node node : neighbours) {
+            EastNorth eastNorthNode = node.getEastNorth();
+            double tAngle = Geometry.getCornerAngle(eastNorthComparison, eastNorthIntersection, eastNorthNode);
+            if (Math.abs(tAngle) < angle) angle = Math.abs(tAngle);
+        }
+        return Geometry.getNormalizedAngleInDegrees(angle);
+    }
+}
Index: test/unit/org/openstreetmap/josm/data/validation/tests/IntersectionIssuesTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/validation/tests/IntersectionIssuesTest.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/data/validation/tests/IntersectionIssuesTest.java	(working copy)
@@ -0,0 +1,210 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.validation.tests;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.TestUtils;
+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.Way;
+import org.openstreetmap.josm.data.validation.TestError;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.tools.Utils;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * JUnit Test of "Long Segment" validation test.
+ */
+public class IntersectionIssuesTest {
+
+    /**
+     * Setup test.
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules().projection();
+
+    private static List<TestError> test(DataSet ds) throws Exception {
+        IntersectionIssues test = new IntersectionIssues();
+        test.initialize();
+        test.startTest(null);
+        for (Way w : ds.getWays()) {
+            test.visit(w);
+        }
+        test.endTest();
+        return test.getErrors();
+    }
+
+    /**
+     * This test area has 3 ways, where two have the same name and one has a
+     * different name where there is a short disconnect between the two with
+     * the same name.
+     * @return The collection of ways to test.
+     */
+    private static Collection<OsmPrimitive> getTestArea1() {
+        Node node1 = new Node(new LatLon(43.85619540309, 18.36535033094));
+        Node node2 = new Node(new LatLon(43.85658651031, 18.36534961159));
+        Node node3 = new Node(new LatLon(43.85662897034, 18.36534953349));
+        Node node4 = new Node(new LatLon(43.85694640771, 18.36534894963));
+        Node node5 = new Node(new LatLon(43.85658576291, 18.36456808743));
+        Node node6 = new Node(new LatLon(43.8566296379, 18.36604757608));
+
+        Way way1 = TestUtils.newWay("highway=residential name=\"Test Road 1\"",
+                node1, node2);
+        Way way2 = TestUtils.newWay("highway=residential name=\"Test Road 1\"",
+                node3, node4);
+        Way way3 = TestUtils.newWay("highway=residential name=\"Test Road 2\"",
+                node5, node2, node3, node6);
+
+        HashSet<OsmPrimitive> collection = new HashSet<>();
+        collection.addAll(way1.getNodes());
+        collection.addAll(way2.getNodes());
+        collection.addAll(way3.getNodes());
+        collection.add(way1);
+        collection.add(way2);
+        collection.add(way3);
+        return collection;
+    }
+
+    private static Collection<OsmPrimitive> getTestArea2() {
+        Node node1 = new Node(new LatLon(43.85641709632, 18.36725849681));
+        Node node2 = new Node(new LatLon(43.85680820208, 18.36725777746));
+        Node node3 = new Node(new LatLon(43.85685066196, 18.36725769936));
+        Node node4 = new Node(new LatLon(43.85716809815, 18.3672571155));
+        Node node5 = new Node(new LatLon(43.85680745469, 18.3664762533));
+        Node node6 = new Node(new LatLon(43.85685132951, 18.36795574195));
+        Way way1 = TestUtils.newWay("highway=residential name=\"Test Road 1\"",
+                node1, node2);
+        Way way2 = TestUtils.newWay("highway=residential name=\"Test Road 1\"",
+                node2, node3);
+        Way way3 = TestUtils.newWay("highway=residential name=\"Test Road 1\"",
+                node3, node4);
+        Way way4 = TestUtils.newWay("highway=residential name=\"Test Road 2\"",
+                node5, node2);
+        Way way5 = TestUtils.newWay("highway=residential name=\"Test Road 2\"",
+                node3, node6);
+
+        HashSet<OsmPrimitive> collection = new HashSet<>();
+        collection.addAll(way1.getNodes());
+        collection.addAll(way2.getNodes());
+        collection.addAll(way3.getNodes());
+        collection.addAll(way4.getNodes());
+        collection.addAll(way5.getNodes());
+        collection.add(way1);
+        collection.add(way2);
+        collection.add(way3);
+        collection.add(way4);
+        collection.add(way5);
+        return collection;
+    }
+
+    private static Collection<OsmPrimitive> getTestArea3() {
+        Node node1 = new Node(new LatLon(43.85570051259, 18.36651114378));
+        Node node2 = new Node(new LatLon(43.85613408344, 18.36651034633));
+        Node node3 = new Node(new LatLon(43.85645152344, 18.36650976248));
+        Node node4 = new Node(new LatLon(43.85609087565, 18.36572890027));
+        Node node5 = new Node(new LatLon(43.85609162303, 18.3665104064));
+        Node node6 = new Node(new LatLon(43.85613475101, 18.36720838893));
+        Way way1 = TestUtils.newWay("highway=residential name=\"Test Road 1\"",
+                node1, node2, node3);
+        Way way2 = TestUtils.newWay("highway=residential name=\"Test Road 2\"",
+                node4, node5, node2, node6);
+
+        Collection<OsmPrimitive> collection = new HashSet<>();
+        collection.addAll(way1.getNodes());
+        collection.addAll(way2.getNodes());
+        collection.add(way1);
+        collection.add(way2);
+        return collection;
+    }
+
+    private static Collection<OsmPrimitive> getTestAreaRealWorld1() {
+        // This was at https://www.openstreetmap.org/node/6123937677
+        Node node1 = new Node(new LatLon(16.4151329, -95.0267841));
+        Node node2 = new Node(new LatLon(16.4150313, -95.0267948));
+        Node node3 = new Node(new LatLon(16.4149297, -95.0268057));
+        Way way1 = TestUtils.newWay("highway=residential name=Calle Los Olivos",
+                node1, node3);
+        Way way2 = TestUtils.newWay(
+                "highway=residential name=Calle Camino Carretero (La Amistad)",
+                node1, node2, node3);
+
+        Collection<OsmPrimitive> collection = new HashSet<>();
+        collection.addAll(way1.getNodes());
+        collection.addAll(way2.getNodes());
+        collection.add(way1);
+        collection.add(way2);
+        return collection;
+    }
+
+    private static DataSet createDataSet(Collection<OsmPrimitive> primitives) {
+        DataSet ds = new DataSet();
+        for (Node node : Utils.filteredCollection(primitives, Node.class)) {
+            if (ds.containsNode(node)) continue;
+            ds.addPrimitive(node);
+        }
+        for (Way way : Utils.filteredCollection(primitives, Way.class)) {
+            for (Node node : way.getNodes()) {
+                if (ds.containsNode(node)) continue;
+                ds.addPrimitive(node);
+            }
+            if (ds.containsWay(way)) continue;
+            ds.addPrimitive(way);
+        }
+        return ds;
+    }
+
+    /**
+     * Unit test for {@link IntersectionIssues#checkNearbyEnds}
+     * @throws Exception if any error occurs
+     */
+    @Test
+    public void testCheckNearbyEnds() throws Exception {
+        DataSet area1 = createDataSet(getTestArea1());
+        List<TestError> testResults = test(area1);
+        Assert.assertEquals(1, testResults.size());
+        Assert.assertEquals(IntersectionIssues.SHORT_DISCONNECT, testResults.get(0).getCode());
+
+        DataSet area2 = createDataSet(getTestArea2());
+        testResults = test(area2);
+        Assert.assertEquals(1, testResults.size());
+        Assert.assertEquals(IntersectionIssues.SHORT_DISCONNECT, testResults.get(0).getCode());
+
+        area1.mergeFrom(area2);
+        testResults = test(area1);
+        Assert.assertEquals(2, testResults.size());
+        for (TestError error : testResults) {
+            Assert.assertEquals(IntersectionIssues.SHORT_DISCONNECT, error.getCode());
+        }
+    }
+
+    /**
+     * Unit test for {@link IntersectionIssues#checkNearbyNodes}
+     * @throws Exception if any error occurs
+     */
+    @Test
+    public void testCheckAlmostOverlappingWays() throws Exception {
+        Collection<OsmPrimitive> area1 = getTestArea1();
+        List<TestError> testResults = test(createDataSet(area1));
+        Assert.assertEquals(1, testResults.size());
+        Assert.assertNotEquals(IntersectionIssues.NEARBY_NODE, testResults.get(0).getCode());
+
+        Collection<OsmPrimitive> area3 = getTestArea3();
+        testResults = test(createDataSet(area3));
+        Assert.assertEquals(1, testResults.size());
+        Assert.assertEquals(IntersectionIssues.NEARBY_NODE, testResults.get(0).getCode());
+
+        Collection<OsmPrimitive> realWorldArea1 = getTestAreaRealWorld1();
+        testResults = test(createDataSet(realWorldArea1));
+        Assert.assertEquals(1, testResults.size());
+        Assert.assertEquals(IntersectionIssues.NEARBY_NODE, testResults.get(0).getCode());
+    }
+}
