Get_single_char for current input stream

Hi,
get_single_char/1 gets a single character from input stream user (without waiting for a CR). Is there a version for the current input stream with the same behaviour?
Thanks!

No. Why would you want that? It requires a terminal and tty control or something that emulates that to work anyway.

I’m building test cases for a system which reads such inputs and I’d like to automate this without resorting to actually hit keystrokes. But from your answer I guess that this cannot be emulated changing the user input to ‘seeing’ a file.

Wondering if what you describe is substantially different from the solution I use the the Prolog conformance suite bundled with Logtalk. An example unit test for the standard get_char/1 predicate:

succeeds(iso_get_char_2_01) :-
	^^set_text_input('qwerty'),
	{get_char(Char)},
	Char == 'q',
	^^check_text_input('werty').

In your case, you would replace the call to get_char/1 with a call to the predicate that reads those inputs. Is something like this that you’re looking for?

I’m not sure, first I should learn the specifics of Logtalk :slight_smile: Anyway I wondered whether I could automatically test an interactive system (built with SWI-Prolog) which expects in particular such system user inputs (similar to pressing keys for selecting menu items, i.e., without resorting to press Enter).

You system being built using SWI-Prolog is not an issue. The solution I hinted above can be used to test plain Prolog or Prolog module applications.

Simulating text user inputs, either a single one or a sequence of them, is easy. The main issue is if the system reaction to those inputs can be automatically verified and how. Maybe an example will help. Consider the adventure example bundled with Logtalk. E.g. sleepy.lgt. This is a toy text adventure game using a loop:

start :-
	do(help),
	do(look),
	repeat,
		write('> '),
		read(Command), nl,
		once(do(Command)),
	Command == halt,
	!,	% just to silence a compiler warning
	halt.

A simple test would be:

test(sleepy_01) :-
	^^set_text_input('off. use(bed). sleep. halt.'),
	sleepy::start.

Predicates are available to set and check text and binary input and output. Would such a solution work in your case? Do you have a similar read-eval-loop or something different?

I have some experience automating (non toy) interactive applications but those used Logtalk’s message printing and question asking mechanisms where both messages and questions can be intercepted, which provided the basis for the tests. If your system makes use of the SWI-Prolog message printing, it may provide part of the solution.

Capturing and validating the outcome of the system is not an issue in my case. Following your example, I understand that read can consume inputs in 'off. use(bed). sleep. halt.' because of the dots for each single input. For this example, I’d like to use something (old-style) similar to the goal:

?- see(shell_inputs), start, seen. 

where shell_inputs contains the test inputs 'off. use(bed). sleep. halt.'. But depending on the state of the system, this system may prompt for a single keystroke (with get_single_char instead of read). So, if we had:

start :-
	repeat,
		get_single_char(Key),
		process_keystroke(Key),
	"q"=[Key]. % Again, old stuff

for which feeding it with a file as input does not work.

Another alternative is to change the system itself, replacing reading keystrokes in test mode by read predicates, which is not hard to do. Many thanks for the help.

For testing interactive I/O, this tool might help:

https://en.wikipedia.org/wiki/Expect

That’s interesting. Thanks

I see. I see, except for all the external tools, two ways. One is to redefine get_single_char/1 in the user module for your tests. For example:

get_single_char(X) :-
    get_code(X).

The other is to use set_stream(Stream, alias(user_input)) to connect user input to another physical stream. I think the above is the most straight forward way.

Both solutions are great. I just successfuly used the second one in my application. As always, many thanks.

Great. For completeness, note that you can use this to get the current aliased stream, so you can use setup_call_cleanup/3 to safely redirect user_input temporary.

1 ?- stream_property(X, alias(user_input)).
X = <stream>(0x7fd424d90240).

:+1: :grinning: