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  package org.apache.commons.lang3.tuple;
18  
19  import static org.apache.commons.lang3.LangAssertions.assertNullPointerException;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
22  import static org.junit.jupiter.api.Assertions.assertNotEquals;
23  import static org.junit.jupiter.api.Assertions.assertNull;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.util.AbstractMap;
27  import java.util.Calendar;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.LinkedHashMap;
31  import java.util.Map;
32  import java.util.Map.Entry;
33  import java.util.Objects;
34  import java.util.TreeMap;
35  import java.util.WeakHashMap;
36  import java.util.concurrent.ConcurrentHashMap;
37  import java.util.concurrent.ConcurrentSkipListMap;
38  import java.util.stream.Stream;
39  
40  import org.apache.commons.lang3.AbstractLangTest;
41  import org.junit.jupiter.api.Test;
42  import org.junit.jupiter.params.ParameterizedTest;
43  import org.junit.jupiter.params.provider.MethodSource;
44  
45  /**
46   * Test the Pair class.
47   */
48  class PairTest extends AbstractLangTest {
49  
50      // This is a @MethodSource
51      public static Stream<Class<? extends Map>> mapClassFactory() {
52          return Stream.of(ConcurrentHashMap.class, ConcurrentSkipListMap.class, HashMap.class, TreeMap.class, WeakHashMap.class, LinkedHashMap.class);
53      }
54  
55      @Test
56      void testAccept() {
57          final Pair<String, String> pair1 = Pair.of("A", "D");
58          final Pair<String, String> pair2 = Pair.of("B", "C");
59          final Map<String, String> map = new HashMap<>();
60          pair1.accept(map::put);
61          pair2.accept(map::put);
62          assertEquals("D", map.get("A"));
63          assertEquals("C", map.get("B"));
64          pair1.accept(map::put);
65          pair2.accept(map::put);
66          assertEquals("D", map.get("A"));
67          assertEquals("C", map.get("B"));
68      }
69  
70      @Test
71      void testApply() {
72          final Pair<String, String> pair1 = Pair.of("A", "D");
73          final Pair<String, String> pair2 = Pair.of("B", "C");
74          final Map<String, String> map = new HashMap<>();
75          assertNull(pair1.apply(map::put));
76          assertNull(pair2.apply(map::put));
77          assertEquals("D", map.get("A"));
78          assertEquals("C", map.get("B"));
79          assertEquals("D", pair1.apply(map::put));
80          assertEquals("C", pair2.apply(map::put));
81          assertEquals("D", map.get("A"));
82          assertEquals("C", map.get("B"));
83      }
84  
85      @Test
86      void testComparableAllDifferent() {
87          final Pair<String, String> pair1 = Pair.of("A", "D");
88          final Pair<String, String> pair2 = Pair.of("B", "C");
89          assertEquals(0, pair1.compareTo(pair1));
90          assertTrue(pair1.compareTo(pair2) < 0);
91          assertEquals(0, pair2.compareTo(pair2));
92          assertTrue(pair2.compareTo(pair1) > 0);
93      }
94  
95      @Test
96      void testComparableLeftEquals() {
97          final Pair<String, String> pair1 = Pair.of("E", "C");
98          final Pair<String, String> pair2 = Pair.of("E", "D");
99          assertEquals(0, pair1.compareTo(pair1));
100         assertTrue(pair1.compareTo(pair2) < 0);
101         assertEquals(0, pair2.compareTo(pair2));
102         assertTrue(pair2.compareTo(pair1) > 0);
103     }
104 
105     @Test
106     void testComparableRightEquals() {
107         final Pair<String, String> pair1 = Pair.of("A", "E");
108         final Pair<String, String> pair2 = Pair.of("B", "E");
109         assertEquals(0, pair1.compareTo(pair1));
110         assertTrue(pair1.compareTo(pair2) < 0);
111         assertEquals(0, pair2.compareTo(pair2));
112         assertTrue(pair2.compareTo(pair1) > 0);
113     }
114 
115     @Test
116     void testCompatibilityBetweenPairs() {
117         final Pair<Integer, String> pair = ImmutablePair.of(0, "foo");
118         final Pair<Integer, String> pair2 = MutablePair.of(0, "foo");
119         assertEquals(pair, pair2);
120         assertEquals(pair.hashCode(), pair2.hashCode());
121         final HashSet<Pair<Integer, String>> set = new HashSet<>();
122         set.add(pair);
123         assertTrue(set.contains(pair2));
124 
125         pair2.setValue("bar");
126         assertNotEquals(pair, pair2);
127         assertNotEquals(pair.hashCode(), pair2.hashCode());
128     }
129 
130     @Test
131     void testEmptyArrayGenerics() {
132         final Pair<Integer, String>[] empty = Pair.emptyArray();
133         assertEquals(0, empty.length);
134     }
135 
136     @Test
137     void testEmptyArrayLength() {
138         @SuppressWarnings("unchecked")
139         final Pair<Integer, String>[] empty = (Pair<Integer, String>[]) Pair.EMPTY_ARRAY;
140         assertEquals(0, empty.length);
141     }
142 
143     @Test
144     void testEqualsAnonynous() {
145         final Pair<String, String> pair = Pair.of("a", "b");
146         final String key = "a";
147         final String value = "b";
148         final Map.Entry<String, String> entry = new Map.Entry<String, String>() {
149 
150             @Override
151             public boolean equals(final Object o) {
152                 if (!(o instanceof Map.Entry)) {
153                     return false;
154                 }
155                 final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
156                 // FYI java.util.AbstractMap.SimpleEntry.equals(Object) and JDK-8015417
157                 return Objects.equals(getKey(), e.getKey()) && Objects.equals(getValue(), e.getValue());
158             }
159 
160             @Override
161             public String getKey() {
162                 return key;
163             }
164 
165             @Override
166             public String getValue() {
167                 return value;
168             }
169 
170             @Override
171             public int hashCode() {
172                 return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode());
173             }
174 
175             @Override
176             public String setValue(final String value) {
177                 return null;
178             }
179         };
180         final Map.Entry<String, String> entry2 = new Map.Entry<String, String>() {
181 
182             @Override
183             public boolean equals(final Object o) {
184                 if (!(o instanceof Map.Entry)) {
185                     return false;
186                 }
187                 final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
188                 // FYI java.util.AbstractMap.SimpleEntry.equals(Object) and JDK-8015417
189                 return Objects.equals(getKey(), e.getKey()) && Objects.equals(getValue(), e.getValue());
190             }
191 
192             @Override
193             public String getKey() {
194                 return key;
195             }
196 
197             @Override
198             public String getValue() {
199                 return value;
200             }
201             @Override
202             public int hashCode() {
203                 return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode());
204             }
205 
206             @Override
207             public String setValue(final String value) {
208                 return null;
209             }
210         };
211         assertEquals(pair, entry);
212         assertEquals(pair.hashCode(), entry.hashCode());
213         assertEquals(pair, entry2);
214         assertEquals(pair.hashCode(), entry2.hashCode());
215         assertEquals(entry, entry);
216         assertEquals(entry.hashCode(), entry.hashCode());
217         assertEquals(entry2, entry2);
218         assertEquals(entry2.hashCode(), entry2.hashCode());
219         assertEquals(entry, entry2);
220         assertEquals(entry.hashCode(), entry2.hashCode());
221         assertEquals(entry, pair);
222         assertEquals(entry.hashCode(), pair.hashCode());
223 
224     }
225 
226     @Test
227     void testFormattable_padded() {
228         final Pair<String, String> pair = Pair.of("Key", "Value");
229         assertEquals("         (Key,Value)", String.format("%1$20s", pair));
230     }
231 
232     @Test
233     void testFormattable_simple() {
234         final Pair<String, String> pair = Pair.of("Key", "Value");
235         assertEquals("(Key,Value)", String.format("%1$s", pair));
236     }
237 
238     @ParameterizedTest()
239     @MethodSource("org.apache.commons.lang3.tuple.PairTest#mapClassFactory")
240     public <K, V> void testMapEntries(final Class<Map<Integer, String>> clazz) throws InstantiationException, IllegalAccessException {
241         testMapEntry(clazz.newInstance());
242     }
243 
244     public <K, V> void testMapEntries(final Map<Integer, String> map) {
245         testMapEntry(map);
246     }
247 
248     private void testMapEntry(final Map<Integer, String> map) {
249         map.put(0, "foo");
250         final Entry<Integer, String> entry = map.entrySet().iterator().next();
251         final Pair<Integer, String> pair = ImmutablePair.of(0, "foo");
252         assertEquals(pair, entry);
253         assertEquals(pair.hashCode(), entry.hashCode());
254         // LANG-1736:
255         map.clear();
256         map.put(0, "value1");
257         map.put(1, "value2");
258         map.entrySet().forEach(e -> {
259             final Pair<Integer, String> p = ImmutablePair.of(e.getKey(), e.getValue());
260             assertEquals(p, e);
261             assertEquals(p.hashCode(), e.hashCode());
262         });
263     }
264 
265     @Test
266     void testOfNonNull() {
267         assertNullPointerException(() -> Pair.ofNonNull(null, null));
268         assertNullPointerException(() -> Pair.ofNonNull(null, "x"));
269         assertNullPointerException(() -> Pair.ofNonNull("x", null));
270         final Pair<String, String> pair = Pair.ofNonNull("x", "y");
271         assertEquals("x", pair.getLeft());
272         assertEquals("y", pair.getRight());
273     }
274 
275     @Test
276     void testPairOfAbstractMapSimpleEntry() {
277         final Entry<Integer, String> entry = new AbstractMap.SimpleEntry<>(0, "foo");
278         final Pair<Integer, String> pair = Pair.of(entry);
279         assertEquals(entry.getKey(), pair.getLeft());
280         assertEquals(entry.getValue(), pair.getRight());
281         assertEquals(entry, pair);
282         assertEquals(entry.hashCode(), pair.hashCode());
283         assertEquals(pair, entry);
284         assertEquals(pair.hashCode(), entry.hashCode());
285     }
286 
287     @Test
288     void testPairOfMapEntry() {
289         final HashMap<Integer, String> map = new HashMap<>();
290         map.put(0, "foo");
291         final Entry<Integer, String> entry = map.entrySet().iterator().next();
292         final Pair<Integer, String> pair = Pair.of(entry);
293         assertEquals(entry.getKey(), pair.getLeft());
294         assertEquals(entry.getValue(), pair.getRight());
295     }
296 
297     @Test
298     void testPairOfObjects() {
299         final Pair<Integer, String> pair = Pair.of(0, "foo");
300         assertInstanceOf(ImmutablePair.class, pair);
301         assertEquals(0, ((ImmutablePair<Integer, String>) pair).left.intValue());
302         assertEquals("foo", ((ImmutablePair<Integer, String>) pair).right);
303         final Pair<Object, String> pair2 = Pair.of(null, "bar");
304         assertInstanceOf(ImmutablePair.class, pair2);
305         assertNull(((ImmutablePair<Object, String>) pair2).left);
306         assertEquals("bar", ((ImmutablePair<Object, String>) pair2).right);
307         final Pair<?, ?> pair3 = Pair.of(null, null);
308         assertNull(pair3.getLeft());
309         assertNull(pair3.getRight());
310     }
311 
312     @Test
313     void testToString() {
314         final Pair<String, String> pair = Pair.of("Key", "Value");
315         assertEquals("(Key,Value)", pair.toString());
316     }
317 
318     @Test
319     void testToStringCustom() {
320         final Calendar date = Calendar.getInstance();
321         date.set(2011, Calendar.APRIL, 25);
322         final Pair<String, Calendar> pair = Pair.of("DOB", date);
323         assertEquals("Test created on 04-25-2011", pair.toString("Test created on %2$tm-%2$td-%2$tY"));
324     }
325 
326 }