How to redirect test result to txt file by ruby? - ruby

When your own test suite had executed, there were some results.
i want to keep thoes infomation in a txt file or html,
but i don't how to save those outputted message,
if anyone knows,please share with me, thanks in advance
the belowing code is my experiment, but it doesn't work.
require File.join(File.dirname(__FILE__), 'person')
require "test/unit"
filename = "logfile.txt"
$logfile = File.new(filename,"a")
open(logfile,'a') { |f| f << $stdout}
class PersonTest < Test::Unit::TestCase
FIRST_NAME, LAST_NAME, AGE = 'Nathaniel', 'Taldtretbott', 25
def setup
#person = Person.new(FIRST_NAME, LAST_NAME, AGE)
end
def test_first_name
assert_equal "asv", #person.first_name,"try to compare"
end
def test_last_name
assert_equal "Taldtretbott", #person.last_name
end
def test_full_name
assert_equal FIRST_NAME + ' ' + LAST_NAME, #person.full_name
end
end

Why not do
ruby testfile.rb > text_output.txt

I had some time to try some testing on a solution. I managed to put it to work this way:
require 'test/unit'
STDOUT = $stdout = File.open("stdout.txt", "a+")
STDERR = $stderr = File.open("stderr.txt", "a+")
class AreasTest < Test::Unit::TestCase
def test_ok
puts "#{Time.now} Hello "
a = 10
assert_equal(a, 10)
end
def test_fail
a = 9
assert_equal(a, 10)
end
end
It gives a warning cause STDOUT and STDERR were already initialized but just redirecting $stdout and $stderr does not work (works only for normal puts).
I hope it helps.

Try:
$stdout = $logfile
instead of:
open(logfile,'a') { |f| f << $stdout}
This means that you are redirecting the stdout to the file.

Related

Ruby - Set different log levels for different targets

Wondering how to set different log levels for different targets. Below is my Ruby code that writes a line to both Console and File.
# https://stackoverflow.com/a/6407200
class MultiIO
def initialize(*targets)
#targets = targets
end
def write(*args)
#targets.each do |t|
t.write(*args)
end
end
def close
#targets.each(&:close)
end
end
module Logging
def self.logger(logname, programname, debug = false)
log_file = File.open(logname, "a")
log_file.sync = true
zlogger = Logger.new MultiIO.new(log_file, STDOUT)
zlogger.level = Logger::INFO
zlogger.progname = programname
zlogger.formatter = proc do |serverity, datetime, progname, msg|
"#{datetime.strftime('%Y-%m-%d %I:%M:%S %p %:::z %Z')} - #{serverity} - [#{progname}] | #{msg}\n"
end
zlogger
end
end
I can set the level to Debug if a special env variable is found,
$logger.level = Logger::DEBUG if ENV['enable_debug_logs'] == 'true'
But, not sure how to always write Debug lines to the log file and only Info lines to console.
Does anyone know? Any help is greatly appreciated!
Since the log level is a property of the logger, not of the IO, it sounds to me like what you really need to do is define a MultiLogger rather than a MultiIO. Something along the lines of:
class MultiLogger
attr_reader :default_level, :default_progname, :default_formatter
def initialize(**args)
#default_level = args[:default_level]
#default_progname = args[:default_progname]
#default_formatter = args[:default_formatter]
#loggers = []
Array(args[:loggers]).each { |logger| add_logger(logger) }
end
def add_logger(logger)
logger.level = default_level if default_level
logger.progname = default_progname if default_progname
logger.formatter = default_formatter if default_formatter
#loggers << logger
end
def close
#loggers.map(&:close)
end
Logger::Severity.constants.each do |level|
define_method(level.downcase) do |*args|
#loggers.each { |logger| logger.send(__method__, args) }
end
# These methods are a bit weird in the context of a "multi-logger" with varying levels,
# since they are now returning an `Array`; never a falsey value.
# You may want to define them differently, e.g. `#loggers.all? {...}`, or use a non-predicate method name here.
define_method("#{level.downcase}?".to_sym) do
#loggers.map(&__method__)
end
end
end
# Usage:
log_file = File.open(logname, "a")
log_file.sync = true
file_logger = Logger.new(log_file)
console_logger = Logger.new(STDOUT)
console_logger.level = Logger::INFO
multi_logger = MultiLogger.new(
default_progname: programname,
default_formatter: proc do |severity, datetime, progname, msg|
"#{datetime.strftime('%Y-%m-%d %I:%M:%S %p %:::z %Z')} - #{severity} - [#{progname}] | #{msg}\n"
end,
loggers: [file_logger, console_logger]
)

How to define a common module logging shared among others module

I am developing a script with a big main function, which I have split in several modules.
What I need is to have access to the log functionality from all of them, this means that the log file has to be opened only once, and the access be shared.
This is what I have:
require 'module_1'
require 'module_2'
require 'module_3'
module Main
Module_1.Function_startup()
Module_2.Function_configuration()
Module_3.Function_self_test()
end
Here is the dirty module for the logger I need available in all the other modules.
Ideally I would like to call it as "logger.error", where "logger" returns the instance of the logger, and "error" is the function call on rlogger as rlogger.error.
require 'logger'
module Logging
#rlogger = nil
def init_logger
if #rlogger.nil?
puts "initializing logger"
file_path = File.join(File.dirname(__FILE__), 'checker.log')
open_mode = File::TRUNC # or File::APPEND
file = File.open(file_path, File::WRONLY | open_mode)
#rlogger = Logger.new(file)
#rlogger.datetime_format = "%Y-%m-%d %H:%M:%S"
#rlogger.formatter = proc do |severity, datetime, progname, msg|
con_msg = ""
if msg.match("ERROR:")
con_msg = msg.color(:red)
elsif msg.match("OK!")
con_msg = msg.color(:green)
else
con_msg = msg
end
puts ">>>#{con_msg}"
# notice that the colors introduce extra non-printable characters
# which are not nice in the log file.
"#{datetime}: #{msg}\n"
end
# Here is the first log entry
#rlogger.info('Initialize') {"#{Time.new.strftime("%H-%M-%S")}: Checker v#{#version}"}
end
end
# returns the logger
def logger
if #rlogger.nil?
puts "requesting nil rlogger"
end
#rlogger
end
end
end
Just after require you can add this piece of code
$FILE_LOG = Logging.create_log(File.expand_path('LoggingFile.log'), Logger::DEBUG)
Explanation of the above line : It is calling a function in Logging Module, to create File , Level of Logging is debug.
Below is the piece of code for Module
module Logging
def self.create_log(output_location level)
log = Logger.new(output_location, 'weekly').tap do |l|
next unless l
l.level = level
l.progname = File.basename($0)
l.datetime_format = DateTime.iso8601
l.formatter = proc { |severity, datetime, progname, msg| "#{severity}: #{datetime} - (#{progname}) : #{msg}\n" }
end
log.level = level if level < log.level
log
end
def self.log(msg, level)
# Here I am just logging only FATAL and DEBUG, similarly you can add in different level of logs
if level == :FATAL
$FILE_LOG.fatal(msg)
elsif level == :DEBUG
$FILE_LOG.debug(msg)
end
end
end
Then in Every method every Ruby file, we can use this logging as follows
Logging.log("Message",:FATAL)

Ruby: Chatterbot can't load bot data

I'm picking up ruby language and get stuck at playing with the chatterbot i have developed. Similar issue has been asked here Click here , I did what they suggested to change the rescue in order to see the full message.But it doesn't seem right, I was running basic_client.rb at rubybot directory and fred.bot is also generated at that directory . Please see the error message below: Your help very be very much appreciated.
Snailwalkers-MacBook-Pro:~ snailwalker$ cd rubybot
Snailwalkers-MacBook-Pro:rubybot snailwalker$ ruby basic_client.rb
/Users/snailwalker/rubybot/bot.rb:12:in `rescue in initialize': Can't load bot data because: No such file or directory - bot_data (RuntimeError)
from /Users/snailwalker/rubybot/bot.rb:9:in `initialize'
from basic_client.rb:3:in `new'
from basic_client.rb:3:in `<main>'
basic_client.rb
require_relative 'bot.rb'
bot = Bot.new(:name => 'Fred', :data_file => 'fred.bot')
puts bot.greeting
while input = gets and input.chomp != 'end'
puts '>> ' + bot.response_to(input)
end
puts bot.farewell
bot.rb:
require 'yaml'
require './wordplay'
class Bot
attr_reader :name
def initialize(options)
#name = options[:name] || "Unnamed Bot"
begin
#data = YAML.load(File.read('bot_data'))
rescue => e
raise "Can't load bot data because: #{e}"
end
end
def greeting
random_response :greeting
end
def farewell
random_response :farewell
end
def response_to(input)
prepared_input = preprocess(input).downcase
sentence = best_sentence(prepared_input)
reversed_sentence = WordPlay.switch_pronouns(sentence)
responses = possible_responses(sentence)
responses[rand(responses.length)]
end
private
def possible_responses(sentence)
responses = []
#data[:responses].keys.each do |pattern|
next unless pattern.is_a?(String)
if sentence.match('\b' + pattern.gsub(/\*/, '') + '\b')
if pattern.include?('*')
responses << #data[:responses][pattern].collect do |phrase|
matching_section = sentence.sub(/^.*#{pattern}\s+/, '')
phrase.sub('*', WordPlay.switch_pronouns(matching_section))
end
else
responses << #data[:responses][pattern]
end
end
end
responses << #data[:responses][:default] if responses.empty?
responses.flatten
end
def preprocess(input)
perform_substitutions input
end
def perform_substitutions(input)
#data[:presubs].each {|s| input.gsub!(s[0], s[1])}
input
end
# select best_sentence by looking at longest sentence
def best_sentence(input)
hot_words = #data[:responses].keys.select do |k|
k.class == String && k =~ /^\w+$/
end
WordPlay.best_sentence(input.sentences, hot_words)
end
def random_response(key)
random_index = rand(#data[:responses][key].length)
#data[:responses][key][random_index].gsub(/\[name\]/, #name)
end
end
I'm assuming that you are trying to load the :data_file passed into Bot.new, but right now you are statically loading a bot_data file everytime. You never mentioned about bot_data in the question. So if I'm right it should be like this :
#data = YAML.load(File.read(options[:data_file]))
Instead of :
#data = YAML.load(File.read('bot_data'))

Why does this injection of STDOUT through let cause a segmentation fault in ruby?

Full code example is at https://github.com/compwron/seg_replicator
ruby version ruby-2.0.0-p353 (see .rvmrc in example)
Interestingly, if I use .ruby-version the segfault does not replicate (see commit 2, i.e. the diff at https://github.com/compwron/seg_replicator/commit/955ff64fb8a45ac12cc4361f99b581baaa60fa3e for the change that takes the sample code from hanging-forever to segfaulting)
class Game
def initialize(stdout = STDOUT)
$stdout = stdout
end
def foo
0
end
end
require_relative '../lib/game'
describe Game do
let(:output) do
StringIO.new
$stdout = output
end
let(:g) { Game.new output }
describe 'foo' do
it 'test 1' do
expect(g.foo).to eq 0
end
it 'test 2' do
expect(g.foo).to eq 0
end
end
end

rspec puts output test

I'm having trouble understanding how to test for output with puts. I need to know what I need to do in my RSPEC file.
This is my RSPEC file:
require 'game_io'
require 'board'
describe GameIO do
before(:each) do
#gameio = GameIO.new
#board = Board.new
end
context 'welcome_message' do
it 'should display a welcome message' do
test_in = StringIO.new("some test input\n")
test_out = StringIO.new
test_io = GameIO.new(test_in, test_out)
test_io.welcome_message
test_io.game_output.string.should == "Hey, welcome to my game. Get ready to be defeated"
end
end
end
This is the file it is testing against:
class GameIO
attr_reader :game_input, :game_output
def initialize(game_input = $stdin, game_output = $stdout)
#stdin = game_input
#stdout = game_output
end
def welcome_message
output "Hey, welcome to my game. Get ready to be defeated"
end
def output(msg)
#stdout.puts msg
end
def input
#stdin.gets
end
end
NOTE: I updated my RSPEC code to reflect changes I made to my test file given suggestions found elsewhere. To resolve the poblem completly I used the changes suggested by Chris Heald in my main file. Thank you all and thank you Chris.
Your initializer should be:
def initialize(game_input = $stdin, game_output = $stdout)
#game_input = game_input
#game_output = game_output
end
The reason for this is that attr_accessor generates methods like this:
# attr_accessor :game_output
def game_output
#game_output
end
def game_output=(output)
#game_output = output
end
(attr_reader generates only the reader method)
Thus, since you never assign #game_output, your game_output method will always return nil.
Just check you are sending it the message:
#gameio.should_receive(:puts).with("Hey, welcome to my game. Get ready to be defeated")
You could stub puts and print.
Perhaps the most fundamental way is to temporarily reassign STDOUT to a variable, and confirm the variable matches what you expect for output.
And Minitest has must_output as an assertion/spec.
The code is thus:
##
# Fails if stdout or stderr do not output the expected results.
# Pass in nil if you don't care about that streams output. Pass in
# "" if you require it to be silent. Pass in a regexp if you want
# to pattern match.
#
# NOTE: this uses #capture_io, not #capture_subprocess_io.
#
# See also: #assert_silent
def assert_output stdout = nil, stderr = nil
out, err = capture_io do
yield
end
err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr
out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout
y = send err_msg, stderr, err, "In stderr" if err_msg
x = send out_msg, stdout, out, "In stdout" if out_msg
(!stdout || x) && (!stderr || y)
end

Resources