Skip to content

Commit

Permalink
Fixes #1981. Wrong number of reported lines in Coverage API
Browse files Browse the repository at this point in the history
This ended up being much simpler than I think any of us thought.
Base problem was any lines after last *newline* marked node would
not update the primitive 'coverage' array which coverage sets up.
So we just call one method at end up parse to update that array
to include final lines of the file.
enebo committed Mar 21, 2016
1 parent 8e70078 commit b2a0619
Showing 4 changed files with 135 additions and 10 deletions.
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/parser/Parser.java
Original file line number Diff line number Diff line change
@@ -160,6 +160,7 @@ public Node parse(String file, LexerSource lexerSource, DynamicScope blockScope,

// set coverage baseline into coverage data
if (runtime.getCoverageData().isCoverageEnabled()) {
configuration.growCoverageLines(parser.lexer.lineno());
runtime.getCoverageData().prepareCoverage(file, configuration.getCoverage());
}

27 changes: 17 additions & 10 deletions core/src/main/java/org/jruby/parser/ParserConfiguration.java
Original file line number Diff line number Diff line change
@@ -186,20 +186,27 @@ public void coverLine(int i) {
if (i < 0) return; // JRUBY-6868: why would there be negative line numbers?

if (runtime.getCoverageData().isCoverageEnabled()) {
if (coverage == null) {
coverage = new int[i + 1];
} else if (coverage.length <= i) {
int[] newCoverage = new int[i + 1];
Arrays.fill(newCoverage, -1);
System.arraycopy(coverage, 0, newCoverage, 0, coverage.length);
coverage = newCoverage;
}

// zero means coverable, but not yet covered
growCoverageLines(i);
coverage[i] = 0;
}
}

/**
* Called by coverLine to grow it large enough to add new covered line.
* Also called at end up parse to pick up any extra non-code lines which
* should be marked -1 for not valid code lines.
*/
public void growCoverageLines(int i) {
if (coverage == null) {
coverage = new int[i + 1];
} else if (coverage.length <= i) {
int[] newCoverage = new int[i + 1];
Arrays.fill(newCoverage, -1);
System.arraycopy(coverage, 0, newCoverage, 0, coverage.length);
coverage = newCoverage;
}
}

/**
* Get the coverage array, indicating all coverable lines
*/
116 changes: 116 additions & 0 deletions core/src/main/ruby/jruby/java/core_ext/#object.rb#
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
class Object
# include the class specified by +include_class+ into the current namespace,
# using either its base name or by using a name returned from an optional block,
# passing all specified classes in turn and providing the block package name
# and base class name.
def include_class(include_class, &block)
warn "#{__method__} is deprecated. Use java_import."
java_import(include_class, &block)
end

# TODO: this can go away now, but people may be using it
def java_kind_of?(other)
return true if self.kind_of?(other)
return false unless self.respond_to?(:java_class) && other.respond_to?(:java_class) &&
other.kind_of?(Module) && !self.kind_of?(Module)
return other.java_class.assignable_from?(self.java_class)
end

# Import one or many Java classes as follows:
#
# java_import java.lang.System
# java_import java.lang.System, java.lang.Thread
# java_import [java.lang.System, java.lang.Thread]
#
# Optionally java_import can also include a block to be used for custom
#
#
def java_import(*import_classes)
import_classes = import_classes.each_with_object([]) do |classes, flattened|
if classes.is_a?(Array)
flattened.push(*classes)
else
flattened.push(classes)
end
end

import_classes.map do |import_class|
case import_class
when String
cc = java.lang.Character
valid_name = import_class.split(".").all? do |frag|
cc.java_identifier_start? frag[0].ord and
frag.each_char.all? {|c| cc.java_identifier_part? c.ord }
end
unless valid_name
raise ArgumentError.new "not a valid Java identifier: #{import_class}"
end
# pull in the class
raise ArgumentError.new "must use jvm-style name: #{import_class}" if import_class.include? "::"
import_class = JavaUtilities.get_proxy_class(import_class)
when Module
if import_class.respond_to? "java_class"
# ok, it's a proxy
else
raise ArgumentError.new "not a Java class or interface: #{import_class}"
end
else
raise ArgumentError.new "invalid Java class or interface: #{import_class}"
end

java_class = import_class.java_class
class_name = java_class.simple_name

if block_given?
package = java_class.package

# package can be nil if it's default or no package was defined by the classloader
if package
package_name = package.name
elsif java_class.canonical_name =~ /(.*)\.[^.]$/
package_name = $1
else
package_name = ""
end

constant = yield(package_name, class_name)
else
constant = class_name

# Inner classes are separated with $, get last element
if constant =~ /\$([^$])$/
constant = $1
end
end

unless constant =~ /^[A-Z].*/
raise ArgumentError.new "cannot import class `" + java_class.name + "' as `" + constant + "'"
end

# JRUBY-3453: Make import not complain if Java already has already imported the specific Java class
# If no constant is defined, or the constant is not already set to the java_import, assign it
eval_str = "if !defined?(#{constant}) || #{constant} != import_class; #{constant} = import_class; end"
if Module === self
class_eval(eval_str, __FILE__, __LINE__)
else
eval(eval_str, binding, __FILE__, __LINE__)
end

import_class
end
end

private :java_import

def handle_different_imports(*args, &block)
if args.first.respond_to?(:java_class)
java_import(*args, &block)
else
other_import(*args, &block)
end
end

unless respond_to?(:import)
alias :import :java_import
end
end
1 change: 1 addition & 0 deletions core/src/main/ruby/jruby/java/core_ext/.#object.rb

0 comments on commit b2a0619

Please sign in to comment.