$:.unshift File.dirname(__FILE__) require 'fault_injection/fault_condition.rb' require 'fault_injection/parser.rb' module FaultInjection @@injected_faults = nil @@method_stack = Array.new def inject(*args) FaultInjection.inject *args end def self.inject(command,error_class=RuntimeError,msg="injected fault") unless error_class <= Exception raise ArgumentError, "Error class must be a subclass of Exception" end cond = nil if String === command cond = Parser.compile(command) cond.error_class = error_class cond.message = msg elsif Hash === command and command.key?(:line) and command.key?(:file) cond = inject_fault_on_line(command[:file], command[:line], error_class,msg) elsif Array === command cond = inject_fault_on_trace(command,error_class,msg) else raise "Unknown Command" end if @@injected_faults.nil? @@injected_faults = Array.new set_trace_func method(:fault).to_proc end raise "[BUG] Internal Error. Please report this to the author :)" if cond.nil? @@injected_faults << cond true end # delete all injected faults. def self.clear @@injected_faults = nil set_trace_func Proc.new{} end # Same as FaultInjection::clear def clear FaultInjection.clear end private # Create fault condition on file:line. def self.inject_fault_on_line(file,line,error_class,msg) if line <= 0 raise ArgumentError,"line number must not be less than 0" end cond = FaultConditionLine.new(file,line) cond.error_class = error_class cond.message = msg cond end # Create fault condition on method call trace def self.inject_fault_on_trace(trace,error_class,msg) trace = trace.flatten if trace.size == 0 or trace.size % 2 != 0 raise ArgumentError, "command must be array of class/method pair." end cond = FaultConditionCall.new while trace.size > 0 klass = trace.shift.to_sym method = trace.shift.to_sym cond.add_callee klass,method end cond.error_class = error_class cond.message = msg cond end # Callback function called by ruby interpreter. def self.fault(event,file,line,id,binidng,klass) return if @@injected_faults.nil? if event == "line" #puts "event = '#{event}', file = #{file}, line = #{line}" @@injected_faults.each do |f| if f.should_raise_on(file,line) raise f.error_class, f.message, caller(4) end end elsif event == "call" or event == "c-call" @@injected_faults.each do |f| if f.should_raise_on(@@method_stack) raise f.error_class, f.message, caller(4) end end @@method_stack.push([klass,id]) elsif event == "return" or event == "c-return" @@method_stack.pop end end end