简体   繁体   中英

IOException from Runtime.getRuntime().exec() not caught by Android JUnit

I'm trying to test the execution of some commands requiring root permissions on an Android device. With no Superuser app, I get a permission denied error, resulting in an IOException thrown on an attempt to write to the process' output stream, which is fine.

However, that exception, though visible when stepping over with a debugger, is neither intercepted by JUnit (so the test ends with an error), nor can be detected by the debugger in order to pause the execution.

Where is the exception gone? How can I fix the test so that the exception causes an error in JUnit?

Here is my test:

String[] PWD = new String[] { "pwd" };
@Test
public void testSudoForResult() throws IOException {
// this should throw IOException:
    String result = SudoHelper.sudoForResult(PWD); 
// these assertions are evaluated though they should not be, due to an error in the line above 
    assertNotNull(result); 
    assertTrue(result.length() > 0); // this assertion fails
}

And the code under the test:

public static String sudoForResult(String... commands) throws IOException {
        List<String> allCommands = new ArrayList<>(commands.length+1);
        allCommands.addAll(Arrays.asList(commands));
        allCommands.add("exit");
        return execForResult("/system/xbin/su", allCommands.toArray(new String[0]));
    }

private static String execForResult(String command, String... strings) throws IOException {
    String res = "";
    DataOutputStream outputStream = null;
    InputStream response = null;
    InputStream errorStream = null;
    String s = null;
    try {
        Process su = Runtime.getRuntime().exec(command);
        response = su.getInputStream();
        errorStream = su.getErrorStream();
        if (strings != null) {
           outputStream = new DataOutputStream(su.getOutputStream());
           for (int i = 0; i < strings.length; i++) {
                s = strings[i];
                outputStream.writeBytes(s + "\n");
                outputStream.flush();
            }
        }
        try {
            su.waitFor();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        res = readFully(response);
    } catch (IOException e) {
// I can step in here with a debugger, but it won't suspend when I put a breakpoint here
// The exception won't get caught by JUnit, even if I remove this catch block.
        if (errorStream != null) {
// As expected, this will contain the message "Permission denied", due to the lack of Superuser app in my test case:
            String errors = readFully(errorStream);
            Log.e(TAG, "Error while processing command:" + s +": "+errors, e);
        }
        throw e;
    } finally {
        FileUtils.quietlyClose(outputStream);
        FileUtils.quietlyClose(response);
    }
    return res;
}

public static String readFully(InputStream is) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length = 0;
    while ((length = is.read(buffer)) != -1) {
        baos.write(buffer, 0, length);
    }
    return baos.toString("UTF-8");
}

UPDATE: I can see the following behaviour while debugging. After leaving the finally block, I am in a catch block of org.junit.internal.runners.model.ReflectveCallable#run() , with an InvokationTargetException , whose cause is my IOException . It then gets propagated up the structure of Runner 's, where it is consumed by EachTestNotifier#addFailure , which seems to report the failure, but it doesn't.

You could just put a try/catch block around the area where you call SudoHelper.sudoForResult(PWD) . Otherwise, the exception is not handled by you and by JUnit, because JUnit doesn't handle the exception when the testSudoForResult method is called. The program will crash, thus the code after the "unhandled" code will not run.

To fix this, the code would probably look more like this:

@Test
public void testSudoForResult() /* no throwing exceptions here! */ {
    String result = "";
    try {
         result = SudoHelper.sudoForResult(PWD);
    } catch(IOException ex) {
         //handle exception however you want, maybe with e.printStackTrace()
    }

    assertNotNull(result); 
    assertTrue(result.length() > 0);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM