001/* 002 * GeoAPI - Java interfaces for OGC/ISO standards 003 * http://www.geoapi.org 004 * 005 * Copyright (C) 2008-2019 Open Geospatial Consortium, Inc. 006 * All Rights Reserved. http://www.opengeospatial.org/ogc/legal 007 * 008 * Permission to use, copy, and modify this software and its documentation, with 009 * or without modification, for any purpose and without fee or royalty is hereby 010 * granted, provided that you include the following on ALL copies of the software 011 * and documentation or portions thereof, including modifications, that you make: 012 * 013 * 1. The full text of this NOTICE in a location viewable to users of the 014 * redistributed or derivative work. 015 * 2. Notice of any changes or modifications to the OGC files, including the 016 * date changes were made. 017 * 018 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE 019 * NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 020 * TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT 021 * THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY 022 * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. 023 * 024 * COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR 025 * CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. 026 * 027 * The name and trademarks of copyright holders may NOT be used in advertising or 028 * publicity pertaining to the software without specific, written prior permission. 029 * Title to copyright in this software and any associated documentation will at all 030 * times remain with copyright holders. 031 */ 032package org.opengis.test; 033 034import java.util.Collection; 035import java.awt.Shape; 036import java.awt.geom.Rectangle2D; 037import java.awt.geom.PathIterator; 038import java.awt.geom.AffineTransform; 039import java.awt.image.RenderedImage; 040 041import org.opengis.util.InternationalString; 042import org.opengis.metadata.Identifier; 043import org.opengis.metadata.citation.Citation; 044import org.opengis.referencing.operation.Matrix; 045import org.opengis.referencing.cs.AxisDirection; 046import org.opengis.referencing.cs.CoordinateSystem; 047import org.opengis.test.coverage.image.PixelIterator; 048 049 050/** 051 * Extension to JUnit assertion methods. 052 * This class inherits all assertion methods from the {@link org.junit.Assert org.junit.Assert} class. 053 * Consequently, developers can replace the following statement: 054 * 055 * <blockquote><pre>import static org.junit.Assert.*;</pre></blockquote> 056 * 057 * by 058 * 059 * <blockquote><pre>import static org.opengis.test.Assert.*;</pre></blockquote> 060 * 061 * if they wish to use the assertion methods defined here in addition of JUnit methods. 062 * 063 * @author Martin Desruisseaux (Geomatys) 064 * @version 3.1 065 * @since 2.2 066 */ 067public strictfp class Assert extends org.junit.Assert { 068 /** 069 * The keyword for unrestricted value in {@link String} arguments. 070 */ 071 private static final String UNRESTRICTED = "##unrestricted"; 072 073 /** 074 * For subclass constructors only. 075 */ 076 protected Assert() { 077 } 078 079 /** 080 * Returns the given message, or an empty string if the message is null. 081 */ 082 private static String nonNull(final String message) { 083 return (message != null) ? message.trim().concat(" ") : ""; 084 } 085 086 /** 087 * Returns the concatenation of the given message with the given extension. 088 * This method returns the given extension if the message is null or empty. 089 * 090 * <p>Invoking this method is equivalent to invoking {@code nonNull(message) + ext}, 091 * but avoid the creation of temporary objects in the common case where the message 092 * is null.</p> 093 * 094 * @param message the message, or {@code null}. 095 * @param ext the extension to append after the message. 096 * @return the concatenated string. 097 */ 098 private static String concat(String message, final String ext) { 099 if (message == null || (message = message.trim()).isEmpty()) { 100 return ext; 101 } 102 return message + ' ' + ext; 103 } 104 105 /** 106 * Verifies if we expected a null value, then returns {@code true} if the value is null as expected. 107 */ 108 private static boolean isNull(final String message, final Object expected, final Object actual) { 109 final boolean isNull = (actual == null); 110 if (isNull != (expected == null)) { 111 fail(concat(message, isNull ? "Value is null." : "Expected null.")); 112 } 113 return isNull; 114 } 115 116 /** 117 * Asserts that the given value is an instance of the given class. No tests are performed if 118 * the type is {@code null}. If the type is not-null but the value is null, this is considered 119 * as a failure. 120 * 121 * @param message header of the exception message in case of failure, or {@code null} if none. 122 * @param expectedType the expected parent class of the value, or {@code null} if unrestricted. 123 * @param value the value to test, or {@code null} (which is a failure). 124 */ 125 public static void assertInstanceOf(final String message, final Class<?> expectedType, final Object value) { 126 if (expectedType != null && !expectedType.isInstance(value)) { 127 if (value == null) { 128 fail(nonNull(message) + "Value is null."); 129 } else { 130 String expectedName = expectedType.getSimpleName(); 131 String actualName = value.getClass().getSimpleName(); 132 if (expectedName.equals(actualName)) { 133 expectedName = expectedType.getCanonicalName(); 134 actualName = value.getClass().getCanonicalName(); 135 } 136 fail(nonNull(message) + "Value \"" + value + "\" is of type " + actualName + 137 " while the expected type was " + expectedName + " or a subtype."); 138 } 139 } 140 } 141 142 /** 143 * Asserts that the given integer value is positive, including zero. 144 * 145 * @param message header of the exception message in case of failure, or {@code null} if none. 146 * @param value The value to test. 147 */ 148 public static void assertPositive(final String message, final int value) { 149 if (value < 0) { 150 fail(nonNull(message) + "Value is " + value + '.'); 151 } 152 } 153 154 /** 155 * Asserts that the given integer value is strictly positive, excluding zero. 156 * 157 * @param message header of the exception message in case of failure, or {@code null} if none. 158 * @param value the value to test. 159 */ 160 public static void assertStrictlyPositive(final String message, final int value) { 161 if (value <= 0) { 162 fail(nonNull(message) + "Value is " + value + '.'); 163 } 164 } 165 166 /** 167 * Asserts that the given minimum and maximum values make a valid range. More specifically 168 * asserts that if both values are non-null, then the minimum value is not greater than the 169 * maximum value. 170 * 171 * @param <T> the type of values being compared. 172 * @param message header of the exception message in case of failure, or {@code null} if none. 173 * @param minimum the lower bound of the range to test, or {@code null} if unbounded. 174 * @param maximum the upper bound of the range to test, or {@code null} if unbounded. 175 */ 176 @SuppressWarnings("unchecked") 177 public static <T> void assertValidRange(final String message, final Comparable<T> minimum, final Comparable<T> maximum) { 178 if (minimum != null && maximum != null) { 179 if (minimum.compareTo((T) maximum) > 0) { 180 fail(nonNull(message) + "Range found is [" + minimum + " ... " + maximum + "]."); 181 } 182 } 183 } 184 185 /** 186 * Asserts that the given minimum is smaller or equals to the given maximum. 187 * 188 * @param message header of the exception message in case of failure, or {@code null} if none. 189 * @param minimum the lower bound of the range to test. 190 * @param maximum the upper bound of the range to test. 191 */ 192 public static void assertValidRange(final String message, final int minimum, final int maximum) { 193 if (minimum > maximum) { 194 fail(nonNull(message) + "Range found is [" + minimum + " ... " + maximum + "]."); 195 } 196 } 197 198 /** 199 * Asserts that the given minimum is smaller or equals to the given maximum. 200 * If one bound is or both bounds are {@linkplain Double#NaN NaN}, then the test fails. 201 * 202 * @param message header of the exception message in case of failure, or {@code null} if none. 203 * @param minimum the lower bound of the range to test. 204 * @param maximum the upper bound of the range to test. 205 */ 206 public static void assertValidRange(final String message, final double minimum, final double maximum) { 207 if (!(minimum <= maximum)) { // Use '!' for catching NaN. 208 fail(nonNull(message) + "Range found is [" + minimum + " ... " + maximum + "]."); 209 } 210 } 211 212 /** 213 * Asserts that the given value is inside the given range. This method does <strong>not</strong> 214 * test the validity of the given [{@code minimum} … {@code maximum}] range. 215 * 216 * @param <T> the type of values being compared. 217 * @param message header of the exception message in case of failure, or {@code null} if none. 218 * @param minimum the lower bound of the range (inclusive), or {@code null} if unbounded. 219 * @param maximum the upper bound of the range (inclusive), or {@code null} if unbounded. 220 * @param value the value to test, or {@code null} (which is a failure). 221 */ 222 public static <T> void assertBetween(final String message, final Comparable<T> minimum, final Comparable<T> maximum, T value) { 223 if (minimum != null) { 224 if (minimum.compareTo(value) > 0) { 225 fail(nonNull(message) + "Value " + value + " is less than " + minimum + '.'); 226 } 227 } 228 if (maximum != null) { 229 if (maximum.compareTo(value) < 0) { 230 fail(nonNull(message) + "Value " + value + " is greater than " + maximum + '.'); 231 } 232 } 233 } 234 235 /** 236 * Asserts that the given value is inside the given range. This method does <strong>not</strong> 237 * test the validity of the given [{@code minimum} … {@code maximum}] range. 238 * 239 * @param message header of the exception message in case of failure, or {@code null} if none. 240 * @param minimum the lower bound of the range, inclusive. 241 * @param maximum the upper bound of the range, inclusive. 242 * @param value the value to test. 243 */ 244 public static void assertBetween(final String message, final int minimum, final int maximum, final int value) { 245 if (value < minimum) { 246 fail(nonNull(message) + "Value " + value + " is less than " + minimum + '.'); 247 } 248 if (value > maximum) { 249 fail(nonNull(message) + "Value " + value + " is greater than " + maximum + '.'); 250 } 251 } 252 253 /** 254 * Asserts that the given value is inside the given range. If the given {@code value} is 255 * {@linkplain Double#NaN NaN}, then this test passes silently. This method does <strong>not</strong> 256 * test the validity of the given [{@code minimum} … {@code maximum}] range. 257 * 258 * @param message header of the exception message in case of failure, or {@code null} if none. 259 * @param minimum the lower bound of the range, inclusive. 260 * @param maximum the upper bound of the range, inclusive. 261 * @param value the value to test. 262 */ 263 public static void assertBetween(final String message, final double minimum, final double maximum, final double value) { 264 if (value < minimum) { 265 fail(nonNull(message) + "Value " + value + " is less than " + minimum + '.'); 266 } 267 if (value > maximum) { 268 fail(nonNull(message) + "Value " + value + " is greater than " + maximum + '.'); 269 } 270 } 271 272 /** 273 * Asserts that the given value is contained in the given collection. If the given collection 274 * is null, then this test passes silently (a null collection is considered as "unknown", not 275 * empty). If the given value is null, then the test passes only if the given collection 276 * contains the null element. 277 * 278 * @param message header of the exception message in case of failure, or {@code null} if none. 279 * @param collection the collection where to look for inclusion, or {@code null} if unrestricted. 280 * @param value the value to test for inclusion. 281 */ 282 public static void assertContains(final String message, final Collection<?> collection, final Object value) { 283 if (collection != null) { 284 if (!collection.contains(value)) { 285 fail(nonNull(message) + "Looked for value \"" + value + "\" in a collection of " + 286 collection.size() + "elements."); 287 } 288 } 289 } 290 291 /** 292 * Asserts that the title or an alternate title of the given citation is equal to the given string. 293 * This method is typically used for testing if a citation stands for the OGC, OGP or EPSG authority 294 * for instance. Such abbreviations are often declared as {@linkplain Citation#getAlternateTitles() 295 * alternate titles} rather than the main {@linkplain Citation#getTitle() title}, but this method 296 * tests both for safety. 297 * 298 * @param message header of the exception message in case of failure, or {@code null} if none. 299 * @param expected the expected title or alternate title. 300 * @param actual the citation to test. 301 * 302 * @since 3.1 303 */ 304 public static void assertAnyTitleEquals(final String message, final String expected, final Citation actual) { 305 if (isNull(message, expected, actual)) { 306 return; 307 } 308 InternationalString title = actual.getTitle(); 309 if (title != null && expected.equals(title.toString())) { 310 return; 311 } 312 for (final InternationalString t : actual.getAlternateTitles()) { 313 if (expected.equals(t.toString())) { 314 return; 315 } 316 } 317 fail(concat(message, '"' + expected + "\" not found in title or alternate titles.")); 318 } 319 320 /** 321 * Asserts that the given identifier is equals to the given authority, code space, version and code. 322 * If any of the above-cited properties is {@code ""##unrestricted"}, then it will not be verified. 323 * This flexibility is useful in the common case where a test accepts any {@code version} value. 324 * 325 * @param message header of the exception message in case of failure, or {@code null} if none. 326 * @param authority the expected authority title or alternate title (may be {@code null}), or {@code "##unrestricted"}. 327 * @param codeSpace the expected code space (may be {@code null}), or {@code "##unrestricted"}. 328 * @param version the expected version (may be {@code null}), or {@code "##unrestricted"}. 329 * @param code the expected code value (may be {@code null}), or {@code "##unrestricted"}. 330 * @param actual the identifier to test. 331 * 332 * @since 3.1 333 */ 334 public static void assertIdentifierEquals(final String message, final String authority, final String codeSpace, 335 final String version, final String code, final Identifier actual) 336 { 337 if (actual == null) { 338 fail(concat(message, "Identifier is null")); 339 } else { 340 if (!UNRESTRICTED.equals(authority)) assertAnyTitleEquals(message, authority, actual.getAuthority()); 341 if (!UNRESTRICTED.equals(codeSpace)) assertEquals(concat(message, "Wrong code space"), codeSpace, actual.getCodeSpace()); 342 if (!UNRESTRICTED.equals(version)) assertEquals(concat(message, "Wrong version"), version, actual.getVersion()); 343 if (!UNRESTRICTED.equals(code)) assertEquals(concat(message, "Wrong code"), code, actual.getCode()); 344 } 345 } 346 347 /** 348 * @deprecated Renamed {@link #assertUnicodeIdentifierEquals(String, CharSequence, CharSequence, boolean)} 349 * for avoiding confusion with the {@code Identifier} interface. 350 * 351 * @param message header of the exception message in case of failure, or {@code null} if none. 352 * @param expected the expected character sequence. 353 * @param value the character sequence to compare. 354 */ 355 @Deprecated 356 public static void assertIdentifierEquals(final String message, final CharSequence expected, final CharSequence value) { 357 assertUnicodeIdentifierEquals(message, expected, value, true); 358 } 359 360 /** 361 * Asserts that the character sequences are equal, ignoring any characters that are not valid for Unicode identifiers. 362 * First, this method locates the {@linkplain Character#isUnicodeIdentifierStart(int) Unicode identifier start} 363 * in each sequences, ignoring any other characters before them. Then, starting from the identifier starts, this 364 * method compares only the {@linkplain Character#isUnicodeIdentifierPart(int) Unicode identifier parts} until 365 * the end of character sequences. 366 * 367 * <p><b>Examples:</b> {@code "WGS 84"} and {@code "WGS84"} as equal according this method.</p> 368 * 369 * @param message header of the exception message in case of failure, or {@code null} if none. 370 * @param expected the expected character sequence (may be {@code null}), or {@code "##unrestricted"}. 371 * @param actual the character sequence to compare, or {@code null}. 372 * @param ignoreCase {@code true} for ignoring case. 373 * 374 * @since 3.1 375 */ 376 public static void assertUnicodeIdentifierEquals(final String message, 377 final CharSequence expected, final CharSequence actual, final boolean ignoreCase) 378 { 379 if (UNRESTRICTED.equals(expected) || isNull(message, expected, actual)) { 380 return; 381 } 382 final int expLength = expected.length(); 383 final int valLength = actual.length(); 384 int expOffset = 0; 385 int valOffset = 0; 386 boolean expPart = false; 387 boolean valPart = false; 388 while (expOffset < expLength) { 389 int expCode = Character.codePointAt(expected, expOffset); 390 if (isUnicodeIdentifier(expCode, expPart)) { 391 expPart = true; 392 int valCode; 393 do { 394 if (valOffset >= valLength) { 395 fail(nonNull(message) + "Expected \"" + expected + "\" but got \"" + actual + "\". " 396 + "Missing part: \"" + expected.subSequence(expOffset, expLength) + "\"."); 397 return; 398 } 399 valCode = Character.codePointAt(actual, valOffset); 400 valOffset += Character.charCount(valCode); 401 } while (!isUnicodeIdentifier(valCode, valPart)); 402 valPart = true; 403 if (ignoreCase) { 404 expCode = Character.toLowerCase(expCode); 405 valCode = Character.toLowerCase(valCode); 406 } 407 if (valCode != expCode) { 408 fail(nonNull(message) + "Expected \"" + expected + "\" but got \"" + actual + "\"."); 409 return; 410 } 411 } 412 expOffset += Character.charCount(expCode); 413 } 414 while (valOffset < valLength) { 415 final int valCode = Character.codePointAt(actual, valOffset); 416 if (isUnicodeIdentifier(valCode, valPart)) { 417 fail(nonNull(message) + "Expected \"" + expected + "\", but found it with a unexpected " 418 + "trailing string: \"" + actual.subSequence(valOffset, valLength) + "\"."); 419 } 420 valOffset += Character.charCount(valCode); 421 } 422 } 423 424 /** 425 * Returns {@code true} if the given codepoint is an unicode identifier start or part. 426 */ 427 private static boolean isUnicodeIdentifier(final int codepoint, final boolean part) { 428 return part ? Character.isUnicodeIdentifierPart (codepoint) 429 : Character.isUnicodeIdentifierStart(codepoint); 430 } 431 432 /** 433 * Asserts that all axes in the given coordinate system are pointing toward the given 434 * directions, in the same order. 435 * 436 * @param message header of the exception message in case of failure, or {@code null} if none. 437 * @param cs the coordinate system to test. 438 * @param expected the expected axis directions. 439 * 440 * @since 3.1 441 */ 442 public static void assertAxisDirectionsEqual(String message, 443 final CoordinateSystem cs, final AxisDirection... expected) 444 { 445 assertEquals(concat(message, "Wrong coordinate system dimension."), expected.length, cs.getDimension()); 446 message = concat(message, "Wrong axis direction."); 447 for (int i=0; i<expected.length; i++) { 448 assertEquals(message, expected[i], cs.getAxis(i).getDirection()); 449 } 450 } 451 452 /** 453 * Asserts that the given matrix is equals to the expected one, up to the given tolerance value. 454 * 455 * @param message header of the exception message in case of failure, or {@code null} if none. 456 * @param expected the expected matrix, which may be {@code null}. 457 * @param actual the matrix to compare, or {@code null}. 458 * @param tolerance the tolerance threshold. 459 * 460 * @since 3.1 461 * 462 * @see org.opengis.test.referencing.TransformTestCase#assertMatrixEquals(String, Matrix, Matrix, Matrix) 463 */ 464 public static void assertMatrixEquals(final String message, final Matrix expected, final Matrix actual, final double tolerance) { 465 if (isNull(message, expected, actual)) { 466 return; 467 } 468 final int numRow = actual.getNumRow(); 469 final int numCol = actual.getNumCol(); 470 assertEquals("numRow", expected.getNumRow(), numRow); 471 assertEquals("numCol", expected.getNumCol(), numCol); 472 for (int j=0; j<numRow; j++) { 473 for (int i=0; i<numCol; i++) { 474 final double e = expected.getElement(j,i); 475 final double a = actual.getElement(j,i); 476 if (!(StrictMath.abs(e - a) <= tolerance) && Double.doubleToLongBits(a) != Double.doubleToLongBits(e)) { 477 fail(nonNull(message) + "Matrix.getElement(" + j + ", " + i + "): expected " + e + " but got " + a); 478 } 479 } 480 } 481 } 482 483 /** 484 * Asserts that all control points of two shapes are equal. 485 * This method performs the following checks: 486 * 487 * <ol> 488 * <li>Ensures that the {@linkplain Shape#getBounds2D() shape bounds} are equal, 489 * up to the given tolerance thresholds.</li> 490 * <li>{@linkplain Shape#getPathIterator(AffineTransform) Gets the path iterator} of each shape.</li> 491 * <li>Ensures that the {@linkplain PathIterator#getWindingRule() winding rules} are equal.</li> 492 * <li>Iterates over all path segments until the iteration {@linkplain PathIterator#isDone() is done}. 493 * For each iteration step:<ol> 494 * <li>Invokes {@link PathIterator#currentSegment(double[])}.</li> 495 * <li>Ensures that the segment type (one of the {@code SEG_*} constants) is the same.</li> 496 * <li>Ensures that the ordinate values are equal, up to the given tolerance thresholds.</li> 497 * </ol></li> 498 * </ol> 499 * 500 * @param message header of the exception message in case of failure, or {@code null} if none. 501 * @param expected the expected shape, which may be {@code null}. 502 * @param actual the actual shape, or {@code null}. 503 * @param toleranceX the tolerance threshold for <var>x</var> ordinate values. 504 * @param toleranceY the tolerance threshold for <var>y</var> ordinate values. 505 * 506 * @since 3.1 507 */ 508 public static void assertShapeEquals(String message, final Shape expected, 509 final Shape actual, final double toleranceX, final double toleranceY) 510 { 511 if (isNull(message, expected, actual)) { 512 return; 513 } 514 final Rectangle2D b0 = expected.getBounds2D(); 515 final Rectangle2D b1 = actual .getBounds2D(); 516 final String mismatch = concat(message, "Mismatched bounds."); 517 assertEquals(mismatch, b0.getMinX(), b1.getMinX(), toleranceX); 518 assertEquals(mismatch, b0.getMaxX(), b1.getMaxX(), toleranceX); 519 assertEquals(mismatch, b0.getMinY(), b1.getMinY(), toleranceY); 520 assertEquals(mismatch, b0.getMaxY(), b1.getMaxY(), toleranceY); 521 assertPathEquals(message, expected.getPathIterator(null), actual.getPathIterator(null), toleranceX, toleranceY); 522 } 523 524 /** 525 * Asserts that all control points in two geometric paths are equal. 526 * This method performs the following checks: 527 * 528 * <ol> 529 * <li>Ensures that the {@linkplain PathIterator#getWindingRule() winding rules} are equal.</li> 530 * <li>Iterates over all path segments until the iteration {@linkplain PathIterator#isDone() is done}. 531 * For each iteration step:<ol> 532 * <li>Invokes {@link PathIterator#currentSegment(double[])}.</li> 533 * <li>Ensures that the segment type (one of the {@code SEG_*} constants) is the same.</li> 534 * <li>Ensures that the ordinate values are equal, up to the given tolerance thresholds.</li> 535 * </ol></li> 536 * </ol> 537 * 538 * This method can be used instead of {@link #assertShapeEquals(String, Shape, Shape, double, double)} 539 * when the tester needs to compare the shapes with a non-null affine transform or a flatness factor. 540 * in such case, the tester needs to invoke the {@link Shape#getPathIterator(AffineTransform, double)} 541 * method himself. 542 * 543 * @param message header of the exception message in case of failure, or {@code null} if none. 544 * @param expected the expected path, which may be {@code null}. 545 * @param actual the actual path, or {@code null}. 546 * @param toleranceX the tolerance threshold for <var>x</var> ordinate values. 547 * @param toleranceY the tolerance threshold for <var>y</var> ordinate values. 548 * 549 * @since 3.1 550 */ 551 public static void assertPathEquals(final String message, final PathIterator expected, 552 final PathIterator actual, final double toleranceX, final double toleranceY) 553 { 554 if (isNull(message, expected, actual)) { 555 return; 556 } 557 assertEquals(concat(message, "Mismatched winding rule."), expected.getWindingRule(), actual.getWindingRule()); 558 final String mismatchedType = concat(message, "Mismatched path segment type."); 559 final String mismatchedX = concat(message, "Mismatched X ordinate value."); 560 final String mismatchedY = concat(message, "Mismatched Y ordinate value."); 561 final String endOfPath = concat(message, "Premature end of path."); 562 final double[] expectedCoords = new double[6]; 563 final double[] actualCoords = new double[6]; 564 while (!expected.isDone()) { 565 assertFalse(endOfPath, actual.isDone()); 566 final int type = expected.currentSegment(expectedCoords); 567 assertEquals(mismatchedType, type, actual.currentSegment(actualCoords)); 568 final int length; 569 switch (type) { 570 case PathIterator.SEG_CLOSE: length = 0; break; 571 case PathIterator.SEG_MOVETO: // Fallthrough 572 case PathIterator.SEG_LINETO: length = 2; break; 573 case PathIterator.SEG_QUADTO: length = 4; break; 574 case PathIterator.SEG_CUBICTO: length = 6; break; 575 default: throw new AssertionError(nonNull(message) + "Unknown segment type: " + type); 576 } 577 for (int i=0; i<length;) { 578 assertEquals(mismatchedX, expectedCoords[i], actualCoords[i++], toleranceX); 579 assertEquals(mismatchedY, expectedCoords[i], actualCoords[i++], toleranceY); 580 } 581 actual.next(); 582 expected.next(); 583 } 584 assertTrue(concat(message, "Expected end of path."), actual.isDone()); 585 } 586 587 /** 588 * Asserts that all sample values in the given images are equal. This method requires the images 589 * {@linkplain RenderedImage#getWidth() width}, {@linkplain RenderedImage#getHeight() height} 590 * and the {@linkplain java.awt.image.SampleModel#getNumBands() number of bands} to be equal, 591 * but does <em>not</em> require the {@linkplain RenderedImage#getTile(int, int) tiling}, 592 * {@linkplain java.awt.image.ColorModel color model} or 593 * {@linkplain java.awt.image.SampleModel#getDataType() datatype} to be equal. 594 * 595 * @param message header of the exception message in case of failure, or {@code null} if none. 596 * @param expected an image containing the expected values, which may be {@code null}. 597 * @param actual the actual image containing the sample values to compare, or {@code null}. 598 * @param tolerance tolerance threshold for floating point comparisons. 599 * This threshold is ignored if both images use integer datatype. 600 * 601 * @see PixelIterator#assertSampleValuesEqual(PixelIterator, double) 602 * 603 * @since 3.1 604 */ 605 public static void assertSampleValuesEqual(final String message, final RenderedImage expected, 606 final RenderedImage actual, final double tolerance) 607 { 608 if (isNull(message, expected, actual)) { 609 return; 610 } 611 assertEquals(concat(message, "Mismatched image width."), expected.getWidth(), actual.getWidth()); 612 assertEquals(concat(message, "Mismatched image height."), expected.getHeight(), actual.getHeight()); 613 assertEquals(concat(message, "Mismatched number of bands."), 614 expected.getSampleModel().getNumBands(), actual.getSampleModel().getNumBands()); 615 final PixelIterator iterator = new PixelIterator(expected); 616 iterator.assertSampleValuesEqual(new PixelIterator(actual), tolerance); 617 } 618}