junit - 使用 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
我怎样才能使这项工作?
解决方案
这个问题相当古老,似乎已在 Gilles Gouaillardet 的评论中成功解决。在我工作的学术环境中,我还尝试将 Junit 与 Java 和 MPI 一起使用。我无法成功使用 Gilles Gouaillardet 提出的技巧,最终得到了一个完全不同的解决方案。
自定义 Junit4 跑步者 - 总体思路
使用 MPI 运行 Junit 测试的另一种方法是实现自定义 Junit 运行器。
在此自定义 Junit 运行器中,您可以使用 ProcessLauncher 启动自定义命令,而不是“直接”调用测试方法。在我的实现中,我让每个 MPI 进程都使用普通的 Junit4 运行时来运行测试方法。但是,RunNotifier
MPI 进程没有使用正常的 Junit 运行时,而是使用我的自定义RunNotifier
,它将接收到的调用写入文件。调用的文件
回到我的自定义运行器中,一旦 mpirun 进程完成,我会汇总每个 MPI 进程的每个测试方法的结果,并将这些结果传输到正常的RunNotifier
.
好处
使用这个系统,您将停留在“正常”的 Junit4 框架内。就我而言,我试图从 Maven 运行 Junit 测试。我还可以成功地将测试结果与 Eclipse Junit 视图集成(这需要一些技巧,这些技巧未在下面的代码摘录中显示)。
这是运行测试后我的 Eclipse 环境的捕获(由于我的特定环境的一些额外复杂性,类名与下面摘录中的类名略有不同)。
一些选定的代码细节
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();
}
}
推荐阅读
- c - C中的卫生宏
- cmake - 找不到 ZSTD(缺少:ZSTD_LIBRARY ZSTD_INCLUDE_DIR)
- r - R 中的 for 循环——生成和计算不同的结果
- java - 空 Recyclerview 火力基地
- javascript - React Native 箭头语法解释
- pdf - 使用 KendoPDF 在 PDF 中转换画布的质量低
- asp.net - 如何在asp.net mvc5我的网站中每分钟自动执行代码
- nginx - nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
- python-3.x - 仅将数据框中的新行添加到 csv 文件
- javascript - 在javascript中转换为base 42