Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/tools/jar/compat/CLICompatibility.java
41152 views
1
/*
2
* Copyright (c) 2015, 2018, 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.*;
25
import java.lang.reflect.Method;
26
import java.nio.file.Files;
27
import java.nio.file.Path;
28
import java.nio.file.Paths;
29
import java.util.ArrayList;
30
import java.util.Arrays;
31
import java.util.List;
32
import java.util.function.Consumer;
33
import java.util.jar.JarEntry;
34
import java.util.jar.JarInputStream;
35
import java.util.jar.JarOutputStream;
36
import java.util.stream.Stream;
37
38
import jdk.test.lib.util.FileUtils;
39
import jdk.test.lib.JDKToolFinder;
40
import org.testng.annotations.BeforeTest;
41
import org.testng.annotations.Test;
42
43
import static java.lang.String.format;
44
import static java.lang.System.out;
45
import static java.nio.charset.StandardCharsets.UTF_8;
46
import static org.testng.Assert.assertFalse;
47
import static org.testng.Assert.assertTrue;
48
49
/*
50
* @test
51
* @bug 8170952
52
* @library /lib/testlibrary /test/lib
53
* @build jdk.test.lib.Platform
54
* jdk.test.lib.util.FileUtils
55
* jdk.test.lib.JDKToolFinder
56
* @run testng CLICompatibility
57
* @summary Basic test for compatibility of CLI options
58
*/
59
60
public class CLICompatibility {
61
static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", "."));
62
static final Path USER_DIR = Paths.get(System.getProperty("user.dir"));
63
64
static final String TOOL_VM_OPTIONS = System.getProperty("test.tool.vm.opts", "");
65
66
final boolean legacyOnly; // for running on older JDK's ( test validation )
67
68
// Resources we know to exist, that can be used for creating jar files.
69
static final String RES1 = "CLICompatibility.class";
70
static final String RES2 = "CLICompatibility$Result.class";
71
72
@BeforeTest
73
public void setupResourcesForJar() throws Exception {
74
// Copy the files that we are going to use for creating/updating test
75
// jar files, so that they can be referred to without '-C dir'
76
Files.copy(TEST_CLASSES.resolve(RES1), USER_DIR.resolve(RES1));
77
Files.copy(TEST_CLASSES.resolve(RES2), USER_DIR.resolve(RES2));
78
}
79
80
static final IOConsumer<InputStream> ASSERT_CONTAINS_RES1 = in -> {
81
try (JarInputStream jin = new JarInputStream(in)) {
82
assertTrue(jarContains(jin, RES1), "Failed to find " + RES1);
83
}
84
};
85
static final IOConsumer<InputStream> ASSERT_CONTAINS_RES2 = in -> {
86
try (JarInputStream jin = new JarInputStream(in)) {
87
assertTrue(jarContains(jin, RES2), "Failed to find " + RES2);
88
}
89
};
90
static final IOConsumer<InputStream> ASSERT_CONTAINS_MAINFEST = in -> {
91
try (JarInputStream jin = new JarInputStream(in)) {
92
assertTrue(jin.getManifest() != null, "No META-INF/MANIFEST.MF");
93
}
94
};
95
static final IOConsumer<InputStream> ASSERT_DOES_NOT_CONTAIN_MAINFEST = in -> {
96
try (JarInputStream jin = new JarInputStream(in)) {
97
assertTrue(jin.getManifest() == null, "Found unexpected META-INF/MANIFEST.MF");
98
}
99
};
100
101
static final FailCheckerWithMessage FAIL_TOO_MANY_MAIN_OPS =
102
new FailCheckerWithMessage("You may not specify more than one '-cuxtid' options",
103
/* legacy */ "{ctxui}[vfmn0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files");
104
105
// Create
106
107
@Test
108
public void createBadArgs() {
109
final FailCheckerWithMessage FAIL_CREATE_NO_ARGS = new FailCheckerWithMessage(
110
"'c' flag requires manifest or input files to be specified!");
111
112
jar("c")
113
.assertFailure()
114
.resultChecker(FAIL_CREATE_NO_ARGS);
115
116
jar("-c")
117
.assertFailure()
118
.resultChecker(FAIL_CREATE_NO_ARGS);
119
120
if (!legacyOnly)
121
jar("--create")
122
.assertFailure()
123
.resultChecker(FAIL_CREATE_NO_ARGS);
124
125
jar("ct")
126
.assertFailure()
127
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
128
129
jar("-ct")
130
.assertFailure()
131
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
132
133
if (!legacyOnly)
134
jar("--create --list")
135
.assertFailure()
136
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
137
}
138
139
@Test
140
public void createWriteToFile() throws IOException {
141
Path path = Paths.get("createJarFile.jar"); // for creating
142
String jn = path.toString();
143
for (String opts : new String[]{"cf " + jn, "-cf " + jn, "--create --file=" + jn}) {
144
if (legacyOnly && opts.startsWith("--"))
145
continue;
146
147
jar(opts, RES1)
148
.assertSuccess()
149
.resultChecker(r -> {
150
ASSERT_CONTAINS_RES1.accept(Files.newInputStream(path));
151
ASSERT_CONTAINS_MAINFEST.accept(Files.newInputStream(path));
152
});
153
}
154
FileUtils.deleteFileIfExistsWithRetry(path);
155
}
156
157
@Test
158
public void createWriteToStdout() throws IOException {
159
for (String opts : new String[]{"c", "-c", "--create"}) {
160
if (legacyOnly && opts.startsWith("--"))
161
continue;
162
163
jar(opts, RES1)
164
.assertSuccess()
165
.resultChecker(r -> {
166
ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());
167
ASSERT_CONTAINS_MAINFEST.accept(r.stdoutAsStream());
168
});
169
}
170
}
171
172
@Test
173
public void createWriteToStdoutNoManifest() throws IOException {
174
for (String opts : new String[]{"cM", "-cM", "--create --no-manifest"} ){
175
if (legacyOnly && opts.startsWith("--"))
176
continue;
177
178
jar(opts, RES1)
179
.assertSuccess()
180
.resultChecker(r -> {
181
ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());
182
ASSERT_DOES_NOT_CONTAIN_MAINFEST.accept(r.stdoutAsStream());
183
});
184
}
185
}
186
187
// Update
188
189
@Test
190
public void updateBadArgs() {
191
final FailCheckerWithMessage FAIL_UPDATE_NO_ARGS = new FailCheckerWithMessage(
192
"'u' flag requires manifest, 'e' flag or input files to be specified!");
193
194
jar("u")
195
.assertFailure()
196
.resultChecker(FAIL_UPDATE_NO_ARGS);
197
198
jar("-u")
199
.assertFailure()
200
.resultChecker(FAIL_UPDATE_NO_ARGS);
201
202
if (!legacyOnly)
203
jar("--update")
204
.assertFailure()
205
.resultChecker(FAIL_UPDATE_NO_ARGS);
206
207
jar("ut")
208
.assertFailure()
209
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
210
211
jar("-ut")
212
.assertFailure()
213
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
214
215
if (!legacyOnly)
216
jar("--update --list")
217
.assertFailure()
218
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
219
}
220
221
@Test
222
public void updateReadFileWriteFile() throws IOException {
223
Path path = Paths.get("updateReadWriteStdout.jar"); // for updating
224
String jn = path.toString();
225
226
for (String opts : new String[]{"uf " + jn, "-uf " + jn, "--update --file=" + jn}) {
227
if (legacyOnly && opts.startsWith("--"))
228
continue;
229
230
createJar(path, RES1);
231
jar(opts, RES2)
232
.assertSuccess()
233
.resultChecker(r -> {
234
ASSERT_CONTAINS_RES1.accept(Files.newInputStream(path));
235
ASSERT_CONTAINS_RES2.accept(Files.newInputStream(path));
236
ASSERT_CONTAINS_MAINFEST.accept(Files.newInputStream(path));
237
});
238
}
239
FileUtils.deleteFileIfExistsWithRetry(path);
240
}
241
242
@Test
243
public void updateReadStdinWriteStdout() throws IOException {
244
Path path = Paths.get("updateReadStdinWriteStdout.jar");
245
246
for (String opts : new String[]{"u", "-u", "--update"}) {
247
if (legacyOnly && opts.startsWith("--"))
248
continue;
249
250
createJar(path, RES1);
251
jarWithStdin(path.toFile(), opts, RES2)
252
.assertSuccess()
253
.resultChecker(r -> {
254
ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());
255
ASSERT_CONTAINS_RES2.accept(r.stdoutAsStream());
256
ASSERT_CONTAINS_MAINFEST.accept(r.stdoutAsStream());
257
});
258
}
259
FileUtils.deleteFileIfExistsWithRetry(path);
260
}
261
262
@Test
263
public void updateReadStdinWriteStdoutNoManifest() throws IOException {
264
Path path = Paths.get("updateReadStdinWriteStdoutNoManifest.jar");
265
266
for (String opts : new String[]{"uM", "-uM", "--update --no-manifest"} ){
267
if (legacyOnly && opts.startsWith("--"))
268
continue;
269
270
createJar(path, RES1);
271
jarWithStdin(path.toFile(), opts, RES2)
272
.assertSuccess()
273
.resultChecker(r -> {
274
ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());
275
ASSERT_CONTAINS_RES2.accept(r.stdoutAsStream());
276
ASSERT_DOES_NOT_CONTAIN_MAINFEST.accept(r.stdoutAsStream());
277
});
278
}
279
FileUtils.deleteFileIfExistsWithRetry(path);
280
}
281
282
// List
283
284
@Test
285
public void listBadArgs() {
286
jar("tx")
287
.assertFailure()
288
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
289
290
jar("-tx")
291
.assertFailure()
292
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
293
294
if (!legacyOnly)
295
jar("--list --extract")
296
.assertFailure()
297
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
298
}
299
300
@Test
301
public void listReadFromFileWriteToStdout() throws IOException {
302
Path path = Paths.get("listReadFromFileWriteToStdout.jar"); // for listing
303
createJar(path, RES1);
304
String jn = path.toString();
305
306
for (String opts : new String[]{"tf " + jn, "-tf " + jn, "--list --file " + jn}) {
307
if (legacyOnly && opts.startsWith("--"))
308
continue;
309
310
jar(opts)
311
.assertSuccess()
312
.resultChecker(r ->
313
assertTrue(r.output.contains("META-INF/MANIFEST.MF") && r.output.contains(RES1),
314
"Failed, got [" + r.output + "]")
315
);
316
}
317
FileUtils.deleteFileIfExistsWithRetry(path);
318
}
319
320
@Test
321
public void listReadFromStdinWriteToStdout() throws IOException {
322
Path path = Paths.get("listReadFromStdinWriteToStdout.jar");
323
createJar(path, RES1);
324
325
for (String opts : new String[]{"t", "-t", "--list"} ){
326
if (legacyOnly && opts.startsWith("--"))
327
continue;
328
329
jarWithStdin(path.toFile(), opts)
330
.assertSuccess()
331
.resultChecker(r ->
332
assertTrue(r.output.contains("META-INF/MANIFEST.MF") && r.output.contains(RES1),
333
"Failed, got [" + r.output + "]")
334
);
335
}
336
FileUtils.deleteFileIfExistsWithRetry(path);
337
}
338
339
// Extract
340
341
@Test
342
public void extractBadArgs() {
343
jar("xi")
344
.assertFailure()
345
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
346
347
jar("-xi")
348
.assertFailure()
349
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
350
351
if (!legacyOnly) {
352
jar("--extract --generate-index")
353
.assertFailure()
354
.resultChecker(new FailCheckerWithMessage(
355
"option --generate-index requires an argument"));
356
357
jar("--extract --generate-index=foo")
358
.assertFailure()
359
.resultChecker(FAIL_TOO_MANY_MAIN_OPS);
360
}
361
}
362
363
@Test
364
public void extractReadFromStdin() throws IOException {
365
Path path = Paths.get("extract");
366
Path jarPath = path.resolve("extractReadFromStdin.jar"); // for extracting
367
createJar(jarPath, RES1);
368
369
for (String opts : new String[]{"x" ,"-x", "--extract"}) {
370
if (legacyOnly && opts.startsWith("--"))
371
continue;
372
373
jarWithStdinAndWorkingDir(jarPath.toFile(), path.toFile(), opts)
374
.assertSuccess()
375
.resultChecker(r ->
376
assertTrue(Files.exists(path.resolve(RES1)),
377
"Expected to find:" + path.resolve(RES1))
378
);
379
FileUtils.deleteFileIfExistsWithRetry(path.resolve(RES1));
380
}
381
FileUtils.deleteFileTreeWithRetry(path);
382
}
383
384
@Test
385
public void extractReadFromFile() throws IOException {
386
Path path = Paths.get("extract");
387
String jn = "extractReadFromFile.jar";
388
Path jarPath = path.resolve(jn);
389
createJar(jarPath, RES1);
390
391
for (String opts : new String[]{"xf "+jn ,"-xf "+jn, "--extract --file "+jn}) {
392
if (legacyOnly && opts.startsWith("--"))
393
continue;
394
395
jarWithStdinAndWorkingDir(null, path.toFile(), opts)
396
.assertSuccess()
397
.resultChecker(r ->
398
assertTrue(Files.exists(path.resolve(RES1)),
399
"Expected to find:" + path.resolve(RES1))
400
);
401
FileUtils.deleteFileIfExistsWithRetry(path.resolve(RES1));
402
}
403
FileUtils.deleteFileTreeWithRetry(path);
404
}
405
406
// Basic help
407
408
@Test
409
public void helpBadOptionalArg() {
410
if (legacyOnly)
411
return;
412
413
jar("--help:")
414
.assertFailure();
415
416
jar("--help:blah")
417
.assertFailure();
418
}
419
420
@Test
421
public void help() {
422
if (legacyOnly)
423
return;
424
425
jar("-h")
426
.assertSuccess()
427
.resultChecker(r ->
428
assertTrue(r.output.startsWith("Usage: jar [OPTION...] [ [--release VERSION] [-C dir] files]"),
429
"Failed, got [" + r.output + "]")
430
);
431
432
jar("--help")
433
.assertSuccess()
434
.resultChecker(r -> {
435
assertTrue(r.output.startsWith("Usage: jar [OPTION...] [ [--release VERSION] [-C dir] files]"),
436
"Failed, got [" + r.output + "]");
437
assertFalse(r.output.contains("--do-not-resolve-by-default"));
438
assertFalse(r.output.contains("--warn-if-resolved"));
439
});
440
441
jar("--help:compat")
442
.assertSuccess()
443
.resultChecker(r ->
444
assertTrue(r.output.startsWith("Compatibility Interface:"),
445
"Failed, got [" + r.output + "]")
446
);
447
448
jar("--help-extra")
449
.assertSuccess()
450
.resultChecker(r -> {
451
assertTrue(r.output.startsWith("Usage: jar [OPTION...] [ [--release VERSION] [-C dir] files]"),
452
"Failed, got [" + r.output + "]");
453
assertTrue(r.output.contains("--do-not-resolve-by-default"));
454
assertTrue(r.output.contains("--warn-if-resolved"));
455
});
456
}
457
458
// -- Infrastructure
459
460
static boolean jarContains(JarInputStream jis, String entryName)
461
throws IOException
462
{
463
JarEntry e;
464
boolean found = false;
465
while((e = jis.getNextJarEntry()) != null) {
466
if (e.getName().equals(entryName))
467
return true;
468
}
469
return false;
470
}
471
472
/* Creates a simple jar with entries of size 0, good enough for testing */
473
static void createJar(Path path, String... entries) throws IOException {
474
FileUtils.deleteFileIfExistsWithRetry(path);
475
Path parent = path.getParent();
476
if (parent != null)
477
Files.createDirectories(parent);
478
try (OutputStream out = Files.newOutputStream(path);
479
JarOutputStream jos = new JarOutputStream(out)) {
480
JarEntry je = new JarEntry("META-INF/MANIFEST.MF");
481
jos.putNextEntry(je);
482
jos.closeEntry();
483
484
for (String entry : entries) {
485
je = new JarEntry(entry);
486
jos.putNextEntry(je);
487
jos.closeEntry();
488
}
489
}
490
}
491
492
static class FailCheckerWithMessage implements Consumer<Result> {
493
final String[] messages;
494
FailCheckerWithMessage(String... m) {
495
messages = m;
496
}
497
@Override
498
public void accept(Result r) {
499
//out.printf("%s%n", r.output);
500
boolean found = false;
501
for (String m : messages) {
502
if (r.output.contains(m)) {
503
found = true;
504
break;
505
}
506
}
507
assertTrue(found,
508
"Excepted out to contain one of: " + Arrays.asList(messages)
509
+ " but got: " + r.output);
510
}
511
}
512
513
static Result jar(String... args) {
514
return jarWithStdinAndWorkingDir(null, null, args);
515
}
516
517
static Result jarWithStdin(File stdinSource, String... args) {
518
return jarWithStdinAndWorkingDir(stdinSource, null, args);
519
}
520
521
static Result jarWithStdinAndWorkingDir(File stdinFrom,
522
File workingDir,
523
String... args) {
524
String jar = getJDKTool("jar");
525
List<String> commands = new ArrayList<>();
526
commands.add(jar);
527
if (!TOOL_VM_OPTIONS.isEmpty()) {
528
commands.addAll(Arrays.asList(TOOL_VM_OPTIONS.split("\\s+", -1)));
529
}
530
Stream.of(args).map(s -> s.split(" "))
531
.flatMap(Arrays::stream)
532
.forEach(x -> commands.add(x));
533
ProcessBuilder p = new ProcessBuilder(commands);
534
if (stdinFrom != null)
535
p.redirectInput(stdinFrom);
536
if (workingDir != null)
537
p.directory(workingDir);
538
return run(p);
539
}
540
541
static Result run(ProcessBuilder pb) {
542
Process p;
543
byte[] stdout, stderr;
544
out.printf("Running: %s%n", pb.command());
545
try {
546
p = pb.start();
547
} catch (IOException e) {
548
throw new RuntimeException(
549
format("Couldn't start process '%s'", pb.command()), e);
550
}
551
552
String output;
553
try {
554
stdout = readAllBytes(p.getInputStream());
555
stderr = readAllBytes(p.getErrorStream());
556
557
output = toString(stdout, stderr);
558
} catch (IOException e) {
559
throw new RuntimeException(
560
format("Couldn't read process output '%s'", pb.command()), e);
561
}
562
563
try {
564
p.waitFor();
565
} catch (InterruptedException e) {
566
throw new RuntimeException(
567
format("Process hasn't finished '%s'", pb.command()), e);
568
}
569
return new Result(p.exitValue(), stdout, stderr, output);
570
}
571
572
static final Path JAVA_HOME = Paths.get(System.getProperty("java.home"));
573
574
static String getJDKTool(String name) {
575
try {
576
return JDKToolFinder.getJDKTool(name);
577
} catch (Exception x) {
578
Path j = JAVA_HOME.resolve("bin").resolve(name);
579
if (Files.exists(j))
580
return j.toString();
581
j = JAVA_HOME.resolve("..").resolve("bin").resolve(name);
582
if (Files.exists(j))
583
return j.toString();
584
throw new RuntimeException(x);
585
}
586
}
587
588
static String toString(byte[] ba1, byte[] ba2) {
589
return (new String(ba1, UTF_8)).concat(new String(ba2, UTF_8));
590
}
591
592
static class Result {
593
final int exitValue;
594
final byte[] stdout;
595
final byte[] stderr;
596
final String output;
597
598
private Result(int exitValue, byte[] stdout, byte[] stderr, String output) {
599
this.exitValue = exitValue;
600
this.stdout = stdout;
601
this.stderr = stderr;
602
this.output = output;
603
}
604
605
InputStream stdoutAsStream() { return new ByteArrayInputStream(stdout); }
606
607
Result assertSuccess() { assertTrue(exitValue == 0, output); return this; }
608
Result assertFailure() { assertTrue(exitValue != 0, output); return this; }
609
610
Result resultChecker(IOConsumer<Result> r) {
611
try { r.accept(this); return this; }
612
catch (IOException x) { throw new UncheckedIOException(x); }
613
}
614
615
Result resultChecker(FailCheckerWithMessage c) { c.accept(this); return this; }
616
}
617
618
interface IOConsumer<T> { void accept(T t) throws IOException ; }
619
620
// readAllBytes implementation so the test can be run pre 1.9 ( legacyOnly )
621
static byte[] readAllBytes(InputStream is) throws IOException {
622
byte[] buf = new byte[8192];
623
int capacity = buf.length;
624
int nread = 0;
625
int n;
626
for (;;) {
627
// read to EOF which may read more or less than initial buffer size
628
while ((n = is.read(buf, nread, capacity - nread)) > 0)
629
nread += n;
630
631
// if the last call to read returned -1, then we're done
632
if (n < 0)
633
break;
634
635
// need to allocate a larger buffer
636
capacity = capacity << 1;
637
638
buf = Arrays.copyOf(buf, capacity);
639
}
640
return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
641
}
642
643
// Standalone entry point for running with, possibly older, JDKs.
644
public static void main(String[] args) throws Throwable {
645
boolean legacyOnly = false;
646
if (args.length != 0 && args[0].equals("legacyOnly"))
647
legacyOnly = true;
648
649
CLICompatibility test = new CLICompatibility(legacyOnly);
650
for (Method m : CLICompatibility.class.getDeclaredMethods()) {
651
if (m.getAnnotation(Test.class) != null) {
652
System.out.println("Invoking " + m.getName());
653
m.invoke(test);
654
}
655
}
656
}
657
CLICompatibility(boolean legacyOnly) { this.legacyOnly = legacyOnly; }
658
CLICompatibility() { this.legacyOnly = false; }
659
}
660
661