/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.mediaframeworktest.unit; import android.test.suitebuilder.annotation.SmallTest; import android.util.Rational; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Field; import static android.util.Rational.*; /** * <pre> * adb shell am instrument \ * -e class 'com.android.mediaframeworktest.unit.RationalTest' \ * -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner * </pre> */ public class RationalTest extends junit.framework.TestCase { /** (1,1) */ private static final Rational UNIT = new Rational(1, 1); /** * Test @hide greatest common divisior functionality that cannot be tested in CTS. */ @SmallTest public void testGcd() { assertEquals(1, Rational.gcd(1, 2)); assertEquals(1, Rational.gcd(2, 3)); assertEquals(78, Rational.gcd(5*78, 7*78)); assertEquals(1, Rational.gcd(-1, 2)); assertEquals(1, Rational.gcd(-2, 3)); } @SmallTest public void testConstructor() { // Simple case Rational r = new Rational(1, 2); assertEquals(1, r.getNumerator()); assertEquals(2, r.getDenominator()); // Denominator negative r = new Rational(-1, 2); assertEquals(-1, r.getNumerator()); assertEquals(2, r.getDenominator()); // Numerator negative r = new Rational(1, -2); assertEquals(-1, r.getNumerator()); assertEquals(2, r.getDenominator()); // Both negative r = new Rational(-1, -2); assertEquals(1, r.getNumerator()); assertEquals(2, r.getDenominator()); // Infinity. r = new Rational(1, 0); assertEquals(1, r.getNumerator()); assertEquals(0, r.getDenominator()); // Negative infinity. r = new Rational(-1, 0); assertEquals(-1, r.getNumerator()); assertEquals(0, r.getDenominator()); // NaN. r = new Rational(0, 0); assertEquals(0, r.getNumerator()); assertEquals(0, r.getDenominator()); } @SmallTest public void testEquals() { Rational r = new Rational(1, 2); assertEquals(1, r.getNumerator()); assertEquals(2, r.getDenominator()); assertEquals(r, r); assertFalse(r.equals(null)); assertFalse(r.equals(new Object())); Rational twoThirds = new Rational(2, 3); assertFalse(r.equals(twoThirds)); assertFalse(twoThirds.equals(r)); Rational fourSixths = new Rational(4, 6); assertEquals(twoThirds, fourSixths); assertEquals(fourSixths, twoThirds); Rational moreComplicated = new Rational(5*6*7*8*9, 1*2*3*4*5); Rational moreComplicated2 = new Rational(5*6*7*8*9*78, 1*2*3*4*5*78); assertEquals(moreComplicated, moreComplicated2); assertEquals(moreComplicated2, moreComplicated); // Ensure negatives are fine twoThirds = new Rational(-2, 3); fourSixths = new Rational(-4, 6); assertEquals(twoThirds, fourSixths); assertEquals(fourSixths, twoThirds); moreComplicated = new Rational(-5*6*7*8*9, 1*2*3*4*5); moreComplicated2 = new Rational(-5*6*7*8*9*78, 1*2*3*4*5*78); assertEquals(moreComplicated, moreComplicated2); assertEquals(moreComplicated2, moreComplicated); // Zero is always equal to itself Rational zero2 = new Rational(0, 100); assertEquals(ZERO, zero2); assertEquals(zero2, ZERO); // NaN is always equal to itself Rational nan = NaN; Rational nan2 = new Rational(0, 0); assertTrue(nan.equals(nan)); assertTrue(nan.equals(nan2)); assertTrue(nan2.equals(nan)); assertFalse(nan.equals(r)); assertFalse(r.equals(nan)); // Infinities of the same sign are equal. Rational posInf = POSITIVE_INFINITY; Rational posInf2 = new Rational(2, 0); Rational negInf = NEGATIVE_INFINITY; Rational negInf2 = new Rational(-2, 0); assertEquals(posInf, posInf); assertEquals(negInf, negInf); assertEquals(posInf, posInf2); assertEquals(negInf, negInf2); // Infinities aren't equal to anything else. assertFalse(posInf.equals(negInf)); assertFalse(negInf.equals(posInf)); assertFalse(negInf.equals(r)); assertFalse(posInf.equals(r)); assertFalse(r.equals(negInf)); assertFalse(r.equals(posInf)); assertFalse(posInf.equals(nan)); assertFalse(negInf.equals(nan)); assertFalse(nan.equals(posInf)); assertFalse(nan.equals(negInf)); } @SmallTest public void testReduction() { Rational moreComplicated = new Rational(5 * 78, 7 * 78); assertEquals(new Rational(5, 7), moreComplicated); assertEquals(5, moreComplicated.getNumerator()); assertEquals(7, moreComplicated.getDenominator()); Rational posInf = new Rational(5, 0); assertEquals(1, posInf.getNumerator()); assertEquals(0, posInf.getDenominator()); assertEquals(POSITIVE_INFINITY, posInf); Rational negInf = new Rational(-100, 0); assertEquals(-1, negInf.getNumerator()); assertEquals(0, negInf.getDenominator()); assertEquals(NEGATIVE_INFINITY, negInf); Rational zero = new Rational(0, -100); assertEquals(0, zero.getNumerator()); assertEquals(1, zero.getDenominator()); assertEquals(ZERO, zero); Rational flipSigns = new Rational(1, -1); assertEquals(-1, flipSigns.getNumerator()); assertEquals(1, flipSigns.getDenominator()); Rational flipAndReduce = new Rational(100, -200); assertEquals(-1, flipAndReduce.getNumerator()); assertEquals(2, flipAndReduce.getDenominator()); } @SmallTest public void testCompareTo() { // unit is equal to itself assertCompareEquals(UNIT, new Rational(1, 1)); // NaN is greater than anything but NaN assertCompareEquals(NaN, new Rational(0, 0)); assertGreaterThan(NaN, UNIT); assertGreaterThan(NaN, POSITIVE_INFINITY); assertGreaterThan(NaN, NEGATIVE_INFINITY); assertGreaterThan(NaN, ZERO); // Positive infinity is greater than any other non-NaN assertCompareEquals(POSITIVE_INFINITY, new Rational(1, 0)); assertGreaterThan(POSITIVE_INFINITY, UNIT); assertGreaterThan(POSITIVE_INFINITY, NEGATIVE_INFINITY); assertGreaterThan(POSITIVE_INFINITY, ZERO); // Negative infinity is smaller than any other non-NaN assertCompareEquals(NEGATIVE_INFINITY, new Rational(-1, 0)); assertLessThan(NEGATIVE_INFINITY, UNIT); assertLessThan(NEGATIVE_INFINITY, POSITIVE_INFINITY); assertLessThan(NEGATIVE_INFINITY, ZERO); // A finite number with the same denominator is trivially comparable assertGreaterThan(new Rational(3, 100), new Rational(1, 100)); assertGreaterThan(new Rational(3, 100), ZERO); // Compare finite numbers with different divisors assertGreaterThan(new Rational(5, 25), new Rational(1, 10)); assertGreaterThan(new Rational(5, 25), ZERO); // Compare finite numbers with different signs assertGreaterThan(new Rational(5, 25), new Rational(-1, 10)); assertLessThan(new Rational(-5, 25), ZERO); } @SmallTest public void testConvenienceMethods() { // isFinite assertFinite(ZERO, true); assertFinite(NaN, false); assertFinite(NEGATIVE_INFINITY, false); assertFinite(POSITIVE_INFINITY, false); assertFinite(UNIT, true); // isInfinite assertInfinite(ZERO, false); assertInfinite(NaN, false); assertInfinite(NEGATIVE_INFINITY, true); assertInfinite(POSITIVE_INFINITY, true); assertInfinite(UNIT, false); // isNaN assertNaN(ZERO, false); assertNaN(NaN, true); assertNaN(NEGATIVE_INFINITY, false); assertNaN(POSITIVE_INFINITY, false); assertNaN(UNIT, false); // isZero assertZero(ZERO, true); assertZero(NaN, false); assertZero(NEGATIVE_INFINITY, false); assertZero(POSITIVE_INFINITY, false); assertZero(UNIT, false); } @SmallTest public void testValueConversions() { // Unit, simple case assertValueEquals(UNIT, 1.0f); assertValueEquals(UNIT, 1.0); assertValueEquals(UNIT, 1L); assertValueEquals(UNIT, 1); assertValueEquals(UNIT, (short)1); // Zero, simple case assertValueEquals(ZERO, 0.0f); assertValueEquals(ZERO, 0.0); assertValueEquals(ZERO, 0L); assertValueEquals(ZERO, 0); assertValueEquals(ZERO, (short)0); // NaN is 0 for integers, not-a-number for floating point assertValueEquals(NaN, Float.NaN); assertValueEquals(NaN, Double.NaN); assertValueEquals(NaN, 0L); assertValueEquals(NaN, 0); assertValueEquals(NaN, (short)0); // Positive infinity, saturates upwards for integers assertValueEquals(POSITIVE_INFINITY, Float.POSITIVE_INFINITY); assertValueEquals(POSITIVE_INFINITY, Double.POSITIVE_INFINITY); assertValueEquals(POSITIVE_INFINITY, Long.MAX_VALUE); assertValueEquals(POSITIVE_INFINITY, Integer.MAX_VALUE); assertValueEquals(POSITIVE_INFINITY, (short)-1); // Negative infinity, saturates downwards for integers assertValueEquals(NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); assertValueEquals(NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); assertValueEquals(NEGATIVE_INFINITY, Long.MIN_VALUE); assertValueEquals(NEGATIVE_INFINITY, Integer.MIN_VALUE); assertValueEquals(NEGATIVE_INFINITY, (short)0); // Normal finite values, round down for integers final Rational oneQuarter = new Rational(1, 4); assertValueEquals(oneQuarter, 1.0f / 4.0f); assertValueEquals(oneQuarter, 1.0 / 4.0); assertValueEquals(oneQuarter, 0L); assertValueEquals(oneQuarter, 0); assertValueEquals(oneQuarter, (short)0); final Rational nineFifths = new Rational(9, 5); assertValueEquals(nineFifths, 9.0f / 5.0f); assertValueEquals(nineFifths, 9.0 / 5.0); assertValueEquals(nineFifths, 1L); assertValueEquals(nineFifths, 1); assertValueEquals(nineFifths, (short)1); final Rational negativeHundred = new Rational(-1000, 10); assertValueEquals(negativeHundred, -100.f / 1.f); assertValueEquals(negativeHundred, -100.0 / 1.0); assertValueEquals(negativeHundred, -100L); assertValueEquals(negativeHundred, -100); assertValueEquals(negativeHundred, (short)-100); // Short truncates if the result is too large assertValueEquals(new Rational(Integer.MAX_VALUE, 1), (short)Integer.MAX_VALUE); assertValueEquals(new Rational(0x00FFFFFF, 1), (short)0x00FFFFFF); assertValueEquals(new Rational(0x00FF00FF, 1), (short)0x00FF00FF); } @SmallTest public void testSerialize() throws ClassNotFoundException, IOException { /* * Check correct [de]serialization */ assertEqualsAfterSerializing(ZERO); assertEqualsAfterSerializing(NaN); assertEqualsAfterSerializing(NEGATIVE_INFINITY); assertEqualsAfterSerializing(POSITIVE_INFINITY); assertEqualsAfterSerializing(UNIT); assertEqualsAfterSerializing(new Rational(100, 200)); assertEqualsAfterSerializing(new Rational(-100, 200)); assertEqualsAfterSerializing(new Rational(5, 1)); assertEqualsAfterSerializing(new Rational(Integer.MAX_VALUE, Integer.MIN_VALUE)); /* * Check bad deserialization fails */ try { Rational badZero = createIllegalRational(0, 100); // [0, 100] , should be [0, 1] Rational results = serializeRoundTrip(badZero); fail("Deserializing " + results + " should not have succeeded"); } catch (InvalidObjectException e) { // OK } try { Rational badPosInfinity = createIllegalRational(100, 0); // [100, 0] , should be [1, 0] Rational results = serializeRoundTrip(badPosInfinity); fail("Deserializing " + results + " should not have succeeded"); } catch (InvalidObjectException e) { // OK } try { Rational badNegInfinity = createIllegalRational(-100, 0); // [-100, 0] , should be [-1, 0] Rational results = serializeRoundTrip(badNegInfinity); fail("Deserializing " + results + " should not have succeeded"); } catch (InvalidObjectException e) { // OK } try { Rational badReduced = createIllegalRational(2, 4); // [2,4] , should be [1, 2] Rational results = serializeRoundTrip(badReduced); fail("Deserializing " + results + " should not have succeeded"); } catch (InvalidObjectException e) { // OK } try { Rational badReducedNeg = createIllegalRational(-2, 4); // [-2, 4] should be [-1, 2] Rational results = serializeRoundTrip(badReducedNeg); fail("Deserializing " + results + " should not have succeeded"); } catch (InvalidObjectException e) { // OK } } private static void assertValueEquals(Rational object, float expected) { assertEquals("Checking floatValue() for " + object + ";", expected, object.floatValue()); } private static void assertValueEquals(Rational object, double expected) { assertEquals("Checking doubleValue() for " + object + ";", expected, object.doubleValue()); } private static void assertValueEquals(Rational object, long expected) { assertEquals("Checking longValue() for " + object + ";", expected, object.longValue()); } private static void assertValueEquals(Rational object, int expected) { assertEquals("Checking intValue() for " + object + ";", expected, object.intValue()); } private static void assertValueEquals(Rational object, short expected) { assertEquals("Checking shortValue() for " + object + ";", expected, object.shortValue()); } private static void assertFinite(Rational object, boolean expected) { assertAction("finite", object, expected, object.isFinite()); } private static void assertInfinite(Rational object, boolean expected) { assertAction("infinite", object, expected, object.isInfinite()); } private static void assertNaN(Rational object, boolean expected) { assertAction("NaN", object, expected, object.isNaN()); } private static void assertZero(Rational object, boolean expected) { assertAction("zero", object, expected, object.isZero()); } private static <T> void assertAction(String action, T object, boolean expected, boolean actual) { String expectedMessage = expected ? action : ("not " + action); assertEquals("Expected " + object + " to be " + expectedMessage, expected, actual); } private static <T extends Comparable<? super T>> void assertLessThan(T left, T right) { assertTrue("Expected (LR) left " + left + " to be less than right " + right, left.compareTo(right) < 0); assertTrue("Expected (RL) left " + left + " to be less than right " + right, right.compareTo(left) > 0); } private static <T extends Comparable<? super T>> void assertGreaterThan(T left, T right) { assertTrue("Expected (LR) left " + left + " to be greater than right " + right, left.compareTo(right) > 0); assertTrue("Expected (RL) left " + left + " to be greater than right " + right, right.compareTo(left) < 0); } private static <T extends Comparable<? super T>> void assertCompareEquals(T left, T right) { assertTrue("Expected (LR) left " + left + " to be compareEquals to right " + right, left.compareTo(right) == 0); assertTrue("Expected (RL) left " + left + " to be compareEquals to right " + right, right.compareTo(left) == 0); } private static <T extends Serializable> byte[] serialize(T obj) throws IOException { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); try (ObjectOutputStream objectStream = new ObjectOutputStream(byteStream)) { objectStream.writeObject(obj); } return byteStream.toByteArray(); } private static <T extends Serializable> T deserialize(byte[] array, Class<T> klass) throws IOException, ClassNotFoundException { ByteArrayInputStream bais = new ByteArrayInputStream(array); ObjectInputStream ois = new ObjectInputStream(bais); Object obj = ois.readObject(); return klass.cast(obj); } @SuppressWarnings("unchecked") private static <T extends Serializable> T serializeRoundTrip(T obj) throws IOException, ClassNotFoundException { Class<T> klass = (Class<T>) obj.getClass(); byte[] arr = serialize(obj); T serialized = deserialize(arr, klass); return serialized; } private static <T extends Serializable> void assertEqualsAfterSerializing(T obj) throws ClassNotFoundException, IOException { T serialized = serializeRoundTrip(obj); assertEquals("Expected values to be equal after serialization round-trip", obj, serialized); } private static Rational createIllegalRational(int numerator, int denominator) { Rational r = new Rational(numerator, denominator); mutateField(r, "mNumerator", numerator); mutateField(r, "mDenominator", denominator); return r; } private static <T> void mutateField(T object, String name, int value) { try { Field f = object.getClass().getDeclaredField(name); f.setAccessible(true); f.set(object, value); } catch (NoSuchFieldException e) { throw new AssertionError(e); } catch (IllegalAccessException e) { throw new AssertionError(e); } catch (IllegalArgumentException e) { throw new AssertionError(e); } } }