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 
 25 /*
 26  * @test
 27  * @summary "AOT" aliases for traditional CDS command-line options
 28  * @requires vm.cds
 29  * @requires vm.flagless
 30  * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
 31  * @build Hello
 32  * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello
 33  * @run driver AOTFlags
 34  */
 35 
 36 import java.io.File;
 37 import jdk.test.lib.cds.CDSTestUtils;
 38 import jdk.test.lib.helpers.ClassFileInstaller;
 39 import jdk.test.lib.process.OutputAnalyzer;
 40 import jdk.test.lib.process.ProcessTools;
 41 
 42 public class AOTFlags {
 43     static String appJar = ClassFileInstaller.getJarPath("hello.jar");
 44     static String aotConfigFile = "hello.aotconfig";
 45     static String aotCacheFile = "hello.aot";
 46     static String helloClass = "Hello";
 47 
 48     public static void main(String[] args) throws Exception {
 49         positiveTests();
 50         negativeTests();
 51     }
 52 
 53     static void positiveTests() throws Exception {
 54         //----------------------------------------------------------------------
 55         printTestCase("Training Run");
 56         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
 57             "-XX:AOTMode=record",
 58             "-XX:AOTConfiguration=" + aotConfigFile,
 59             "-Xlog:aot=debug",
 60             "-cp", appJar, helloClass);
 61 
 62         OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "train");
 63         out.shouldContain("Hello World");
 64         out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
 65         out.shouldHaveExitValue(0);
 66 
 67         //----------------------------------------------------------------------
 68         printTestCase("Assembly Phase (AOTClassLinking unspecified -> should be enabled by default)");
 69         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
 70             "-XX:AOTMode=create",
 71             "-XX:AOTConfiguration=" + aotConfigFile,
 72             "-XX:AOTCache=" + aotCacheFile,
 73             "-Xlog:aot",
 74             "-cp", appJar);
 75         out = CDSTestUtils.executeAndLog(pb, "asm");
 76         out.shouldContain("AOTCache creation is complete");
 77         out.shouldMatch("hello[.]aot");
 78         out.shouldHaveExitValue(0);
 79 
 80         //----------------------------------------------------------------------
 81         printTestCase("Production Run with AOTCache");
 82         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
 83             "-XX:AOTCache=" + aotCacheFile,
 84             "-Xlog:aot",
 85             "-cp", appJar, helloClass);
 86         out = CDSTestUtils.executeAndLog(pb, "prod");
 87         out.shouldContain("Using AOT-linked classes: true (static archive: has aot-linked classes)");
 88         out.shouldContain("Opened AOT cache hello.aot.");
 89         out.shouldContain("Hello World");
 90         out.shouldHaveExitValue(0);
 91 
 92         //----------------------------------------------------------------------
 93         printTestCase("AOTMode=off");
 94         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
 95             "-XX:AOTCache=" + aotCacheFile,
 96             "--show-version",
 97             "-Xlog:aot",
 98             "-XX:AOTMode=off",
 99             "-cp", appJar, helloClass);
100         out = CDSTestUtils.executeAndLog(pb, "prod");
101         out.shouldNotContain(", sharing");
102         out.shouldNotContain("Opened AOT cache hello.aot.");
103         out.shouldContain("Hello World");
104         out.shouldHaveExitValue(0);
105 
106         //----------------------------------------------------------------------
107         printTestCase("AOTMode=auto");
108         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
109             "-XX:AOTCache=" + aotCacheFile,
110             "--show-version",
111             "-Xlog:aot",
112             "-XX:AOTMode=auto",
113             "-cp", appJar, helloClass);
114         out = CDSTestUtils.executeAndLog(pb, "prod");
115         out.shouldContain(", sharing");
116         out.shouldContain("Opened AOT cache hello.aot.");
117         out.shouldContain("Hello World");
118         out.shouldHaveExitValue(0);
119 
120         //----------------------------------------------------------------------
121         printTestCase("AOTMode=on");
122         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
123             "-XX:AOTCache=" + aotCacheFile,
124             "--show-version",
125             "-Xlog:aot",
126             "-XX:AOTMode=on",
127             "-cp", appJar, helloClass);
128         out = CDSTestUtils.executeAndLog(pb, "prod");
129         out.shouldContain(", sharing");
130         out.shouldContain("Opened AOT cache hello.aot.");
131         out.shouldContain("Hello World");
132         out.shouldHaveExitValue(0);
133 
134         //----------------------------------------------------------------------
135         printTestCase("Assembly Phase with -XX:-AOTClassLinking");
136         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
137             "-XX:AOTMode=create",
138             "-XX:-AOTClassLinking",
139             "-XX:AOTConfiguration=" + aotConfigFile,
140             "-XX:AOTCache=" + aotCacheFile,
141             "-Xlog:aot",
142             "-cp", appJar);
143         out = CDSTestUtils.executeAndLog(pb, "asm");
144         out.shouldContain("AOTCache creation is complete");
145         out.shouldMatch("hello[.]aot");
146         out.shouldHaveExitValue(0);
147 
148         //----------------------------------------------------------------------
149         printTestCase("Production Run with AOTCache, which was created with -XX:-AOTClassLinking");
150         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
151             "-XX:AOTCache=" + aotCacheFile,
152             "-Xlog:aot",
153             "-cp", appJar, helloClass);
154         out = CDSTestUtils.executeAndLog(pb, "prod");
155         out.shouldContain("Using AOT-linked classes: false (static archive: no aot-linked classes)");
156         out.shouldContain("Opened AOT cache hello.aot.");
157         out.shouldContain("Hello World");
158         out.shouldHaveExitValue(0);
159 
160         //----------------------------------------------------------------------
161         printTestCase("Training run with -XX:-AOTClassLinking, but assembly run with -XX:+AOTClassLinking");
162         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
163             "-XX:AOTMode=record",
164             "-XX:-AOTClassLinking",
165             "-XX:AOTConfiguration=" + aotConfigFile,
166             "-Xlog:aot=debug",
167             "-cp", appJar, helloClass);
168         out = CDSTestUtils.executeAndLog(pb, "train");
169         out.shouldContain("Hello World");
170         out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
171         out.shouldHaveExitValue(0);
172 
173         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
174             "-XX:AOTMode=create",
175             "-XX:+AOTClassLinking",
176             "-XX:AOTConfiguration=" + aotConfigFile,
177             "-XX:AOTCache=" + aotCacheFile,
178             "-Xlog:aot=debug",
179             "-cp", appJar);
180         out = CDSTestUtils.executeAndLog(pb, "asm");
181         out.shouldContain("Writing AOTCache file:");
182         out.shouldMatch("aot.*hello[.]aot");
183         out.shouldHaveExitValue(0);
184 
185         //----------------------------------------------------------------------
186         printTestCase("One step training run (JEP-514");
187 
188         // Set all AOTMode/AOTCacheOutput/AOTConfiguration
189         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
190             "-XX:AOTMode=record",
191             "-XX:AOTCacheOutput=" + aotCacheFile,
192             "-XX:AOTConfiguration=" + aotConfigFile,
193             "-Xlog:aot=debug",
194             "-cp", appJar, helloClass);
195         out = CDSTestUtils.executeAndLog(pb, "ontstep-train");
196         out.shouldContain("Hello World");
197         out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
198         out.shouldContain("AOTCache creation is complete: hello.aot");
199         out.shouldHaveExitValue(0);
200 
201         // Set AOTCacheOutput/AOTConfiguration only; Ergo for: AOTMode=record
202         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
203             "-XX:AOTCacheOutput=" + aotCacheFile,
204             "-XX:AOTConfiguration=" + aotConfigFile,
205             "-Xlog:aot=debug",
206             "-cp", appJar, helloClass);
207         out = CDSTestUtils.executeAndLog(pb, "ontstep-train");
208         out.shouldContain("Hello World");
209         out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
210         out.shouldContain("AOTCache creation is complete: hello.aot");
211         out.shouldHaveExitValue(0);
212 
213         // Set AOTCacheOutput/AOTConfiguration/AOTMode=auto; Ergo changes: AOTMode=record
214         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
215             "-XX:AOTMode=auto",
216             "-XX:AOTCacheOutput=" + aotCacheFile,
217             "-XX:AOTConfiguration=" + aotConfigFile,
218             "-Xlog:aot=debug",
219             "-cp", appJar, helloClass);
220         out = CDSTestUtils.executeAndLog(pb, "ontstep-train");
221         out.shouldContain("Hello World");
222         out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
223         out.shouldContain("AOTCache creation is complete: hello.aot");
224         out.shouldHaveExitValue(0);
225 
226         // Set AOTCacheOutput only; Ergo for: AOTMode=record, AOTConfiguration=<temp>
227         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
228             "-XX:AOTCacheOutput=" + aotCacheFile,
229             "-Xlog:aot=debug",
230             "-cp", appJar, helloClass);
231         out = CDSTestUtils.executeAndLog(pb, "ontstep-train");
232         out.shouldContain("Hello World");
233         out.shouldContain("Temporary AOTConfiguration recorded: " + aotCacheFile + ".config");
234         out.shouldContain("AOTCache creation is complete: hello.aot");
235         out.shouldHaveExitValue(0);
236 
237         // Set AOTCacheOutput/AOTMode=auto only; Ergo for: AOTMode=record, AOTConfiguration=<temp>
238         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
239             "-XX:AOTMode=auto",
240             "-XX:AOTCacheOutput=" + aotCacheFile,
241             "-Xlog:aot=debug",
242             "-cp", appJar, helloClass);
243         out = CDSTestUtils.executeAndLog(pb, "ontstep-train");
244         out.shouldContain("Hello World");
245         out.shouldContain("Temporary AOTConfiguration recorded: " + aotCacheFile + ".config");
246         out.shouldContain("AOTCache creation is complete: hello.aot");
247         out.shouldHaveExitValue(0);
248 
249         // Quoating of space characters in child JVM process
250         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
251             "-XX:AOTCacheOutput=" + aotCacheFile,
252             "-Dmy.prop=My string -Xshare:off here", // -Xshare:off should not be treated as a single VM opt for the child JVM
253             "-Xlog:aot=debug",
254             "-cp", appJar, helloClass);
255         out = CDSTestUtils.executeAndLog(pb, "ontstep-train");
256         out.shouldContain("Hello World");
257         out.shouldContain("AOTCache creation is complete: hello.aot");
258         out.shouldMatch("Picked up JAVA_TOOL_OPTIONS:.* -Dmy.prop=My' 'string' '-Xshare:off' 'here");
259         out.shouldHaveExitValue(0);
260     }
261 
262     static void negativeTests() throws Exception {
263        //----------------------------------------------------------------------
264         printTestCase("Mixing old and new options");
265         String mixOldNewErrSuffix = " cannot be used at the same time with -Xshare:on, -Xshare:auto, "
266             + "-Xshare:off, -Xshare:dump, DumpLoadedClassList, SharedClassListFile, "
267             + "or SharedArchiveFile";
268 
269         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
270             "-Xshare:off",
271             "-XX:AOTConfiguration=" + aotConfigFile,
272             "-cp", appJar, helloClass);
273 
274         OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "neg");
275         out.shouldContain("Option AOTConfiguration" + mixOldNewErrSuffix);
276         out.shouldNotHaveExitValue(0);
277 
278         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
279             "-XX:SharedArchiveFile=" + aotCacheFile,
280             "-XX:AOTCache=" + aotCacheFile,
281             "-cp", appJar, helloClass);
282         out = CDSTestUtils.executeAndLog(pb, "neg");
283         out.shouldContain("Option AOTCache" + mixOldNewErrSuffix);
284         out.shouldNotHaveExitValue(0);
285 
286         //----------------------------------------------------------------------
287         printTestCase("Use AOTConfiguration without AOTMode/AOTCacheOutput");
288         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
289             "-XX:AOTConfiguration=" + aotConfigFile,
290             "-cp", appJar, helloClass);
291 
292         out = CDSTestUtils.executeAndLog(pb, "neg");
293         out.shouldContain("AOTConfiguration can only be used with when AOTMode is record or create (selected AOTMode = auto)");
294         out.shouldNotHaveExitValue(0);
295 
296         //----------------------------------------------------------------------
297         printTestCase("Use AOTConfiguration with AOTMode=on");
298         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
299             "-XX:AOTMode=on",
300             "-XX:AOTConfiguration=" + aotConfigFile,
301             "-cp", appJar, helloClass);
302 
303         out = CDSTestUtils.executeAndLog(pb, "neg");
304         out.shouldContain("AOTConfiguration can only be used with when AOTMode is record or create (selected AOTMode = on)");
305         out.shouldNotHaveExitValue(0);
306 
307         //----------------------------------------------------------------------
308         printTestCase("Use AOTMode without AOTCacheOutput or AOTConfiguration");
309         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
310             "-XX:AOTMode=record",
311             "-cp", appJar, helloClass);
312 
313         out = CDSTestUtils.executeAndLog(pb, "neg");
314         out.shouldContain("At least one of AOTCacheOutput and AOTConfiguration must be specified when using -XX:AOTMode=record");
315 
316         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
317             "-XX:AOTMode=create",
318             "-cp", appJar, helloClass);
319 
320         out = CDSTestUtils.executeAndLog(pb, "neg");
321         out.shouldContain("AOTConfiguration must be specified when using -XX:AOTMode=create");
322         out.shouldNotHaveExitValue(0);
323 
324         //----------------------------------------------------------------------
325         printTestCase("Bad AOTMode");
326         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
327             "-XX:AOTMode=foo",
328             "-cp", appJar, helloClass);
329 
330         out = CDSTestUtils.executeAndLog(pb, "neg");
331         out.shouldContain("Unrecognized value foo for AOTMode. Must be one of the following: off, record, create, auto, on");
332         out.shouldNotHaveExitValue(0);
333 
334         //----------------------------------------------------------------------
335         printTestCase("AOTCache specified with -XX:AOTMode=record");
336         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
337             "-XX:AOTMode=record",
338             "-XX:AOTConfiguration=" + aotConfigFile,
339             "-XX:AOTCache=" + aotCacheFile,
340             "-cp", appJar, helloClass);
341 
342         out = CDSTestUtils.executeAndLog(pb, "neg");
343         out.shouldContain("AOTCache must not be specified when using -XX:AOTMode=record");
344         out.shouldNotHaveExitValue(0);
345 
346         //----------------------------------------------------------------------
347         printTestCase("AOTCache/AOTCacheOutput not specified with -XX:AOTMode=create");
348         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
349             "-XX:AOTMode=create",
350             "-XX:AOTConfiguration=" + aotConfigFile,
351             "-cp", appJar, helloClass);
352 
353         out = CDSTestUtils.executeAndLog(pb, "neg");
354         out.shouldContain("AOTCache or AOTCacheOutput must be specified when using -XX:AOTMode=create");
355         out.shouldNotHaveExitValue(0);
356 
357         //----------------------------------------------------------------------
358         printTestCase("AOTCache and AOTCacheOutput have different values");
359         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
360             "-XX:AOTMode=create",
361             "-XX:AOTConfiguration=" + aotConfigFile,
362             "-XX:AOTCache=aaa",
363             "-XX:AOTCacheOutput=aaa",
364             "-cp", appJar, helloClass);
365 
366         out = CDSTestUtils.executeAndLog(pb, "neg");
367         out.shouldContain("Only one of AOTCache or AOTCacheOutput can be specified");
368         out.shouldNotHaveExitValue(0);
369 
370         //----------------------------------------------------------------------
371         printTestCase("No such config file");
372         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
373             "-XX:AOTMode=create",
374             "-XX:AOTConfiguration=no-such-file",
375             "-XX:AOTCache=" + aotCacheFile,
376             "-cp", appJar, helloClass);
377 
378         out = CDSTestUtils.executeAndLog(pb, "neg");
379         out.shouldContain("Must be a valid AOT configuration generated by the current JVM: no-such-file");
380         out.shouldNotHaveExitValue(0);
381 
382         //----------------------------------------------------------------------
383         printTestCase("AOTConfiguration file cannot be used as a CDS archive");
384 
385         // first make sure we have a valid aotConfigFile
386         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
387             "-XX:AOTMode=record",
388             "-XX:AOTConfiguration=" + aotConfigFile,
389             "-cp", appJar, helloClass);
390 
391         out = CDSTestUtils.executeAndLog(pb, "train");
392         out.shouldHaveExitValue(0);
393 
394         // Cannot use this config file as a AOT cache
395         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
396             "-XX:AOTMode=on",
397             "-XX:AOTCache=" + aotConfigFile,
398             "-cp", appJar, helloClass);
399 
400         out = CDSTestUtils.executeAndLog(pb, "neg");
401         out.shouldContain("Not a valid AOT cache (hello.aotconfig)");
402         out.shouldNotHaveExitValue(0);
403 
404         // Cannot use this config file as a CDS archive
405         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
406             "-Xshare:on",
407             "-XX:SharedArchiveFile=" + aotConfigFile,
408             "-cp", appJar, helloClass);
409 
410         out = CDSTestUtils.executeAndLog(pb, "neg");
411         out.shouldContain("Not a valid shared archive file (hello.aotconfig)");
412         out.shouldNotHaveExitValue(0);
413 
414         //----------------------------------------------------------------------
415         printTestCase("Classpath mismatch when creating archive");
416 
417         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
418             "-XX:AOTMode=create",
419             "-XX:AOTConfiguration=" + aotConfigFile,
420             "-XX:AOTCache=" + aotCacheFile,
421             "-cp", "noSuchJar.jar");
422 
423         out = CDSTestUtils.executeAndLog(pb, "neg");
424         out.shouldContain("class path and/or module path are not compatible with the ones " +
425                           "specified when the AOTConfiguration file was recorded");
426         out.shouldContain("Unable to use create AOT cache");
427         out.shouldHaveExitValue(1);
428 
429         //----------------------------------------------------------------------
430         printTestCase("Cannot use multiple paths in AOTConfiguration");
431 
432         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
433             "-XX:AOTMode=record",
434             "-XX:AOTConfiguration=" + aotConfigFile + File.pathSeparator + "dummy",
435             "-cp", "noSuchJar.jar");
436 
437         out = CDSTestUtils.executeAndLog(pb, "neg");
438         out.shouldContain("Option AOTConfiguration must specify a single file name");
439         out.shouldHaveExitValue(1);
440 
441         //----------------------------------------------------------------------
442         printTestCase("Cannot use multiple paths in AOTCache");
443 
444         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
445             "-XX:AOTMode=create",
446             "-XX:AOTConfiguration=" + aotConfigFile,
447             "-XX:AOTCache=" + aotCacheFile + File.pathSeparator + "dummy",
448             "-cp", "noSuchJar.jar");
449 
450         out = CDSTestUtils.executeAndLog(pb, "neg");
451         out.shouldContain("Option AOTCache must specify a single file name");
452         out.shouldHaveExitValue(1);
453 
454         //----------------------------------------------------------------------
455         printTestCase("Cannot use a dynamic CDS archive for -XX:AOTCache");
456         String staticArchive = "static.jsa";
457         String dynamicArchive = "dynamic.jsa";
458 
459         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
460             "-Xshare:dump",
461             "-XX:SharedArchiveFile=" + staticArchive);
462         out = CDSTestUtils.executeAndLog(pb, "static");
463         out.shouldHaveExitValue(0);
464 
465         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
466             "-XX:SharedArchiveFile=" + staticArchive,
467             "-XX:ArchiveClassesAtExit=" + dynamicArchive,
468             "--version");
469         out = CDSTestUtils.executeAndLog(pb, "dynamic");
470         out.shouldHaveExitValue(0);
471 
472         pb = ProcessTools.createLimitedTestJavaProcessBuilder(
473             "-Xlog:aot",
474             "-XX:AOTMode=on",
475             "-XX:AOTCache=" + dynamicArchive,
476             "--version");
477 
478         out = CDSTestUtils.executeAndLog(pb, "neg");
479         out.shouldContain("Unable to use AOT cache.");
480         out.shouldContain("Not a valid AOT cache (dynamic.jsa)");
481         out.shouldHaveExitValue(1);
482 
483         //----------------------------------------------------------------------
484         testEmptyValue("AOTCache");
485         testEmptyValue("AOTConfiguration");
486         testEmptyValue("AOTMode");
487     }
488 
489     static void testEmptyValue(String option) throws Exception {
490         printTestCase("Empty values for " + option);
491         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
492             "-XX:" + option + "=", "--version");
493         OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "neg");
494         out.shouldContain("Improperly specified VM option '" + option + "='");
495         out.shouldHaveExitValue(1);
496     }
497 
498     static int testNum = 0;
499     static void printTestCase(String s) {
500         System.out.println("vvvvvvv TEST CASE " + testNum + ": " + s + ": starts here vvvvvvv");
501         testNum++;
502     }
503 }