Module: Ibex::CLIFuzzRegressions
- Included in:
- CLIFuzz
- Defined in:
- lib/ibex/cli/fuzz_regressions.rb,
sig/ibex/cli/fuzz_regressions.rbs
Overview
Automatic bounded minimization and persistence for fuzz differences.
Instance Method Summary collapse
- #fuzz_regression_document(mismatch, result, grammar_path, external) ⇒ Hash[Symbol, Object?]
- #minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external) ⇒ Hash[Symbol, untyped]
- #persist_fuzz_regression(mismatch, result, grammar_path, external) ⇒ Hash[Symbol, String]?
- #validate_fuzz_regression_target!(path) ⇒ void
- #write_fuzz_budget_report(error, external, phase:) ⇒ Integer
- #write_fuzz_report(report) ⇒ void
- #write_minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external) ⇒ Integer
Instance Method Details
#fuzz_regression_document(mismatch, result, grammar_path, external) ⇒ Hash[Symbol, Object?]
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/ibex/cli/fuzz_regressions.rb', line 128 def fuzz_regression_document(mismatch, result, grammar_path, external) document = { ibex_report: "fuzz-regression", schema_version: 1, grammar: grammar_path, seed: @options.fetch(:fuzz_seed, 0), kind: mismatch[:kind], sentence: mismatch[:sentence], algorithms: mismatch[:outcomes].keys.reject { |name| name == :external }.map(&:to_s), original_tokens: mismatch[:tokens], minimized_tokens: result.items, bounds: mismatch[:bounds], reduction: { original_size: result.original_size, minimized_size: result.items.length, trials: result.trials, complete: result.complete } } #: Hash[Symbol, Object?] document[:external] = external if external document end |
#minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external) ⇒ Hash[Symbol, untyped]
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/ibex/cli/fuzz_regressions.rb', line 85 def minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external) result = fuzzer.minimize( mismatch, max_trials: @options.fetch(:fuzz_max_reduction_trials, 1_000) ) details = mismatch.details.dup #: Hash[Symbol, untyped] details[:minimized_tokens] = result.items details[:reduction] = { original_size: result.original_size, minimized_size: result.items.length, trials: result.trials, complete: result.complete } regression = persist_fuzz_regression( mismatch.details, result, grammar_path, external&.[](1) ) details[:regression] = regression if regression details end |
#persist_fuzz_regression(mismatch, result, grammar_path, external) ⇒ Hash[Symbol, String]?
104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/ibex/cli/fuzz_regressions.rb', line 104 def persist_fuzz_regression(mismatch, result, grammar_path, external) return if @options.fetch(:fuzz_save_regression, true) == false directory = @options.fetch(:fuzz_regression_dir, "test/fuzz/regressions") FileUtils.mkdir_p(directory) document = fuzz_regression_document(mismatch, result, grammar_path, external) source = "#{JSON.pretty_generate(document)}\n" digest = Digest::SHA256.hexdigest(source) seed = @options.fetch(:fuzz_seed, 0) path = File.join(directory, "fuzz-seed-#{seed}-#{digest.slice(0, 16)}.json") validate_fuzz_regression_target!(path) atomic_write_ir(path, source) { path: path, sha256: digest } end |
#validate_fuzz_regression_target!(path) ⇒ void
This method returns an undefined value.
120 121 122 123 124 |
# File 'lib/ibex/cli/fuzz_regressions.rb', line 120 def validate_fuzz_regression_target!(path) return unless File.symlink?(path) || (File.exist?(path) && File.stat(path).nlink > 1) raise Ibex::Error, "(fuzz):1:1: regression output refuses symlinks and files with multiple hard links" end |
#write_fuzz_budget_report(error, external, phase:) ⇒ Integer
73 74 75 76 77 78 79 80 81 |
# File 'lib/ibex/cli/fuzz_regressions.rb', line 73 def write_fuzz_budget_report(error, external, phase:) report = { ibex_report: "fuzz", schema_version: 1, result: "budget_exhausted", budget: error.details.merge(phase: phase) } #: Hash[Symbol, untyped] report[:external] = external[1] if external write_fuzz_report(report) 2 end |
#write_fuzz_report(report) ⇒ void
This method returns an undefined value.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/ibex/cli/fuzz_regressions.rb', line 44 def write_fuzz_report(report) if @options.fetch(:fuzz_format, "json") == "json" @stdout.puts JSON.pretty_generate(report) return end @stdout.puts("result=#{report.fetch(:result)}") case report.fetch(:result) when "no_difference_within_bounds" @stdout.puts("seed=#{report.fetch(:seed)} bounds=#{report.fetch(:bounds).inspect}") @stdout.puts("no difference found within the declared bounds; this is not a proof of equivalence") when "difference" mismatch = report.fetch(:mismatch) #: Hash[Symbol, untyped] @stdout.puts("kind=#{mismatch.fetch(:kind)} tokens=#{mismatch.fetch(:tokens).inspect}") @stdout.puts("minimized=#{mismatch.fetch(:minimized_tokens).inspect} " \ "reduction_complete=#{mismatch.dig(:reduction, :complete)}") @stdout.puts("regression=#{mismatch.dig(:regression, :path)}") if mismatch[:regression] when "budget_exhausted" @stdout.puts("search incomplete: #{report.dig(:budget, :message)}") end external = report[:external] #: fuzz_external_description? return unless external @stdout.puts("target_runtime=#{external.fetch(:target_runtime)}") @stdout.puts("target_command=#{external.fetch(:command)}") end |
#write_minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external) ⇒ Integer
33 34 35 36 37 38 39 40 41 |
# File 'lib/ibex/cli/fuzz_regressions.rb', line 33 def write_minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external) details = minimized_fuzz_mismatch(fuzzer, mismatch, grammar_path, external) report = { ibex_report: "fuzz", schema_version: 1, result: "difference", mismatch: details } #: Hash[Symbol, untyped] report[:external] = external[1] if external write_fuzz_report(report) 1 end |