Skip to content

Commit

Permalink
Add JacocoCoverageRunner to junitrunner.
Browse files Browse the repository at this point in the history
(series 3/4 of open-sourcing coverage command for java test)

--
PiperOrigin-RevId: 141046146
MOS_MIGRATED_REVID=141046146
  • Loading branch information
hermione521 authored and damienmg committed Dec 5, 2016
1 parent 21759e7 commit af3c412
Show file tree
Hide file tree
Showing 14 changed files with 1,514 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,11 @@ genrule(
"//src/java_tools/singlejar:SingleJar_deploy.jar",
"//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass:GenClass_deploy.jar",
"//src/java_tools/junitrunner/java/com/google/testing/junit/runner:Runner_deploy.jar",
"//src/java_tools/junitrunner/java/com/google/testing/coverage:embedded_tools",
"//third_party/ijar",
"//third_party/java/apkbuilder:embedded_tools",
"//third_party/java/apksig:embedded_tools",
"//third_party/java/jacoco:srcs",
] + select({
":darwin": [
":darwin_tools",
Expand Down Expand Up @@ -284,6 +286,7 @@ filegroup(
name = "srcs",
srcs = glob(["**"]) + [
"//src/java_tools/buildjar:srcs",
"//src/java_tools/junitrunner/java/com/google/testing/coverage:srcs",
"//src/java_tools/junitrunner/java/com/google/testing/junit/junit4:srcs",
"//src/java_tools/junitrunner/java/com/google/testing/junit/runner:srcs",
"//src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal:srcs",
Expand Down
71 changes: 71 additions & 0 deletions src/java_tools/junitrunner/java/com/google/testing/coverage/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package(
default_visibility = ["//visibility:legacy_public"],
)

licenses(["notice"])

filegroup(
name = "bazel-srcs",
testonly = 0,
srcs = glob([
"**/*.java",
"BUILD.tools",
]),
visibility = ["//third_party/bazel:__subpackages__"],
)

filegroup(
name = "srcs",
srcs = glob(["**"]),
)

filegroup(
name = "embedded_tools",
srcs = [
"BUILD.tools",
"JacocoCoverage_deploy.jar",
],
)

filegroup(
name = "jacoco_coverage_runtime",
srcs = ["JacocoCoverage_deploy.jar"],
)

# Bazel custom Jacoco runner used to provide proper initialization and lcov
# report generation when using offline Jacoco instrumentation.
# This target should not be used as a dependency (except when writing tests for
# it).
#
# An implicit dependency of all "java_binary" rules.
java_binary(
name = "JacocoCoverage",
srcs = [
"BranchCoverageDetail.java",
"BranchDetailAnalyzer.java",
"BranchExp.java",
"ClassProbesMapper.java",
"CovExp.java",
"JacocoCoverageRunner.java",
"JacocoLCOVFormatter.java",
"MethodProbesMapper.java",
"ProbeExp.java",
],
deps = [
":bitfield",
"//third_party/java/jacoco:blaze-agent-neverlink",
"//third_party/java/jacoco:core",
"//third_party/java/jacoco:report",
],
)

java_library(
name = "bitfield",
srcs = [
"BitField.java",
"IllegalStringException.java",
],
deps = [
"//third_party:apache_commons_lang",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package(
default_visibility = ["//visibility:public"],
)

java_import(
name = "JacocoCoverage",
jars = ["JacocoCoverage_deploy.jar"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// Copyright 2016 The Bazel Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.testing.coverage;

import java.util.Arrays;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

/**
* Abstracts bit field operations.
*/
public class BitField {

private byte[] bytes;

private static final int[] BIT_COUNT_LOOKUP = new int[256];

static {
BIT_COUNT_LOOKUP[0] = 0;
BIT_COUNT_LOOKUP[1] = 1;
for (int i = 2; i < 256; i += 2) {
int count = BIT_COUNT_LOOKUP[i / 2];
BIT_COUNT_LOOKUP[i] = count;
BIT_COUNT_LOOKUP[i + 1] = count + 1;
}
}

public BitField() {
this(new byte[0]);
}

public BitField(byte[] bytes) {
this.bytes = bytes.clone();
}

/**
* Returns a copy of the underlying byte array.
*
* @return byte array copy
*/
public byte[] getBytes() {
return bytes.clone();
}

/**
* Sets or clears a bit at the given index.
*
* @param index bit index
*/
public void setBit(int index) {
setBit(index, true);
}

/**
* Clears a bit at the given index
*
* @param index bit index
*/
public void clearBit(int index) {
setBit(index, false);
}

/**
* Sets or clears a bit at the given index.
*
* @param index bit index
*/
private void setBit(int index, boolean isSet) {
int byteIndex = index / 8;
int newByteSize = byteIndex + 1;
if (bytes.length < newByteSize) {
bytes = Arrays.copyOf(bytes, newByteSize);
}

int bitIndex = index % 8;
int mask = 1 << bitIndex;

if (isSet) {
bytes[byteIndex] = (byte) (bytes[byteIndex] | mask);
} else {
bytes[byteIndex] = (byte) (bytes[byteIndex] & ~mask);
}
}

/**
* Checks whether a bit at the given index is set.
*
* @param index bit index
* @return true if set, false otherwise
*/
public boolean isBitSet(int index) {
int byteIndex = index / 8;

if (byteIndex >= bytes.length) {
return false;
}

int bitIndex = index % 8;
int mask = 1 << bitIndex;
return (bytes[byteIndex] & mask) != 0;
}

/** Performs a non-destructive bit-wise "and" of this bit field with another one. */
public BitField and(BitField other) {
int size = Math.min(bytes.length, other.bytes.length);
byte[] result = new byte[size];

for (int i = 0; i < size; i++) {
result[i] = (byte) (bytes[i] & other.bytes[i]);
}

return new BitField(result);
}

/**
* Performs a non-destructive bit-wise merge of this bit field and another one.
*
* @param other the other bit field
* @return this bit field
*/
public BitField or(BitField other) {
byte[] largerArray, smallerArray;
if (bytes.length < other.bytes.length) {
largerArray = other.bytes;
smallerArray = bytes;
} else {
largerArray = bytes;
smallerArray = other.bytes;
}

// Start out with a copy of the larger of the two arrays.
byte[] result = Arrays.copyOf(largerArray, largerArray.length);

for (int i = 0; i < smallerArray.length; i++) {
result[i] |= smallerArray[i];
}

return new BitField(result);
}

/**
* Compares two bit fields for equality.
*
* @param obj another object
* @return true if the other object is a bit field with the same bits set
*/
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}

/**
* Compare a BitField object with an array of bytes
*
* @param other a byte array to compare to
* @return true if the underlying byte array is equal to the given byte array
*/
public boolean equals(byte[] other) {
return Arrays.equals(bytes, other);
}

@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}

public int countBitsSet() {
int count = 0;
for (byte b : bytes) {
// JAVA doesn't have the concept of unsigned byte; need to & with 255
// to avoid exception of IndexOutOfBoundException when b < 0.
count += BIT_COUNT_LOOKUP[0xFF & b];
}
return count;
}

public BitField not() {
byte[] invertedBytes = new byte[bytes.length];
for (int i = 0; i < bytes.length; i++) {
invertedBytes[i] = Integer.valueOf(~bytes[i]).byteValue();
}
return new BitField(invertedBytes);
}

public int sizeInBits() {
return bytes.length * 8;
}

public boolean any() {
for (int i = 0; i < bytes.length; i++) {
if (bytes[i] != (byte) 0) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2016 The Bazel Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.testing.coverage;

import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

/** Details of branch coverage information. */
public class BranchCoverageDetail {
private final Map<Integer, BitField> branchTaken;
private final Map<Integer, Integer> branches;

public BranchCoverageDetail() {
branchTaken = new TreeMap<Integer, BitField>();
branches = new TreeMap<Integer, Integer>();
}

private BitField getBranchForLine(int line) {
BitField value = branchTaken.get(line);
if (value != null) {
return value;
}
value = new BitField();
branchTaken.put(line, value);
return value;
}

/** Returns true if the line has branches. */
public boolean hasBranches(int line) {
return branches.containsKey(line);
}

/** Sets the number of branches entry. */
public void setBranches(int line, int n) {
branches.put(line, n);
}

/** Gets the number of branches in the line, returns 0 if there is no branch. */
public int getBranches(int line) {
Integer value = branches.get(line);
if (value == null) {
return 0;
}
return value;
}

/** Sets the taken bit of the given line for the given branch index. */
public void setTakenBit(int line, int branchIdx) {
getBranchForLine(line).setBit(branchIdx);
}

public boolean getTakenBit(int line, int branchIdx) {
return getBranchForLine(line).isBitSet(branchIdx);
}

/** Calculate executed bit using heuristics. */
public boolean getExecutedBit(int line) {
// If any of the branch is taken, the branch must have executed. Otherwise assume it is not.
return getBranchForLine(line).any();
}

/** Returns line numbers where more than one branch is present. */
public Set<Integer> linesWithBranches() {
Set<Integer> result = new TreeSet<Integer>();
for (int i : branches.keySet()) {
if (branches.get(i) > 1) {
result.add(i);
}
}
return result;
}
}
Loading

0 comments on commit af3c412

Please sign in to comment.