1+ /*
2+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation.
8+ *
9+ * This code is distributed in the hope that it will be useful, but WITHOUT
10+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+ * version 2 for more details (a copy is included in the LICENSE file that
13+ * accompanied this code).
14+ *
15+ * You should have received a copy of the GNU General Public License version
16+ * 2 along with this work; if not, write to the Free Software Foundation,
17+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+ *
19+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+ * or visit www.oracle.com if you need additional information or have any
21+ * questions.
22+ */
23+
24+ import java .io .IOException ;
25+ import java .util .*;
26+ import java .util .stream .Stream ;
27+
28+ import org .junit .jupiter .api .BeforeAll ;
29+ import org .junit .jupiter .api .BeforeEach ;
30+ import org .junit .jupiter .api .Test ;
31+ import org .junit .jupiter .params .ParameterizedTest ;
32+ import org .junit .jupiter .params .provider .MethodSource ;
33+
34+ import jdk .tools .jlink .internal .PluginRepository ;
35+ import jdk .tools .jlink .internal .TaskHelper ;
36+ import jdk .tools .jlink .internal .TaskHelper .Option ;
37+ import jdk .tools .jlink .internal .TaskHelper .OptionsHelper ;
38+ import jdk .tools .jlink .plugin .Plugin ;
39+ import jdk .tools .jlink .plugin .ResourcePool ;
40+ import jdk .tools .jlink .plugin .ResourcePoolBuilder ;
41+
42+ import static org .junit .jupiter .api .Assertions .assertEquals ;
43+ import static org .junit .jupiter .api .Assertions .assertTrue ;
44+ import static org .junit .jupiter .api .Assertions .fail ;
45+
46+ import jdk .tools .jlink .internal .TaskHelper .BadArgs ;
47+
48+ /*
49+ * @test
50+ * @summary Test TaskHelper option parsing
51+ * @bug 8303884
52+ * @modules jdk.jlink/jdk.tools.jlink.internal
53+ * jdk.jlink/jdk.tools.jlink.plugin
54+ * @run junit TaskHelperTest
55+ */
56+ public class TaskHelperTest {
57+ private static TaskHelper taskHelper ;
58+ private static OptionsHelper <TaskHelperTest > optionsHelper ;
59+
60+ private static final List <Option <TaskHelperTest >> OPTIONS = List .of (
61+ new Option <>(true , (task , opt , arg ) -> {
62+ System .out .println (arg );
63+ mainArgValue = arg ;
64+ }, true , "--main-expecting" ),
65+ new Option <>(false , (task , opt , arg ) -> {
66+ mainFlag = true ;
67+ }, true , "--main-no-arg" )
68+ );
69+
70+ private static String argValue ;
71+ private static String mainArgValue ;
72+ private static boolean mainFlag = false ;
73+
74+ public record ArgTestCase (String cmdLine , String [] tokens , String pluginArgValue , String mainArgValue , boolean mainFlagSet ) {};
75+
76+ public static class TestPluginWithRawOption implements Plugin {
77+ @ Override
78+ public ResourcePool transform (ResourcePool in , ResourcePoolBuilder out ) {
79+ return out .build ();
80+ }
81+
82+ @ Override
83+ public boolean hasArguments () {
84+ return true ;
85+ }
86+
87+ @ Override
88+ public boolean hasRawArgument () {
89+ return true ;
90+ }
91+
92+ @ Override
93+ public String getName () {
94+ return "raw-arg-plugin" ;
95+ }
96+
97+ @ Override
98+ public void configure (Map <String , String > config ) {
99+ config .forEach ((k , v ) -> {
100+ System .out .println (k + " -> " + v );
101+ });
102+ var v = config .get (getName ());
103+ if (v == null )
104+ throw new AssertionError ();
105+ argValue = v ;
106+ }
107+ }
108+
109+ @ BeforeAll
110+ public static void setup () {
111+ taskHelper = new TaskHelper (TaskHelper .JLINK_BUNDLE );
112+ optionsHelper = taskHelper .newOptionsHelper (TaskHelperTest .class , OPTIONS .toArray (Option []::new ));
113+ PluginRepository .registerPlugin (new TestPluginWithRawOption ());
114+ }
115+
116+ @ BeforeEach
117+ public void reset () {
118+ argValue = null ;
119+ mainArgValue = null ;
120+ mainFlag = false ;
121+ }
122+
123+ public static Stream <ArgTestCase > gnuStyleUsages () {
124+ return Stream .of (
125+ new ArgTestCase (
126+ "--main-expecting=--main-no-arg --main-no-arg" ,
127+ new String [] { "--main-expecting=--main-no-arg" , "--main-no-arg" },
128+ null ,
129+ "--main-no-arg" ,
130+ true
131+ ),
132+ new ArgTestCase (
133+ "--main-expecting ' --main-no-arg' --main-no-arg" ,
134+ new String [] { "--main-expecting" , " --main-no-arg" , "--main-no-arg" },
135+ null ,
136+ " --main-no-arg" ,
137+ true
138+ ),
139+ new ArgTestCase (
140+ "--raw-arg-plugin=--main-no-arg --main-no-arg" ,
141+ new String [] { "--raw-arg-plugin=--main-no-arg" , "--main-no-arg" },
142+ "--main-no-arg" ,
143+ null ,
144+ true
145+ ),
146+ new ArgTestCase (
147+ "--raw-arg-plugin ' --main-no-arg' --main-no-arg" ,
148+ new String [] { "--raw-arg-plugin" , " --main-no-arg" , "--main-no-arg" },
149+ " --main-no-arg" ,
150+ null ,
151+ true
152+ ),
153+ new ArgTestCase (
154+ "--raw-arg-plugin=--main-expecting=value --main-no-arg" ,
155+ new String [] { "--raw-arg-plugin=--main-expecting=value" , "--main-no-arg" },
156+ "--main-expecting=value" ,
157+ null ,
158+ true
159+ ),
160+ new ArgTestCase (
161+ "--raw-arg-plugin='--main-expecting value' --main-no-arg" ,
162+ new String [] { "--raw-arg-plugin=--main-expecting value" , "--main-no-arg" },
163+ "--main-expecting value" ,
164+ null ,
165+ true
166+ ),
167+ new ArgTestCase (
168+ "--raw-arg-plugin='--main-expecting value' --main-expecting realValue" ,
169+ new String [] { "--raw-arg-plugin=--main-expecting value" , "--main-expecting" , "realValue" },
170+ "--main-expecting value" ,
171+ "realValue" ,
172+ false
173+ ));
174+ }
175+
176+ @ ParameterizedTest
177+ @ MethodSource ("gnuStyleUsages" )
178+ public void testGnuStyleOptionAsArgValue (ArgTestCase testCase ) throws TaskHelper .BadArgs {
179+ System .out .println ("Test cmdline: " + testCase .cmdLine ());
180+ var args = testCase .tokens ();
181+ var remaining = optionsHelper .handleOptions (this , args );
182+ try {
183+ // trigger Plugin::configure
184+ taskHelper .getPluginsConfig (null , null , null );
185+ } catch (IOException ex ) {
186+ fail ("Unexpected IOException" );
187+ }
188+ assertTrue (remaining .isEmpty ());
189+ assertEquals (testCase .mainFlagSet (), mainFlag );
190+ assertEquals (testCase .pluginArgValue (), argValue );
191+ assertEquals (testCase .mainArgValue (), mainArgValue );
192+ }
193+
194+ @ Test
195+ public void testGnuStyleOptionAsArgValueMissing () {
196+ var invalidFormat = new String [][] {
197+ { "--main-expecting" , "--main-no-arg --list" , "--main-no-arg" },
198+ { "--main-expecting" , "--main-no-arg" , "--main-no-arg" },
199+ { "--raw-arg-plugin" , "--main-no-arg --list" , "--main-no-arg" },
200+ { "--raw-arg-plugin" , "--main-no-arg" , "--main-no-arg" },
201+ { "--raw-arg-plugin" , "--main-expecting" , "value" , "--main-no-arg" }
202+ };
203+
204+ for (var args : invalidFormat ) {
205+ try {
206+ optionsHelper .handleOptions (this , args );
207+ fail ("Should get an ambiguous error" );
208+ } catch (BadArgs ex ) {
209+ // expected
210+ }
211+ }
212+ }
213+
214+ @ Test
215+ public void testRemaining () throws BadArgs {
216+ String [] args = { "--raw-arg-plugin=--main-expecting" , "value" , "--main-no-arg" };
217+ var remaining = optionsHelper .handleOptions (this , args );
218+ assertEquals (2 , remaining .size ());
219+ }
220+ }
0 commit comments