Basic std.Io.File.stdout (0.16)

0.16

I’m missing something pretty basic here, but that’s probably all the more reason to document.

I wrote a function that takes a writer. I think it works fine for its normal intended use, but for some quick tracing, I wanted to just send it a writer I constructed from stdout. (Obviously, it’s more normal to just std.debug.print() to debug-print, but, again, I have an interface I’d like to take advantage of.) Handily, std.Io.File.stdout() exists. I’d never tried it before. So I just tried this proof-of-concept:

   const io = std.testing.io;
   const f = std.Io.File.stdout();
   defer f.close();
   var buf: [512]u8 = undefined;
   var w = f.writer(io, &buf);
   _ = try w.interface.write("hello there!\n");

And got nuthin’. I tried flush(), though a comment in the STD API (I think for stdout()) indicated that flush() was NOT needed in this case. But flush() just caused a forever hang. I could have dug deeper, but I think I know what’s going on there, and didn’t pursue. This is “simple enough”-looking that I thought I probably shouldn’t have to dig very deep (perhaps that’s my mistake)… so… what am I missing?

1 Like

Have you tried the following (one-liner, comes from langref)?

    try std.Io.File.stdout().writeStreamingAll(std.testing.io, "Hello, World!\n");

It streams to stdout (so never uses a buffer). I don’t know the exact reason you had the issue, but at least this should work.

Just another small thing: AFAIK it’s a bad idea to close the stdout file (what you did with defer).

1 Like

This works with master.

var threaded: std.Io.Threaded = .init_single_threaded;
const io = threaded.ioBasic();
const f = std.Io.File.stdout();
defer f.close(io);
var buf: [512]u8 = undefined;
var w: std.Io.File.Writer = .init(.stdout(), io, &buf);
_ = try w.interface.write("hello there!\n");
try w.interface.flush();

std.testing.io as its name suggests should only be used in tests.

If you’re just printf-debugging non-testing code, using std.debug.print is better (it does use a buffer, but it does the flushing for you). std.debug.print writes to stderr, which is not the same as stdout.

Well, specifically (not shown in my posted code, but just mentioned), I have a function (of my own) that takes a writer, and just wanted to send an stdout writer to the function. I’m not just looking to std.debug.print() - that would be easy! :slight_smile:

Ah! Right. Funny. I don’t have that in my code… I typed it in without thinking, forgetting myself that this was stdout!

Is the takeaway here that const io = std.testing.io is simply broken in master right now, and that std.Io.Threaded is needed, instead? Or am I missing something? I didn’t think about trying juicy io; I’m in a test block, not a main, and still haven’t even tried juicy even in a main, though it looks like a nice easy access….

Yes, this is a test block. It’s even only a temporary proof-of-concept bit that will disappear; I just thought it “should work”.

As mentioned, in this case, std.debug.print() is not a candidate. It’s not that I’m looking to debug print, it’s that I’ve got a function that streams data to a writer, and I wanted to simply shunt that to stdout as a simple proof of concept. I’ve actually moved on and am doing the real streaming originally intended, and no harm done, but I am still curious about the failure.

Actually, sorry to report that this executes the same way as my code - no output. But only after fixes. Indeed, close() is a mistake, and, in fact, results in a crash. Removing that and the flush(), this code behaves as my original. (In both cases, flush() just causes eternal hang.)

I’m using master. Llvm, this time, though I’m often on standalone, and will try that soon, for reference.

I’m guessing this is just a transient, after all. If I was more helpful, I’d dig in to find out what’s up.

Ok, a little more detail - the code (both variations) runs fine in main(), but not in a test block.

“Solution”, at the end of the day:

All varieties work from within main(); none work from a testing block. (Of course, within main(), you can’t use std.testing.io.) Also, flush() is essential, as probably supposed, though flush() hangs indefinitely from within a test block. The most no-nonsense way to do this from main is to just use juicy:

pub fn main(init: std.process.Init) !void {
   const f = std.Io.File.stdout();
   var buf: [512]u8 = undefined;
   var w = f.writer(init.io, &buf);
   //OR: var w: std.Io.File.Writer = .init(.stdout(), init.io, &buf); // instead of File.stdout() above
   _ = try w.interface.write("hello there\n");
     // (my actual code sends w.interface to the function that takes a Writer)
   try w.interface.flush();
}

Edit: explanations for why stdout doesn’t work from a test block are provided below by @miagi and @vulpesx; in short: stderr should work, but causes a test to fail, and stdout is unavailable.

In test block stdout is taken by the test runner itself, and you can only print into stderr, but any printing in stderr causes a test to fail.

1 Like

Nothing works from the test block because the tests are run as a sub process of the build system and stdout isn’t propagated, in fact in my outdated master install stdout is closed which hangs the test when trying to write to it, in 0.15 it doesn’t hang but prints nothing.

stderr does work, and even marks the test as a failure if anything is written to it.

If you run zig test directly you do see stdout, and stderr doesn’t fail the test since it can’t inspect its own stderr output.

3 Likes

EDIT: I’m making this the “solution” since it’s the closest match to my original interest. The previous “solution”, here still has good concluding content and explanations for why stdout won’t work in a test block, etc. But…


Duh. There’s another way to do this that will probably be my go-to when I need this again. Not sure why it didn’t occur to me.

But, I should re-cast… my OP code almost begged, “why not just std.debug.print()?” The answer is that I wanted to utilize a function written, we’ll say it’s:

pub fn foo(writer: *Writer) Writer.Error! usize { ...

So, the hope is, within a test block, to get this to “write” to the console. The procedure would be to do this just to confirm that I see what I want, and to debug. THEN, the test block is turned into regular “real” test block (nothing printed to console). That’s not the only reason this wants to be in a test block, rather than a (test) main() - I also want to take advantage of std.testing.allocator, for it’s advantages during debugging/testing.

Why so verbose about this? Well, in case it’s helpful pseudo-documentation.

So what’s the grand “solution” - easy, use std.Io.Writer’s .fixed(), then just normal std.debug.print() after:

test "basics" { // hurrah, test-block
   var buf: [512]u8 = undefined;
   var writer: std.Io.Writer = .fixed(&buf);
   const written = try foo(&writer);
   std.debug.print("{s}\n", .{buf[0..written]});
}

No need for std.Io.File.stdout(), no need to flush() in this case. The std.debug.print() disappears after initial use, and turns into a typical string-comparison test. A little too easy. Of course, it depends on knowing how big the buf needs to be, but in a test block, that’s often known, as you’re expecting certain output.

Anyway, perhaps too obvious to post, but, again, sometimes stuff is only obvious in hindsight.

1 Like

For tests there is another option, write your own test runner which handles print to stdout and/or stderr differently (however you like it to be handled).

For example I wrote a custom test runner for testing raylib examples where I run the test in a subprocess and capture stdout and stderr, but I only show that output if the test signaled via non-zero exit code that it should be considered a test failure. (Because treating output as an error isn’t useful in every testing scenario and by using a custom test runner you can decide what should happen with stdout/stderr)

I found these resources useful while writing my custom test runner:
https://www.openmymind.net/Using-A-Custom-Test-Runner-In-Zig/
Zig - Creating and using a custom test runner is easy!

2 Likes

What you should learn from this is to state your intentions more clearly, if we knew you wanted to verify the written output of a function we could have given you that answer in the first place.

Instead of the less helpful answers about printing to stdout, since that’s what you asked about, but not what you wanted.

2 Likes

Nice. And fancy. In the cases I’ve encountered so far, traceouts are only helpful as I’m cooking up the bit of code… then I don’t want anything spitting out to the terminal. So this dumb simple approach suffices, but thanks for posting, as I don’t think I would otherwise have known that could be done.

Indeed. I wondered if I’d oversimplified in the OP. Though, the original idea WAS to send the stdout-bound Writer to a function. The idea to use a Writer to just write to a buffer, for me to later debug.print(), hadn’t even occurred to me. A case of “I didn’t know what all the possibilites might be,” and got rather pigeonholed on not being able to stdout from a test block. Anyway, learned some things.

The original goal was to get the output so you could verify it. Writing to stdout is just your attempted solution.

You may not have been able to put it into words at the time, understandable as it is a skill to dissect your thoughts, but you did have a motivation that lead you to asking the question.

No, sorry; seems I really buggered the communication.

The original and final goals were the same, and are two-part: primarily - to send a writer to my function, and to see the result of the write-out in the console. Then, ultimately - to get rid of the stdout, and just make it a normal verify test. But the focus was always providing an Io.Writer to the function, since that’s the way I want the interface to work, and the preference was always to do it in a test-block, since, ultimately, I wanted the test code to stick around forever.

So, as you saw, I briefly used a main() (since I couldn’t stdout in test-block) and then, once happy with the code and traceouts I was seeing, I converted the stdout file to an actual tmp File, and put the code back in a test block where I wanted it. THEN I realized that .fixed(&buf) could solve both problems: I could debug.print() the final buffer contents while working the code up to my liking, and then I could just remove the debug.print() and compare the final buffer, for the long-term test-code, rather than file-writing and concocting the test around the file contents.

I hope that makes more sense. I dwell on it a little because something of this sort will become fairly patterned for my Io code that writes to a Writer, as I tend to like to see the writing on the wall as I noodle the code. Perhaps it’s how everybody does this, but I hadn’t quite figured it out. Grateful for the help and patience as always; with time, I’m sure I’ll get better at coding and communicating.

In a test, you’d write to a buffer and the compare the contents with your expected output, and fail the test if your result doesn’t match expected. Writing to the standard outs is not allowed in a Zig test.

What I think you’re describing is not a test, but simply prototyping code. That can be done with a regular program, and write whatever you like.

2 Likes