1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix.selftest;
19
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25 import java.io.OutputStreamWriter;
26 import java.io.PipedInputStream;
27 import java.io.PipedOutputStream;
28 import java.io.PrintStream;
29 import java.io.PrintWriter;
30 import java.io.StringWriter;
31 import java.io.Writer;
32 import java.nio.charset.Charset;
33 import java.nio.charset.StandardCharsets;
34 import java.nio.file.Files;
35 import java.nio.file.Path;
36 import java.time.Duration;
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.HashSet;
40 import java.util.LinkedHashMap;
41 import java.util.LinkedHashSet;
42 import java.util.List;
43 import java.util.Locale;
44 import java.util.Map;
45 import java.util.Map.Entry;
46 import java.util.Objects;
47 import java.util.Optional;
48 import java.util.Properties;
49 import java.util.Set;
50 import java.util.TreeMap;
51 import java.util.TreeSet;
52 import java.util.function.Supplier;
53
54 import org.junit.jupiter.engine.JupiterTestEngine;
55 import org.junit.jupiter.engine.discovery.DiscoverySelectorResolver;
56 import org.junit.platform.engine.TestExecutionResult;
57 import org.junit.platform.engine.support.descriptor.EngineDescriptor;
58 import org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine;
59 import org.junit.platform.launcher.TestIdentifier;
60 import org.junit.platform.launcher.listeners.TestExecutionSummary;
61 import org.newsclub.net.unix.AFSocket;
62 import org.newsclub.net.unix.AFSocketCapability;
63 import org.newsclub.net.unix.AFUNIXSocket;
64
65 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
66 import com.kohlschutter.testutil.TestAbortedNotAnIssueException;
67 import com.kohlschutter.testutil.TestAbortedWithImportantMessageException;
68 import com.kohlschutter.testutil.TestAbortedWithImportantMessageException.MessageType;
69 import com.kohlschutter.util.ConsolePrintStream;
70 import com.kohlschutter.util.SystemPropertyUtil;
71
72
73
74
75
76
77
78
79
80
81
82 @SuppressWarnings({
83 "PMD.CyclomaticComplexity", "PMD.CognitiveComplexity", "PMD.CouplingBetweenObjects"})
84 public class Selftest {
85 private final Class<?> diagnosticsHelperClass = resolveOptionalClass(
86 "org.newsclub.net.unix.SelftestDiagnosticsHelper");
87 private final ConsolePrintStream out;
88 private final Map<String, ModuleResult> results = new LinkedHashMap<>();
89 private final List<AFSocketCapability> supportedCapabilites = new ArrayList<>();
90 private final List<AFSocketCapability> unsupportedCapabilites = new ArrayList<>();
91 private boolean withIssues = false;
92 private boolean fail = false;
93 private boolean modified = false;
94 private boolean isSupportedAFUNIX = false;
95 private final Set<String> important = new LinkedHashSet<>();
96 private boolean inconclusive = false;
97 private final SelftestProvider sp;
98
99 private enum Result {
100 AUTOSKIP, SKIP, PASS, DONE, NONE, FAIL
101 }
102
103 private enum SkipMode {
104 UNDECLARED(false), KEEP(false), SKIP(true), SKIP_FORCE(true), SKIP_AUTO(true);
105
106 final boolean skip;
107
108 SkipMode(boolean skip) {
109 this.skip = skip;
110 }
111
112 boolean isSkip() {
113 return skip;
114 }
115
116 boolean isDeclared() {
117 return this != UNDECLARED;
118 }
119
120 boolean isForce() {
121 return this == SKIP_FORCE || this == SKIP_AUTO;
122 }
123
124 public static SkipMode parse(String skipMode) {
125 if (skipMode == null || skipMode.isEmpty()) {
126 return SkipMode.UNDECLARED;
127 } else if ("force".equalsIgnoreCase(skipMode)) {
128 return SkipMode.SKIP_FORCE;
129 } else if ("force_auto".equalsIgnoreCase(skipMode)) {
130 return SkipMode.SKIP_AUTO;
131 } else {
132 return Boolean.parseBoolean(skipMode) ? SkipMode.SKIP : SkipMode.KEEP;
133 }
134 }
135
136 }
137
138
139
140
141
142
143
144 @SuppressFBWarnings("UUF_UNUSED_FIELD")
145 static final class MinimizeJarDependencies {
146 JupiterTestEngine jte;
147 HierarchicalTestEngine<?> hte;
148 EngineDescriptor ed;
149 DiscoverySelectorResolver dsr;
150 org.newsclub.lib.junixsocket.common.NarMetadata nmCommon;
151 org.newsclub.lib.junixsocket.custom.NarMetadata nmCustom;
152 }
153
154 public Selftest(PrintStream out, SelftestProvider sp) {
155 System.setProperty("com.kohlschutter.selftest", getClass().getName());
156
157 this.out = ConsolePrintStream.wrapPrintStream(out);
158 this.sp = sp;
159
160 checkSystemProperties();
161 }
162
163 private void checkSystemProperties() {
164 String tmpDir = System.getProperty("java.io.tmpdir", "");
165 if (System.getProperty("java.home", "").isEmpty()) {
166 System.setProperty("java.home", tmpDir);
167 out.println("Setting java.home to temporary directory: " + tmpDir);
168 }
169 }
170
171 public void checkVM() {
172 boolean isSubstrateVM = "Substrate VM".equals(System.getProperty("java.vm.name"));
173
174 if (isSubstrateVM) {
175 important.add("Substrate VM detected: Support for native-images is work in progress");
176
177 String vendorVersion = System.getProperty("java.vendor.version", "");
178 if (vendorVersion.contains("GraalVM 20") || vendorVersion.contains("GraalVM 19")) {
179 if (!getSkipModeForModule("junixsocket-rmi").isDeclared()) {
180 important.add("Auto-skipping junixsocket-rmi tests due to old Substrate VM");
181 System.setProperty("selftest.skip.junixsocket-rmi", "force_auto");
182 withIssues = true;
183 }
184
185 if (!getSkipModeForClass("org.newsclub.net.unix.FileDescriptorCastTest").isDeclared()) {
186 important.add("Auto-skipping FileDescriptorCastTest tests due to Substrate VM");
187 System.setProperty("selftest.skip.FileDescriptorCastTest", "force_auto");
188 withIssues = true;
189 }
190 }
191 } else {
192 if (!getSkipModeForModule("junixsocket-rmi").isDeclared()) {
193 try {
194 Class.forName("java.rmi.Remote");
195 } catch (ClassNotFoundException e) {
196 important.add("Auto-skipping junixsocket-rmi tests due to java.rmi.Remote class missing");
197 System.setProperty("selftest.skip.junixsocket-rmi", "force_auto");
198 withIssues = true;
199 }
200
201 if (!AFSocket.supports(AFSocketCapability.CAPABILITY_LARGE_PORTS)) {
202 important.add(
203 "Auto-skipping junixsocket-rmi tests due to missing CAPABILITY_LARGE_PORTS");
204 System.setProperty("selftest.skip.junixsocket-rmi", "force_auto");
205 withIssues = true;
206 }
207 }
208 }
209 }
210
211
212
213
214
215
216
217
218
219 @SuppressFBWarnings({
220 "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"})
221 public static void main(String[] args) throws Exception {
222 int delay = SystemPropertyUtil.getIntSystemProperty("selftest.delay.at-start", 0);
223 if (delay > 0) {
224 System.out.println("Delaying execution of selftest by " + delay + " seconds");
225 Thread.sleep(Duration.ofSeconds(delay).toMillis());
226 }
227
228 int rc = runSelftest();
229
230 if (SystemPropertyUtil.getBooleanSystemProperty("selftest.wait.at-end", false)) {
231 System.gc();
232 System.out.print("Press any key to end test. ");
233 System.out.flush();
234 System.in.read();
235 System.out.println("RC=" + rc);
236 }
237 System.out.flush();
238
239 System.exit(rc);
240 }
241
242
243
244
245
246
247
248
249 @SuppressFBWarnings({
250 "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"})
251 public static int runSelftest() throws Exception {
252 return runSelftest(System.out);
253 }
254
255 private static void printStackTrace(Throwable t) {
256 t.printStackTrace();
257 }
258
259 public static int runSelftest(Writer out) throws Exception {
260 PipedInputStream pis = new PipedInputStream();
261 @SuppressWarnings("all")
262 PipedOutputStream pos = new PipedOutputStream(pis);
263 @SuppressWarnings("all")
264 PrintStream ps = new PrintStream(pos, false, Charset.defaultCharset().name());
265
266 InputStreamReader isr = new InputStreamReader(pis, Charset.defaultCharset());
267
268 Thread t = new Thread(new Runnable() {
269 @Override
270 public void run() {
271 char[] buf = new char[4096];
272 int read;
273 try {
274 while ((read = isr.read(buf)) >= 0) {
275 out.write(buf, 0, read);
276 out.flush();
277 }
278 } catch (IOException e) {
279 printStackTrace(e);
280 }
281 }
282 });
283 t.start();
284
285 return runSelftest0(ps, () -> {
286 ps.close();
287 try {
288 t.join();
289 } catch (InterruptedException e) {
290 printStackTrace(e);
291 }
292 });
293 }
294
295 @SuppressFBWarnings({
296 "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"})
297 public static int runSelftest(PrintStream out) throws Exception {
298 return runSelftest0(out, null);
299 }
300
301 private static int runSelftest0(PrintStream out, Runnable whenDone) throws Exception {
302 int rc;
303 PrintStream origSystemOut = System.out;
304 System.setOut(out);
305 try {
306 rc = runSelftest0(out);
307 } finally {
308 out.flush();
309 System.setOut(origSystemOut);
310 if (whenDone != null) {
311 whenDone.run();
312 }
313 }
314 return rc;
315 }
316
317 private static int runSelftest0(PrintStream out) throws Exception {
318 SelftestProvider sp = new SelftestProvider();
319 Selftest st = new Selftest(out, sp);
320
321 st.checkVM();
322 st.printExplanation();
323 st.dumpAdditionalProperties();
324 st.dumpSystemProperties();
325 st.dumpOSReleaseFiles();
326 st.checkSupported();
327 st.checkCapabilities();
328
329 Set<String> disabledModules = sp.modulesDisabledByDefault();
330
331 List<String> messagesAtEnd = new ArrayList<>();
332 for (Entry<String, Class<?>[]> en : sp.tests().entrySet()) {
333 String module = en.getKey();
334 if (disabledModules.contains(module)) {
335 if (SystemPropertyUtil.getBooleanSystemProperty("selftest.enable-module." + module,
336 false)) {
337
338 st.modified = true;
339 } else {
340 messagesAtEnd.add("Skipping optional module: " + module
341 + "; enable by launching with -Dselftest.enable-module." + module + "=true");
342 continue;
343 }
344 } else if (SystemPropertyUtil.getBooleanSystemProperty("selftest.disable-module." + module,
345 false)) {
346 messagesAtEnd.add("Skipping required module: " + module + "; this taints the test");
347 st.withIssues = true;
348 }
349 try {
350 st.runTests(module, en.getValue());
351 } catch (Error | RuntimeException t) {
352 messagesAtEnd.add("INTERNAL INCONSISTENCY: Unexpected error while running tests for "
353 + module + ": " + t);
354 t.printStackTrace();
355 st.fail = true;
356 }
357 }
358
359 if (!messagesAtEnd.isEmpty()) {
360 for (String m : messagesAtEnd) {
361 out.println(m);
362 }
363 }
364
365 st.checkInitError();
366 st.dumpResults();
367
368 int rc = st.isFail() ? 1 : 0;
369
370 out.flush();
371 return rc;
372 }
373
374 private void dumpAdditionalProperties() {
375 PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, Charset.defaultCharset()));
376 sp.printAdditionalProperties(pw);
377 pw.flush();
378 out.println();
379 }
380
381 public void printExplanation() throws IOException {
382 out.println(
383 "This program determines whether junixsocket is supported on the current platform.");
384 out.println("The final line should say whether the selftest passed or failed.");
385 out.println();
386 out.println(
387 "If the selftest failed, please visit https://github.com/kohlschutter/junixsocket/issues");
388 out.println("and file a new bug report with the output below.");
389 out.println();
390 out.println("junixsocket selftest version " + AFUNIXSocket.getVersion());
391
392 Map<String, String> buildProperties = new LinkedHashMap<>(retrieveBuildProperties());
393 try (InputStream in = getClass().getResourceAsStream(
394 "/META-INF/maven/com.kohlschutter.junixsocket/junixsocket-selftest/git.properties")) {
395 if (in != null) {
396 Properties props = new Properties();
397 props.load(in);
398 for (String key : new TreeSet<>(props.stringPropertyNames())) {
399 buildProperties.put(key, props.getProperty(key));
400 }
401 }
402 }
403 out.println();
404 out.println("Build properties:");
405 for (Map.Entry<String, String> en : buildProperties.entrySet()) {
406 out.println(en.getKey() + ": " + en.getValue());
407 }
408 out.println();
409 }
410
411 public void dumpSystemProperties() {
412 Map<Object, Object> map = new TreeMap<>(System.getProperties());
413
414
415
416
417
418
419
420 for (String expectedKey : new String[] {
421 "android.icu.library.version",
422 "android.icu.unicode.version",
423 "android.icu.cldr.version",
424 "ICUDebug",
425 "android.icu.text.DecimalFormat.SkipExtendedSeparatorParsing",
426 "android.icu.text.MessagePattern.ApostropheMode",
427 "sun.io.useCanonCaches",
428 "sun.io.useCanonPrefixCache",
429 "sun.stdout.encoding",
430 "sun.stderr.encoding",
431 "http.keepAlive",
432 "http.keepAliveDuration",
433 "http.maxConnections",
434 "javax.net.debug",
435 "com.sun.security.preserveOldDCEncoding",
436 "java.util.logging.manager",
437
438 "file.encoding",
439 "file.separator",
440 "line.separator",
441 "path.separator",
442 "java.boot.class.path",
443 "java.class.path",
444 "java.class.version",
445 "java.compiler",
446 "java.ext.dirs",
447 "java.home",
448 "java.io.tmpdir",
449 "java.library.path",
450 "java.vendor",
451 "java.vendor.url",
452 "java.version",
453 "java.net.preferIPv6Addresses",
454 "java.specification.version",
455 "java.specification.vendor",
456 "java.specification.name",
457 "java.vm.version",
458 "java.vm.vendor",
459 "java.vm.vendor.url",
460 "java.vm.name",
461 "java.vm.specification.version",
462 "java.vm.specification.vendor",
463 "java.vm.specification.name",
464 "os.arch",
465 "os.name",
466 "os.version",
467 "user.dir",
468 "user.home",
469 "user.language",
470 "user.region",
471 "user.variant",
472 "user.name"
473 }) {
474 if (!map.containsKey(expectedKey)) {
475 String value = System.getProperty(expectedKey);
476 if (value != null) {
477 map.put(expectedKey, value);
478 }
479 }
480 }
481
482 out.println("System properties:");
483 out.println();
484 for (Map.Entry<Object, Object> en : map.entrySet()) {
485 String key = String.valueOf(en.getKey());
486 String value = String.valueOf(en.getValue());
487 StringBuilder sb = new StringBuilder();
488 for (int i = 0; i < value.length(); i++) {
489 char c = value.charAt(i);
490 switch (c) {
491 case '\n':
492 sb.append("\\n");
493 break;
494 case '\r':
495 sb.append("\\r");
496 break;
497 case '\t':
498 sb.append("\\r");
499 break;
500 default:
501 if (c < 32 || c >= 127) {
502 sb.append(String.format(Locale.ENGLISH, "\\u%04x", (int) c));
503 }
504 sb.append(c);
505 break;
506 }
507 }
508 out.println(key + ": " + sb.toString());
509 }
510 out.println();
511 }
512
513 public void checkSupported() {
514 out.print("AFSocket.isSupported: ");
515 out.flush();
516
517 boolean isSupported = AFSocket.isSupported();
518 out.println(isSupported);
519 out.println();
520 out.flush();
521
522 if (!isSupported) {
523 out.println("FAIL: junixsocket is not supported on this platform");
524 out.println();
525 fail = true;
526 }
527
528 out.print("AFUNIXSocket.isSupported: ");
529 out.flush();
530
531 isSupportedAFUNIX = AFUNIXSocket.isSupported();
532 out.println(isSupportedAFUNIX);
533 out.println();
534 out.flush();
535
536 if (!isSupportedAFUNIX) {
537 out.println("WARNING: AF_UNIX sockets are not supported on this platform");
538 out.println();
539 withIssues = true;
540 }
541 }
542
543 public void checkCapabilities() {
544 for (AFSocketCapability cap : AFSocketCapability.values()) {
545 boolean supported = AFSocket.supports(cap);
546 (supported ? supportedCapabilites : unsupportedCapabilites).add(cap);
547 }
548 }
549
550
551
552
553
554
555 public boolean isFail() {
556 return fail;
557 }
558
559 private void checkInitError() {
560 Throwable t = retrieveInitError();
561 if (t == null) {
562 return;
563 }
564
565 important.add("The native library failed to load.");
566
567 StringWriter sw = new StringWriter();
568 PrintWriter pw = new PrintWriter(sw);
569 t.printStackTrace(pw);
570 pw.flush();
571 String ts = sw.toString();
572 String tsLower = ts.toLowerCase(Locale.ENGLISH);
573
574 if (tsLower.contains("not permitted") || ts.contains("permission")) {
575 important.add("It looks like there were some permission errors.");
576 }
577
578 if (tsLower.contains("failed to map segment")) {
579 important.add("Your temporary directory is probably mounted with \"noexec\", "
580 + "which prevents the native library from loading.");
581 important.add("see: https://github.com/kohlschutter/junixsocket/issues/99");
582 Object tmpDir = retrieveTempDir();
583 if (tmpDir == null) {
584 tmpDir = System.getProperty("java.io.tmpdir");
585 }
586 if (tmpDir != null) {
587 important.add("Temp dir: " + tmpDir);
588 }
589 important.add(
590 "You can specify a different directory using -Dorg.newsclub.net.unix.library.tmpdir=");
591 }
592 }
593
594
595
596
597
598 public void dumpResults() {
599 if (modified) {
600 important.add("Selftest was modified, for example to exclude/include certain tests.");
601 inconclusive = true;
602 }
603 if (!isSupportedAFUNIX) {
604 important.add(
605 "Environment does not support UNIX sockets, which is an important part of junixsocket.");
606
607 }
608 if (inconclusive) {
609 important.add("Selftest results may be inconclusive.");
610 }
611
612 if (withIssues) {
613 important.add("\"With issues\": "
614 + "Please carefully check the output above; the software may not be able to do what you want.");
615 }
616
617 out.println();
618 out.println("Selftest results:");
619
620 for (Map.Entry<String, ModuleResult> en : results.entrySet()) {
621 ModuleResult res = en.getValue();
622
623 String result = res == null ? null : res.result.name();
624 String extra;
625 if (res == null || ((res.result == Result.SKIP || res.result == Result.AUTOSKIP)
626 && res.throwable == null)) {
627 result = "SKIP";
628 if (res != null && res.result == Result.AUTOSKIP) {
629 extra = "(skipped automatically)";
630 } else {
631 extra = "(skipped by user request)";
632 }
633 } else if (res.summary == null) {
634 extra = res.throwable == null ? "(unknown error)" : res.throwable.toString();
635 fail = true;
636 } else {
637 TestExecutionSummary summary = res.summary;
638
639 long nSucceeded = (summary.getTestsSucceededCount() + res.getNumAbortedNonIssues());
640 extra = nSucceeded + "/" + summary.getTestsFoundCount();
641 long nSkipped = summary.getTestsSkippedCount();
642 if (nSkipped > 0) {
643 extra += " (" + nSkipped + " skipped)";
644 }
645 }
646
647 out.println(result + "\t" + en.getKey() + "\t" + extra);
648 }
649 out.println();
650
651 if (!important.isEmpty()) {
652 for (String l : important) {
653 out.println("IMPORTANT: " + l);
654 }
655 out.println();
656 }
657
658 out.println("Supported capabilities: " + supportedCapabilites);
659 out.println("Unsupported capabilities: " + unsupportedCapabilites);
660 out.println();
661
662 if (fail) {
663 out.println("Selftest FAILED");
664 } else if (inconclusive || modified) {
665 out.println("Selftest INCONCLUSIVE");
666 } else if (withIssues) {
667 out.println("Selftest PASSED WITH ISSUES");
668 } else {
669 out.println("Selftest PASSED");
670 }
671 }
672
673 private SkipMode getSkipModeForModule(String moduleName) {
674 return SkipMode.parse(System.getProperty("selftest.skip." + moduleName));
675 }
676
677 private SkipMode getSkipModeForClass(String className) {
678 SkipMode skipMode = SkipMode.parse(System.getProperty("selftest.skip." + className));
679 if (skipMode.isDeclared()) {
680 return skipMode;
681 }
682 int i = className.lastIndexOf('.');
683 if (i < 0) {
684 return SkipMode.UNDECLARED;
685 }
686
687 className = className.substring(i + 1);
688 return SkipMode.parse(System.getProperty("selftest.skip." + className));
689 }
690
691
692
693
694
695
696
697 @SuppressWarnings({"PMD.ExcessiveMethodLength", "PMD.NcssCount", "PMD.NPathComplexity"})
698 public void runTests(String module, Class<?>[] testClasses) {
699 String prefix = "Testing \"" + module + "\"... ";
700 out.markPosition();
701 out.update(prefix);
702 out.flush();
703
704 String only = System.getProperty("selftest.only", "");
705 if (!only.isEmpty()) {
706 modified = true;
707 }
708
709 boolean skipped = false;
710
711 final ModuleResult moduleResult;
712
713 SkipMode skipMode;
714
715 if ((skipMode = getSkipModeForModule(module)).isSkip()) {
716 boolean autoSkip = skipMode == SkipMode.SKIP_AUTO;
717
718 out.println("Skipping module " + module + "; skipped " + (autoSkip ? "automatically"
719 : "by user request" + (skipMode.isForce() ? " (force)" : "")));
720 if (!skipMode.isForce()) {
721 withIssues = true;
722 modified = true;
723 }
724 moduleResult = new ModuleResult(autoSkip ? Result.AUTOSKIP : Result.SKIP, null, null);
725 } else {
726 List<Class<?>> list = new ArrayList<>(testClasses.length);
727 for (Class<?> testClass : testClasses) {
728 if (testClass == null) {
729
730 continue;
731 }
732 String className = testClass.getName();
733 String simpleName = testClass.getSimpleName();
734
735 if (!only.isEmpty() && !only.equals(className) && !only.equals(simpleName)) {
736 continue;
737 }
738
739 if ((skipMode = getSkipModeForClass(className)).isSkip()) {
740 out.println("Skipping test class " + className + "; skipped by request" + (skipMode
741 .isForce() ? " (force)" : ""));
742 if (!skipMode.isForce()) {
743 modified = true;
744 withIssues = true;
745 }
746 } else {
747 list.add(testClass);
748 }
749 }
750
751 TestExecutionSummary summary = null;
752 Exception exception = null;
753 long numAbortedNonIssues = 0;
754 try {
755 SelftestExecutor ex = new SelftestExecutor(list, prefix);
756 summary = ex.execute(out);
757
758 for (Map.Entry<TestIdentifier, TestExecutionResult> en : ex.getTestsWithWarnings()
759 .entrySet()) {
760 TestIdentifier tid = en.getKey();
761 TestExecutionResult res = en.getValue();
762 Optional<Throwable> t = res.getThrowable();
763 if (!t.isPresent()) {
764 continue;
765 }
766 Throwable throwable = t.get();
767 if (throwable instanceof TestAbortedWithImportantMessageException) {
768 String key = module + ": " + ex.getTestIdentifier(tid.getParentId().get())
769 .getDisplayName() + "." + tid.getDisplayName();
770 TestAbortedWithImportantMessageException ime =
771 (TestAbortedWithImportantMessageException) t.get();
772
773 MessageType messageType = ime.messageType();
774 if (messageType.isIncludeTestInfo()) {
775 important.add(ime.getSummaryMessage() + "; " + key);
776 } else {
777 String msg = ime.getSummaryMessage();
778 if (!msg.isEmpty()) {
779 important.add(msg);
780 }
781 }
782 if (!messageType.isWithIssues()) {
783 numAbortedNonIssues++;
784 }
785 } else if (throwable instanceof TestAbortedNotAnIssueException) {
786 numAbortedNonIssues++;
787 }
788 }
789 } catch (Exception e) {
790 e.printStackTrace(out);
791 exception = e;
792 }
793
794 if (skipped) {
795 moduleResult = new ModuleResult(Result.SKIP, null, exception);
796 } else if (exception != null || summary == null) {
797 moduleResult = new ModuleResult(Result.FAIL, null, exception);
798 fail = true;
799 } else {
800 final Result result;
801 if (summary.getTestsFailedCount() > 0) {
802 result = Result.FAIL;
803 fail = true;
804 } else if (summary.getTestsFoundCount() == 0) {
805 result = Result.NONE;
806 } else if ((summary.getTestsSucceededCount() + summary.getTestsSkippedCount()
807 + numAbortedNonIssues) == summary.getTestsFoundCount()) {
808 result = Result.PASS;
809 } else if (summary.getTestsAbortedCount() > 0) {
810 result = Result.DONE;
811 withIssues = true;
812 } else {
813 result = Result.DONE;
814 }
815
816 moduleResult = new ModuleResult(result, summary, null);
817 moduleResult.numAbortedNonIssues = numAbortedNonIssues;
818 }
819 }
820 results.put(module, moduleResult);
821 }
822
823 private void dumpContentsOfSystemConfigFile(File file) {
824 if (!file.exists()) {
825 return;
826 }
827 String p = file.getAbsolutePath();
828 out.println("BEGIN contents of file: " + p);
829
830 final int maxToRead = 4096;
831 char[] buf = new char[4096];
832 int numRead = 0;
833 try (InputStreamReader isr = new InputStreamReader(new FileInputStream(file),
834 StandardCharsets.UTF_8);) {
835
836 OutputStreamWriter outWriter = new OutputStreamWriter(out, Charset.defaultCharset());
837 int read = -1;
838 boolean lastWasNewline = false;
839 while (numRead < maxToRead && (read = isr.read(buf)) != -1) {
840 numRead += read;
841 outWriter.write(buf, 0, read);
842 outWriter.flush();
843 lastWasNewline = (read > 0 && buf[read - 1] == '\n');
844 }
845 if (!lastWasNewline) {
846 out.println();
847 }
848 if (read != -1) {
849 out.println("[...]");
850 }
851 } catch (Exception e) {
852 out.println("ERROR while reading contents of file: " + p + ": " + e);
853 }
854 out.println("=END= contents of file: " + p);
855 out.println();
856 }
857
858 public void dumpOSReleaseFiles() throws IOException {
859 Set<Path> canonicalPaths = new HashSet<>();
860 for (String f : new String[] {
861 "/etc/os-release", "/etc/lsb-release", "/etc/lsb_release", "/etc/system-release",
862 "/etc/system-release-cpe",
863
864 "/etc/debian_version", "/etc/fedora-release", "/etc/redhat-release", "/etc/centos-release",
865 "/etc/centos-release-upstream", "/etc/SuSE-release", "/etc/arch-release",
866 "/etc/gentoo-release", "/etc/ubuntu-release",}) {
867
868 File file = new File(f);
869 if (!file.exists() || file.isDirectory()) {
870 continue;
871 }
872 Path p = file.toPath().toAbsolutePath();
873 for (int i = 0; i < 2; i++) {
874 if (Files.isSymbolicLink(p)) {
875 Path p2 = Files.readSymbolicLink(p);
876 if (!p2.isAbsolute()) {
877 p = new File(p.toFile().getParentFile(), p2.toString()).toPath().toAbsolutePath();
878 }
879 }
880 }
881
882 if (!canonicalPaths.add(p)) {
883 continue;
884 }
885
886 dumpContentsOfSystemConfigFile(file);
887 }
888 }
889
890 private Throwable retrieveInitError() {
891 return callStaticMethod(diagnosticsHelperClass, "initError", null);
892 }
893
894 private File retrieveTempDir() {
895 return callStaticMethod(diagnosticsHelperClass, "tempDir", null);
896 }
897
898 private Map<String, String> retrieveBuildProperties() {
899 return callStaticMethod(diagnosticsHelperClass, "buildProperties", () -> Collections
900 .emptyMap());
901 }
902
903 private static Class<?> resolveOptionalClass(String name) {
904 try {
905 return Class.forName(name);
906 } catch (Exception e) {
907 return null;
908 }
909 }
910
911 @SuppressWarnings({"unchecked", "null"})
912 private static <T> T callStaticMethod(Class<?> clazz, String methodName,
913 Supplier<T> defaultSupplier) {
914 try {
915 return (T) clazz.getMethod(methodName).invoke(null);
916 } catch (Exception e) {
917 return defaultSupplier == null ? (T) null : defaultSupplier.get();
918 }
919 }
920
921 private static final class ModuleResult {
922 private final Result result;
923 private final TestExecutionSummary summary;
924 private final Throwable throwable;
925 private long numAbortedNonIssues = 0;
926
927 ModuleResult(Result result, TestExecutionSummary summary, Throwable t) {
928 Objects.requireNonNull(result);
929 this.result = result;
930 this.summary = summary;
931 this.throwable = t;
932 }
933
934 long getNumAbortedNonIssues() {
935 return numAbortedNonIssues;
936 }
937 }
938 }