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