ExternalEditorTest.java revision 3339:73717a51063b
1/* 2 * Copyright (c) 2015, 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 * @test 26 * @summary Testing external editor. 27 * @bug 8080843 8143955 28 * @ignore 8080843 29 * @modules jdk.jshell/jdk.internal.jshell.tool 30 * @build ReplToolTesting CustomEditor EditorTestBase 31 * @run testng ExternalEditorTest 32 */ 33 34import java.io.BufferedWriter; 35import java.io.DataInputStream; 36import java.io.DataOutputStream; 37import java.io.IOException; 38import java.io.UncheckedIOException; 39import java.net.ServerSocket; 40import java.net.Socket; 41import java.net.SocketTimeoutException; 42import java.nio.charset.StandardCharsets; 43import java.nio.file.Files; 44import java.nio.file.Path; 45import java.nio.file.Paths; 46import java.util.concurrent.ExecutionException; 47import java.util.concurrent.Future; 48import java.util.function.Consumer; 49 50import org.testng.annotations.AfterClass; 51import org.testng.annotations.BeforeClass; 52import org.testng.annotations.Test; 53 54import static org.testng.Assert.fail; 55 56public class ExternalEditorTest extends EditorTestBase { 57 58 private static Path executionScript; 59 private static ServerSocket listener; 60 61 private DataInputStream inputStream; 62 private DataOutputStream outputStream; 63 64 @Override 65 public void writeSource(String s) { 66 try { 67 outputStream.writeInt(CustomEditor.SOURCE_CODE); 68 byte[] bytes = s.getBytes(StandardCharsets.UTF_8); 69 outputStream.writeInt(bytes.length); 70 outputStream.write(bytes); 71 } catch (IOException e) { 72 throw new UncheckedIOException(e); 73 } 74 } 75 76 @Override 77 public String getSource() { 78 try { 79 outputStream.writeInt(CustomEditor.GET_SOURCE_CODE); 80 int length = inputStream.readInt(); 81 byte[] bytes = new byte[length]; 82 inputStream.readFully(bytes); 83 return new String(bytes, StandardCharsets.UTF_8); 84 } catch (IOException e) { 85 throw new UncheckedIOException(e); 86 } 87 } 88 89 private void sendCode(int code) { 90 try { 91 outputStream.writeInt(code); 92 } catch (IOException e) { 93 throw new UncheckedIOException(e); 94 } 95 } 96 97 @Override 98 public void accept() { 99 sendCode(CustomEditor.ACCEPT_CODE); 100 } 101 102 @Override 103 public void exit() { 104 sendCode(CustomEditor.EXIT_CODE); 105 inputStream = null; 106 outputStream = null; 107 } 108 109 @Override 110 public void cancel() { 111 sendCode(CustomEditor.CANCEL_CODE); 112 } 113 114 @Override 115 public void testEditor(boolean defaultStartup, String[] args, ReplTest... tests) { 116 ReplTest[] t = new ReplTest[tests.length + 1]; 117 t[0] = a -> assertCommandCheckOutput(a, "/set editor " + executionScript, 118 assertStartsWith("| Editor set to: " + executionScript)); 119 System.arraycopy(tests, 0, t, 1, tests.length); 120 super.testEditor(defaultStartup, args, t); 121 } 122 123 private static boolean isWindows() { 124 return System.getProperty("os.name").startsWith("Windows"); 125 } 126 127 @BeforeClass 128 public static void setUpExternalEditorTest() throws IOException { 129 listener = new ServerSocket(0); 130 listener.setSoTimeout(30000); 131 int localPort = listener.getLocalPort(); 132 133 executionScript = Paths.get(isWindows() ? "editor.bat" : "editor.sh").toAbsolutePath(); 134 Path java = Paths.get(System.getProperty("java.home")).resolve("bin").resolve("java"); 135 try (BufferedWriter writer = Files.newBufferedWriter(executionScript)) { 136 if(!isWindows()) { 137 writer.append(java.toString()).append(" ") 138 .append(" -cp ").append(System.getProperty("java.class.path")) 139 .append(" CustomEditor ").append(Integer.toString(localPort)).append(" $@"); 140 executionScript.toFile().setExecutable(true); 141 } else { 142 writer.append(java.toString()).append(" ") 143 .append(" -cp ").append(System.getProperty("java.class.path")) 144 .append(" CustomEditor ").append(Integer.toString(localPort)).append(" %*"); 145 } 146 } 147 } 148 149 private Future<?> task; 150 @Override 151 public void assertEdit(boolean after, String cmd, 152 Consumer<String> checkInput, Consumer<String> checkOutput, Action action) { 153 if (!after) { 154 setCommandInput(cmd + "\n"); 155 task = getExecutor().submit(() -> { 156 try (Socket socket = listener.accept()) { 157 inputStream = new DataInputStream(socket.getInputStream()); 158 outputStream = new DataOutputStream(socket.getOutputStream()); 159 checkInput.accept(getSource()); 160 action.accept(); 161 } catch (SocketTimeoutException e) { 162 fail("Socket timeout exception.\n Output: " + getCommandOutput() + 163 "\n, error: " + getCommandErrorOutput()); 164 } catch (Throwable e) { 165 shutdownEditor(); 166 if (e instanceof AssertionError) { 167 throw (AssertionError) e; 168 } 169 throw new RuntimeException(e); 170 } 171 }); 172 } else { 173 try { 174 task.get(); 175 checkOutput.accept(getCommandOutput()); 176 } catch (ExecutionException e) { 177 if (e.getCause() instanceof AssertionError) { 178 throw (AssertionError) e.getCause(); 179 } 180 throw new RuntimeException(e); 181 } catch (Exception e) { 182 throw new RuntimeException(e); 183 } 184 } 185 } 186 187 @Override 188 public void shutdownEditor() { 189 if (outputStream != null) { 190 exit(); 191 } 192 } 193 194 @Test 195 public void setUnknownEditor() { 196 test( 197 a -> assertCommand(a, "/set editor", "| The '/set editor' command requires a path argument"), 198 a -> assertCommand(a, "/set editor UNKNOWN", "| Editor set to: UNKNOWN"), 199 a -> assertCommand(a, "int a;", null), 200 a -> assertCommand(a, "/ed 1", 201 "| Edit Error: process IO failure: Cannot run program \"UNKNOWN\": error=2, No such file or directory") 202 ); 203 } 204 205 @Test(enabled = false) 206 public void testRemoveTempFile() { 207 test(new String[]{"-nostartup"}, 208 a -> assertCommandCheckOutput(a, "/set editor " + executionScript, 209 assertStartsWith("| Editor set to: " + executionScript)), 210 a -> assertVariable(a, "int", "a", "0", "0"), 211 a -> assertEditOutput(a, "/e 1", assertStartsWith("| Edit Error: Failure read edit file:"), () -> { 212 sendCode(CustomEditor.REMOVE_CODE); 213 exit(); 214 }), 215 a -> assertCommandCheckOutput(a, "/v", assertVariables()) 216 ); 217 } 218 219 @AfterClass 220 public static void shutdown() throws IOException { 221 executorShutdown(); 222 if (listener != null) { 223 listener.close(); 224 } 225 } 226} 227