-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add JacocoCoverageRunner to junitrunner.
(series 3/4 of open-sourcing coverage command for java test) -- PiperOrigin-RevId: 141046146 MOS_MIGRATED_REVID=141046146
- Loading branch information
1 parent
21759e7
commit af3c412
Showing
14 changed files
with
1,514 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
src/java_tools/junitrunner/java/com/google/testing/coverage/BUILD
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", | ||
], | ||
) |
8 changes: 8 additions & 0 deletions
8
src/java_tools/junitrunner/java/com/google/testing/coverage/BUILD.tools
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"], | ||
) |
209 changes: 209 additions & 0 deletions
209
src/java_tools/junitrunner/java/com/google/testing/coverage/BitField.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
86 changes: 86 additions & 0 deletions
86
src/java_tools/junitrunner/java/com/google/testing/coverage/BranchCoverageDetail.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.