Module: Ibex::CLIGrammarTests

Defined in:
lib/ibex/cli/grammar_tests.rb,
sig/ibex/cli/grammar_tests.rbs

Overview

%test command parsing and human-readable result reporting.

Instance Method Summary collapse

Instance Method Details

#default_grammar_test_settingsHash[Symbol, untyped]

RBS:

  • () -> Hash[Symbol, untyped]

Returns:

  • (Hash[Symbol, untyped])


48
49
50
51
52
53
54
55
# File 'lib/ibex/cli/grammar_tests.rb', line 48

def default_grammar_test_settings
  {
    mode: Configuration::Registry.fetch("grammar.mode").default,
    algorithm: Configuration::Registry.fetch("parser.algorithm").default,
    entry_isolation: Configuration::Registry.fetch("parser.entries").default == :isolated,
    timeout: GrammarTests::DEFAULT_TIMEOUT, coverage: nil
  }
end

#grammar_test_failure(result) ⇒ String

RBS:

  • (GrammarTests::Result result) -> String

Parameters:

Returns:

  • (String)


134
135
136
137
138
# File 'lib/ibex/cli/grammar_tests.rb', line 134

def grammar_test_failure(result)
  return "parser #{result.actual}ed the input" unless result.actual == :error

  "#{result.error_class}: #{result.error_message}"
end

#grammar_test_option_parser(settings) ⇒ OptionParser

RBS:

  • (Hash[Symbol, untyped] settings) -> OptionParser

Parameters:

  • settings (Hash[Symbol, untyped])

Returns:

  • (OptionParser)


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/ibex/cli/grammar_tests.rb', line 58

def grammar_test_option_parser(settings)
  OptionParser.new do |options|
    options.banner = "Usage: ibex test [options] grammarfile"
    options.on("--mode=MODE", %w[default extended], "grammar mode") do |value|
      settings[:mode] = value.to_sym
      mark_configuration_option(:mode)
    end
    options.on("--algorithm=NAME", %w[slr lalr ielr lr1], "parser construction algorithm") do |value|
      settings[:algorithm] = value.to_sym
      mark_configuration_option(:algorithm)
    end
    options.on("--entry-isolation", "build independent state sets for each start symbol") do
      settings[:entry_isolation] = true
      mark_configuration_option(:entry_isolation)
    end
    options.on("--timeout=SECONDS", Integer, "whole-suite timeout (default: 10)") do |value|
      raise OptionParser::InvalidArgument, "timeout must be positive" unless value.positive?

      settings[:timeout] = value
    end
    options.on("--coverage=PERCENT", Integer, "require production coverage (1..100)") do |value|
      settings[:coverage] = value
    end
    options.on("-h", "--help", "show this help") { settings[:help] = true }
  end
end

#normalize_analysis_grammar(resolution) ⇒ IR::Grammar

RBS:

  • (Frontend::Resolution resolution) -> IR::Grammar

Parameters:

Returns:



95
96
97
98
# File 'lib/ibex/cli/grammar_tests.rb', line 95

def normalize_analysis_grammar(resolution)
  grammar = Normalizer.new(resolution, mode: configuration_value("grammar.mode")).normalize
  activate_analysis_grammar(grammar)
end

#render_grammar_test_coverage(coverage, minimum, failures) ⇒ Integer

RBS:

  • (GrammarTests::ProductionCoverage coverage, Integer minimum, Integer failures) -> Integer

Parameters:

Returns:

  • (Integer)


121
122
123
124
125
126
127
128
129
130
131
# File 'lib/ibex/cli/grammar_tests.rb', line 121

def render_grammar_test_coverage(coverage, minimum, failures)
  @stdout.puts(
    format(
      "production coverage: %<covered>d/%<total>d (%<percentage>.1f%%), required %<minimum>d%%",
      covered: coverage.covered_ids.length, total: coverage.production_count,
      percentage: coverage.percentage, minimum: minimum
    )
  )
  @stdout.puts("missing production ids: #{coverage.missing_ids.join(', ')}") unless coverage.complete?
  failures.zero? && coverage.meets?(minimum) ? 0 : 1
end

#render_grammar_test_results(results) ⇒ Integer

RBS:

  • (Array[GrammarTests::Result] results) -> Integer

Parameters:

Returns:

  • (Integer)


105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/ibex/cli/grammar_tests.rb', line 105

def render_grammar_test_results(results)
  failures = 0
  results.each_with_index do |result, index|
    label = "#{result.expectation} #{result.location[:file]}:#{result.location[:line]}"
    if result.passed?
      @stdout.puts("ok #{index + 1} - #{label}")
    else
      failures += 1
      @stdout.puts("not ok #{index + 1} - #{label} (#{grammar_test_failure(result)})")
    end
  end
  @stdout.puts("#{results.length} tests, #{failures} failures")
  failures.zero? ? 0 : 1
end

#run_grammar_tests_command(arguments) ⇒ Integer

RBS:

  • (Array[String] arguments) -> Integer

Parameters:

  • arguments (Array[String])

Returns:

  • (Integer)


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/ibex/cli/grammar_tests.rb', line 21

def run_grammar_tests_command(arguments)
  settings = default_grammar_test_settings
  options = grammar_test_option_parser(settings)
  remaining = options.parse(arguments)
  return print_help(options) if settings[:help]
  unless settings[:coverage].nil? || settings[:coverage].between?(1, 100)
    raise OptionParser::InvalidArgument, "coverage must be between 1 and 100"
  end

  path = single_grammar_test_path(remaining, options)
  @options[:mode] = settings[:mode]
  @options[:algorithm] = settings[:algorithm]
  @options[:entry_isolation] = settings[:entry_isolation]
  resolution = resolve_grammar_path(path)
  grammar = normalize_analysis_grammar(resolution)
  handle_grammar_warnings(grammar, path)
  automaton = build_automaton(grammar, path)
  runner = GrammarTests::Runner.new(automaton, timeout: settings[:timeout])
  results = runner.run
  failures = render_grammar_test_results(results)
  coverage = settings[:coverage]
  return failures unless coverage

  render_grammar_test_coverage(runner.production_coverage(results), coverage, failures)
end

#single_grammar_test_path(remaining, options) ⇒ String

RBS:

  • (Array[String] remaining, OptionParser options) -> String

Parameters:

  • remaining (Array[String])
  • options (OptionParser)

Returns:

  • (String)


86
87
88
89
90
91
92
# File 'lib/ibex/cli/grammar_tests.rb', line 86

def single_grammar_test_path(remaining, options)
  return remaining.fetch(0) if remaining.one?

  raise OptionParser::MissingArgument, options.banner if remaining.empty?

  raise OptionParser::InvalidArgument, "test accepts exactly one grammarfile"
end