spec/command/command_spec.rb in command_exec-0.1.3 vs spec/command/command_spec.rb in command_exec-0.2.0

- old
+ new

@@ -1,125 +1,421 @@ +#encoding: utf-8 + require 'spec_helper' describe Command do - let(:logger) {Logger.new(StringIO.new)} + let(:lib_logger) {Logger.new(StringIO.new)} #let(:logger) {Logger.new($stdout)} - let(:log_level) {:info} - let(:command) { Command.new(:echo , :log_level => :silent, :logger => logger, :parameter => "hello world" , :error_keywords => %q[abc def], :working_directory => '/tmp' ) } + let(:lib_log_level) {:info} + let(:command) { Command.new(:echo , :lib_logger => lib_logger, :parameter => "hello world" , :error_keywords => %q[abc def], :working_directory => '/tmp' ) } + context :public_api do + test_dir = File.expand_path('test_data', File.dirname(__FILE__)) - it "has a path" do - command.path.should == '/bin/echo' - end + it "supports relative paths" do + Dir.chdir('spec/command') do + command = Command.new('test_data/true_test') + expect(command.path).to eq(File.join(test_dir, 'true_test')) + end - it "has parameter" do - command.parameter.should == 'hello world' - end + Dir.chdir test_dir do + command = Command.new('./true_test') + expect(command.path).to eq(File.join(test_dir, 'true_test')) + end - it "has options" do - command.options.should == '' - end + Dir.chdir '/tmp/' do + command = Command.new('../bin/true') + expect(command.path).to eq('/bin/true') + end + end - it "offers the possibility to change the working directory of the process" do - command.working_directory.should == '/tmp' - Dir.pwd.should == '/tmp' - end + it 'searches $PATH to find the command' do + environment({ 'PATH' => '/bin' }) do + command = Command.new(:true) + expect(command.path).to eq("/bin/true") + end + end - it "has special keywords indicating errors in stdout" do - command.error_keywords.should == %q[abc def] - end + it 'offers an option to change $PATH for the command execution' do + command = Command.new(:echo_test, search_paths: [test_dir]) + expect(command.path).to eq(File.join(test_dir, 'echo_test')) + end - it "can be used to construct a command string, which can be executed" do - command = Command.new(:pdflatex, :log_level => :silent, :logger => logger, :parameter => "index.tex blub.tex", :options => "-a -b") - command.send(:build_cmd_string).should == "/usr/bin/pdflatex -a -b index.tex blub.tex" - end + it "checks if exec is executable" do + command = Command.new('/bin/true') + expect(command.executable?).to eq(true) - it "runs programms" do - command.run - command.result.should == true - end + command = Command.new('/etc/passwd') + expect(command.executable?).to eq(false) + end - it "returns the textual rep of a command" do - command.to_txt.should == '/bin/echo hello world' - end + it "checks if exec exists" do + command = Command.new('/bin/true') + expect(command.exists?).to eq(true) - it "execute existing programs" do - command = Command.execute(:echo, :log_level => :silent, :logger => logger ,:parameter => "index.tex blub.tex", :options => "-- -a -b") - command.result.should == true - end - - it "does not execute non-existing programs" do - command = Command.execute(:grep, :log_level => :silent, :logger => logger, :parameter => "index.tex blub.tex", :options => "-- -a -b") - command.result.should == false - end + command = Command.new('/usr/bin/does_not_exist') + expect(command.exists?).to eq(false) + end - it "checks if errors have happend during execution" do - lambda { Command.new(:echo1, :log_level => :silent, :logger => logger, :parameter => "index.tex blub.tex", :options => "-- -a -b") }.should raise_error CommandNotFound - end + it "checks if exec is valid (exists, executable, type = file)" do + #does not exist + command = Command.new('/usr/bin/does_not_exist') + expect(command.valid?).to eq(false) - it "decides which output should be returned to the user" do - logfile = StringIO.new - logfile << 'Error in ... found' + #is a directory not a file + command = Command.new('/tmp') + expect(command.valid?).to eq(false) - stderr = StringIO.new - stderr << 'Error found' + #exists and is executable and is a file + command = Command.new('/bin/true') + expect(command.valid?).to eq(true) + end - stdout = StringIO.new - stdout << 'Error found' + it "has parameter" do + command = Command.new(:true, :parameter=>'parameter') + expect(command.parameter).to eq('parameter') + end - #result = command.send(:help_logger)({ :error_in_exec => true , :error_in_stdout => false} , { :logfile => logfile, :stderr => stderr , :stdout => stdout }) - result = command.send(:help_output, { :error_in_exec => true , :error_in_stdout => false} , { :logfile => logfile, :stderr => stderr , :stdout => stdout }) - result.should == ["================== LOGFILE ================== ", "Error in ... found", "================== STDOUT ================== ", "Error found", "================== STDERR ================== ", "Error found"] + it "has options" do + expect(command.options).to eq('') + end - result = command.send(:help_output, { :error_in_exec => false , :error_in_stdout => true} , { :logfile => logfile, :stderr => stderr , :stdout => stdout }) - result.should == ["================== STDOUT ================== ", "Error found"] + it "offers the possibility to change the working directory of the process without any side effects" do + expect(command.working_directory).to eq('/tmp') - result = command.send(:help_output, { :error_in_exec => true , :error_in_stdout => true} , { :logfile => logfile, :stderr => stderr , :stdout => stdout }) - result.should == ["================== LOGFILE ================== ", "Error in ... found", "================== STDOUT ================== ", "Error found", "================== STDERR ================== ", "Error found"] + #no side effects + lambda { command.run } + expect(Dir.pwd).to eq(File.expand_path('../..', File.dirname(__FILE__))) + end - result = command.send(:help_output, { :error_in_exec => false , :error_in_stdout => false} , { :logfile => logfile, :stderr => stderr , :stdout => stdout }) - result.should == [] + it "can be used to construct a command string, which can be executed" do + environment('PATH' => '/bin') { + command = Command.new(:true, :parameter => "index.tex blub.tex", :options => "-a -b") + expect(command.to_s).to eq("/bin/true -a -b index.tex blub.tex") + } + end - end + it "runs programms" do + command = Command.new(:echo, :parameter => "output", :lib_log_level => :silent ) + command.run + expect(command.result.status).to eq(:success) + end - it "finds errors in stdout" do - command.send(:error_in_string_found?, ['error'] , 'long string witherror inside' ).should == true - command.send(:error_in_string_found?, ['long', 'inside'] , 'long string witherror inside' ).should == true - command.send(:error_in_string_found?, ['error'] , 'long string with erro inside' ).should == false - end + it "execute existing programs" do + command = Command.execute(:echo, :parameter => "output", :options => "-- -a -b", :lib_log_level => :silent ) + expect(command.result.status).to eq(:success) + end - it "output a message" do - command.send(:message, false, 'Hello_world').should == "\e[1m\e[31mFAILED\e[0m\e[0m\nHello_world" - command.send(:message, true, 'Hello_world').should == "\e[1m\e[32mOK\e[0m\e[0m" - command.send(:message, true ).should == "\e[1m\e[32mOK\e[0m\e[0m" - end + it "is very verbose and returns a lot of output" do + bucket = StringIO.new + lib_logger = Logger.new(bucket) + Command.execute(:echo, :lib_logger => lib_logger ,:parameter => "output", :lib_log_level => :debug) - it "is very verbose and returns a lot of output" do - bucket = StringIO.new - logger = Logger.new(bucket) - Command.execute(:echo, :logger => logger ,:parameter => "index.tex blub.tex", :options => "-- -a -b" , :log_level => :debug) + expect(bucket.string['DEBUG']).to_not eq(nil) + end - bucket.string.should =~ /OK/ - end + it "is silent and returns no output" do + # if you choose the system runner output of commands will be not suppressed" + bucket = StringIO.new + lib_logger = Logger.new(bucket) + Command.execute(:echo, :lib_logger => lib_logger ,:parameter => "output", :lib_log_level => :silent) - it "is silent and returns no output" do - bucket = StringIO.new - logger = Logger.new(bucket) - Command.execute(:echo, :logger => logger ,:parameter => "index.tex blub.tex", :options => "-- -a -b" , :log_level => :silent) + expect(bucket.string).to eq("") + end - bucket.string.should == "" - end + it "supports other log levels as well" do + bucket = StringIO.new + lib_logger = Logger.new(bucket) - # not completed - #it "use a log file if given" do - # application_log_file = Tempfile.new('command_exec_test') - # application_log_file.write "ERROR" + Command.execute(:echo, :lib_logger => lib_logger ,:parameter => "output", :lib_log_level => :info) + Command.execute(:echo, :lib_logger => lib_logger ,:parameter => "output", :lib_log_level => :warn) + Command.execute(:echo, :lib_logger => lib_logger ,:parameter => "output", :lib_log_level => :error) + Command.execute(:echo, :lib_logger => lib_logger ,:parameter => "output", :lib_log_level => :fatal) + Command.execute(:echo, :lib_logger => lib_logger ,:parameter => "output", :lib_log_level => :unknown) + Command.execute(:echo, :lib_logger => lib_logger ,:parameter => "output", :lib_log_level => :garbage_sasdfasf) + end - # binding.pry - # Command.execute(:echo, :logger => logger ,:parameter => "index.tex blub.tex", :options => "-- -a -b" , :log_level => :silent, :logfile => application_log_file, :error_keywords => %W[ERROR]) + it "use a log file if given" do + application_log_file = create_temp_file_with('command_exec_test', 'TEXT IN LOG') - #end + bucket = StringIO.new + lib_logger = Logger.new(bucket) + command = Command.new(:logger_test , + :lib_logger => lib_logger , + :log_file => application_log_file , + :search_paths => File.expand_path('test_data', File.dirname(__FILE__))).run + end + it "outputs only warnings when told to output those" do + bucket = StringIO.new + lib_logger = Logger.new(bucket) + command = Command.new(:logger_test , + :lib_logger => lib_logger , + :lib_log_level => :warn, + :log_file => '/tmp/i_do_not_exist.log', + :search_paths => File.expand_path('test_data', File.dirname(__FILE__))).run + + expect(bucket.string['WARN']).to_not eq(nil) + end + + it "considers status for error handling (default 0)" do + command = Command.new(:exit_status_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :parameter => '1', + :lib_log_level => :silent, + :error_detection_on => [:return_code], + ) + command.run + expect(command.result.status).to eq(:failed) + end + + it "considers status for error handling (single value as array)" do + command = Command.new(:exit_status_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :parameter => '1', + :lib_log_level => :silent, + :error_detection_on => [:return_code], + :error_indicators => { :allowed_return_code => [0] }) + command.run + expect(command.result.status).to eq(:failed) + end + + it "considers status for error handling (single value as symbol)" do + command = Command.new(:exit_status_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :parameter => '1', + :lib_log_level => :silent, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [0] }) + command.run + expect(command.result.status).to eq(:failed) + end + + it "considers status for error handling (single value)" do + command = Command.new(:exit_status_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :parameter => '0', + :lib_log_level => :silent, + :error_detection_on => [:return_code], + :error_indicators => { :allowed_return_code => [0,2] }) + command.run + expect(command.result.status).to eq(:success) + + command = Command.new(:exit_status_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :parameter => '2', + :lib_log_level => :silent, + :error_detection_on => [:return_code], + :error_indicators => { :allowed_return_code => [0,2] }) + command.run + expect(command.result.status).to eq(:success) + end + + it "considers stderr for error handling" do + command = Command.new(:stderr_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :error_detection_on => :stderr, + :error_indicators => { :forbidden_words_in_stderr => %w{error} }) + command.run + expect(command.result.status).to eq(:failed) + end + + it "considers stderr for error handling but can make exceptions" do + command = Command.new(:stderr_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :error_detection_on => :stderr, + :error_indicators => { :forbidden_words_in_stderr => %w{error}, :allowed_words_in_stderr => ["error. execution failed"]}) + command.run + expect(command.result.status).to eq(:success) + end + + it "considers stdout for error handling" do + command = Command.new(:stdout_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :error_detection_on => :stdout, + :error_indicators => { :forbidden_words_in_stdout => %w{error} }) + command.run + expect(command.result.status).to eq(:failed) + end + + + it "removes newlines from stdout" do + #same for stderr + command = Command.new(:stdout_multiple_lines_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :error_detection_on => :stdout, + :error_indicators => { :forbidden_words_in_stdout => %w{error} }) + command.run + expect(command.result.stdout).to eq(["error. execution failed", "error. execution failed"]) + end + + it "considers log file for error handling" do + temp_file = create_temp_file_with('log_file_test', 'error, huh, what goes on' ) + + command = Command.new(:log_file_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :log_file => temp_file, + :error_detection_on => :log_file, + :error_indicators => { :forbidden_words_in_log_file => %w{error} }) + command.run + expect(command.result.status).to eq(:failed) + end + + it "returns the result of command execution as process object (defaults to :return_process_information)" do + command = Command.new(:output_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [ 0 ]}) + command.run + expect(command.result.class).to eq(CommandExec::Process) + end + + it "returns the result of command execution as process object" do + command = Command.new(:output_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :on_error_do => :return_process_information, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [ 0 ]}) + command.run + expect(command.result.class).to eq(CommandExec::Process) + end + + it "does nothing on error if told so" do + command = Command.new(:raise_error_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :on_error_do => :nothing, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [ 0 ]}) + expect{command.run}.to_not raise_error + expect{command.run}.to_not throw_symbol + end + + it "raises an exception" do + command = Command.new(:raise_error_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :on_error_do => :raise_error, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [ 0 ]}) + expect{command.run}.to raise_error(CommandExec::Exceptions::CommandExecutionFailed) + + command = Command.new(:not_raise_error_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :on_error_do => :raise_error, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [ 0 ]}) + expect{command.run}.to_not raise_error + end + + it "throws an error" do + command = Command.new(:throw_error_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :on_error_do => :throw_error, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [ 0 ]}) + expect{command.run}.to throw_symbol(:command_execution_failed) + + command = Command.new(:not_throw_error_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :on_error_do => :throw_error, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [ 0 ]}) + expect{command.run}.to_not throw_symbol + end + + it "support open3 as runner" do + #implicit via default value (open3) + command = Command.new(:runner_open3_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :lib_log_level => :silent, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [ 0 ]}) + command.run + expect(command.result.status).to eq(:success) + + #or explicit + command = Command.new(:runner_open3_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :run_via => :open3, + :lib_log_level => :silent, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [ 0 ]}) + command.run + expect(command.result.status).to eq(:success) + end + + it "support system as runner" do + command = Command.new(:runner_system_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :run_via => :system, + :lib_log_level => :silent, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [ 0 ]}) + command.run + expect(command.result.status).to eq(:success) + end + + it "has a default runner: open3" do + command = Command.new(:runner_system_test, + :search_paths => File.expand_path('test_data', File.dirname(__FILE__)), + :run_via => :unknown_runner, + :lib_log_level => :silent, + :error_detection_on => :return_code, + :error_indicators => { :allowed_return_code => [ 0 ]}) + command.run + expect(command.result.status).to eq(:success) + end + + it "find errors beyond newlines in the string" do + command = CommandExec::Command.new( :echo , + :options => '-e', + :parameter => "\"wow, a test. That's great.\nBut an error occured in this line\"", + :error_detection_on => [:stdout], + :error_indicators => { + :forbidden_words_in_stdout => %w{ error } + }, + ) + command.run + expect(command.result.status).to eq(:failed) + end + end + + context :private_api do + it "raises an error if command is not executable" do + command = Command.new('/etc/passwd', lib_log_level: :silent) + expect{command.send(:check_path)}.to raise_error CommandNotExecutable + end + + it "raises an error if command does not exist" do + command = Command.new('/usr/bin/does_not_exist', lib_log_level: :silent) + expect{command.send(:check_path)}.to raise_error CommandNotFound + end + + it "raises an error if command is not a file" do + command = Command.new('/tmp', lib_log_level: :silent) + expect{command.send(:check_path)}.to raise_error CommandIsNotAFile + end + + it "finds errors in string" do + expect(command.send(:error_occured?, ['error'] , [], ['long string witherrorinside'] )).to eq(true) + expect(command.send(:error_occured?, ['error', 'inside'] , [], ['long string with error inside'] )).to eq(true) + expect(command.send(:error_occured?, ['error'] , [], ['long string with no erro"r" inside'] )).to eq(false) + + expect(command.send(:error_occured?, ['error'] , ['long string with error inside but an exception defined'], ['long string with error inside but an exception defined'] )).to eq(false) + expect(command.send(:error_occured?, ['error'] , ['substring exception defined'], ['long string with error inside but a substring exception defined'] )).to eq(false) + end + end end