View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.lang3;
19  
20  import static org.apache.commons.lang3.LangAssertions.assertIllegalArgumentException;
21  import static org.apache.commons.lang3.LangAssertions.assertNullPointerException;
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertFalse;
24  import static org.junit.jupiter.api.Assertions.assertNotEquals;
25  import static org.junit.jupiter.api.Assertions.assertNotNull;
26  import static org.junit.jupiter.api.Assertions.assertSame;
27  import static org.junit.jupiter.api.Assertions.assertTrue;
28  
29  import java.util.Comparator;
30  
31  import org.junit.jupiter.api.BeforeEach;
32  import org.junit.jupiter.api.Test;
33  
34  /**
35   * Tests {@link Range}.
36   */
37  @SuppressWarnings("boxing")
38  class RangeTest extends AbstractLangTest {
39  
40      abstract static class AbstractComparable implements Comparable<AbstractComparable> {
41          @Override
42          public int compareTo(final AbstractComparable o) {
43              return 0;
44          }
45      }
46  
47      static final class DerivedComparableA extends AbstractComparable {
48          // empty
49      }
50  
51      static final class DerivedComparableB extends AbstractComparable {
52          // empty
53      }
54  
55      private Range<Byte> byteRange;
56      private Range<Byte> byteRange2;
57      private Range<Byte> byteRange3;
58      private Range<Double> doubleRange;
59      private Range<Float> floatRange;
60      private Range<Integer> intRange;
61      private Range<Long> longRange;
62  
63      @BeforeEach
64      public void setUp() {
65          byteRange = Range.of((byte) 0, (byte) 5);
66          byteRange2 = Range.of((byte) 0, (byte) 5);
67          byteRange3 = Range.of((byte) 0, (byte) 10);
68          intRange = Range.of(10, 20);
69          longRange = Range.of(10L, 20L);
70          floatRange = Range.of((float) 10, (float) 20);
71          doubleRange = Range.of((double) 10, (double) 20);
72      }
73  
74      @Test
75      void testBetweenWithCompare() {
76          // all integers are equal
77          final Comparator<Integer> c = (o1, o2) -> 0;
78          final Comparator<String> lengthComp = Comparator.comparingInt(String::length);
79          Range<Integer> rb = Range.between(-10, 20);
80          assertFalse(rb.contains(null), "should not contain null");
81          assertTrue(rb.contains(10), "should contain 10");
82          assertTrue(rb.contains(-10), "should contain -10");
83          assertFalse(rb.contains(21), "should not contain 21");
84          assertFalse(rb.contains(-11), "should not contain -11");
85          rb = Range.between(-10, 20, c);
86          assertFalse(rb.contains(null), "should not contain null");
87          assertTrue(rb.contains(10), "should contain 10");
88          assertTrue(rb.contains(-10), "should contain -10");
89          assertTrue(rb.contains(21), "should contain 21");
90          assertTrue(rb.contains(-11), "should contain -11");
91          Range<String> rbstr = Range.between("house", "i");
92          assertFalse(rbstr.contains(null), "should not contain null");
93          assertTrue(rbstr.contains("house"), "should contain house");
94          assertTrue(rbstr.contains("i"), "should contain i");
95          assertFalse(rbstr.contains("hose"), "should not contain hose");
96          assertFalse(rbstr.contains("ice"), "should not contain ice");
97          rbstr = Range.between("house", "i", lengthComp);
98          assertFalse(rbstr.contains(null), "should not contain null");
99          assertTrue(rbstr.contains("house"), "should contain house");
100         assertTrue(rbstr.contains("i"), "should contain i");
101         assertFalse(rbstr.contains("houses"), "should not contain houses");
102         assertFalse(rbstr.contains(""), "should not contain ''");
103         assertNullPointerException(() -> Range.between(null, null, lengthComp));
104     }
105 
106     @SuppressWarnings({"rawtypes", "unchecked"})
107     @Test
108     void testComparableConstructors() {
109         final Comparable c = other -> 1;
110         final Range r1 = Range.is(c);
111         final Range r2 = Range.between(c, c);
112         assertTrue(r1.isNaturalOrdering());
113         assertTrue(r2.isNaturalOrdering());
114     }
115 
116     @Test
117     void testConstructorSignatureWithAbstractComparableClasses() {
118         final DerivedComparableA derivedComparableA = new DerivedComparableA();
119         final DerivedComparableB derivedComparableB = new DerivedComparableB();
120         final Range<AbstractComparable> mixed = Range.between(derivedComparableA, derivedComparableB, null);
121         assertTrue(mixed.contains(derivedComparableA));
122         final Range<AbstractComparable> same = Range.between(derivedComparableA, derivedComparableA, null);
123         assertTrue(same.contains(derivedComparableA));
124         final Range<DerivedComparableA> rangeA = Range.between(derivedComparableA, derivedComparableA, null);
125         assertTrue(rangeA.contains(derivedComparableA));
126         final Range<DerivedComparableB> rangeB = Range.is(derivedComparableB, null);
127         assertTrue(rangeB.contains(derivedComparableB));
128     }
129 
130     @Test
131     void testContains() {
132         assertFalse(intRange.contains(null));
133         assertFalse(intRange.contains(5));
134         assertTrue(intRange.contains(10));
135         assertTrue(intRange.contains(15));
136         assertTrue(intRange.contains(20));
137         assertFalse(intRange.contains(25));
138     }
139 
140     @Test
141     void testContainsRange() {
142         // null handling
143         assertFalse(intRange.containsRange(null));
144         // easy inside range
145         assertTrue(intRange.containsRange(Range.between(12, 18)));
146         // outside range on each side
147         assertFalse(intRange.containsRange(Range.between(32, 45)));
148         assertFalse(intRange.containsRange(Range.between(2, 8)));
149         // equals range
150         assertTrue(intRange.containsRange(Range.between(10, 20)));
151         // overlaps
152         assertFalse(intRange.containsRange(Range.between(9, 14)));
153         assertFalse(intRange.containsRange(Range.between(16, 21)));
154         // touches lower boundary
155         assertTrue(intRange.containsRange(Range.between(10, 19)));
156         assertFalse(intRange.containsRange(Range.between(10, 21)));
157         // touches upper boundary
158         assertTrue(intRange.containsRange(Range.between(11, 20)));
159         assertFalse(intRange.containsRange(Range.between(9, 20)));
160         // negative
161         assertFalse(intRange.containsRange(Range.between(-11, -18)));
162     }
163 
164     @Test
165     void testElementCompareTo() {
166         assertNullPointerException(() -> intRange.elementCompareTo(null));
167         assertEquals(-1, intRange.elementCompareTo(5));
168         assertEquals(0, intRange.elementCompareTo(10));
169         assertEquals(0, intRange.elementCompareTo(15));
170         assertEquals(0, intRange.elementCompareTo(20));
171         assertEquals(1, intRange.elementCompareTo(25));
172     }
173 
174     @Test
175     void testEqualsObject() {
176         assertEquals(byteRange, byteRange);
177         assertEquals(byteRange, byteRange2);
178         assertEquals(byteRange2, byteRange2);
179         assertEquals(byteRange, byteRange);
180         assertEquals(byteRange2, byteRange2);
181         assertEquals(byteRange3, byteRange3);
182         assertNotEquals(byteRange2, byteRange3);
183         assertNotEquals(null, byteRange2);
184         assertNotEquals("Ni!", byteRange2);
185     }
186 
187     @Test
188     void testFit() {
189         assertEquals(intRange.getMinimum(), intRange.fit(Integer.MIN_VALUE));
190         assertEquals(intRange.getMinimum(), intRange.fit(intRange.getMinimum()));
191         assertEquals(intRange.getMaximum(), intRange.fit(Integer.MAX_VALUE));
192         assertEquals(intRange.getMaximum(), intRange.fit(intRange.getMaximum()));
193         assertEquals(15, intRange.fit(15));
194     }
195 
196     @Test
197     void testFitNull() {
198         assertNullPointerException(() -> intRange.fit(null));
199     }
200 
201     @Test
202     void testGetMaximum() {
203         assertEquals(20, (int) intRange.getMaximum());
204         assertEquals(20L, (long) longRange.getMaximum());
205         assertEquals(20f, floatRange.getMaximum(), 0.00001f);
206         assertEquals(20d, doubleRange.getMaximum(), 0.00001d);
207     }
208 
209     @Test
210     void testGetMinimum() {
211         assertEquals(10, (int) intRange.getMinimum());
212         assertEquals(10L, (long) longRange.getMinimum());
213         assertEquals(10f, floatRange.getMinimum(), 0.00001f);
214         assertEquals(10d, doubleRange.getMinimum(), 0.00001d);
215     }
216 
217     @Test
218     void testHashCode() {
219         assertEquals(byteRange.hashCode(), byteRange2.hashCode());
220         assertNotEquals(byteRange.hashCode(), byteRange3.hashCode());
221         assertEquals(intRange.hashCode(), intRange.hashCode());
222         assertTrue(intRange.hashCode() != 0);
223     }
224 
225     @Test
226     void testIntersectionWith() {
227         assertSame(intRange, intRange.intersectionWith(intRange));
228         assertSame(byteRange, byteRange.intersectionWith(byteRange));
229         assertSame(longRange, longRange.intersectionWith(longRange));
230         assertSame(floatRange, floatRange.intersectionWith(floatRange));
231         assertSame(doubleRange, doubleRange.intersectionWith(doubleRange));
232         assertEquals(Range.between(10, 15), intRange.intersectionWith(Range.between(5, 15)));
233     }
234 
235     @Test
236     void testIntersectionWithNonOverlapping() {
237         assertIllegalArgumentException(() -> intRange.intersectionWith(Range.between(0, 9)));
238     }
239 
240     @Test
241     void testIntersectionWithNull() {
242         assertIllegalArgumentException(() -> intRange.intersectionWith(null));
243     }
244 
245     @Test
246     void testIsAfter() {
247         assertFalse(intRange.isAfter(null));
248         assertTrue(intRange.isAfter(5));
249         assertFalse(intRange.isAfter(10));
250         assertFalse(intRange.isAfter(15));
251         assertFalse(intRange.isAfter(20));
252         assertFalse(intRange.isAfter(25));
253     }
254 
255     @Test
256     void testIsAfterRange() {
257         assertFalse(intRange.isAfterRange(null));
258         assertTrue(intRange.isAfterRange(Range.between(5, 9)));
259         assertFalse(intRange.isAfterRange(Range.between(5, 10)));
260         assertFalse(intRange.isAfterRange(Range.between(5, 20)));
261         assertFalse(intRange.isAfterRange(Range.between(5, 25)));
262         assertFalse(intRange.isAfterRange(Range.between(15, 25)));
263         assertFalse(intRange.isAfterRange(Range.between(21, 25)));
264         assertFalse(intRange.isAfterRange(Range.between(10, 20)));
265     }
266 
267     @Test
268     void testIsBefore() {
269         assertFalse(intRange.isBefore(null));
270         assertFalse(intRange.isBefore(5));
271         assertFalse(intRange.isBefore(10));
272         assertFalse(intRange.isBefore(15));
273         assertFalse(intRange.isBefore(20));
274         assertTrue(intRange.isBefore(25));
275     }
276 
277     @Test
278     void testIsBeforeRange() {
279         assertFalse(intRange.isBeforeRange(null));
280         assertFalse(intRange.isBeforeRange(Range.between(5, 9)));
281         assertFalse(intRange.isBeforeRange(Range.between(5, 10)));
282         assertFalse(intRange.isBeforeRange(Range.between(5, 20)));
283         assertFalse(intRange.isBeforeRange(Range.between(5, 25)));
284         assertFalse(intRange.isBeforeRange(Range.between(15, 25)));
285         assertTrue(intRange.isBeforeRange(Range.between(21, 25)));
286         assertFalse(intRange.isBeforeRange(Range.between(10, 20)));
287     }
288 
289     @Test
290     void testIsEndedBy() {
291         assertFalse(intRange.isEndedBy(null));
292         assertFalse(intRange.isEndedBy(5));
293         assertFalse(intRange.isEndedBy(10));
294         assertFalse(intRange.isEndedBy(15));
295         assertTrue(intRange.isEndedBy(20));
296         assertFalse(intRange.isEndedBy(25));
297     }
298 
299     @Test
300     void testIsOverlappedBy() {
301         // null handling
302         assertFalse(intRange.isOverlappedBy(null));
303         // easy inside range
304         assertTrue(intRange.isOverlappedBy(Range.between(12, 18)));
305         // outside range on each side
306         assertFalse(intRange.isOverlappedBy(Range.between(32, 45)));
307         assertFalse(intRange.isOverlappedBy(Range.between(2, 8)));
308         // equals range
309         assertTrue(intRange.isOverlappedBy(Range.between(10, 20)));
310         // overlaps
311         assertTrue(intRange.isOverlappedBy(Range.between(9, 14)));
312         assertTrue(intRange.isOverlappedBy(Range.between(16, 21)));
313         // touches lower boundary
314         assertTrue(intRange.isOverlappedBy(Range.between(10, 19)));
315         assertTrue(intRange.isOverlappedBy(Range.between(10, 21)));
316         // touches upper boundary
317         assertTrue(intRange.isOverlappedBy(Range.between(11, 20)));
318         assertTrue(intRange.isOverlappedBy(Range.between(9, 20)));
319         // negative
320         assertFalse(intRange.isOverlappedBy(Range.between(-11, -18)));
321         // outside range whole range
322         assertTrue(intRange.isOverlappedBy(Range.between(9, 21)));
323     }
324 
325     @Test
326     void testIsStartedBy() {
327         assertFalse(intRange.isStartedBy(null));
328         assertFalse(intRange.isStartedBy(5));
329         assertTrue(intRange.isStartedBy(10));
330         assertFalse(intRange.isStartedBy(15));
331         assertFalse(intRange.isStartedBy(20));
332         assertFalse(intRange.isStartedBy(25));
333     }
334 
335     @Test
336     void testIsWithCompare() {
337         // all integers are equal
338         final Comparator<Integer> c = (o1, o2) -> 0;
339         Range<Integer> ri = Range.is(10);
340         assertFalse(ri.contains(null), "should not contain null");
341         assertTrue(ri.contains(10), "should contain 10");
342         assertFalse(ri.contains(11), "should not contain 11");
343         ri = Range.is(10, c);
344         assertFalse(ri.contains(null), "should not contain null");
345         assertTrue(ri.contains(10), "should contain 10");
346         assertTrue(ri.contains(11), "should contain 11");
347     }
348 
349     @Test
350     void testNaNEndpointsRejected() {
351         // A NaN endpoint sorts above every value under Double/Float.compareTo's total order, silently
352         // constructing a half-unbounded range: contains()/fit() then accept every value above the minimum.
353         // Construction fails closed instead (mirroring Validate.notNaN); non-NaN behavior is unchanged.
354         assertIllegalArgumentException(() -> Range.of(5.0, Double.NaN));
355         assertIllegalArgumentException(() -> Range.of(Double.NaN, 5.0));
356         assertIllegalArgumentException(() -> Range.of(5.0f, Float.NaN));
357         assertIllegalArgumentException(() -> Range.of(Float.NaN, 5.0f));
358         assertIllegalArgumentException(() -> Range.between(5.0, Double.NaN));
359         assertIllegalArgumentException(() -> Range.between(5.0f, Float.NaN));
360         assertIllegalArgumentException(() -> Range.is(Double.NaN));
361         assertIllegalArgumentException(() -> Range.is(Float.NaN));
362         // rejected under a custom comparator as well: no comparator gives a NaN endpoint a sound meaning
363         assertIllegalArgumentException(() -> Range.of(5.0, Double.NaN, Comparator.naturalOrder()));
364         // NumberRange over Float/Double shares the same root constructor
365         assertIllegalArgumentException(() -> new NumberRange<>(5.0f, Float.NaN, null));
366         assertIllegalArgumentException(() -> new NumberRange<>(Double.NaN, 5.0, null));
367         assertNotNull(new NumberRange<>(5.0f, 6.0f, null));
368         // infinities remain legal endpoints
369         assertTrue(Range.of(0.0f, Float.POSITIVE_INFINITY).contains(Float.MAX_VALUE));
370     }
371 
372     @Test
373     void testOfWithCompare() {
374         // all integers are equal
375         final Comparator<Integer> c = (o1, o2) -> 0;
376         final Comparator<String> lengthComp = Comparator.comparingInt(String::length);
377         Range<Integer> rb = Range.of(-10, 20);
378         assertFalse(rb.contains(null), "should not contain null");
379         assertTrue(rb.contains(10), "should contain 10");
380         assertTrue(rb.contains(-10), "should contain -10");
381         assertFalse(rb.contains(21), "should not contain 21");
382         assertFalse(rb.contains(-11), "should not contain -11");
383         rb = Range.of(-10, 20, c);
384         assertFalse(rb.contains(null), "should not contain null");
385         assertTrue(rb.contains(10), "should contain 10");
386         assertTrue(rb.contains(-10), "should contain -10");
387         assertTrue(rb.contains(21), "should contain 21");
388         assertTrue(rb.contains(-11), "should contain -11");
389         Range<String> rbstr = Range.of("house", "i");
390         assertFalse(rbstr.contains(null), "should not contain null");
391         assertTrue(rbstr.contains("house"), "should contain house");
392         assertTrue(rbstr.contains("i"), "should contain i");
393         assertFalse(rbstr.contains("hose"), "should not contain hose");
394         assertFalse(rbstr.contains("ice"), "should not contain ice");
395         rbstr = Range.of("house", "i", lengthComp);
396         assertFalse(rbstr.contains(null), "should not contain null");
397         assertTrue(rbstr.contains("house"), "should contain house");
398         assertTrue(rbstr.contains("i"), "should contain i");
399         assertFalse(rbstr.contains("houses"), "should not contain houses");
400         assertFalse(rbstr.contains(""), "should not contain ''");
401         assertNullPointerException(() -> Range.of(null, null, lengthComp));
402     }
403 
404     @Test
405     void testRangeOfChars() {
406         final Range<Character> chars = Range.between('a', 'z');
407         assertTrue(chars.contains('b'));
408         assertFalse(chars.contains('B'));
409     }
410 
411     @Test
412     void testSerializing() {
413         SerializationUtils.clone(intRange);
414     }
415 
416     @Test
417     void testToString() {
418         assertNotNull(byteRange.toString());
419 
420         final String str = intRange.toString();
421         assertEquals("[10..20]", str);
422         assertEquals("[-20..-10]", Range.between(-20, -10).toString());
423     }
424 
425     @Test
426     void testToStringFormat() {
427         final String str = intRange.toString("From %1$s to %2$s");
428         assertEquals("From 10 to 20", str);
429     }
430 }