123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- require 'json'
- require 'iconv'
- ISO_8859_1_TO_UTF8 = Iconv.new('utf-8', 'iso-8859-15')
- class ::String
- def to_utf8
- ISO_8859_1_TO_UTF8.iconv self
- end
- end
- class Fuzzer
- def initialize(n, freqs = {})
- sum = freqs.inject(0.0) { |s, x| s + x.last }
- freqs.each_key { |x| freqs[x] /= sum }
- s = 0.0
- freqs.each_key do |x|
- freqs[x] = s .. (s + t = freqs[x])
- s += t
- end
- @freqs = freqs
- @n = n
- @alpha = (0..0xff).to_a
- end
- def random_string
- s = ''
- 30.times { s << @alpha[rand(@alpha.size)] }
- s.to_utf8
- end
- def pick
- r = rand
- found = @freqs.find { |k, f| f.include? rand }
- found && found.first
- end
- def make_pick
- k = pick
- case
- when k == Hash, k == Array
- k.new
- when k == true, k == false, k == nil
- k
- when k == String
- random_string
- when k == Fixnum
- rand(2 ** 30) - 2 ** 29
- when k == Bignum
- rand(2 ** 70) - 2 ** 69
- end
- end
- def fuzz(current = nil)
- if @n > 0
- case current
- when nil
- @n -= 1
- current = fuzz [ Hash, Array ][rand(2)].new
- when Array
- while @n > 0
- @n -= 1
- current << case p = make_pick
- when Array, Hash
- fuzz(p)
- else
- p
- end
- end
- when Hash
- while @n > 0
- @n -= 1
- current[random_string] = case p = make_pick
- when Array, Hash
- fuzz(p)
- else
- p
- end
- end
- end
- end
- current
- end
- end
- class MyState < JSON.state
- WS = " \r\t\n"
- def initialize
- super(
- :indent => make_spaces,
- :space => make_spaces,
- :space_before => make_spaces,
- :object_nl => make_spaces,
- :array_nl => make_spaces,
- :max_nesting => false
- )
- end
- def make_spaces
- s = ''
- rand(1).times { s << WS[rand(WS.size)] }
- s
- end
- end
- n = (ARGV.shift || 500).to_i
- loop do
- fuzzer = Fuzzer.new(n,
- Hash => 25,
- Array => 25,
- String => 10,
- Fixnum => 10,
- Bignum => 10,
- nil => 5,
- true => 5,
- false => 5
- )
- o1 = fuzzer.fuzz
- json = JSON.generate o1, MyState.new
- if $DEBUG
- puts "-" * 80
- puts json, json.size
- else
- puts json.size
- end
- begin
- o2 = JSON.parse(json, :max_nesting => false)
- rescue JSON::ParserError => e
- puts "Caught #{e.class}: #{e.message}\n#{e.backtrace * "\n"}"
- puts "o1 = #{o1.inspect}", "json = #{json}", "json_str = #{json.inspect}"
- puts "locals = #{local_variables.inspect}"
- exit
- end
- if o1 != o2
- puts "mismatch", "o1 = #{o1.inspect}", "o2 = #{o2.inspect}",
- "json = #{json}", "json_str = #{json.inspect}"
- puts "locals = #{local_variables.inspect}"
- end
- end
|