• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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