提问人:ehbong 提问时间:8/28/2023 最后编辑:Tsyvarevehbong 更新时间:8/28/2023 访问量:84
如何在java测试代码中使用多线程在scanner类方法中自动完成输入?
How to autocomplete input in scanner class method using multithreading in java test code?
问:
我正在尝试编写一个 Java 测试源。 如何处理在多线程环境中通过 Scanner 类方法接收输入的测试目标的自动输入?
class SystemInInterceptor extends InputStream {
private String input;
private int position = 0;
public SystemInInterceptor(String input) {
this.input = input + "\n"; // Add a newline at the end to simulate Enter key
}
@Override
public int read() {
if (position >= input.length()) {
return -1;
}
return input.charAt(position++);
}
}
Thread[] threads = new Thread[5];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
try {
String testInput = "o\n611019\n2\n748943\n8\n \nq\n";
InputStream inputStream = new SystemInInterceptor(testInput);
System.setIn(inputStream);
App.main(null);
} catch (SoldOutException e) {
throw new RuntimeException(e);
}
}
});
}
for(Thread thread: threads){
thread.start();
}
for(Thread thread: threads){
thread.join();
}
当我运行上面的源代码时,第一个线程工作正常,但其他线程会抛出
Exception in thread "Thread-0" Exception in thread "Thread-2" Exception in thread "Thread-1" java.util.NoSuchElementException: No line found
它在非多线程情况下工作正常。
答:
1赞
gthanop
8/28/2023
#1
在我所知道的简单情况下,一个进程只能有一个输入流,以及一个输出流和一个错误流。因此,如果您无法控制哪些内容,或者您不想修改它,那么您可以创建多个进程。App.main
想象一下,多次执行以下操作:
- 打开 CLI 实例。
- 执行您想要的可运行程序(据我所知,从您的问题中调用的程序),但使用重定向的输入流。
App.main
您可以使用 以编程方式执行此操作。java.lang.Process
我知道进程比线程重(就资源消耗而言),但如果您的方案很简单(例如,您只需要并行的几个进程),那么您可以尝试以下操作:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
public class MultiSystemIn {
private static class ExecuteProcessRunnable implements Runnable {
private final String input;
private final List<String> command;
public ExecuteProcessRunnable(final List<String> command,
final String input) {
this.input = Objects.requireNonNull(input);
if (command == null || command.isEmpty())
throw new IllegalArgumentException("Empty command.");
this.command = new ArrayList<>(command);
}
@Override
public void run() {
final ProcessBuilder builder = new ProcessBuilder(command);
Process process = null;
try {
//Start the command:
process = builder.start();
//process.getOutputStream() is what the process will be reading as its own System.in...
try (final OutputStreamWriter osw = new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8)) {
osw.write(input);
}
//process.getErrorStream() is what we can read as the corresponding System.err of the command.get(0) program...
try (final InputStreamReader isr = new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8);
final BufferedReader br = new BufferedReader(isr)) {
for (String errline = br.readLine(); errline != null; errline = br.readLine())
System.out.println("Process " + command + " error stream says: " + errline);
}
}
catch (final IOException ioe) {
System.err.println("Process " + command + " failed with " + ioe);
}
finally {
if (process != null) {
process.destroyForcibly();
try {
process.waitFor();
}
catch (final InterruptedException ie) {
}
}
}
}
}
public static void main(final String[] args) {
for (int i = 0; i < 10; ++i) {
System.out.println("Starting process " + i + "...");
final Thread t = new Thread(new ExecuteProcessRunnable(
Arrays.asList("java", "App"),
"o\n611019\n2\n748943\n8\n \nq\n"
));
t.setDaemon(false);
t.start();
}
}
}
上面的代码也使用线程,其中每个线程对应于管理其中一个进程(例如中继错误流,写入进程的标准输入并等待进程终止)。
默认情况下,以这种方式创建的进程使用管道作为其标准输入流,但管道没有无限的缓冲区(您必须在某个时候从它们读取,以便在缓冲区已满的情况下能够再次写入它们)。这就是我也使用线程的原因,因为如果一个人在没有线程的情况下做同样的事情(即只是在创建和启动 es 时运行一个循环,然后写入它们的流),那么如果输入由于某种原因变得太大,那么管道可能会阻塞,不让其他进程启动。main
Process
String
您也可以通过重定向带有文件的流来做到这一点,而不是手动写入每个进程的流。还可以进行一些其他自定义(只需阅读 和 的文档)。Process
ProcessBuilder
评论
0赞
ehbong
8/28/2023
此方法还将测试目标作为独立进程运行,因此不适合对与使用多线程时相同的值进行并发测试。但感谢您的真诚回复。
0赞
gthanop
8/28/2023
啊,好吧!事实上,我不知道您需要在通话之间共享状态。人们也许可以使用IPC(以防他们使用进程),但是我现在明白多处理不是您要做的事情。App.main
评论
App.main(null)
System.setIn
InputStream
.position
.position