~sircmpwn/hare-users

6 4

Test a function that prints to stdout

Details
Message ID
<D0IS8Q0L2RJQ.1NLO24XMIS7XM@mxsr.de>
DKIM signature
pass
Download raw message
Hi all,

I would like to write a test for a function that prints something to 
stdout. My first try was to create a memio buffer and to redirect stdout 
into this buffer somehow. See this minimal example:


use fmt;
use memio;
use os;

fn print(s: str) void = {
	// some other stuff
	fmt::printfln("{}", s)!;
	// some other stuff
};

@test fn print() void = {
	let buf = memio::dynamic();
	os::stdout = buf;
	print("hello");
	assert(memio::string(&buf)! == "hello");
};

export fn main() void = {
	print("hello");
};


But it doesn't compile because os::stdout is an io::handle and buf is a 
memio::stream. How can I do this correctly?

Searching the internet, I found this solution as a workaround (probably 
language independent):

Split the function that writes to stdout into one that returns strings 
and another one that prints the returned strings. Then the first 
function can be tested as usual and the second one is trivial.

But in general, I prefer to add some extra lines to the test than to the 
"production" code.

Best regards,
Max
Details
Message ID
<ZhotNVGoLeYzS-IW@altai>
In-Reply-To
<D0IS8Q0L2RJQ.1NLO24XMIS7XM@mxsr.de> (view parent)
DKIM signature
pass
Download raw message
On Sat, Apr 13, 2024 at 08:31:43AM +0200, Max Schillinger wrote:
> @test fn print() void = {
> 	let buf = memio::dynamic();
> 	os::stdout = buf;
> 	print("hello");
> 	assert(memio::string(&buf)! == "hello");
> };

os::stdout is a io::handle. And io::handle is a tagged union like:

	type handle = (file | *stream)

so if you assign a stream to handle, you should pass it via address. Like this:

```
@test fn print() void = {
	let buf = memio::dynamic();
	os::stdout = &buf; // THIS
	print("hello");
	assert(memio::string(&buf)! == "hello\n");
};
```

This test passed.
Details
Message ID
<D0IW26LNSBFX.10HGQQI95LEDY@turminal.net>
In-Reply-To
<D0IS8Q0L2RJQ.1NLO24XMIS7XM@mxsr.de> (view parent)
DKIM signature
pass
Download raw message
On Sat Apr 13, 2024 at 8:31 AM CEST, Max Schillinger wrote:
> Hi all,
>
> I would like to write a test for a function that prints something to 
> stdout. My first try was to create a memio buffer and to redirect stdout 
> into this buffer somehow. See this minimal example:

That's a nice way to do it. Note however, that the testing runtime won't be
able to capture stdout for you if you do this, and you'll have a harder time
making use of it for your debugging.


> use fmt;
> use memio;
> use os;
>
> fn print(s: str) void = {
> 	// some other stuff
> 	fmt::printfln("{}", s)!;
> 	// some other stuff
> };
>
> @test fn print() void = {
> 	let buf = memio::dynamic();
> 	os::stdout = buf;
> 	print("hello");
> 	assert(memio::string(&buf)! == "hello");
> };
>
> export fn main() void = {
> 	print("hello");
> };
>
>
> But it doesn't compile because os::stdout is an io::handle and buf is a 
> memio::stream. How can I do this correctly?

io::handle is defined as (*io::stream | io::file). If you look at the
definition of memio::stream, you can see that it's first field is an io::stream.
Thus it's safe to use a pointer to memio::stream as a pointer to io::stream.

So you can change `os::stdout = buf` to `os::stdout = &buf` and harec will
recognize the pointer type correctly and allow the assignment to happen.


> Searching the internet, I found this solution as a workaround (probably 
> language independent):
>
> Split the function that writes to stdout into one that returns strings 
> and another one that prints the returned strings. Then the first 
> function can be tested as usual and the second one is trivial.

The method above is already a good solution, so this is just a side note:
Depending on how you write these functions, this may not work. It works
without problem in languages with automatic memory management, but in Hare,
you'd need to be careful not to stack-allocate any part of the string data.
Details
Message ID
<D0IVTX01KT5C.1M2WNMACPL29@cmpwn.com>
In-Reply-To
<ZhotNVGoLeYzS-IW@altai> (view parent)
DKIM signature
pass
Download raw message
You can replace os::stdout with your own handle like Max suggests, but
another thing you can do is replace the file descriptor with io::dup2.
We don't have anything in Hare to create memfds though so YMMV.
Details
Message ID
<D0IWH6LPV45H.20HQVSTUSYCFT@turminal.net>
In-Reply-To
<D0IW26LNSBFX.10HGQQI95LEDY@turminal.net> (view parent)
DKIM signature
pass
Download raw message
Ah, I see kurth4cker already answered the same thing. Sorry!
Details
Message ID
<D0JPCCFLOD9B.216PD6HWKFHFV@mxsr.de>
In-Reply-To
<ZhotNVGoLeYzS-IW@altai> (view parent)
DKIM signature
pass
Download raw message
On Sat Apr 13, 2024 at 8:59 AM CEST,  wrote:
> os::stdout is a io::handle. And io::handle is a tagged union like:
>
> 	type handle = (file | *stream)
>
> so if you assign a stream to handle, you should pass it via address. Like this:
>
> ```
> @test fn print() void = {
> 	let buf = memio::dynamic();
> 	os::stdout = &buf; // THIS
> 	print("hello");
> 	assert(memio::string(&buf)! == "hello\n");
> };
> ```
>
> This test passed.

Thank you, this works perfectly!


On Sat Apr 13, 2024 at 11:31 AM CEST, Bor Grošelj Simić wrote:
> > Searching the internet, I found this solution as a workaround (probably 
> > language independent):
> >
> > Split the function that writes to stdout into one that returns strings 
> > and another one that prints the returned strings. Then the first 
> > function can be tested as usual and the second one is trivial.
>
> The method above is already a good solution, so this is just a side note:
> Depending on how you write these functions, this may not work. It works
> without problem in languages with automatic memory management, but in Hare,
> you'd need to be careful not to stack-allocate any part of the string data.

That's good point. The real function takes a list of strings as input 
and prints substrings of some of them. I would have to change it to 
return an array of slices, I guess.


On Sat Apr 13, 2024 at 11:50 AM CEST, Bor Grošelj Simić wrote:
> Ah, I see kurth4cker already answered the same thing. Sorry!

Thank you nevertheless! Actually, I got your answer first because 
kurth4cker didn't add me as CC and I somehow wasn't subscribed to this 
list (which is strange since my mailbox is full of hare-users mails).


On Sat Apr 13, 2024 at 11:20 AM CEST, Drew DeVault wrote:
> You can replace os::stdout with your own handle like Max suggests, but
> another thing you can do is replace the file descriptor with io::dup2.
> We don't have anything in Hare to create memfds though so YMMV.

Does this mean I would have to create a real file first? This sounds 
like it has a larger overhead. Is there some benefit? This drawback 
mentioned by Bor still applies, right?

> Note however, that the testing runtime won't be able to capture stdout 
> for you if you do this, and you'll have a harder time making use of it 
> for your debugging.


Best regards,
Max
Details
Message ID
<D0JPGZ1572B5.3F57EHFF8I1UE@cmpwn.com>
In-Reply-To
<D0JPCCFLOD9B.216PD6HWKFHFV@mxsr.de> (view parent)
DKIM signature
pass
Download raw message
On Sun Apr 14, 2024 at 10:28 AM CEST, Max Schillinger wrote:
> On Sat Apr 13, 2024 at 11:20 AM CEST, Drew DeVault wrote:
> > You can replace os::stdout with your own handle like Max suggests, but
> > another thing you can do is replace the file descriptor with io::dup2.
> > We don't have anything in Hare to create memfds though so YMMV.
>
> Does this mean I would have to create a real file first? This sounds 
> like it has a larger overhead. Is there some benefit? This drawback 
> mentioned by Bor still applies, right?

You could make a pipe or a memfd or shm
Reply to thread Export thread (mbox)