Timed Input Delivery Methods for Multi-Threaded Interactive Console Applications
Manual Console Input
Overview
Directly type interactive elevator requests into the terminal during program execution.
Implementation
N/A.
Tradeoffs
Pros: Zero setup, no external tools required. Cons: Impossible to time precisely, inefficient for large test suites, and typing gets disrupted by interleaved program stdout. Recommendation: Not recommended for structured testing.
Timed Output Generator + Unix/Windows Pipeline
Overview
Create a lightweight input scheduler that parses timestamped test data and emits lines at specified offsets, then pipe its output directly into the target applicasion.
Implementation
Scheduler Program
This tool reads timestamped input lines either from stdin or a file, waits the required duration from the last emission, and flushes each line immediately to avoid buffering delays.
// timed_sender.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#define sleep_ms(ms) Sleep((DWORD)(ms))
#else
#include <unistd.h>
#define sleep_ms(ms) usleep((useconds_t)(ms) * 1000)
#endif
#define MAX_LINE_LENGTH 1024
int main(void) {
char line_buffer[MAX_LINE_LENGTH];
long next_offset_ms;
long last_offset = 0;
while (fgets(line_buffer, MAX_LINE_LENGTH, stdin) != NULL) {
char *content_ptr;
next_offset_ms = strtol(line_buffer, &content_ptr, 10);
if (*content_ptr == ':') {
content_ptr++;
}
long wait_ms = next_offset_ms - last_offset;
if (wait_ms > 0) {
sleep_ms(wait_ms);
}
fputs(content_ptr, stdout);
fflush(stdout);
last_offset = next_offset_ms;
}
return 0;
}
Test Input Format
Each line starts with an integer offset in milliseconds from program start, followed by a colon and the actual input line. Example:
0:Initialize
2000:Elevator request 1-5
3200:Elevator request 3-2
5500:Shutdown
Execution Command
Compile the scheduler, prepare a test file, and pipe them together:
# Linux/macOS
./timed_sender < test_cases.txt | java -jar elevator_system.jar
# Windows Command Prompt
timed_sender.exe < test_cases.txt | java -jar elevator_system.jar
Tradeoffs
Pros: Fully scriptable, ideal for automated regression and batch testing. Cons: Cannot use IDE breakpoints easily, requires a precompiled JAR or separate CLI setup. Recommendation: Use for continuous local testing or peer review automation.
Key Notes
Always call fflush(stdout) immediately after writing each line to prevent pipeline buffering, which would delay all inputs until the scheduler exits. On Unix-like systems, use ./ to execute the compiled binary if it’s not in you're PATH.
Java Timer + Piped Streams
Overview
Embed a debug input handler directly into your Java application using Timer for scheduling and PipedInputStream/PipedOutputStream to mimic standard input without leaving the IDE.
Implementation
Core Concepts
- Timer & TimerTask: Schedule input lines to be written at precise timestamps relative to program launch.
- Piped Streams: Create a thread-safe communication channel between your debug handler and the official input class;
PipedOutputStreamwrites data,PipedInputStreamreads it.
Step-by-Step Setup
- Initialize a connected pair of piped streams:
import java.io.*;
public class Main {
private static final boolean USE_DEBUG_INPUT = true; // Toggle before submission!
public static void main(String[] args) throws IOException {
PipedInputStream debugIn = new PipedInputStream();
PipedOutputStream debugOut = new PipedOutputStream(debugIn);
ElevatorInput inputSource;
if (USE_DEBUG_INPUT) {
inputSource = new ElevatorInput(debugIn);
new Thread(new DebugInputFeeder(debugOut)).start();
} else {
inputSource = new ElevatorInput(System.in);
}
// Start main application threads with inputSource
}
}
- Create the
DebugInputFeederclass to parse timestamped input and schedule writes:
import java.util.*;
import java.util.regex.*;
public class DebugInputFeeder implements Runnable {
private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("^\\[(\\d+(?:\\.\\d+)?)\\](.*)$");
private final OutputStream targetStream;
public DebugInputFeeder(OutputStream stream) {
this.targetStream = stream;
}
@Override
public void run() {
Timer scheduler = new Timer("Debug-Input-Timer", true);
long programStart = System.currentTimeMillis();
List<TimerTask> cleanupTasks = new ArrayList<>();
try (Scanner fileScanner = new Scanner(System.in)) {
while (fileScanner.hasNextLine()) {
String rawLine = fileScanner.nextLine().trim();
if (rawLine.isEmpty()) continue;
Matcher matcher = TIMESTAMP_PATTERN.matcher(rawLine);
if (!matcher.matches()) continue;
double offsetSec = Double.parseDouble(matcher.group(1));
long offsetMs = (long) (offsetSec * 1000);
String payload = matcher.group(2).trim() + "\n";
TimerTask writeTask = new TimerTask() {
@Override
public void run() {
try {
targetStream.write(payload.getBytes());
targetStream.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
};
scheduler.schedule(writeTask, new Date(programStart + offsetMs));
cleanupTasks.add(writeTask);
}
}
// Schedule cleanup after the last payload
if (!cleanupTasks.isEmpty()) {
TimerTask lastTask = cleanupTasks.get(cleanupTasks.size() - 1);
scheduler.schedule(new TimerTask() {
@Override
public void run() {
try {
targetStream.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
scheduler.cancel();
}
}, new Date(programStart + 2000)); // Add extra buffer
}
}
}
- Configure IDE input redirection (e.g., IntelliJ IDEA: Run → Edit Configurations → Modify options → Redirect input from file).
Test Input Format (Regex-Enhanced)
[0.0]InitializeSystem
[1.2]1-FROM-1-TO-10
[2.7]2-FROM-5-TO-2
[4.0]Terminate
Tradeoffs
Pros: Full IDE debugging suport, works with both direct file input and stdin, no external binaries needed. Cons: Requires adding debug-only code to your project (toggle the switch off before submission!). Recommendation: Ideal for iterative development and breakpoint debugging.