java-topology/test/jdk/java/util/regex/CaseFoldingTest.java
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
2026-03-26 17:11:57 -04:00

165 lines
7.2 KiB
Java

/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @summary tests RegExp unicode case-insensitive match (?ui)
* @bug 8360459
* @library /lib/testlibrary/java/lang
* @run junit CaseFoldingTest
*/
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CaseFoldingTest {
@Test
void testUnicodeCaseInsensitiveMatch() throws Throwable {
var testAll = true; // true to test all codepoints defined in CaseFolding.txt
var verbose = true; // true to display all codepoints being tested
var filter = "^.*; [CTS]; .*$"; // update C,T,S to test different type
var excluded = Set.of(
// these 'S' characters failed for known reason. they don't map to their
// folding form with toUpperCase or toLowerCase, only map with case-folding.
// exclude them for now.
0x1fd3, // 1FD3 [lo: 1fd3, up: 1fd3] 0390 [lo: 0390, up: 0390]
0x1fe3, // 1FE3 [lo: 1fe3, up: 1fe3] 03B0 [lo: 03b0, up: 03b0]
0xfb05 // FB05 [lo: fb05, up: fb05] FB06 [lo: fb06, up: fb06]
);
var results = Files.lines(UCDFiles.CASEFOLDING)
.filter(line -> !line.startsWith("#") && line.matches(filter))
.map(line -> {
var strs = line.split("; ");
return new String[] {strs[0], strs[1], strs[2]};
})
.filter(cps -> {
var cp1 = Integer.parseInt(cps[0], 16);
var cp2 = Integer.parseInt(cps[2], 16);
if (excluded.contains(cp1))
return false;
if (testAll) {
return true;
}
// the folding codepoint doesn't map back to the original codepoint.
return Character.toUpperCase(cp2) != cp1 && Character.toLowerCase(cp2) != cp1;
})
.flatMap(cps -> {
// test slice, single & range
var cp = Integer.parseInt(cps[0], 16);
var folding = Integer.parseInt(cps[2], 16);
var errors = testCaseFolding(cp, folding);
if (verbose)
System.out.format(" [%s] %s [lo: %04x, up: %04x] %s [lo: %04x, up: %04x]\n",
cps[1],
cps[0],
Character.toLowerCase(cp),
Character.toUpperCase(cp),
cps[2],
Character.toLowerCase(folding),
Character.toUpperCase(folding)
);
errors.forEach(error -> System.out.print(error));
return errors.stream();
})
.collect(Collectors.toList());
assertEquals(results.size(), 0);
}
private static ArrayList<String> testCaseFolding(int cp, int folding) {
ArrayList<String> errors = new ArrayList<>();
testCaseFolding0(cp, folding, errors, "s-t");
testCaseFolding0(folding, cp, errors, "t-s");
// test all uppercase, lowercase combinations
var up = Character.toUpperCase(cp);
var lo = Character.toLowerCase(cp);
var folding_up = Character.toUpperCase(folding); // folding should be normally lowercase
if (up != cp) {
testCaseFolding0(up, folding, errors, "s(u)-t");
testCaseFolding0(folding, up, errors, "t-s(u)");
if (folding_up != folding) {
testCaseFolding0(up, folding_up, errors, "s(u)-t(u)");
testCaseFolding0(folding_up, up, errors, "t(u)-s(u)");
}
}
if (lo != cp) {
testCaseFolding0(lo, folding, errors, "s(l)-t");
testCaseFolding0(folding, lo, errors, "t-s(l)");
if (folding_up != folding) {
testCaseFolding0(lo, folding_up, errors, "s(l)-t(u)");
testCaseFolding0(folding_up, lo, errors, "t(u)-s(l)");
}
}
return errors;
}
private static void testCaseFolding0(int cp, int folding, ArrayList<String> errors, String type) {
var cp_str = Character.isSupplementaryCodePoint(cp)
? String.format("\\u%04x\\u%04x", (int)Character.highSurrogate(cp), (int)Character.lowSurrogate(cp))
: String.format("\\u%04x", cp);
var t = new String(Character.toChars(folding));
var p = String.format("(?iu)%s", cp_str);
if (Pattern.compile(p).matcher(t).matches() == false) {
errors.add(String.format(" [FAILED] slice: %-20s t: u+%04x (%s)\n", p, folding, type));
}
p = String.format("(?iu)[%s]", cp_str);
if (Pattern.compile(p).matcher(t).matches() == false) {
errors.add(String.format(" [FAILED] single: %-20s t: u+%04x (%s)\n", p, folding, type));
}
p = String.format("(?iu)[%s-%s]", cp_str, cp_str);
if (Pattern.compile(p).matcher(t).matches() == false) {
errors.add(String.format(" [FAILED] range: %-20s t: u+%04x (%s)\n", p, folding, type));
}
// small range
var end_cp = cp + 16;
var end_cp_str = Character.isSupplementaryCodePoint(end_cp)
? String.format("\\u%04x\\u%04x", (int)Character.highSurrogate(end_cp), (int)Character.lowSurrogate(end_cp))
: String.format("\\u%04x", end_cp);
p = String.format("(?iu)[%s-%s]", cp_str, end_cp_str);
if (Pattern.compile(p).matcher(t).matches() == false) {
errors.add(String.format(" [FAILED] range: %-20s t: u+%04x (%s)\n", p, folding, type));
}
end_cp = cp + 128; // bigger than the expanded_casefolding_map.
end_cp_str = Character.isSupplementaryCodePoint(end_cp)
? String.format("\\u%04x\\u%04x", (int)Character.highSurrogate(end_cp), (int)Character.lowSurrogate(end_cp))
: String.format("\\u%04x", end_cp);
p = String.format("(?iu)[%s-%s]", cp_str, end_cp_str);
if (Pattern.compile(p).matcher(t).matches() == false) {
errors.add(String.format(" [FAILED] range: %-20s t: u+%04x (%s)\n", p, folding, type));
}
}
}