1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * This test is created from Android CTS tests: 17 * 18 * https://cs.android.com/android/platform/superproject/main/+/main:cts/tests/tests/util/src/android/util/cts/RationalTest.java 19 */ 20 21 package android.util; 22 23 import static android.util.Rational.NEGATIVE_INFINITY; 24 import static android.util.Rational.NaN; 25 import static android.util.Rational.POSITIVE_INFINITY; 26 import static android.util.Rational.ZERO; 27 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertFalse; 29 import static org.junit.Assert.assertTrue; 30 import static org.junit.Assert.fail; 31 32 import androidx.test.ext.junit.runners.AndroidJUnit4; 33 import java.io.ByteArrayInputStream; 34 import java.io.ByteArrayOutputStream; 35 import java.io.IOException; 36 import java.io.InvalidObjectException; 37 import java.io.ObjectInputStream; 38 import java.io.ObjectOutputStream; 39 import java.io.Serializable; 40 import java.lang.reflect.Field; 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 import org.robolectric.annotation.internal.DoNotInstrument; 44 45 @DoNotInstrument 46 @RunWith(AndroidJUnit4.class) 47 public class RationalTest { 48 49 /** (1,1) */ 50 private static final Rational UNIT = new Rational(1, 1); 51 52 @Test testConstructor()53 public void testConstructor() { 54 55 // Simple case 56 Rational r = new Rational(1, 2); 57 assertEquals(1, r.getNumerator()); 58 assertEquals(2, r.getDenominator()); 59 60 // Denominator negative 61 r = new Rational(-1, 2); 62 assertEquals(-1, r.getNumerator()); 63 assertEquals(2, r.getDenominator()); 64 65 // Numerator negative 66 r = new Rational(1, -2); 67 assertEquals(-1, r.getNumerator()); 68 assertEquals(2, r.getDenominator()); 69 70 // Both negative 71 r = new Rational(-1, -2); 72 assertEquals(1, r.getNumerator()); 73 assertEquals(2, r.getDenominator()); 74 75 // Infinity. 76 r = new Rational(1, 0); 77 assertEquals(1, r.getNumerator()); 78 assertEquals(0, r.getDenominator()); 79 80 // Negative infinity. 81 r = new Rational(-1, 0); 82 assertEquals(-1, r.getNumerator()); 83 assertEquals(0, r.getDenominator()); 84 85 // NaN. 86 r = new Rational(0, 0); 87 assertEquals(0, r.getNumerator()); 88 assertEquals(0, r.getDenominator()); 89 } 90 91 @Test testEquals()92 public void testEquals() { 93 Rational r = new Rational(1, 2); 94 assertEquals(1, r.getNumerator()); 95 assertEquals(2, r.getDenominator()); 96 97 assertEquals(r, r); 98 assertFalse(r.equals(null)); 99 assertFalse(r.equals(new Object())); 100 101 Rational twoThirds = new Rational(2, 3); 102 assertFalse(r.equals(twoThirds)); 103 assertFalse(twoThirds.equals(r)); 104 105 Rational fourSixths = new Rational(4, 6); 106 assertEquals(twoThirds, fourSixths); 107 assertEquals(fourSixths, twoThirds); 108 109 Rational moreComplicated = new Rational(5 * 6 * 7 * 8 * 9, 1 * 2 * 3 * 4 * 5); 110 Rational moreComplicated2 = new Rational(5 * 6 * 7 * 8 * 9 * 78, 1 * 2 * 3 * 4 * 5 * 78); 111 assertEquals(moreComplicated, moreComplicated2); 112 assertEquals(moreComplicated2, moreComplicated); 113 114 // Ensure negatives are fine 115 twoThirds = new Rational(-2, 3); 116 fourSixths = new Rational(-4, 6); 117 assertEquals(twoThirds, fourSixths); 118 assertEquals(fourSixths, twoThirds); 119 120 moreComplicated = new Rational(-5 * 6 * 7 * 8 * 9, 1 * 2 * 3 * 4 * 5); 121 moreComplicated2 = new Rational(-5 * 6 * 7 * 8 * 9 * 78, 1 * 2 * 3 * 4 * 5 * 78); 122 assertEquals(moreComplicated, moreComplicated2); 123 assertEquals(moreComplicated2, moreComplicated); 124 125 // Zero is always equal to itself 126 Rational zero2 = new Rational(0, 100); 127 assertEquals(ZERO, zero2); 128 assertEquals(zero2, ZERO); 129 130 // NaN is always equal to itself 131 Rational nan = NaN; 132 Rational nan2 = new Rational(0, 0); 133 assertTrue(nan.equals(nan)); 134 assertTrue(nan.equals(nan2)); 135 assertTrue(nan2.equals(nan)); 136 assertFalse(nan.equals(r)); 137 assertFalse(r.equals(nan)); 138 139 // Infinities of the same sign are equal. 140 Rational posInf = POSITIVE_INFINITY; 141 Rational posInf2 = new Rational(2, 0); 142 Rational negInf = NEGATIVE_INFINITY; 143 Rational negInf2 = new Rational(-2, 0); 144 assertEquals(posInf, posInf); 145 assertEquals(negInf, negInf); 146 assertEquals(posInf, posInf2); 147 assertEquals(negInf, negInf2); 148 149 // Infinities aren't equal to anything else. 150 assertFalse(posInf.equals(negInf)); 151 assertFalse(negInf.equals(posInf)); 152 assertFalse(negInf.equals(r)); 153 assertFalse(posInf.equals(r)); 154 assertFalse(r.equals(negInf)); 155 assertFalse(r.equals(posInf)); 156 assertFalse(posInf.equals(nan)); 157 assertFalse(negInf.equals(nan)); 158 assertFalse(nan.equals(posInf)); 159 assertFalse(nan.equals(negInf)); 160 } 161 162 @Test testReduction()163 public void testReduction() { 164 Rational moreComplicated = new Rational(5 * 78, 7 * 78); 165 assertEquals(new Rational(5, 7), moreComplicated); 166 assertEquals(5, moreComplicated.getNumerator()); 167 assertEquals(7, moreComplicated.getDenominator()); 168 169 Rational posInf = new Rational(5, 0); 170 assertEquals(1, posInf.getNumerator()); 171 assertEquals(0, posInf.getDenominator()); 172 assertEquals(POSITIVE_INFINITY, posInf); 173 174 Rational negInf = new Rational(-100, 0); 175 assertEquals(-1, negInf.getNumerator()); 176 assertEquals(0, negInf.getDenominator()); 177 assertEquals(NEGATIVE_INFINITY, negInf); 178 179 Rational zero = new Rational(0, -100); 180 assertEquals(0, zero.getNumerator()); 181 assertEquals(1, zero.getDenominator()); 182 assertEquals(ZERO, zero); 183 184 Rational flipSigns = new Rational(1, -1); 185 assertEquals(-1, flipSigns.getNumerator()); 186 assertEquals(1, flipSigns.getDenominator()); 187 188 Rational flipAndReduce = new Rational(100, -200); 189 assertEquals(-1, flipAndReduce.getNumerator()); 190 assertEquals(2, flipAndReduce.getDenominator()); 191 } 192 193 @Test testCompareTo()194 public void testCompareTo() { 195 // unit is equal to itself 196 verifyCompareEquals(UNIT, new Rational(1, 1)); 197 198 // NaN is greater than anything but NaN 199 verifyCompareEquals(NaN, new Rational(0, 0)); 200 verifyGreaterThan(NaN, UNIT); 201 verifyGreaterThan(NaN, POSITIVE_INFINITY); 202 verifyGreaterThan(NaN, NEGATIVE_INFINITY); 203 verifyGreaterThan(NaN, ZERO); 204 205 // Positive infinity is greater than any other non-NaN 206 verifyCompareEquals(POSITIVE_INFINITY, new Rational(1, 0)); 207 verifyGreaterThan(POSITIVE_INFINITY, UNIT); 208 verifyGreaterThan(POSITIVE_INFINITY, NEGATIVE_INFINITY); 209 verifyGreaterThan(POSITIVE_INFINITY, ZERO); 210 211 // Negative infinity is smaller than any other non-NaN 212 verifyCompareEquals(NEGATIVE_INFINITY, new Rational(-1, 0)); 213 verifyLessThan(NEGATIVE_INFINITY, UNIT); 214 verifyLessThan(NEGATIVE_INFINITY, POSITIVE_INFINITY); 215 verifyLessThan(NEGATIVE_INFINITY, ZERO); 216 217 // A finite number with the same denominator is trivially comparable 218 verifyGreaterThan(new Rational(3, 100), new Rational(1, 100)); 219 verifyGreaterThan(new Rational(3, 100), ZERO); 220 221 // Compare finite numbers with different divisors 222 verifyGreaterThan(new Rational(5, 25), new Rational(1, 10)); 223 verifyGreaterThan(new Rational(5, 25), ZERO); 224 225 // Compare finite numbers with different signs 226 verifyGreaterThan(new Rational(5, 25), new Rational(-1, 10)); 227 verifyLessThan(new Rational(-5, 25), ZERO); 228 } 229 230 @Test testConvenienceMethods()231 public void testConvenienceMethods() { 232 // isFinite 233 verifyFinite(ZERO, true); 234 verifyFinite(NaN, false); 235 verifyFinite(NEGATIVE_INFINITY, false); 236 verifyFinite(POSITIVE_INFINITY, false); 237 verifyFinite(UNIT, true); 238 239 // isInfinite 240 verifyInfinite(ZERO, false); 241 verifyInfinite(NaN, false); 242 verifyInfinite(NEGATIVE_INFINITY, true); 243 verifyInfinite(POSITIVE_INFINITY, true); 244 verifyInfinite(UNIT, false); 245 246 // isNaN 247 verifyNaN(ZERO, false); 248 verifyNaN(NaN, true); 249 verifyNaN(NEGATIVE_INFINITY, false); 250 verifyNaN(POSITIVE_INFINITY, false); 251 verifyNaN(UNIT, false); 252 253 // isZero 254 verifyZero(ZERO, true); 255 verifyZero(NaN, false); 256 verifyZero(NEGATIVE_INFINITY, false); 257 verifyZero(POSITIVE_INFINITY, false); 258 verifyZero(UNIT, false); 259 } 260 261 @Test testValueConversions()262 public void testValueConversions() { 263 // Unit, simple case 264 verifyValueEquals(UNIT, 1.0f); 265 verifyValueEquals(UNIT, 1.0); 266 verifyValueEquals(UNIT, 1L); 267 verifyValueEquals(UNIT, 1); 268 verifyValueEquals(UNIT, (short) 1); 269 270 // Zero, simple case 271 verifyValueEquals(ZERO, 0.0f); 272 verifyValueEquals(ZERO, 0.0); 273 verifyValueEquals(ZERO, 0L); 274 verifyValueEquals(ZERO, 0); 275 verifyValueEquals(ZERO, (short) 0); 276 277 // NaN is 0 for integers, not-a-number for floating point 278 verifyValueEquals(NaN, Float.NaN); 279 verifyValueEquals(NaN, Double.NaN); 280 verifyValueEquals(NaN, 0L); 281 verifyValueEquals(NaN, 0); 282 verifyValueEquals(NaN, (short) 0); 283 284 // Positive infinity, saturates upwards for integers 285 verifyValueEquals(POSITIVE_INFINITY, Float.POSITIVE_INFINITY); 286 verifyValueEquals(POSITIVE_INFINITY, Double.POSITIVE_INFINITY); 287 verifyValueEquals(POSITIVE_INFINITY, Long.MAX_VALUE); 288 verifyValueEquals(POSITIVE_INFINITY, Integer.MAX_VALUE); 289 verifyValueEquals(POSITIVE_INFINITY, (short) -1); 290 291 // Negative infinity, saturates downwards for integers 292 verifyValueEquals(NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); 293 verifyValueEquals(NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); 294 verifyValueEquals(NEGATIVE_INFINITY, Long.MIN_VALUE); 295 verifyValueEquals(NEGATIVE_INFINITY, Integer.MIN_VALUE); 296 verifyValueEquals(NEGATIVE_INFINITY, (short) 0); 297 298 // Normal finite values, round down for integers 299 final Rational oneQuarter = new Rational(1, 4); 300 verifyValueEquals(oneQuarter, 1.0f / 4.0f); 301 verifyValueEquals(oneQuarter, 1.0 / 4.0); 302 verifyValueEquals(oneQuarter, 0L); 303 verifyValueEquals(oneQuarter, 0); 304 verifyValueEquals(oneQuarter, (short) 0); 305 306 final Rational nineFifths = new Rational(9, 5); 307 verifyValueEquals(nineFifths, 9.0f / 5.0f); 308 verifyValueEquals(nineFifths, 9.0 / 5.0); 309 verifyValueEquals(nineFifths, 1L); 310 verifyValueEquals(nineFifths, 1); 311 verifyValueEquals(nineFifths, (short) 1); 312 313 final Rational negativeHundred = new Rational(-1000, 10); 314 verifyValueEquals(negativeHundred, -100.f / 1.f); 315 verifyValueEquals(negativeHundred, -100.0 / 1.0); 316 verifyValueEquals(negativeHundred, -100L); 317 verifyValueEquals(negativeHundred, -100); 318 verifyValueEquals(negativeHundred, (short) -100); 319 320 // Short truncates if the result is too large 321 verifyValueEquals(new Rational(Integer.MAX_VALUE, 1), (short) Integer.MAX_VALUE); 322 verifyValueEquals(new Rational(0x00FFFFFF, 1), (short) 0x00FFFFFF); 323 verifyValueEquals(new Rational(0x00FF00FF, 1), (short) 0x00FF00FF); 324 } 325 326 @Test testSerialize()327 public void testSerialize() throws ClassNotFoundException, IOException, NoSuchFieldException { 328 /* 329 * Check correct [de]serialization 330 */ 331 verifyEqualsAfterSerializing(ZERO); 332 verifyEqualsAfterSerializing(NaN); 333 verifyEqualsAfterSerializing(NEGATIVE_INFINITY); 334 verifyEqualsAfterSerializing(POSITIVE_INFINITY); 335 verifyEqualsAfterSerializing(UNIT); 336 verifyEqualsAfterSerializing(new Rational(100, 200)); 337 verifyEqualsAfterSerializing(new Rational(-100, 200)); 338 verifyEqualsAfterSerializing(new Rational(5, 1)); 339 verifyEqualsAfterSerializing(new Rational(Integer.MAX_VALUE, Integer.MIN_VALUE)); 340 341 /* 342 * Check bad deserialization fails 343 */ 344 try { 345 Rational badZero = createIllegalRational(0, 100); // [0, 100] , should be [0, 1] 346 Rational results = serializeRoundTrip(badZero); 347 fail("Deserializing " + results + " should not have succeeded"); 348 } catch (InvalidObjectException e) { 349 // OK 350 } 351 352 try { 353 Rational badPosInfinity = createIllegalRational(100, 0); // [100, 0] , should be [1, 0] 354 Rational results = serializeRoundTrip(badPosInfinity); 355 fail("Deserializing " + results + " should not have succeeded"); 356 } catch (InvalidObjectException e) { 357 // OK 358 } 359 360 try { 361 Rational badNegInfinity = createIllegalRational(-100, 0); // [-100, 0] , should be [-1, 0] 362 Rational results = serializeRoundTrip(badNegInfinity); 363 fail("Deserializing " + results + " should not have succeeded"); 364 } catch (InvalidObjectException e) { 365 // OK 366 } 367 368 try { 369 Rational badReduced = createIllegalRational(2, 4); // [2,4] , should be [1, 2] 370 Rational results = serializeRoundTrip(badReduced); 371 fail("Deserializing " + results + " should not have succeeded"); 372 } catch (InvalidObjectException e) { 373 // OK 374 } 375 376 try { 377 Rational badReducedNeg = createIllegalRational(-2, 4); // [-2, 4] should be [-1, 2] 378 Rational results = serializeRoundTrip(badReducedNeg); 379 fail("Deserializing " + results + " should not have succeeded"); 380 } catch (InvalidObjectException e) { 381 // OK 382 } 383 } 384 385 @Test testParseRational()386 public void testParseRational() { 387 assertEquals(new Rational(1, 2), Rational.parseRational("3:+6")); 388 assertEquals(new Rational(1, 2), Rational.parseRational("-3:-6")); 389 assertEquals(Rational.NaN, Rational.parseRational("NaN")); 390 assertEquals(Rational.POSITIVE_INFINITY, Rational.parseRational("Infinity")); 391 assertEquals(Rational.NEGATIVE_INFINITY, Rational.parseRational("-Infinity")); 392 assertEquals(Rational.ZERO, Rational.parseRational("0/261")); 393 assertEquals(Rational.NaN, Rational.parseRational("0/-0")); 394 assertEquals(Rational.POSITIVE_INFINITY, Rational.parseRational("1000/+0")); 395 assertEquals(Rational.NEGATIVE_INFINITY, Rational.parseRational("-1000/-0")); 396 397 Rational r = new Rational(10, 15); 398 assertEquals(r, Rational.parseRational(r.toString())); 399 } 400 401 @Test(expected = NumberFormatException.class) testParseRationalInvalid1()402 public void testParseRationalInvalid1() { 403 Rational.parseRational("1.5"); 404 } 405 406 @Test(expected = NumberFormatException.class) testParseRationalInvalid2()407 public void testParseRationalInvalid2() { 408 Rational.parseRational("239"); 409 } 410 verifyValueEquals(Rational object, float expected)411 private static void verifyValueEquals(Rational object, float expected) { 412 assertEquals("Checking floatValue() for " + object + ";", expected, object.floatValue(), 0.0f); 413 } 414 verifyValueEquals(Rational object, double expected)415 private static void verifyValueEquals(Rational object, double expected) { 416 assertEquals( 417 "Checking doubleValue() for " + object + ";", expected, object.doubleValue(), 0.0f); 418 } 419 verifyValueEquals(Rational object, long expected)420 private static void verifyValueEquals(Rational object, long expected) { 421 assertEquals("Checking longValue() for " + object + ";", expected, object.longValue()); 422 } 423 verifyValueEquals(Rational object, int expected)424 private static void verifyValueEquals(Rational object, int expected) { 425 assertEquals("Checking intValue() for " + object + ";", expected, object.intValue()); 426 } 427 verifyValueEquals(Rational object, short expected)428 private static void verifyValueEquals(Rational object, short expected) { 429 assertEquals("Checking shortValue() for " + object + ";", expected, object.shortValue()); 430 } 431 verifyFinite(Rational object, boolean expected)432 private static void verifyFinite(Rational object, boolean expected) { 433 verifyAction("finite", object, expected, object.isFinite()); 434 } 435 verifyInfinite(Rational object, boolean expected)436 private static void verifyInfinite(Rational object, boolean expected) { 437 verifyAction("infinite", object, expected, object.isInfinite()); 438 } 439 verifyNaN(Rational object, boolean expected)440 private static void verifyNaN(Rational object, boolean expected) { 441 verifyAction("NaN", object, expected, object.isNaN()); 442 } 443 verifyZero(Rational object, boolean expected)444 private static void verifyZero(Rational object, boolean expected) { 445 verifyAction("zero", object, expected, object.isZero()); 446 } 447 verifyAction(String action, T object, boolean expected, boolean actual)448 private static <T> void verifyAction(String action, T object, boolean expected, boolean actual) { 449 String expectedMessage = expected ? action : ("not " + action); 450 assertEquals("Expected " + object + " to be " + expectedMessage, expected, actual); 451 } 452 verifyLessThan(T left, T right)453 private static <T extends Comparable<? super T>> void verifyLessThan(T left, T right) { 454 assertTrue( 455 "Expected (LR) left " + left + " to be less than right " + right, 456 left.compareTo(right) < 0); 457 assertTrue( 458 "Expected (RL) left " + left + " to be less than right " + right, 459 right.compareTo(left) > 0); 460 } 461 verifyGreaterThan(T left, T right)462 private static <T extends Comparable<? super T>> void verifyGreaterThan(T left, T right) { 463 assertTrue( 464 "Expected (LR) left " + left + " to be greater than right " + right, 465 left.compareTo(right) > 0); 466 assertTrue( 467 "Expected (RL) left " + left + " to be greater than right " + right, 468 right.compareTo(left) < 0); 469 } 470 verifyCompareEquals(T left, T right)471 private static <T extends Comparable<? super T>> void verifyCompareEquals(T left, T right) { 472 assertTrue( 473 "Expected (LR) left " + left + " to be compareEquals to right " + right, 474 left.compareTo(right) == 0); 475 assertTrue( 476 "Expected (RL) left " + left + " to be compareEquals to right " + right, 477 right.compareTo(left) == 0); 478 } 479 serialize(T obj)480 private static <T extends Serializable> byte[] serialize(T obj) throws IOException { 481 ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); 482 try (ObjectOutputStream objectStream = new ObjectOutputStream(byteStream)) { 483 objectStream.writeObject(obj); 484 } 485 return byteStream.toByteArray(); 486 } 487 deserialize(byte[] array, Class<T> klass)488 private static <T extends Serializable> T deserialize(byte[] array, Class<T> klass) 489 throws IOException, ClassNotFoundException { 490 ByteArrayInputStream bais = new ByteArrayInputStream(array); 491 ObjectInputStream ois = new ObjectInputStream(bais); 492 Object obj = ois.readObject(); 493 return klass.cast(obj); 494 } 495 496 @SuppressWarnings("unchecked") serializeRoundTrip(T obj)497 private static <T extends Serializable> T serializeRoundTrip(T obj) 498 throws IOException, ClassNotFoundException { 499 Class<T> klass = (Class<T>) obj.getClass(); 500 byte[] arr = serialize(obj); 501 T serialized = deserialize(arr, klass); 502 return serialized; 503 } 504 verifyEqualsAfterSerializing(T obj)505 private static <T extends Serializable> void verifyEqualsAfterSerializing(T obj) 506 throws ClassNotFoundException, IOException { 507 T serialized = serializeRoundTrip(obj); 508 assertEquals("Expected values to be equal after serialization round-trip", obj, serialized); 509 } 510 createIllegalRational(int numerator, int denominator)511 private static Rational createIllegalRational(int numerator, int denominator) { 512 Rational r = new Rational(numerator, denominator); 513 mutateField(r, "mNumerator", numerator); 514 mutateField(r, "mDenominator", denominator); 515 return r; 516 } 517 mutateField(T object, String name, int value)518 private static <T> void mutateField(T object, String name, int value) { 519 try { 520 Field f = object.getClass().getDeclaredField(name); 521 f.setAccessible(true); 522 f.set(object, value); 523 } catch (NoSuchFieldException e) { 524 throw new AssertionError(e); 525 } catch (IllegalAccessException e) { 526 throw new AssertionError(e); 527 } catch (IllegalArgumentException e) { 528 throw new AssertionError(e); 529 } 530 } 531 } 532