class Bashcov::Lexer
Simple lexer which analyzes Bash files in order to get information for coverage
Constants
- IGNORE_END_WITH
Lines ending with one of these tokens are irrelevant for coverage
- IGNORE_IS
Lines containing only one of these keywords are irrelevant for coverage
- IGNORE_START_WITH
Lines starting with one of these tokens are irrelevant for coverage
Public Class Methods
new(filename, coverage)
click to toggle source
@param [String] filename File to analyze @param [Hash] coverage Coverage with executed lines marked @raise [ArgumentError] if the given filename
is invalid.
# File lib/bashcov/lexer.rb, line 21 def initialize(filename, coverage) @filename = filename @coverage = coverage raise ArgumentError, "#{@filename} is not a file" unless File.file?(@filename) end
Public Instance Methods
complete_coverage()
click to toggle source
Process and complete initial coverage. @return [void]
# File lib/bashcov/lexer.rb, line 30 def complete_coverage lines = File.read(@filename).encode("utf-8", invalid: :replace).lines lines.each_with_index do |line, lineno| # multi-line arrays mark_multiline( lines, lineno, /\A[^\n]*\b=\([^()]*\)/, forward: false ) # heredoc mark_multiline( lines, lineno, /\A[^\n]+<<-?'?(\w+)'?.*$.*\1/m ) # multiline string concatenated with backslashes mark_multiline( lines, lineno, /\A[^\n]+\\$(\s*['"][^'"]*['"]\s*\\$){1,}\s*['"][^'"]*['"]\s*$/ ) # simple line continuations with backslashes mark_multiline( lines, lineno, /\A([^\n&|;]*[^\\&|;](\\\\)*\\\n)+[^\n&|;]*[^\n\\&|;](\\\\)*$/ ) # multiline string concatenated with newlines %w[' "].each do |char| mark_multiline( lines, lineno, /\A[^\n]+[\s=]+#{char}[^#{char}]*#{char}/m, forward: false ) end mark_line(line, lineno) end end
Private Instance Methods
mark_line(line, lineno)
click to toggle source
# File lib/bashcov/lexer.rb, line 92 def mark_line(line, lineno) return unless @coverage[lineno] == Bashcov::Line::IGNORED @coverage[lineno] = Bashcov::Line::UNCOVERED if relevant?(line) end
mark_multiline(lines, lineno, regexp, forward: true)
click to toggle source
# File lib/bashcov/lexer.rb, line 74 def mark_multiline(lines, lineno, regexp, forward: true) seek_forward = lines[lineno..].join return unless (multiline_match = seek_forward.match(regexp)) length = multiline_match.to_s.count($/) first, last = lineno + 1, lineno + length range = (forward ? first.upto(last) : (last - 1).downto(first - 1)) reference_lineno = (forward ? first - 1 : last) # don't seek backward if first line is already covered return if !forward && @coverage[first - 1] range.each do |sub_lineno| # mark related lines with the same coverage as the reference line @coverage[sub_lineno] ||= @coverage[reference_lineno] end end
relevant?(line)
click to toggle source
# File lib/bashcov/lexer.rb, line 98 def relevant?(line) line.sub!(/\s#.*\Z/, "") # remove comments line.strip! relevant = true relevant &= false if line.empty? || IGNORE_IS.include?(line) || line.start_with?(*IGNORE_START_WITH) || line.end_with?(*IGNORE_END_WITH) relevant &= false if line =~ /\A[a-zA-Z_][a-zA-Z0-9_:]*\(\)/ # function declared without the `function` keyword relevant &= false if line =~ /\A[^)]+\)\Z/ # case statement selector, e.g. `--help)` relevant end