Class: Ast::Merge::Git::LocalBenchmark
- Inherits:
-
Object
- Object
- Ast::Merge::Git::LocalBenchmark
- Defined in:
- lib/ast/merge/git/local_benchmark.rb,
sig/ast/merge/git.rbs
Overview
Offline Slice 1022-compatible corpus validation and deterministic selection. rubocop:disable Metrics/AbcSize, Metrics/ClassLength, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity -- benchmark evidence is intentionally explicit
Defined Under Namespace
Classes: Error
Constant Summary collapse
- SCHEMA =
'structuredmerge.benchmark.corpus/v1'- CASE_SCHEMA =
'structuredmerge.benchmark/v1'- OPERATIONS =
%w[merge3 metamorphic diff].freeze
- EXECUTABLE_OPERATIONS =
%w[merge3 metamorphic].freeze
- PARTITIONS =
%w[sentinel gold metamorphic].freeze
- EXPECTATIONS =
%w[clean conflict error excluded_ambiguous].freeze
- SEVERITIES =
%w[none low high critical].freeze
- PRESERVATION =
%w[required allowed_to_change not_applicable].freeze
- TRANSFORMATIONS =
%w[ rename move reorder formatting comment independent_edit delete_modify duplicate_key_identity schema_aware_mutation ].freeze
- METAMORPHIC_INVARIANTS =
%w[ comment-retained no-semantic-edit same-json-value same-jsonc-value ].freeze
- ID_PATTERN =
/\A[a-z0-9]+(?:[.-][a-z0-9]+)*\z/- PROVENANCE_FIELDS =
%w[ origin_uri revision spdx_license license_evidence_uri authorship author_review reviewer derivation ].freeze
- SELECTOR_FIELDS =
%w[provider_id family dialect backend profile require].freeze
- INPUT_ROLES =
{ 'merge3' => %w[base ours theirs], 'metamorphic' => %w[source transformed], 'diff' => %w[before after] }.freeze
Instance Attribute Summary collapse
-
#corpus_digest ⇒ String
readonly
Returns the value of attribute corpus_digest.
-
#document ⇒ Hash[String, untyped]
readonly
Returns the value of attribute document.
-
#path ⇒ Pathname?
readonly
Returns the value of attribute path.
Class Method Summary collapse
Instance Method Summary collapse
- #case_by_id(id) ⇒ Hash[String, untyped]
- #cases ⇒ Array[Hash[String, untyped]]
-
#initialize(document, path: nil, corpus_digest: nil) ⇒ LocalBenchmark
constructor
A new instance of LocalBenchmark.
- #select(profile:, changed_paths: []) ⇒ Hash[String, untyped]
- #validate! ⇒ true
Constructor Details
#initialize(document, path: nil, corpus_digest: nil) ⇒ LocalBenchmark
Returns a new instance of LocalBenchmark.
53 54 55 56 57 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 53 def initialize(document, path: nil, corpus_digest: nil) @document = document @path = path && Pathname(path). @corpus_digest = corpus_digest || digest(canonical_json(document)) end |
Instance Attribute Details
#corpus_digest ⇒ String (readonly)
Returns the value of attribute corpus_digest.
42 43 44 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 42 def corpus_digest @corpus_digest end |
#document ⇒ Hash[String, untyped] (readonly)
Returns the value of attribute document.
42 43 44 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 42 def document @document end |
#path ⇒ Pathname? (readonly)
Returns the value of attribute path.
42 43 44 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 42 def path @path end |
Class Method Details
.load(path) ⇒ LocalBenchmark
44 45 46 47 48 49 50 51 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 44 def self.load(path) source = File.binread(path) new(JSON.parse(source), path: path, corpus_digest: Digest::SHA256.hexdigest(source)).tap(&:validate!) rescue JSON::ParserError => e raise Error, "invalid corpus JSON: #{e.}" rescue SystemCallError => e raise Error, "cannot read corpus: #{e.}" end |
Instance Method Details
#case_by_id(id) ⇒ Hash[String, untyped]
132 133 134 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 132 def case_by_id(id) document.fetch('cases').find { |item| item['id'] == id } || error!("unknown case: #{id}") end |
#cases ⇒ Array[Hash[String, untyped]]
75 76 77 78 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 75 def cases validate! document.fetch('cases') end |
#select(profile:, changed_paths: []) ⇒ Hash[String, untyped]
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 80 def select(profile:, changed_paths: []) validate! profile = profile.to_s definition = document.fetch('profiles')[profile] error!("unknown profile: #{profile}") unless definition paths = changed_paths.map(&:to_s).uniq.sort inferred = paths.to_h { |changed| [changed, capabilities_for(changed)] } capabilities = inferred.values.flatten.uniq.sort direct = cases.select { |item| direct_case?(item, capabilities) }.map { |item| item.fetch('id') } sentinels = definition.fetch('mandatory_sentinels') selected = ordered(sentinels + direct) population = cases.map { |item| item.fetch('id') } - selected neighbors = neighbor_order(population).first(definition.fetch('neighbor_count')) selected = ordered(selected + neighbors) selected = sentinels if profile == 'micro' { 'profile' => profile, 'seed' => document.dig('selection', 'seed'), 'selected_case_ids' => selected, 'excluded_case_ids' => cases.map { |item| item.fetch('id') } - selected, 'changed_paths' => inferred.map { |changed, caps| { 'path' => changed, 'capabilities' => caps } }, 'inferred_capabilities' => capabilities, 'direct_cases' => direct, 'direct_case_reasons' => capabilities.to_h do |capability| matching = cases.filter_map do |item| item['id'] if direct_case?(item, [capability]) end [capability, matching] end, 'sentinels' => sentinels, 'neighbor_sample' => { 'population' => population, 'ordering_algorithm' => document.dig('selection', 'neighbor_order'), 'seed' => document.dig('selection', 'seed'), 'selected_case_ids' => neighbors }, 'unsupported_selected_cases' => selected.reject do |id| EXECUTABLE_OPERATIONS.include?(case_by_id(id)['operation']) end, 'budgets' => definition.fetch('budgets'), 'explanation' => explanation( profile, inferred: inferred, direct: direct, sentinels: sentinels, population: population, neighbors: neighbors ) } end |
#validate! ⇒ true
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 59 def validate! require_keys(document, %w[schema_version kind id version extends provenance profiles capability_map selection competitors cases expected_summary], 'corpus') error!('unsupported corpus schema') unless document['schema_version'] == SCHEMA error!('corpus must extend Slice 1022 v1') unless document['extends'] == CASE_SCHEMA error!('network must be denied') unless document['network_policy'] == 'denied' error!('services must be empty') unless document['services'] == [] validate_provenance!(document['provenance'], 'corpus') validate_profiles! validate_capability_map! validate_competitors! validate_cases! validate_summary! true end |