1 /* 2 * Copyright (c) 2019, 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 package jdk.jpackage.test; 24 25 import java.io.IOException; 26 import java.io.PrintWriter; 27 import java.io.StringWriter; 28 import java.nio.file.Files; 29 import java.nio.file.Path; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.Collections; 33 import java.util.List; 34 import java.util.spi.ToolProvider; 35 import java.util.stream.Stream; 36 37 public final class Executor extends CommandArguments<Executor> { 38 39 public Executor() { 40 saveOutputType = SaveOutputType.NONE; 41 } 42 43 public Executor setExecutable(String v) { 44 executable = v; 45 if (executable != null) { 46 toolProvider = null; 47 } 48 return this; 49 } 50 51 public Executor setToolProvider(ToolProvider v) { 52 toolProvider = v; 53 if (toolProvider != null) { 54 executable = null; 55 } 56 return this; 57 } 58 59 public Executor setDirectory(Path v) { 60 directory = v; 61 return this; 62 } 63 64 public Executor setExecutable(JavaTool v) { 65 return setExecutable(v.getPath().getAbsolutePath()); 66 } 67 68 public Executor saveOutput() { 69 saveOutputType = SaveOutputType.FULL; 70 return this; 71 } 72 73 public Executor saveFirstLineOfOutput() { 74 saveOutputType = SaveOutputType.FIRST_LINE; 75 return this; 76 } 77 78 public class Result { 79 80 Result(int exitCode) { 81 this.exitCode = exitCode; 82 } 83 84 public String getFirstLineOfOutput() { 85 return output.get(0).trim(); 86 } 87 88 public List<String> getOutput() { 89 return output; 90 } 91 92 public String getPrintableCommandLine() { 93 return Executor.this.getPrintableCommandLine(); 94 } 95 96 public Result assertExitCodeIs(int expectedExitCode) { 97 Test.assertEquals(expectedExitCode, exitCode, String.format( 98 "Check command %s exited with %d code", 99 getPrintableCommandLine(), expectedExitCode)); 100 return this; 101 } 102 103 public Result assertExitCodeIsZero() { 104 return assertExitCodeIs(0); 105 } 106 107 final int exitCode; 108 private List<String> output; 109 } 110 111 public Result execute() { 112 if (toolProvider != null) { 113 return runToolProvider(); 114 } 115 116 try { 117 if (executable != null) { 118 return runExecutable(); 119 } 120 } catch (RuntimeException e) { 121 throw e; 122 } catch (IOException | InterruptedException e) { 123 throw new RuntimeException(e); 124 } 125 126 throw new IllegalStateException("No command to execute"); 127 } 128 129 private Result runExecutable() throws IOException, InterruptedException { 130 List<String> command = new ArrayList<>(); 131 command.add(executable); 132 command.addAll(args); 133 Path outputFile = null; 134 ProcessBuilder builder = new ProcessBuilder(command); 135 StringBuilder sb = new StringBuilder(getPrintableCommandLine()); 136 if (saveOutputType != SaveOutputType.NONE) { 137 outputFile = Test.createTempFile(".out"); 138 builder.redirectErrorStream(true); 139 builder.redirectOutput(outputFile.toFile()); 140 sb.append(String.format("; redirect output to [%s]", outputFile)); 141 } 142 if (directory != null) { 143 builder.directory(directory.toFile()); 144 sb.append(String.format("; in directory [%s]", directory)); 145 } 146 147 try { 148 Test.trace("Execute " + sb.toString() + "..."); 149 Process process = builder.start(); 150 Result reply = new Result(process.waitFor()); 151 Test.trace("Done. Exit code: " + reply.exitCode); 152 if (saveOutputType == SaveOutputType.FIRST_LINE) { 153 reply.output = Arrays.asList( 154 Files.readAllLines(outputFile).stream().findFirst().get()); 155 } else if (saveOutputType == SaveOutputType.FULL) { 156 reply.output = Collections.unmodifiableList(Files.readAllLines( 157 outputFile)); 158 } 159 return reply; 160 } finally { 161 if (outputFile != null) { 162 Files.deleteIfExists(outputFile); 163 } 164 } 165 } 166 167 private Result runToolProvider() { 168 StringWriter writer = new StringWriter(); 169 PrintWriter pw = new PrintWriter(writer); 170 171 Test.trace("Execute " + getPrintableCommandLine() + "..."); 172 Result reply = new Result(toolProvider.run(pw, pw, args.toArray( 173 String[]::new))); 174 Test.trace("Done. Exit code: " + reply.exitCode); 175 176 List lines = List.of(writer.toString().split("\\R", -1)); 177 178 if (saveOutputType == SaveOutputType.FIRST_LINE) { 179 reply.output = Stream.of(lines).findFirst().get(); 180 } else if (saveOutputType == SaveOutputType.FULL) { 181 reply.output = Collections.unmodifiableList(lines); 182 } 183 return reply; 184 } 185 186 public String getPrintableCommandLine() { 187 String argsStr = String.format("; args(%d)=%s", args.size(), 188 Arrays.toString(args.toArray())); 189 190 if (toolProvider == null && executable == null) { 191 return "[null]; " + argsStr; 192 } 193 194 if (toolProvider != null) { 195 return String.format("tool provider=[%s]; ", toolProvider.name()) + argsStr; 196 } 197 198 return String.format("[%s]; ", executable) + argsStr; 199 } 200 201 private ToolProvider toolProvider; 202 private String executable; 203 private SaveOutputType saveOutputType; 204 private Path directory; 205 206 private static enum SaveOutputType { 207 NONE, FULL, FIRST_LINE 208 }; 209 }