I want to store what is printed from the code using puts
or p
, but I don’t find a good way to store the messages in such a way to be able to trace them back to which test they come from.
Therefore is my question is there some method to be able to store p messages associated with the tests it comes from?
Whats your use case? If possible could consider using the Log
module which would then allow you to leverage Log - Crystal 1.7.2.
Otherwise your best bet would prob be to re-define/create your own printing methods that store metadata about it to an array or something and clear it before each test case. E.g.
PRINTS = [] of PrintContext
record PrintContext,
message : String,
file : String,
line : Int32
def puts(message : String, file : String = __FILE__, line : Int32 = __LINE__) : Nil
PRINTS << PrintContext.new message, file, line
STDOUT.puts message
end
puts "foo" # => foo
puts "bar" # => bar
pp PRINTS.map &.message # => ["foo", "bar"]
PRINTS.clear
pp PRINTS.map &.message # => []
I work on the test runner for exercism, in some languages you can use a the “print” method to see what the code outputs, here is an example for python:
I want a way there I can store the output assisited with each test, I don’t know what the person using the test runner may end up printing. If I knew what was going to be printed I could find a way, I can also get p to work in a good way but not puts. But I guess I could modify the puts and p method to store the output in a file. I was just wondering if there were a “simplier” way.
Okay, so for this use case you probably want to redirect stdout (maybe also stderr) for the duration of a test into a memory buffer.
It’s possible to reopen the standard streams in order to redirect them, it’s just not very straighforward to implement.
The gist is something like this:
original_stdout = File.open("/dev/null")
original_stdout.reopen(STDOUT)
IO.pipe do |reader, writer|
STDOUT.reopen(writer)
begin
puts "foo"
ensure
writer.close
STDOUT.reopen(original_stdout)
end
puts reader.gets_to_end.upcase
end
We recently discussed this on discord: Discord
If you want to capture stderr as well, you need some more plumbing. @watzon iterated on that in Discord and this is what he came up with for his use case:
original_stdout = File.open("/dev/null")
original_stdout.reopen(STDOUT)
original_stderr = File.open("/dev/null")
original_stderr.reopen(STDERR)
begin
stdout_reader, stdout_writer = IO.pipe
stderr_reader, stderr_writer = IO.pipe
STDERR.reopen(stderr_writer)
STDOUT.reopen(stdout_writer)
user_response = App.exec(request_data, runtime_response)
user_stdout = stdout_reader.gets_to_end
user_stderr = stderr_reader.gets_to_end
return context.put_status(200).json({
"response" => user_response,
"stdout" => user_stdout,
})
rescue e : Exception
error_string = String.build do |str|
str << user_stderr
str << e.message
e.backtrace.each do |line|
str << line
end
end
return context.put_status(500).json({
"stdout" => user_stdout,
"stderr" => error_string.strip,
})
ensure
[
stdout_reader, stdout_writer, stderr_reader,
stderr_writer, original_stdout, original_stderr,
].each(&.try(&.close))
STDOUT.reopen(original_stdout)
STDERR.reopen(original_stderr)
end
2 Likes
I don’t know if this is really it. I have no problem getting the data, I want to be able to sort prints to their tests.
Say I have this test file:
describe "something" do
it "does something" do
some_method(5)
end
it "does something else" do
some_method(10)
end
end
And this method:
def some_method(number)
if number == 5
p "number is 5"
elsif number == 10
p "number is 10"
end
end
I would like to be able to say:
On test_1 it gave the output: “number is 5” and on test_2 it gave the output: “number is 10”
I have found an “okay” way to be able to get this data and split it when using p
, since I can read what is output to the console and then split everywhere it starts with an “F” or “.” but if you use puts and start a print statement with an “F” that would cause some issues.
Thanks!
Combining both of these methods made it work, I just have to figure out some file system things but that is not crystal related.
Thanks so much for all of the help, the update to the test runner is live:
1 Like