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    *      http://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.juneau.commons.lang;
18  
19  import static org.apache.juneau.commons.utils.CollectionUtils.*;
20  import static org.junit.jupiter.api.Assertions.*;
21  
22  import java.util.*;
23  
24  import org.apache.juneau.*;
25  import org.junit.jupiter.api.*;
26  
27  /**
28   * Tests for {@link HashCode}.
29   */
30  class HashCode_Test extends TestBase {
31  
32  	//====================================================================================================
33  	// create() tests
34  	//====================================================================================================
35  
36  	@Test
37  	void a01_create_returnsNewInstance() {
38  		var hc1 = HashCode.create();
39  		var hc2 = HashCode.create();
40  		assertNotSame(hc1, hc2);
41  	}
42  
43  	@Test
44  	void a02_create_initialHashCode() {
45  		var hc = HashCode.create();
46  		assertEquals(1, hc.get());
47  	}
48  
49  	//====================================================================================================
50  	// of(Object...) tests
51  	//====================================================================================================
52  
53  	@Test
54  	void b01_of_empty() {
55  		var hashCode = HashCode.of();
56  		assertEquals(1, hashCode);
57  	}
58  
59  	@Test
60  	void b02_of_singleObject() {
61  		var hashCode = HashCode.of("test");
62  		assertEquals(31 * 1 + "test".hashCode(), hashCode);
63  	}
64  
65  	@Test
66  	void b03_of_multipleObjects() {
67  		var hashCode = HashCode.of("a", "b", "c");
68  		var expected = HashCode.create().add("a").add("b").add("c").get();
69  		assertEquals(expected, hashCode);
70  	}
71  
72  	@Test
73  	void b04_of_withNull() {
74  		var hashCode = HashCode.of("a", null, "c");
75  		var expected = HashCode.create().add("a").add(null).add("c").get();
76  		assertEquals(expected, hashCode);
77  	}
78  
79  	@Test
80  	void b05_of_withArray() {
81  		var arr = a("a", "b");
82  		// When passing an array to varargs, it's treated as a single array object
83  		var hashCode = HashCode.of((Object)arr);
84  		var expected = HashCode.create().add(arr).get();
85  		assertEquals(expected, hashCode);
86  	}
87  
88  	@Test
89  	void b06_of_withPrimitives() {
90  		var hashCode = HashCode.of(1, 2, 3);
91  		var expected = HashCode.create().add(1).add(2).add(3).get();
92  		assertEquals(expected, hashCode);
93  	}
94  
95  	//====================================================================================================
96  	// add(int) tests
97  	//====================================================================================================
98  
99  	@Test
100 	void c01_addInt_single() {
101 		var hc = HashCode.create();
102 		hc.add(42);
103 		assertEquals(31 * 1 + 42, hc.get());
104 	}
105 
106 	@Test
107 	void c02_addInt_multiple() {
108 		var hc = HashCode.create();
109 		hc.add(1).add(2).add(3);
110 		var expected = 31 * (31 * (31 * 1 + 1) + 2) + 3;
111 		assertEquals(expected, hc.get());
112 	}
113 
114 	@Test
115 	void c03_addInt_zero() {
116 		var hc = HashCode.create();
117 		hc.add(0);
118 		assertEquals(31 * 1 + 0, hc.get());
119 	}
120 
121 	@Test
122 	void c04_addInt_negative() {
123 		var hc = HashCode.create();
124 		hc.add(-1);
125 		assertEquals(31 * 1 + (-1), hc.get());
126 	}
127 
128 	@Test
129 	void c05_addInt_returnsThis() {
130 		var hc = HashCode.create();
131 		var result = hc.add(42);
132 		assertSame(hc, result);
133 	}
134 
135 	@Test
136 	void c06_addInt_largeValue() {
137 		var hc = HashCode.create();
138 		hc.add(Integer.MAX_VALUE);
139 		assertEquals(31 * 1 + Integer.MAX_VALUE, hc.get());
140 	}
141 
142 	@Test
143 	void c07_addInt_minValue() {
144 		var hc = HashCode.create();
145 		hc.add(Integer.MIN_VALUE);
146 		assertEquals(31 * 1 + Integer.MIN_VALUE, hc.get());
147 	}
148 
149 	//====================================================================================================
150 	// add(Object) - null tests
151 	//====================================================================================================
152 
153 	@Test
154 	void d01_addObject_null() {
155 		var hc = HashCode.create();
156 		hc.add((Object)null);
157 		assertEquals(31 * 1 + 0, hc.get());
158 	}
159 
160 	@Test
161 	void d02_addObject_nullMultiple() {
162 		var hc = HashCode.create();
163 		hc.add(null).add(null).add(null);
164 		var expected = 31 * (31 * (31 * 1 + 0) + 0) + 0;
165 		assertEquals(expected, hc.get());
166 	}
167 
168 	//====================================================================================================
169 	// add(Object) - String tests
170 	//====================================================================================================
171 
172 	@Test
173 	void e01_addObject_string() {
174 		var hc = HashCode.create();
175 		hc.add("test");
176 		assertEquals(31 * 1 + "test".hashCode(), hc.get());
177 	}
178 
179 	@Test
180 	void e02_addObject_stringEmpty() {
181 		var hc = HashCode.create();
182 		hc.add("");
183 		assertEquals(31 * 1 + "".hashCode(), hc.get());
184 	}
185 
186 	@Test
187 	void e03_addObject_stringMultiple() {
188 		var hc = HashCode.create();
189 		hc.add("a").add("b").add("c");
190 		var expected = 31 * (31 * (31 * 1 + "a".hashCode()) + "b".hashCode()) + "c".hashCode();
191 		assertEquals(expected, hc.get());
192 	}
193 
194 	//====================================================================================================
195 	// add(Object) - Array tests
196 	//====================================================================================================
197 
198 	@Test
199 	void f01_addObject_objectArray() {
200 		var arr = a("a", "b", "c");
201 		var hc = HashCode.create();
202 		hc.add(arr);
203 		assertEquals(31 * 1 + Arrays.hashCode(arr), hc.get());
204 	}
205 
206 	@Test
207 	void f02_addObject_intArray() {
208 		var arr = new int[] {1, 2, 3};
209 		var hc = HashCode.create();
210 		hc.add(arr);
211 		assertEquals(31 * 1 + Arrays.hashCode(arr), hc.get());
212 	}
213 
214 	@Test
215 	void f03_addObject_longArray() {
216 		var arr = new long[] {1L, 2L, 3L};
217 		var hc = HashCode.create();
218 		hc.add(arr);
219 		assertEquals(31 * 1 + Arrays.hashCode(arr), hc.get());
220 	}
221 
222 	@Test
223 	void f04_addObject_shortArray() {
224 		var arr = new short[] {1, 2, 3};
225 		var hc = HashCode.create();
226 		hc.add(arr);
227 		assertEquals(31 * 1 + Arrays.hashCode(arr), hc.get());
228 	}
229 
230 	@Test
231 	void f05_addObject_byteArray() {
232 		var arr = new byte[] {1, 2, 3};
233 		var hc = HashCode.create();
234 		hc.add(arr);
235 		assertEquals(31 * 1 + Arrays.hashCode(arr), hc.get());
236 	}
237 
238 	@Test
239 	void f06_addObject_charArray() {
240 		var arr = new char[] {'a', 'b', 'c'};
241 		var hc = HashCode.create();
242 		hc.add(arr);
243 		assertEquals(31 * 1 + Arrays.hashCode(arr), hc.get());
244 	}
245 
246 	@Test
247 	void f07_addObject_booleanArray() {
248 		var arr = new boolean[] {true, false, true};
249 		var hc = HashCode.create();
250 		hc.add(arr);
251 		assertEquals(31 * 1 + Arrays.hashCode(arr), hc.get());
252 	}
253 
254 	@Test
255 	void f08_addObject_floatArray() {
256 		var arr = new float[] {1.0f, 2.0f, 3.0f};
257 		var hc = HashCode.create();
258 		hc.add(arr);
259 		assertEquals(31 * 1 + Arrays.hashCode(arr), hc.get());
260 	}
261 
262 	@Test
263 	void f09_addObject_doubleArray() {
264 		var arr = new double[] {1.0, 2.0, 3.0};
265 		var hc = HashCode.create();
266 		hc.add(arr);
267 		assertEquals(31 * 1 + Arrays.hashCode(arr), hc.get());
268 	}
269 
270 	@Test
271 	void f10_addObject_emptyArray() {
272 		var arr = new String[0];
273 		var hc = HashCode.create();
274 		hc.add(arr);
275 		assertEquals(31 * 1 + Arrays.hashCode(arr), hc.get());
276 	}
277 
278 	@Test
279 	void f11_addObject_arrayWithNulls() {
280 		var arr = new String[] {"a", null, "c"};
281 		var hc = HashCode.create();
282 		hc.add(arr);
283 		assertEquals(31 * 1 + Arrays.hashCode(arr), hc.get());
284 	}
285 
286 	@Test
287 	void f12_addObject_nestedArray() {
288 		var arr = new int[][] {{1, 2}, {3, 4}};
289 		var hc = HashCode.create();
290 		hc.add(arr);
291 		// Nested arrays are treated as Object[], so use Arrays.hashCode
292 		assertEquals(31 * 1 + Arrays.deepHashCode(arr), hc.get());
293 	}
294 
295 	//====================================================================================================
296 	// add(Object) - Regular object tests
297 	//====================================================================================================
298 
299 	@Test
300 	void g01_addObject_integer() {
301 		var hc = HashCode.create();
302 		hc.add(Integer.valueOf(42));
303 		assertEquals(31 * 1 + Integer.valueOf(42).hashCode(), hc.get());
304 	}
305 
306 	@Test
307 	void g02_addObject_list() {
308 		var list = Arrays.asList("a", "b", "c");
309 		var hc = HashCode.create();
310 		hc.add(list);
311 		assertEquals(31 * 1 + list.hashCode(), hc.get());
312 	}
313 
314 	@Test
315 	void g03_addObject_map() {
316 		var map = new HashMap<String, String>();
317 		map.put("key", "value");
318 		var hc = HashCode.create();
319 		hc.add(map);
320 		assertEquals(31 * 1 + map.hashCode(), hc.get());
321 	}
322 
323 	@Test
324 	void g04_addObject_customObject() {
325 		class TestObject {
326 			private final String value;
327 			TestObject(String value) { this.value = value; }
328 			@Override
329 			public int hashCode() { return value.hashCode(); }
330 		}
331 		var obj = new TestObject("test");
332 		var hc = HashCode.create();
333 		hc.add(obj);
334 		assertEquals(31 * 1 + obj.hashCode(), hc.get());
335 	}
336 
337 	//====================================================================================================
338 	// get() tests
339 	//====================================================================================================
340 
341 	@Test
342 	void h01_get_initialValue() {
343 		var hc = HashCode.create();
344 		assertEquals(1, hc.get());
345 	}
346 
347 	@Test
348 	void h02_get_afterAdd() {
349 		var hc = HashCode.create();
350 		hc.add(42);
351 		assertEquals(31 * 1 + 42, hc.get());
352 	}
353 
354 	@Test
355 	void h03_get_multipleCalls() {
356 		var hc = HashCode.create();
357 		hc.add(42);
358 		var first = hc.get();
359 		var second = hc.get();
360 		assertEquals(first, second);
361 	}
362 
363 	//====================================================================================================
364 	// Chaining tests
365 	//====================================================================================================
366 
367 	@Test
368 	void i01_chaining_mixedTypes() {
369 		var hc = HashCode.create();
370 		hc.add(1).add("test").add(2).add(null).add(3);
371 		var expected = HashCode.create()
372 			.add(1)
373 			.add("test")
374 			.add(2)
375 			.add(null)
376 			.add(3)
377 			.get();
378 		assertEquals(expected, hc.get());
379 	}
380 
381 	@Test
382 	void i02_chaining_returnsThis() {
383 		var hc = HashCode.create();
384 		var result1 = hc.add(1);
385 		var result2 = result1.add("test");
386 		assertSame(hc, result1);
387 		assertSame(hc, result2);
388 	}
389 
390 	//====================================================================================================
391 	// Order matters tests
392 	//====================================================================================================
393 
394 	@Test
395 	void j01_orderMatters_differentOrder() {
396 		var hc1 = HashCode.create().add("a").add("b");
397 		var hc2 = HashCode.create().add("b").add("a");
398 		assertNotEquals(hc1.get(), hc2.get());
399 	}
400 
401 	@Test
402 	void j02_orderMatters_sameOrder() {
403 		var hc1 = HashCode.create().add("a").add("b").add("c");
404 		var hc2 = HashCode.create().add("a").add("b").add("c");
405 		assertEquals(hc1.get(), hc2.get());
406 	}
407 
408 	//====================================================================================================
409 	// Consistency tests
410 	//====================================================================================================
411 
412 	@Test
413 	void k01_consistency_sameInputs() {
414 		var hc1 = HashCode.create().add("test").add(42).add("foo");
415 		var hc2 = HashCode.create().add("test").add(42).add("foo");
416 		assertEquals(hc1.get(), hc2.get());
417 	}
418 
419 	@Test
420 	void k02_consistency_differentInstances() {
421 		var hc1 = HashCode.create().add("test");
422 		var hc2 = HashCode.create().add("test");
423 		assertEquals(hc1.get(), hc2.get());
424 	}
425 
426 	//====================================================================================================
427 	// Edge cases
428 	//====================================================================================================
429 
430 	@Test
431 	void l01_edgeCase_emptyHashCode() {
432 		var hc = HashCode.create();
433 		assertEquals(1, hc.get());
434 	}
435 
436 	@Test
437 	void l02_edgeCase_veryLongChain() {
438 		var hc = HashCode.create();
439 		for (var i = 0; i < 100; i++) {
440 			hc.add(i);
441 		}
442 		// Just verify it doesn't throw and produces a value
443 		assertNotNull(hc.get());
444 	}
445 
446 	@Test
447 	void l03_edgeCase_mixedNullsAndValues() {
448 		var hc = HashCode.create();
449 		hc.add("a").add(null).add("b").add(null).add("c");
450 		var expected = HashCode.create()
451 			.add("a")
452 			.add(null)
453 			.add("b")
454 			.add(null)
455 			.add("c")
456 			.get();
457 		assertEquals(expected, hc.get());
458 	}
459 
460 	@Test
461 	void l04_edgeCase_arraysOfDifferentTypes() {
462 		var hc1 = HashCode.create().add(new int[] {1, 2, 3});
463 		var hc2 = HashCode.create().add(new long[] {1L, 2L, 3L});
464 		// Different array types should produce different hashcodes (usually, but not guaranteed)
465 		// Arrays.hashCode for int[] and long[] with same values might coincidentally be the same
466 		// So we just verify both produce valid hashcodes
467 		assertNotNull(hc1.get());
468 		assertNotNull(hc2.get());
469 		// Verify they use Arrays.hashCode correctly
470 		assertEquals(31 * 1 + Arrays.hashCode(new int[] {1, 2, 3}), hc1.get());
471 		assertEquals(31 * 1 + Arrays.hashCode(new long[] {1L, 2L, 3L}), hc2.get());
472 	}
473 
474 	@Test
475 	void l05_edgeCase_sameArrayContent() {
476 		var arr1 = new int[] {1, 2, 3};
477 		var arr2 = new int[] {1, 2, 3};
478 		var hc1 = HashCode.create().add(arr1);
479 		var hc2 = HashCode.create().add(arr2);
480 		// Same content should produce same hashcode
481 		assertEquals(hc1.get(), hc2.get());
482 	}
483 
484 	@Test
485 	void l06_edgeCase_differentArrayContent() {
486 		var arr1 = new int[] {1, 2, 3};
487 		var arr2 = new int[] {1, 2, 4};
488 		var hc1 = HashCode.create().add(arr1);
489 		var hc2 = HashCode.create().add(arr2);
490 		// Different content should produce different hashcodes
491 		assertNotEquals(hc1.get(), hc2.get());
492 	}
493 }
494