Ticket #21139: 21139.2.patch

File 21139.2.patch, 4.8 KB (added by taylor.smock, 3 years ago)

Additionally reset static AbstractProperty preference fields to null after each test (this may cause other test failures)

  • test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java

    diff --git a/test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java b/test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java
    index 1ee93ed28..0ae4a1479 100644
    a b import java.lang.annotation.ElementType;  
    66import java.lang.annotation.Retention;
    77import java.lang.annotation.RetentionPolicy;
    88import java.lang.annotation.Target;
     9import java.lang.reflect.Field;
     10import java.lang.reflect.Modifier;
     11import java.util.Vector;
     12import java.util.stream.Collectors;
    913
    1014import org.junit.jupiter.api.extension.AfterAllCallback;
    1115import org.junit.jupiter.api.extension.AfterEachCallback;
    import org.junit.jupiter.api.extension.ExtendWith;  
    1519import org.junit.jupiter.api.extension.ExtensionContext;
    1620import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
    1721import org.openstreetmap.josm.data.Preferences;
     22import org.openstreetmap.josm.data.preferences.AbstractProperty;
    1823import org.openstreetmap.josm.data.preferences.JosmBaseDirectories;
    1924import org.openstreetmap.josm.data.preferences.JosmUrls;
    2025import org.openstreetmap.josm.spi.preferences.Config;
    public @interface BasicPreferences {  
    4247        @Override
    4348        public void afterAll(ExtensionContext context) throws Exception {
    4449            AnnotationUtils.resetStaticClass(Config.class);
     50
     51            // Ensure that config tests are consistent
     52            resetConfigVariables(null);
    4553        }
    4654
    4755        @Override
    public @interface BasicPreferences {  
    6472
    6573            // Store the pref for other extensions
    6674            context.getStore(Namespace.create(BasicPreferencesExtension.class)).put("preferences", pref);
     75
     76            resetConfigVariables(pref);
    6777        }
    6878
    6979        @Override
    public @interface BasicPreferences {  
    7282                this.beforeAll(context);
    7383            }
    7484        }
     85
     86        /**
     87         * Reset all {@link AbstractProperty} preferences to the new preference
     88         * @param pref The preference to use
     89         * @throws ReflectiveOperationException if reflection fails
     90         */
     91        private void resetConfigVariables(Preferences pref) throws ReflectiveOperationException {
     92            final Field classField = ClassLoader.class.getDeclaredField("classes");
     93            classField.setAccessible(true);
     94            final Field configField = AbstractProperty.class.getDeclaredField("preferences");
     95            final boolean configIsFinal = Modifier.isFinal(configField.getModifiers());
     96            try {
     97                if (configIsFinal) {
     98                    makeFinalFieldModifiable(configField, true);
     99                }
     100                // We are pretty much requiring that this doesn't change.
     101                @SuppressWarnings("unchecked")
     102                Vector<Class<?>> classes = (Vector<Class<?>>) classField.get(this.getClass().getClassLoader());
     103                // We need to copy to another list (ConcurrentModificationException)
     104                for (Class<?> clazz : classes.stream().collect(Collectors.toList())) {
     105                    for (Field field : clazz.getDeclaredFields()) {
     106                        if (Modifier.isStatic(field.getModifiers()) && AbstractProperty.class.isInstance(field.getType())) {
     107                            configField.set(field.get(null), pref);
     108                        }
     109                    }
     110                }
     111            } finally {
     112                if (configIsFinal) {
     113                    makeFinalFieldModifiable(configField, false);
     114                }
     115            }
     116        }
     117
     118        /**
     119         * Make a final field modifiable. This should always be called twice (once prior to modification, once after)
     120         * @param field The field to make modifiable
     121         * @param modifiable {@code false} will make the field final
     122         * @throws ReflectiveOperationException If the reflection operations fail
     123         */
     124        private static void makeFinalFieldModifiable(Field field, boolean modifiable) throws ReflectiveOperationException {
     125            Field modifiersField = Field.class.getDeclaredField("modifiers");
     126            boolean isModifierAccessible = modifiersField.isAccessible();
     127            try {
     128                if (!isModifierAccessible) {
     129                    modifiersField.setAccessible(true);
     130                }
     131                if (modifiable) {
     132                    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
     133                } else {
     134                    modifiersField.setInt(field, field.getModifiers() | Modifier.FINAL);
     135                }
     136            } finally {
     137                if (!isModifierAccessible) {
     138                    modifiersField.setAccessible(false);
     139                }
     140            }
     141        }
    75142    }
    76143}