| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @author Vitaly A. Provodin |
| */ |
| |
| package org.apache.harmony.jpda.tests.jdwp.share; |
| |
| import java.io.IOException; |
| import java.util.Vector; |
| |
| import org.apache.harmony.jpda.tests.framework.LogWriter; |
| import org.apache.harmony.jpda.tests.framework.StreamRedirector; |
| import org.apache.harmony.jpda.tests.framework.TestErrorException; |
| import org.apache.harmony.jpda.tests.framework.jdwp.JDWPDebuggeeWrapper; |
| import org.apache.harmony.jpda.tests.share.JPDATestOptions; |
| |
| /** |
| * This class provides basic DebuggeeWrapper implementation based on JUnit framework, |
| * which can launch and control debuggee process. |
| */ |
| public class JDWPUnitDebuggeeProcessWrapper extends JDWPDebuggeeWrapper { |
| |
| /** |
| * Target VM debuggee process. |
| */ |
| public Process process; |
| |
| protected StreamRedirector errRedir; |
| protected StreamRedirector outRedir; |
| |
| /** |
| * The expected exit code for the debuggee process. |
| */ |
| private int expectedExitCode = 0; |
| |
| /** |
| * Creates new instance with given data. |
| * |
| * @param settings |
| * test run options |
| * @param logWriter |
| * where to print log messages |
| */ |
| public JDWPUnitDebuggeeProcessWrapper(JPDATestOptions settings, LogWriter logWriter) { |
| super(settings, logWriter); |
| } |
| |
| /** |
| * Sets the expected exit code. This is meant to be used by tests that will request target |
| * VM termination with VirtualMachine.Exit command. |
| */ |
| public void setExpectedExitCode(int expectedExitCode) { |
| this.expectedExitCode = expectedExitCode; |
| } |
| |
| /** |
| * Launches process and redirects output. |
| */ |
| public void launchProcessAndRedirectors(String cmdLine) throws IOException { |
| logWriter.println("Launch process: " + cmdLine); |
| process = launchProcess(cmdLine); |
| logWriter.println("Launched process"); |
| if (process != null) { |
| logWriter.println("Start redirectors"); |
| errRedir = new StreamRedirector(process.getErrorStream(), logWriter, "STDERR"); |
| errRedir.setDaemon(true); |
| errRedir.start(); |
| outRedir = new StreamRedirector(process.getInputStream(), logWriter, "STDOUT"); |
| outRedir.setDaemon(true); |
| outRedir.start(); |
| logWriter.println("Started redirectors"); |
| } |
| } |
| |
| /** |
| * Waits for process to exit and closes output redirectors |
| */ |
| public void finishProcessAndRedirectors() { |
| if (process != null) { |
| try { |
| logWriter.println("Waiting for process exit"); |
| WaitForProcessExit(process); |
| logWriter.println("Finished process"); |
| } catch (IOException e) { |
| throw new TestErrorException("IOException in waiting for process exit: ", e); |
| } |
| |
| logWriter.println("Waiting for redirectors finish"); |
| if (outRedir != null) { |
| outRedir.exit(); |
| try { |
| outRedir.join(settings.getTimeout()); |
| } catch (InterruptedException e) { |
| logWriter.println("InterruptedException in stopping outRedirector: " + e); |
| } |
| if (outRedir.isAlive()) { |
| logWriter.println("WARNING: redirector not stopped: " + outRedir.getName()); |
| } |
| } |
| if (errRedir != null) { |
| errRedir.exit(); |
| try { |
| errRedir.join(settings.getTimeout()); |
| } catch (InterruptedException e) { |
| logWriter.println("InterruptedException in stopping errRedirector: " + e); |
| } |
| if (errRedir.isAlive()) { |
| logWriter.println("WARNING: redirector not stopped: " + errRedir.getName()); |
| } |
| } |
| logWriter.println("Finished redirectors"); |
| } |
| } |
| |
| /** |
| * Launches process with given command line. |
| * |
| * @param cmdLine |
| * command line |
| * @return associated Process object or null if not available |
| * @throws IOException |
| * if error occurred in launching process |
| */ |
| protected Process launchProcess(String cmdLine) throws IOException { |
| |
| // Runtime.exec(String) does not preserve quoted arguments |
| // process = Runtime.getRuntime().exec(cmdLine); |
| |
| String args[] = splitCommandLine(cmdLine); |
| process = Runtime.getRuntime().exec(args); |
| return process; |
| } |
| |
| /** |
| * Splits command line into arguments preserving spaces in quoted arguments |
| * either with single and double quotes (not prefixed by '\'). |
| * |
| * @param cmdLine |
| * command line |
| * @return associated Process object or null if not available |
| * @throws IOException |
| * if error occurred in launching process |
| */ |
| /* |
| public String[] splitCommandLine(String cmd) { |
| |
| // allocate array for parsed arguments |
| int max_argc = 250; |
| Vector argv = new Vector(); |
| |
| // parse command line |
| int len = cmd.length(); |
| if (len > 0) { |
| for (int arg = 0; arg < len;) { |
| // skip initial spaces |
| while (Character.isWhitespace(cmd.charAt(arg))) arg++; |
| // parse non-spaced or quoted argument |
| for (int p = arg; ; p++) { |
| // check for ending separator |
| if (p >= len || Character.isWhitespace(cmd.charAt(p))) { |
| if (p > len) p = len; |
| String val = cmd.substring(arg, p); |
| argv.add(val); |
| arg = p + 1; |
| break; |
| } |
| |
| // check for starting quote |
| if (cmd.charAt(p) == '\"') { |
| char quote = cmd.charAt(p++); |
| // skip all chars until terminating quote or end of line |
| for (; p < len; p++) { |
| // check for terminating quote |
| if (cmd.charAt(p) == quote) |
| break; |
| // skip escaped quote |
| if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == quote) |
| p++; |
| } |
| } |
| |
| // skip escaped quote |
| if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == '\"') { |
| p++; |
| } |
| } |
| } |
| } |
| |
| logWriter.println("Splitted command line: " + argv); |
| int size = argv.size(); |
| String args[] = new String[size]; |
| return (String[])argv.toArray(args); |
| } |
| */ |
| public String[] splitCommandLine(String cmd) { |
| |
| int len = cmd.length(); |
| char chars[] = new char[len]; |
| Vector<String> argv = new Vector<String>(); |
| |
| if (len > 0) { |
| for (int arg = 0; arg < len;) { |
| // skip initial spaces |
| while (Character.isWhitespace(cmd.charAt(arg))) arg++; |
| // parse non-spaced or quoted argument |
| for (int p = arg, i = 0; ; p++) { |
| // check for starting quote |
| if (p < len && (cmd.charAt(p) == '\"' || cmd.charAt(p) == '\'')) { |
| char quote = cmd.charAt(p++); |
| // copy all chars until terminating quote or end of line |
| for (; p < len; p++) { |
| // check for terminating quote |
| if (cmd.charAt(p) == quote) { |
| p++; |
| break; |
| } |
| // preserve escaped quote |
| if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == quote) |
| p++; |
| chars[i++] = cmd.charAt(p); |
| } |
| } |
| |
| // check for ending separator |
| if (p >= len || Character.isWhitespace(cmd.charAt(p))) { |
| String val = new String(chars, 0, i); |
| argv.add(val); |
| arg = p + 1; |
| break; |
| } |
| |
| // preserve escaped quote |
| if (cmd.charAt(p) == '\\' && (p+1) < len |
| && (cmd.charAt(p+1) == '\"' || cmd.charAt(p+1) == '\'')) { |
| p++; |
| } |
| |
| // copy current char |
| chars[i++] = cmd.charAt(p); |
| } |
| } |
| } |
| |
| logWriter.println("Splitted command line: " + argv); |
| int size = argv.size(); |
| String args[] = new String[size]; |
| return (String[])argv.toArray((String[])args); |
| } |
| |
| /** |
| * Waits for launched process to exit. |
| * |
| * @param process |
| * associated Process object or null if not available |
| * @throws IOException |
| * if any exception occurs in waiting |
| */ |
| protected void WaitForProcessExit(Process process) throws IOException { |
| ProcessWaiter thrd = new ProcessWaiter(); |
| thrd.setDaemon(true); |
| thrd.start(); |
| try { |
| thrd.join(settings.getTimeout()); |
| } catch (InterruptedException e) { |
| throw new TestErrorException(e); |
| } |
| |
| if (thrd.isAlive()) { |
| // ProcessWaiter thread is still running (after we wait until a timeout) but is |
| // waiting for the debuggee process to exit. We send an interrupt request to |
| // that thread so it receives an InterrupedException and terminates. |
| thrd.interrupt(); |
| } |
| |
| try { |
| int exitCode = process.exitValue(); |
| logWriter.println("Finished debuggee with exit code: " + exitCode); |
| if (exitCode != expectedExitCode) { |
| throw new TestErrorException("Debuggee exited with code " + exitCode + |
| " but we expected code " + expectedExitCode); |
| } |
| } catch (IllegalThreadStateException e) { |
| logWriter.printError("Terminate debuggee process"); |
| throw new TestErrorException("Debuggee process did not finish during timeout", e); |
| } finally { |
| // dispose any resources of the process |
| process.destroy(); |
| } |
| } |
| |
| /** |
| * Separate thread for waiting for process exit for specified timeout. |
| */ |
| class ProcessWaiter extends Thread { |
| public void run() { |
| try { |
| process.waitFor(); |
| } catch (InterruptedException e) { |
| logWriter.println("Ignoring exception in ProcessWaiter thread interrupted: " + e); |
| } |
| } |
| } |
| |
| public void start() { |
| } |
| |
| public void stop() { |
| } |
| } |