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