module FaultInjection class FaultConditionBase attr_accessor :error_class attr_accessor :message attr_accessor :probability attr_accessor :limit_times def initialize @probability ||= 1 @limit_times ||= -1 end def should_raise_on(*args) return false if @limit_times == 0 b = _should_raise_on(*args) if @probability < 1 b = b && rand(0) < @probability end @limit_times -= 1 if b b end private def _should_raise_on(*args) false end end class FaultConditionLine < FaultConditionBase attr_accessor :file attr_accessor :line def initialize(file,line,prob=1) @file = file @line = line self.probability = prob if @line <= 0 raise ArgumentError,"line number must be greater than 0." end if prob < 0 or prob > 1 raise ArgumentError, "Probability must be between 0 and 1." end super() end private def _should_raise_on *args if args.size == 2 file,line = *args reg = Regexp.new(Regexp.escape(@file) + '$') return reg.match(file) && line == @line else false end end end class FaultConditionCall < FaultConditionBase # representing method chain(callee first) # [ [class,'method_name'], # [class,'method_name'], # ... # [class,'method_name'] ] attr_accessor :stack_pattern def initialize @stack_pattern = [] super() end def add_callee(klass,method) @stack_pattern.unshift [klass,method] end private def _should_raise_on *args return false if @stack_pattern.nil? or @stack_pattern.size == 0 if args[0].instance_of? Array stack = args[0] #STDERR.puts @stack_pattern.map{|e| "#{e[0]}##{e[1]}"}.join("\n") return false if stack.size < @stack_pattern.size @stack_pattern.each_index do |i| e = @stack_pattern[i] b = stack[-i-1] return false if e[0] != b[0].to_s.to_sym or e[1] != b[1].to_sym end true else false end end end end