Skip to content

Commit 4a021ec

Browse files
committed
[LANG-1802] Fix collision in CharRange.hashCode()
More tests
1 parent 97e572c commit 4a021ec

File tree

1 file changed

+36
-8
lines changed

1 file changed

+36
-8
lines changed

src/test/java/org/apache/commons/lang3/CharRangeTest.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -316,14 +316,42 @@ void testHashCode() {
316316
*/
317317
@Test
318318
void testHashCodeLang1802() {
319-
// case A:hash=99
320-
final CharRange a1 = CharRange.isNotIn((char) 1, (char) 2); // 1,2,true → 83+1+14+1=99
321-
final CharRange a2 = CharRange.isIn((char) 2, (char) 2); // 2,2,false → 83+2+14+0=99
322-
assertNotEquals(a1.hashCode(), a2.hashCode()); // Collision
323-
// case B:hash=123
324-
final CharRange b1 = CharRange.isIn((char) 5, (char) 5); // 5,5,false →83+5+35+0=123
325-
final CharRange b2 = CharRange.isNotIn((char) 4, (char) 5); // 4,5,true →83+4+35+1=123
326-
assertNotEquals(b1.hashCode(), b2.hashCode()); // Collision
319+
// Test various combinations of different ranges
320+
final CharRange range1 = CharRange.is('a');
321+
final CharRange range2 = CharRange.is('b');
322+
final CharRange range3 = CharRange.isIn('a', 'z');
323+
final CharRange range4 = CharRange.isIn('b', 'z');
324+
final CharRange range5 = CharRange.isNot('a');
325+
final CharRange range6 = CharRange.isNotIn('a', 'z');
326+
final CharRange range7 = CharRange.isNotIn('b', 'z');
327+
final CharRange range8 = CharRange.isIn((char) 1, (char) 2);
328+
final CharRange range9 = CharRange.isNotIn((char) 1, (char) 2);
329+
// Previously problematic cases from LANG-1802 should now have different hash codes
330+
final CharRange a1 = CharRange.isNotIn((char) 1, (char) 2);
331+
final CharRange a2 = CharRange.isIn((char) 2, (char) 2);
332+
assertNotEquals(a1, a2, "Different ranges should not be equal");
333+
assertNotEquals(a1.hashCode(), a2.hashCode(), "Different ranges should have different hash codes");
334+
final CharRange b1 = CharRange.isIn((char) 5, (char) 5);
335+
final CharRange b2 = CharRange.isNotIn((char) 4, (char) 5);
336+
assertNotEquals(b1, b2, "Different ranges should not be equal");
337+
assertNotEquals(b1.hashCode(), b2.hashCode(), "Different ranges should have different hash codes");
338+
// Test that negated and non-negated ranges with same bounds have different hash codes
339+
final CharRange normal = CharRange.isIn('x', 'y');
340+
final CharRange negated = CharRange.isNotIn('x', 'y');
341+
assertNotEquals(normal, negated, "Negated and normal ranges should not be equal");
342+
assertNotEquals(normal.hashCode(), negated.hashCode(), "Negated and normal ranges should have different hash codes");
343+
// Test that ranges with different start/end produce different hash codes
344+
assertNotEquals(range1.hashCode(), range2.hashCode(), "is('a') vs is('b')");
345+
assertNotEquals(range1.hashCode(), range3.hashCode(), "is('a') vs isIn('a','z')");
346+
assertNotEquals(range3.hashCode(), range4.hashCode(), "isIn('a','z') vs isIn('b','z')");
347+
assertNotEquals(range1.hashCode(), range5.hashCode(), "is('a') vs isNot('a')");
348+
assertNotEquals(range3.hashCode(), range6.hashCode(), "isIn('a','z') vs isNotIn('a','z')");
349+
assertNotEquals(range6.hashCode(), range7.hashCode(), "isNotIn('a','z') vs isNotIn('b','z')");
350+
assertNotEquals(range8.hashCode(), range9.hashCode(), "isIn(1,2) vs isNotIn(1,2)");
351+
// Test that equal ranges have equal hash codes
352+
final CharRange sameAsRange1 = CharRange.is('a');
353+
assertEquals(range1, sameAsRange1, "Equal ranges should be equal");
354+
assertEquals(range1.hashCode(), sameAsRange1.hashCode(), "Equal ranges should have equal hash codes");
327355
}
328356

329357
@Test

0 commit comments

Comments
 (0)