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
- 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
- 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.
49 50 51 52 53 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 49 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.
38 39 40 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 38 def corpus_digest @corpus_digest end |
#document ⇒ Hash[String, untyped] (readonly)
Returns the value of attribute document.
38 39 40 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 38 def document @document end |
#path ⇒ Pathname? (readonly)
Returns the value of attribute path.
38 39 40 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 38 def path @path end |
Class Method Details
.load(path) ⇒ LocalBenchmark
40 41 42 43 44 45 46 47 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 40 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]
125 126 127 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 125 def case_by_id(id) document.fetch('cases').find { |item| item['id'] == id } || error!("unknown case: #{id}") end |
#cases ⇒ Array[Hash[String, untyped]]
70 71 72 73 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 70 def cases validate! document.fetch('cases') end |
#select(profile:, changed_paths: []) ⇒ Hash[String, untyped]
75 76 77 78 79 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 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 75 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 { |id| case_by_id(id)['operation'] == 'merge3' }, 'budgets' => definition.fetch('budgets'), 'explanation' => explanation( profile, inferred: inferred, direct: direct, sentinels: sentinels, population: population, neighbors: neighbors ) } end |
#validate! ⇒ true
55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/ast/merge/git/local_benchmark.rb', line 55 def validate! require_keys(document, %w[schema_version kind id version extends provenance profiles capability_map selection 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_cases! validate_summary! true end |