Enable SurrogatePairLengthTest - #116
Conversation
|
An alternative approach that avoids the separate forked JVM: instead of reading the system property once in a // TypeValidator.java
public int getDataLength(Object value) {
if (value instanceof String) {
final String str = (String)value;
if (!isCodePointCountEnabled()) {
return str.length();
}
return getCodePointLength(str);
}
return -1;
}
private static boolean isCodePointCountEnabled() {
return AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
Boolean.getBoolean("org.apache.xerces.impl.dv.xs.useCodePointCountForStringLength")
);
}Then |
Change TypeValidator.getDataLength() to read the system property at runtime instead of caching it in a static final field. The test now uses setUp/tearDown to set and reset the property.
|
Updated approach: instead of a separate forked JVM, |
The previous approach added a runtime method with AccessController to TypeValidator, which is undesirable. Instead, we remove the 'final' keyword from the existing private static field (no other change to the initialization logic), then use simple Field.setAccessible/set in the test's setUp/tearDown to toggle the flag. No AccessController, no forked JVM, no JVM --add-opens flags needed.
| static { | ||
| System.setProperty("org.apache.xerces.impl.dv.xs.useCodePointCountForStringLength", "true"); | ||
| try { | ||
| Field f = TypeValidator.class.getDeclaredField("USE_CODE_POINT_COUNT_FOR_STRING_LENGTH"); |
There was a problem hiding this comment.
why is this all in a static block instead of setUp?
|
|
||
| protected void tearDown() throws Exception { | ||
| System.clearProperty(PROPERTY); | ||
| codePointCountField.set(null, false); |
There was a problem hiding this comment.
do we know false was the original value here?
| <include name="schema/config/UseGrammarPoolOnly_True_Test.class"/> | ||
| <include name="schema/config/UnparsedEntityCheckingTest.class"/> | ||
| --> | ||
| <!-- SurrogatePairLengthTest sets a system property in setUp() that |
There was a problem hiding this comment.
This needs to indicate that this test is not safe for parallel execution.
- Inline PROPERTY and LENGTH_ERROR constants per review feedback - Move all reflection setup from static block to setUp() - Save original field value before modifying, restore in tearDown - Update build.xml comment: explicitly state not safe for parallel execution
There was a problem hiding this comment.
Pull request overview
This PR aims to make SurrogatePairLengthTest pass reliably by ensuring the org.apache.xerces.impl.dv.xs.useCodePointCountForStringLength setting takes effect before TypeValidator uses it, so surrogate-pair length is counted in Unicode code points during validation.
Changes:
- Updated
SurrogatePairLengthTestto set the system property duringsetUp()and to force-enable the internalTypeValidatorflag via reflection (restored intearDown()). - Changed
TypeValidator.USE_CODE_POINT_COUNT_FOR_STRING_LENGTHfromstatic finalto mutablestaticso it can be altered reflectively. - Re-enabled
SurrogatePairLengthTestinbuild.xmland added a note about sequential execution constraints.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| tests/schema/config/SurrogatePairLengthTest.java | Sets the property in setup and uses reflection to toggle/restore TypeValidator’s internal behavior for surrogate-pair length counting. |
| src/org/apache/xerces/impl/dv/xs/TypeValidator.java | Makes the internal “use code point length” flag mutable (no longer final). |
| build.xml | Includes SurrogatePairLengthTest in the test batch and documents its (non-parallel-safe) behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private Field codePointCountField; | ||
| private boolean originalCodePointCount; | ||
|
|
||
| protected void setUp() throws Exception { | ||
| super.setUp(); | ||
| System.setProperty("org.apache.xerces.impl.dv.xs.useCodePointCountForStringLength", "true"); | ||
| Field f = TypeValidator.class.getDeclaredField("USE_CODE_POINT_COUNT_FOR_STRING_LENGTH"); | ||
| f.setAccessible(true); | ||
| codePointCountField = f; | ||
| originalCodePointCount = f.getBoolean(null); | ||
| f.set(null, true); | ||
| } | ||
|
|
||
| protected void tearDown() throws Exception { | ||
| System.clearProperty("org.apache.xerces.impl.dv.xs.useCodePointCountForStringLength"); | ||
| if (codePointCountField != null) { | ||
| codePointCountField.set(null, originalCodePointCount); | ||
| } | ||
| super.tearDown(); | ||
| } |
| public abstract class TypeValidator { | ||
|
|
||
| private static final boolean USE_CODE_POINT_COUNT_FOR_STRING_LENGTH = AccessController.doPrivileged(new PrivilegedAction() { | ||
| private static boolean USE_CODE_POINT_COUNT_FOR_STRING_LENGTH = AccessController.doPrivileged(new PrivilegedAction() { |
garydgregory
left a comment
There was a problem hiding this comment.
Note that a better way to manage system properties in unit tests can be achieved more cleanly using JUnit Pioneer:
https://junit-pioneer.org/docs/system-properties/
Revert the reflection-based approach. Instead, SurrogatePairLengthTest now runs in its own forked JVM via a separate <junit> task in build.xml. The test uses a static initializer to set the system property before TypeValidator loads, so its private static final field picks it up. No changes to TypeValidator.java. No reflection, no AccessController.
TypeValidator's private static final field reads the system property at class load time, so it must be set before the class loads. The <junit> task now sets it via <sysproperty> so it's available before any class loading in the forked JVM. The test uses setUp/tearDown to save and restore the property value for correct test hygiene.
TypeValidator loads during BaseTest.setUp() (via sf.newSchema()). Setting the system property before calling super.setUp() ensures it is available when TypeValidator's static final field is initialized. No static initializer, no sysproperty in build.xml, no reflection. Property strings inlined, value saved and restored in tearDown.
|
PTAL I think this is finally in the best shape it can be for now |
SurrogatePairLengthTestrequires setting a system property (org.apache.xerces.impl.dv.xs.useCodePointCountForStringLength) beforeTypeValidator.classloads. Since the class reads this property as astatic finalconstant viaAccessController.doPrivileged(Boolean.getBoolean(...)), it cannot run in the shared-JVM main<batchtest>(forkmode="once") — other tests loadTypeValidatorfirst, and the property is never read.Added a separate
<junit>task withforkmode="perTest"before the main batchtest to run this test in its own JVM, where the static initializer can set the property before any other class referencesTypeValidator.Removed SurrogatePairLengthTest from the commented-out failure list since it now passes.