@@ -70,12 +70,23 @@ public static class PluginImpl implements Plugin {
7070 }
7171 }
7272
73+ // A Plugin used by the end-to-end public-API test; kept separate so it is not initialized by another test first.
74+ public static class ApiPluginImpl implements Plugin {
75+
76+ static {
77+ apiPluginInitializerRan = true ;
78+ }
79+ }
80+
7381 // Set by the static initializer of AClassWithAStaticInitializer; readable without initializing that class.
7482 private static boolean classInitializerRan ;
7583
7684 // Set by the static initializer of PluginImpl; readable without initializing that class.
7785 private static boolean pluginInitializerRan ;
7886
87+ // Set by the static initializer of ApiPluginImpl; readable without initializing that class.
88+ private static boolean apiPluginInitializerRan ;
89+
7990 private static Stream <Arguments > numberTestParameters () {
8091 final List <Arguments > lst = new ArrayList <>();
8192 lst .add (Arguments .of ("123" , Long .valueOf ("123" )));
@@ -121,6 +132,24 @@ void testClassValidatedBeforeInitialization() throws Exception {
121132 assertTrue (pluginInitializerRan );
122133 }
123134
135+ @ Test
136+ void testClassNotInitializedThroughPublicApi () throws Exception {
137+ // What an application using the public API actually does: declare a Class-typed option via a pattern,
138+ // parse argv, query the value, then gate it. Parsing and querying must not run the named class's
139+ // static initializer; only the application deciding to instantiate it should.
140+ final Options options = PatternOptionBuilder .parsePattern ("c+" );
141+ final CommandLine line = new DefaultParser ().parse (options , new String [] {"-c" , ApiPluginImpl .class .getName ()});
142+ final Class <?> cls = line .getParsedOptionValue ("c" );
143+ assertEquals (ApiPluginImpl .class , cls );
144+ // The isAssignableFrom gate a caller uses to reject non-plugins runs without initializing the class.
145+ assertTrue (Plugin .class .isAssignableFrom (cls ));
146+ assertFalse (apiPluginInitializerRan );
147+ // Only when the application instantiates the class does its static initializer run.
148+ final Object instance = cls .getConstructor ().newInstance ();
149+ assertTrue (instance instanceof Plugin );
150+ assertTrue (apiPluginInitializerRan );
151+ }
152+
124153 @ Test
125154 void testDate () throws Exception {
126155 assertThrows (java .text .ParseException .class , () -> Converter .DATE .apply ("whatever" ));
0 commit comments