首页 > 解决方案 > 使用 ant 使用 mpirun 运行单元测试

问题描述

我正在尝试使用 ant 通过 mpirun 运行我的单元测试。我已将任务指定为:

<target name="unitTest" depends="buildUnitTest">
    <mkdir dir="reports"/>
    <junit fork="yes" jvm="mpirun java" printsummary="yes" haltonfailure="yes">
        <classpath>
            <pathelement location="./bin"/>
            <pathelement location="/usr/share/java/junit4.jar"/>
        </classpath>
        <jvmarg value="-DDIM=3"/>
        <jvmarg value="-ea"/>
        <formatter type="plain"/>
        <batchtest todir="reports">
            <fileset dir="test">
                <include name="haparanda/utils/*Test.java"/>
                <include name="haparanda/iterators/*Test.java"/>
                <exclude name="haparanda/iterators/FieldIteratorTest.java"/>
                <include name="haparanda/grid/*Test.java"/>
            </fileset>
        </batchtest>
    </junit>
</target>

运行例如:

 mpirun java -ea -DDIM=3 -cp ./bin:/usr/share/java/junit4.jar org.junit.runner.JUnitCore haparanda.grid.ComputationalComposedBlockTest

从命令行工作正常。但是,当我运行时:

ant unitTest

我收到以下错误:

BUILD FAILED
.../build.xml:28: Process fork failed.

运行带有详细标志的 ant,我被告知我收到带有错误消息的 IOException:

Cannot run program "mpirun java": error=2, No such file or directory

当我指定 mpirun 和 Java 的完整路径时也是这种情况:

<junit fork="yes" jvm="/home/malin/bin/openmpi/bin/mpirun /usr/bin/java" printsummary="yes" haltonfailure="yes">

给我:

.../build.xml:28: Process fork failed.
at ...
Caused by: java.io.IOException: Cannot run program "/home/malin/bin/openmpi/bin/mpirun /usr/bin/java": error=2, No such file or directory

我怎样才能使这项工作?

标签: junitantmpi

解决方案


这个问题相当古老,似乎已在 Gilles Gouaillardet 的评论中成功解决。在我工作的学术环境中,我还尝试将 Junit 与 Java 和 MPI 一起使用。我无法成功使用 Gilles Gouaillardet 提出的技巧,最终得到了一个完全不同的解决方案。

自定义 Junit4 跑步者 - 总体思路

使用 MPI 运行 Junit 测试的另一种方法是实现自定义 Junit 运行器。

在此自定义 Junit 运行器中,您可以使用 ProcessLauncher 启动自定义命令,而不是“直接”调用测试方法。在我的实现中,我让每个 MPI 进程都使用普通的 Junit4 运行时来运行测试方法。但是,RunNotifierMPI 进程没有使用正常的 Junit 运行时,而是使用我的自定义RunNotifier,它将接收到的调用写入文件。调用的文件

回到我的自定义运行器中,一旦 mpirun 进程完成,我会汇总每个 MPI 进程的每个测试方法的结果,并将这些结果传输到正常的RunNotifier.

好处

使用这个系统,您将停留在“正常”的 Junit4 框架内。就我而言,我试图从 Maven 运行 Junit 测试。我还可以成功地将测试结果与 Eclipse Junit 视图集成(这需要一些技巧,这些技巧未在下面的代码摘录中显示)。

这是运行测试后我的 Eclipse 环境的捕获(由于我的特定环境的一些额外复杂性,类名与下面摘录中的类名略有不同)。

eclipse_junitview

一些选定的代码细节

MpiRunner仅显示自定义和最重要的部分MpiTestLauncher。导入、try/catch 结构和许多细节已被删除。我最终会在 GitHub 上提供整个代码,但它还没有完全准备好。

/** A test class using the custom "MpiRunner" */
@RunWith(MpiRunner.class)
public class TestUsingMpi {
    @Test
    public void test() {
        assertTrue("Should run with multiple processes", MPI.COMM_WORLD.Size() > 1);
    }

}
/** Curstom Junit4 Runner */
public class MpiRunner extends Runner {
    // some methods skipped, try/catch blocks have been removed
    @Override
    public void run(RunNotifier notifier) {
        // Build the command
        final ArrayList<String> command = new ArrayList<>();
        command.add("mpirun");
        command.add("-np");
        command.add(String.valueOf(processCount));
        command.add("java");
        // Classpath, UserDirectory, JavaLibraryPath ...
        command.add("MpiTestLauncher "); // Class with main 
        command.add(testClass.getCanonicalName()); // Class under test as argument
        ProcessBuilder pb = new ProcessBuilder(command);
        File mpirunOutFile = new File("MpirunCommandOutput.txt"); 
        pb.redirectOutput(Redirect.appendTo(mpirunOutFile));
        pb.redirectError(Redirect.appendTo(mpirunOutFile));

        Process p = pb.start(); // Launch the mpirun command
        p.waitFor(); // Wait for termination

        // Parse the notifications of each MPI process
        for (int i = o; i < NbProcesses; i++) {
            List<Notification> mpiRankNotifs = parse(i);
            //Re-run those notifications on the parameter "notifier" of this method
            for (Notification n : notifications) {
                //Reconstitute the method call made in the mpi process
                Class<?> paramClass = n.parameters[0].getClass();
                Method m = RunNotifier.class.getDeclaredMethod(n.method, paramClass);
                m.invoke(notifier, n.parameters);
            }
        }
    }
}

/** Main class of the Mpirun java processes */
public class MpiTestLauncher {
    public static void main(String[] args) throws Exception {
        MPI.Init(args);
        commRank = MPI.COMM_WORLD.Rank();
        commSize = MPI.COMM_WORLD.Size();

        Class<?> testClass = Class.forName(args[0]); // Class that contains the tests
        String notificationFileName = testClass.getCanonicalName() + "_" + 
              commRank;
        File f = new File(notificationFileName);
        CustomNotifier notifier = new MpiApgasRunNotifier(f);
        BlockJUnit4ClassRunner junitDefaultRunner = new BlockJUnit4ClassRunner(testClass);
        junitDefaultRunner.run(notifier);
        notifier.close(); //Flushes the underlying buffer

        MPI.Finalize();
    }
}


推荐阅读