Index: trunk/test/unit/org/CustomMatchers.java
===================================================================
--- trunk/test/unit/org/CustomMatchers.java	(revision 11867)
+++ trunk/test/unit/org/CustomMatchers.java	(revision 12906)
@@ -4,4 +4,5 @@
 import java.awt.geom.Point2D;
 import java.util.Collection;
+import java.util.Locale;
 import java.util.Objects;
 import java.util.function.Predicate;
@@ -12,4 +13,5 @@
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.Ignore;
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -21,4 +23,19 @@
 public final class CustomMatchers {
 
+    /**
+     * Error mode, denoting different ways to calculate the error of a number relative to an expected value.
+     */
+    public enum ErrorMode {
+        /**
+         * absolute error (difference of actual and expected value)
+         */
+        ABSOLUTE,
+
+        /**
+         * relative error (difference divided by the expected value)
+         */
+        RELATIVE
+    }
+
     private CustomMatchers() {
         // Hide constructor for utility classes
@@ -126,3 +143,95 @@
         };
     }
+
+    /**
+     * Matcher for a {@link Bounds} object
+     * @param expected expected bounds
+     * @param tolerance acceptable deviation (epsilon)
+     * @return Matcher for a {@link Bounds} object
+     */
+    public static Matcher<Bounds> is(final Bounds expected, double tolerance) {
+        return new TypeSafeMatcher<Bounds>() {
+           @Override
+           public void describeTo(Description description) {
+              description.appendText("is ")
+                      .appendValue(expected)
+                      .appendText(" (tolarance: " + tolerance + ")");
+           }
+
+           @Override
+           protected void describeMismatchSafely(Bounds bounds, Description mismatchDescription) {
+              mismatchDescription.appendText("was ").appendValue(bounds);
+           }
+
+           @Override
+           protected boolean matchesSafely(Bounds bounds) {
+              return Math.abs(expected.getMinLon() - bounds.getMinLon()) <= tolerance &&
+                    Math.abs(expected.getMinLat() - bounds.getMinLat()) <= tolerance &&
+                    Math.abs(expected.getMaxLon() - bounds.getMaxLon()) <= tolerance &&
+                    Math.abs(expected.getMaxLat() - bounds.getMaxLat()) <= tolerance;
+           }
+        };
+    }
+
+    /**
+     * Matcher for a floating point number.
+     * @param expected expected value
+     * @param errorMode the error mode
+     * @param tolerance admissible error
+     * @return Matcher for a floating point number
+     */
+    public static Matcher<Double> isFP(final double expected, ErrorMode errorMode, double tolerance) {
+        return new TypeSafeMatcher<Double>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("is ")
+                        .appendValue(expected)
+                        .appendText(" (tolarance")
+                        .appendText(errorMode == ErrorMode.RELATIVE ? ", relative:" : ":")
+                        .appendText(Double.toString(tolerance))
+                        .appendText(")");
+            }
+
+            @Override
+            protected void describeMismatchSafely(Double was, Description mismatchDescription) {
+                mismatchDescription.appendText("was ").appendValue(was);
+                if (errorMode == ErrorMode.RELATIVE) {
+                    mismatchDescription.appendText(" (actual relative error: ")
+                            .appendText(String.format(Locale.US, "%.2e", Math.abs((was - expected) / expected)))
+                            .appendText(")");
+                }
+            }
+
+            @Override
+            protected boolean matchesSafely(Double x) {
+                switch (errorMode) {
+                    case ABSOLUTE:
+                        return Math.abs(x - expected) <= tolerance;
+                    case RELATIVE:
+                        return Math.abs((x - expected) / expected) <= tolerance;
+                    default:
+                        throw new AssertionError();
+                }
+            }
+        };
+    }
+
+    /**
+     * Matcher for a floating point number.
+     * @param expected expected value
+     * @param tolerance admissible error (absolute)
+     * @return Matcher for a floating point number
+     */
+    public static Matcher<Double> isFP(final double expected, double tolerance) {
+        return isFP(expected, ErrorMode.ABSOLUTE, tolerance);
+    }
+
+    /**
+     * Matcher for a floating point number.
+     * @param expected expected value
+     * @return Matcher for a floating point number
+     */
+    public static Matcher<Double> isFP(final double expected) {
+        return isFP(expected, 1e-8);
+    }
 }
