class Rgot::F

def fuzz_foo(f)

f.add(5, "hello")
f.fuzz do |t, i, s|
  ...
end

Constants

SUPPORTED_TYPES

Attributes

name[R]

@dynamic name

Public Class Methods

new(fuzz_target:, opts:) click to toggle source
Calls superclass method Rgot::Common::new
# File lib/rgot/f.rb, line 102
def initialize(fuzz_target:, opts:)
  super()
  @opts = opts
  @fuzz_target = fuzz_target
  @fuzz_block = nil
  @module = fuzz_target.module
  @name = fuzz_target.name
  @corpus = []
end

Public Instance Methods

add(*args) click to toggle source
# File lib/rgot/f.rb, line 160
def add(*args)
  args.each do |arg|
    unless SUPPORTED_TYPES.key?(arg.class)
      raise "unsupported type to Add #{arg.class}"
    end
  end
  entry = CorpusEntry.new(
    values: args.dup,
    is_seed: true,
    path: "seed##{@corpus.length}"
  )
  @corpus.push(entry)
end
fuzz(&block) click to toggle source
# File lib/rgot/f.rb, line 174
def fuzz(&block)
  unless block
    raise LocalJumpError, "must set block"
  end
  unless 2 <= block.arity
    raise "fuzz target must receive at least two arguments"
  end

  t = T.new(@fuzz_target.module, @fuzz_target.name)

  @corpus.each do |entry|
    unless entry.values.length == (block.arity - 1)
      raise "wrong number of values in corpus entry: #{entry.values.length}, want #{block.arity - 1}"
    end
    block.call(t, *entry.values.dup)
    fail! if t.failed?
  end

  @fuzz_block = block

  nil
end
fuzz?() click to toggle source
# File lib/rgot/f.rb, line 197
def fuzz?
  return false unless @opts.fuzz
  return false unless Regexp.new(@opts.fuzz.to_s).match?(@fuzz_target.name)
  true
end
report() click to toggle source
# File lib/rgot/f.rb, line 203
def report
  puts @output if Rgot.verbose? && !@output.empty?
  duration = Rgot.now - @start
  template = "--- \e[%sm%s\e[m: %s (%.2fs)\n"
  if failed?
    printf template, [41, 1].join(';'), "FAIL", @name, duration
  elsif Rgot.verbose?
    if skipped?
      printf template, [44, 1].join(';'), "SKIP", @name, duration
    else
      printf template, [42, 1].join(';'), "PASS", @name, duration
    end
  end
end
run() click to toggle source

TODO: DRY with T

# File lib/rgot/f.rb, line 113
def run
  catch(:skip) { call }
  finish!
rescue => e
  fail!
  raise e
end
run_fuzzing() click to toggle source
# File lib/rgot/f.rb, line 126
def run_fuzzing
  return unless fuzz?
  raise("must call after #fuzz") unless @fuzz_block

  coordinator = Coordinator.new(
    warmup_input_count: @corpus.length
  )
  coordinator.start_logger

  t = T.new(@fuzz_target.module, @fuzz_target.name)

  begin
    Timeout.timeout(@opts.fuzztime.to_f) do
      loop do
        @corpus.each do |entry|
          values = entry.mutate_values

          @fuzz_block.call(t, *values)

          if 0 < coordinator.diff_coverage
            coordinator.interesting_count += 1
          end
          coordinator.count += 1
          fail! if t.failed?
        end
      end
    end
  rescue Timeout::Error, Interrupt
    coordinator.log_stats
  end

  report
end
run_testing() click to toggle source
# File lib/rgot/f.rb, line 121
def run_testing
  run
  report if !fuzz? || failed?
end

Private Instance Methods

call() click to toggle source
# File lib/rgot/f.rb, line 220
def call
  test_method = @module.instance_method(@name).bind(@module)
  if test_method.arity == 0
    path, line = test_method.source_location
    warn "#{path}:#{line} `#{test_method.name}' is not running. It's a testing method name, But not have argument"
  else
    test_method.call(self)
  end
end