1

The important word in my question is interactive: for a programming contest (UVa online judge), I'm writing interactive java code : it outputs on System.out and waits for responses on System.in.

I want to test the speed of this code, but if I do the interaction manually, my human typing skills are slowing down the execution and the measurement is biased.

Thus, I want a thread/app/script/whatever that sees when my application writes on System.out and (this thread/app/script/whatever) then writes something that is read by my application using its System.in.

My application should communicate using System.in and System.out because that's how it will be judged once I submitted it to the online judge.

I think multithreading wouldn't do the job because System.in is always read from keyboard, not from another thread.

import java.util.Scanner;

public class Main {

    public static void main(final String[] args) {
        System.out.println("What now?");
        final Scanner scanner = new Scanner(System.in);
        final String response = scanner.nextLine();
        scanner.close();
        System.out.println("Finished: " + response);
    }
}

How can I make this code run without a human typing on the keyboard?

trapangle
  • 11
  • 3
  • 3
    The speed of this code is basically irrelevant. The time will be dominated by JVM startup time. – Andy Turner May 17 '19 at 07:55
  • It's not this code I want to test. This is an example so that you understand what I want to do. My code is much longer and has several System.in calls. – trapangle May 17 '19 at 07:58
  • 1
    If you are using linux. you can use this command to check time taken like this: `time java MyClass < put_input_in_this_file.txt` – Saket Patel May 17 '19 at 07:58
  • Also if you want to test the performance of code, you'd measure only relevant business logic. Measuring everything from top to bottom doesn't make sense. Especially when user input is expected, because in theory that could take infinite time. – QBrute May 17 '19 at 07:59
  • @Saket Santosh Patel that might do it. I'm just not sure if it will work because using your method, I can't decide when each line is read from `put_input_in_this_file.txt`. I'll see if it works this evening. – trapangle May 17 '19 at 08:05
  • @QBrute my code performance will be measured from top to bottom by the UVa online judge. – trapangle May 17 '19 at 08:07
  • @trapangle *I can't decide when each line is read* - what do you mean? Each line will be read as you make your calls to `Scanner`. The file won't be read by your application if you don't do that explicitly, so if you have 3 lines in that file and you call `nextLine()` 3 times in your whole app, you'll read these three lines exactly when the call occurs. Giving the app the whole input at once won't make it consume everything in one single call. – BackSlash May 17 '19 at 08:07
  • @BackSlash maybe my code is greedy and reads all input that's available at one moment, then later expects to receive more data. It's greedy if I cannot know in advance how many lines I'm supposed to read and have to read until a line tells me to stop reading. – trapangle May 17 '19 at 08:10
  • I think maybe you need to give more specific examples of the expected input. From what you've said here, I think @SaketSantoshPatel's answer is very good, and probably similar to what the online judge will do. Presumably you either know the exact input that will be provided, or at least the format, so you know if you will be reading line-by-line, or if you need some special character that says "this input is finished" at which point your application presumably does something with that input, before proceeding and maybe reading some more input later? – DaveyDaveDave May 17 '19 at 08:21
  • I think DaveyDaveDave and BackSlash are right, I just need to test it tonight to make sure it works. How can I credit Saket Santosh Patel for his answer? – trapangle May 17 '19 at 08:23
  • It works almost perfect, thank you all. – trapangle May 17 '19 at 20:33

4 Answers4

0

Your use case will certainly be possible with multi-threading. Java robot framework provides mechanisms to send keystrokes. Refer: this post

Ironluca
  • 3,402
  • 4
  • 25
  • 32
0

You could send the input through another thread by using a custom InputStream implementation that takes care of that. My version below also permits user input.

Beware this solution is not perfect, but should give you a rough impression on how to do it.

public static void main(final String[] args) {
    System.out.println("What now?");
    DoubleSourceInputStream inputStream = new DoubleSourceInputStream();
    final Scanner scanner = new Scanner(inputStream);
    new Thread() {

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                inputStream.addBytes(("do task " + i + "\r\n").getBytes());
            }
            // to signal we are done, otherwise the queue would be polled forever
            inputStream.addBytes(new byte[] { -1 });
        }
    }.start();
    final String response = scanner.nextLine();
    scanner.close();
    System.out.println("Finished: " + response);
}

static class DoubleSourceInputStream extends InputStream {

    BlockingQueue<Byte> buffer = new LinkedBlockingQueue<>();

    @Override
    public int read() throws IOException {
        if (System.in.available() > 0)
            return System.in.read();
        try {
            return buffer.take().intValue();
        }
        catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public void addBytes(byte[] bytes) {
        for (byte b : bytes) {
            buffer.offer(Byte.valueOf(b));
        }
    }
}
dosenfant
  • 442
  • 1
  • 5
  • 16
0

Use the Main class as a bootstrap method and implement the code being tested in a separate class that operates on InputStream (input) and PrintStream (output).

import java.util.Scanner;

public class Main {
    public static void main(final String[] args) {
        MyCode code = new MyCode(System.in, System.out);
        code.run();
    }
}

Now you are no longer restricted to System.in and System.out when writing tests for MyCode. When bootstrapping the code from tests, just replace System.in and out with plain streams to which your test code writes and reads.

Torben
  • 3,805
  • 26
  • 31
0

Instead of re_inventing the wheel, use the features of your terminal to do it.

Image you are running your program normally using java -jar program.jar, you now need to run it as java -jar program.jar <input.txt, where input.txt contains all the data you normally pass from your keyboard when running interactively.

This only works if your program is predictable, but timing results from unpredictable programs are usually useless, unless ran thousands of times.

Ferrybig
  • 18,194
  • 6
  • 57
  • 79