来自Runtime.getRuntime()。exec()的IOException未被Android JUnit

时间:2015-11-22 15:42:26

标签: java android junit

我正在尝试测试某些需要在Android设备上拥有root权限的命令的执行。没有超级用户应用程序,我收到permission denied错误,导致尝试写入进程的输出流时抛出IOException,这很好。

但是,该异常虽然在使用调试器时可以看到,但是JUnit既不会拦截该异常(因此测试以错误结束),也不会被调试器检测到暂停执行。

异常在哪里?如何修复测试,以便异常导致JUnit中的错误?

这是我的测试:

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
}

测试中的代码:

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");
}

更新:调试时我可以看到以下行为。离开finally区块后,我位于org.junit.internal.runners.model.ReflectveCallable#run()的一个区块中,其中InvokationTargetException的原因是IOException。然后它会传播到Runner的结构上,EachTestNotifier#addFailure消耗它,它似乎报告失败,但事实并非如此。

1 个答案:

答案 0 :(得分:0)

您可以在try/catch调用的区域周围放置一个SudoHelper.sudoForResult(PWD)块。否则,您和JUnit不会处理异常,因为JUnit在调用testSudoForResult方法时不会处理异常。程序将崩溃,因此代码在&#34;未处理的&#34;之后代码不会运行。

要解决此问题,代码可能看起来更像这样:

@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);
}