Skip to content

Commit

Permalink
Merge pull request #1150 from hcoles/feature/results_interceptors
Browse files Browse the repository at this point in the history
New extension points
  • Loading branch information
hcoles authored Jan 25, 2023
2 parents 598e5f1 + 95e8102 commit ee6e291
Show file tree
Hide file tree
Showing 38 changed files with 586 additions and 263 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ Read all about it at http://pitest.org

## Releases

## 1.10.5 (unreleased)
## 1.11.0 (unreleased)

* #1138 Do not mutate redundant fall through to default switch cases
* #1150 New extension points

Note that #1150 includes breaking interface changes which may require updates to third party plugins.

## 1.10.4

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.pitest.classpath.ClassPath;
import org.pitest.classpath.ClassPathRoot;
import org.pitest.classpath.CodeSource;
import org.pitest.classpath.DefaultCodeSource;
import org.pitest.classpath.DirectoryClassPathRoot;
import org.pitest.classpath.PathFilter;
import org.pitest.classpath.ProjectClassPaths;
Expand All @@ -30,7 +31,7 @@ class CodeSourceAggregator {
}

public CodeSource createCodeSource() {
return new CodeSource(createProjectClassPaths());
return new DefaultCodeSource(createProjectClassPaths());
}

private ProjectClassPaths createProjectClassPaths() {
Expand Down
74 changes: 10 additions & 64 deletions pitest-entry/src/main/java/org/pitest/classpath/CodeSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,89 +3,35 @@
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.pitest.bytecode.analysis.ClassTree;
import org.pitest.classinfo.ClassByteArraySource;
import org.pitest.classinfo.ClassInfo;
import org.pitest.classinfo.ClassInfoSource;
import org.pitest.classinfo.ClassName;
import org.pitest.classinfo.NameToClassInfo;
import org.pitest.classinfo.Repository;
import org.pitest.classinfo.TestToClassMapper;
import org.pitest.functional.Streams;

/**
* Provides access to code and tests on the classpath
*/
public class CodeSource implements ClassInfoSource, ClassByteArraySource {
public interface CodeSource extends ClassInfoSource, ClassByteArraySource {

private final ProjectClassPaths classPath;
private final Repository classRepository;
Stream<ClassTree> codeTrees();

public CodeSource(final ProjectClassPaths classPath) {
this(classPath, new Repository(new ClassPathByteArraySource(
classPath.getClassPath())));
}
Set<ClassName> getCodeUnderTestNames();

CodeSource(final ProjectClassPaths classPath,
final Repository classRepository) {
this.classPath = classPath;
this.classRepository = classRepository;
}
Stream<ClassTree> testTrees();

public Stream<ClassTree> codeTrees() {
return this.classPath.code().stream()
.map(c -> this.getBytes(c.asJavaName()))
.filter(Optional::isPresent)
.map(maybe -> ClassTree.fromBytes(maybe.get()));
}
ClassPath getClassPath();

public Set<ClassName> getCodeUnderTestNames() {
return this.classPath.code().stream().collect(Collectors.toSet());
}
Optional<ClassName> findTestee(String className);

public Stream<ClassTree> testTrees() {
return this.classPath.test().stream()
.map(c -> this.getBytes(c.asJavaName()))
.filter(Optional::isPresent)
.map(maybe -> ClassTree.fromBytes(maybe.get()))
.filter(t -> !t.isAbstract());
}

public ClassPath getClassPath() {
return this.classPath.getClassPath();
}

public Optional<ClassName> findTestee(final String className) {
final TestToClassMapper mapper = new TestToClassMapper(this.classRepository);
return mapper.findTestee(className);
}

public Collection<ClassInfo> getClassInfo(final Collection<ClassName> classes) {
return classes.stream()
.flatMap(nameToClassInfo())
.collect(Collectors.toList());
}

public Optional<byte[]> fetchClassBytes(final ClassName clazz) {
return this.classRepository.querySource(clazz);
}
Optional<byte[]> fetchClassBytes(ClassName clazz);

@Override
public Optional<ClassInfo> fetchClass(final ClassName clazz) {
return this.classRepository.fetchClass(clazz);
}

private Function<ClassName, Stream<ClassInfo>> nameToClassInfo() {
return new NameToClassInfo(this.classRepository)
.andThen(Streams::fromOptional);
}
Optional<ClassInfo> fetchClass(ClassName clazz);
Collection<ClassInfo> getClassInfo(Collection<ClassName> classes);

@Override
public Optional<byte[]> getBytes(String clazz) {
return fetchClassBytes(ClassName.fromString(clazz));
}
Optional<byte[]> getBytes(String clazz);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.pitest.classpath;

import org.pitest.plugin.ToolClasspathPlugin;

public interface CodeSourceFactory extends ToolClasspathPlugin {
CodeSource createCodeSource(ProjectClassPaths classPath);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.pitest.classpath;

import org.pitest.bytecode.analysis.ClassTree;
import org.pitest.classinfo.ClassInfo;
import org.pitest.classinfo.ClassName;
import org.pitest.classinfo.NameToClassInfo;
import org.pitest.classinfo.Repository;
import org.pitest.classinfo.TestToClassMapper;
import org.pitest.functional.Streams;

import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class DefaultCodeSource implements CodeSource {
private final ProjectClassPaths classPath;
private final Repository classRepository;

public DefaultCodeSource(final ProjectClassPaths classPath) {
this(classPath, new Repository(new ClassPathByteArraySource(
classPath.getClassPath())));
}

DefaultCodeSource(final ProjectClassPaths classPath,
final Repository classRepository) {
this.classPath = classPath;
this.classRepository = classRepository;
}

public Stream<ClassTree> codeTrees() {
return this.classPath.code().stream()
.map(c -> this.getBytes(c.asJavaName()))
.filter(Optional::isPresent)
.map(maybe -> ClassTree.fromBytes(maybe.get()));
}

public Set<ClassName> getCodeUnderTestNames() {
return this.classPath.code().stream().collect(Collectors.toSet());
}

public Stream<ClassTree> testTrees() {
return this.classPath.test().stream()
.map(c -> this.getBytes(c.asJavaName()))
.filter(Optional::isPresent)
.map(maybe -> ClassTree.fromBytes(maybe.get()))
.filter(t -> !t.isAbstract());
}

public ClassPath getClassPath() {
return this.classPath.getClassPath();
}

public Optional<ClassName> findTestee(final String className) {
final TestToClassMapper mapper = new TestToClassMapper(this.classRepository);
return mapper.findTestee(className);
}

public Collection<ClassInfo> getClassInfo(final Collection<ClassName> classes) {
return classes.stream()
.flatMap(nameToClassInfo())
.collect(Collectors.toList());
}

public Optional<byte[]> fetchClassBytes(final ClassName clazz) {
return this.classRepository.querySource(clazz);
}

@Override
public Optional<ClassInfo> fetchClass(final ClassName clazz) {
return this.classRepository.fetchClass(clazz);
}

private Function<ClassName, Stream<ClassInfo>> nameToClassInfo() {
return new NameToClassInfo(this.classRepository)
.andThen(Streams::fromOptional);
}

@Override
public Optional<byte[]> getBytes(String clazz) {
return fetchClassBytes(ClassName.fromString(clazz));
}
}
21 changes: 21 additions & 0 deletions pitest-entry/src/main/java/org/pitest/coverage/ClassLines.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import org.pitest.bytecode.analysis.ClassTree;
import org.pitest.classinfo.ClassName;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Collectors;

public final class ClassLines {
private final ClassName name;
Expand All @@ -23,6 +26,16 @@ public ClassName name() {
return name;
}

public ClassLines relocate(ClassName name) {
return new ClassLines(name, codeLines);
}

public List<ClassLine> asList() {
return codeLines.stream()
.map(l -> new ClassLine(name, l))
.collect(Collectors.toList());
}

public int getNumberOfCodeLines() {
return this.codeLines.size();
}
Expand All @@ -47,4 +60,12 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(name);
}

@Override
public String toString() {
return new StringJoiner(", ", ClassLines.class.getSimpleName() + "[", "]")
.add("name=" + name)
.add("codeLines=" + codeLines)
.toString();
}
}
33 changes: 4 additions & 29 deletions pitest-entry/src/main/java/org/pitest/coverage/CoverageData.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

package org.pitest.coverage;

import org.pitest.bytecode.analysis.ClassTree;
import org.pitest.classinfo.ClassInfo;
import org.pitest.classinfo.ClassName;
import org.pitest.classpath.CodeSource;
Expand Down Expand Up @@ -78,11 +77,6 @@ public Collection<TestInfo> getTestsForBlockLocation(BlockLocation location) {
return this.blockCoverage.getOrDefault(location, Collections.emptySet());
}

@Override
public Collection<TestInfo> getTestsForClassLine(final ClassLine classLine) {
return legacyClassCoverage.getTestsForClassLine(classLine);
}

public boolean allTestsGreen() {
return this.failingTestDescriptions.isEmpty();
}
Expand All @@ -96,13 +90,13 @@ public List<Description> getFailingTestDescriptions() {
}

@Override
public Optional<ClassLines> getCoveredLinesForClass(ClassName clazz) {
return legacyClassCoverage.getCoveredLinesForClass(clazz);
public ClassLines getCodeLinesForClass(ClassName clazz) {
return legacyClassCoverage.getCodeLinesForClass(clazz);
}

@Override
public int getNumberOfCoveredLines(final Collection<ClassName> mutatedClass) {
return legacyClassCoverage.getNumberOfCoveredLines(mutatedClass);
public Set<ClassLine> getCoveredLines(ClassName clazz) {
return legacyClassCoverage.getCoveredLines(clazz);
}

@Override
Expand Down Expand Up @@ -144,11 +138,6 @@ public Collection<ClassLines> getClassesForFile(final String sourceFile,
return legacyClassCoverage.getClassesForFile(sourceFile, packageName);
}

@Override
public CoverageSummary createSummary() {
return new CoverageSummary(numberOfLines(), coveredLines());
}

private BigInteger generateCoverageNumber(Collection<TestInfo> coverage) {
BigInteger coverageNumber = BigInteger.ZERO;
Set<ClassName> testClasses = coverage.stream()
Expand All @@ -162,20 +151,6 @@ private BigInteger generateCoverageNumber(Collection<TestInfo> coverage) {
return coverageNumber;
}

private Collection<ClassName> allClasses() {
return this.code.getCodeUnderTestNames();
}

private int numberOfLines() {
return this.code.codeTrees()
.map(ClassTree::numberOfCodeLines)
.reduce(0, Integer::sum);
}

private int coveredLines() {
return getNumberOfCoveredLines(allClasses());
}

private void checkForFailedTest(final CoverageResult cr) {
if (!cr.isGreenTest()) {
recordTestFailure(cr.getTestUnitDescription());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,4 @@ public interface CoverageDatabase extends ReportCoverage {

BigInteger getCoverageIdForClass(ClassName clazz);

CoverageSummary createSummary();

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Line based coverage data, used by html report and the history system
Expand Down Expand Up @@ -41,29 +42,20 @@ public void loadBlockDataOnly(Collection<BlockLocation> coverageData) {
}

@Override
public Optional<ClassLines> getCoveredLinesForClass(final ClassName clazz) {
public ClassLines getCodeLinesForClass(final ClassName clazz) {
return code.fetchClassBytes(clazz)
.map(ClassTree::fromBytes)
.map(ClassLines::fromTree);
.map(ClassLines::fromTree)
.orElse(new ClassLines(clazz, Collections.emptySet()));
}

@Override
public int getNumberOfCoveredLines(Collection<ClassName> mutatedClass) {
return mutatedClass.stream()
.map(this::getLineCoverageForClassName)
.mapToInt(Map::size)
.sum();
}

@Override
public Collection<TestInfo> getTestsForClassLine(final ClassLine classLine) {
final Collection<TestInfo> result = getLineCoverageForClassName(
classLine.getClassName()).get(classLine);
if (result == null) {
return Collections.emptyList();
} else {
return result;
}
public Set<ClassLine> getCoveredLines(ClassName mutatedClass) {
return Stream.of(mutatedClass)
.flatMap(m -> getLineCoverageForClassName(m).entrySet().stream())
.filter(e -> !e.getValue().isEmpty())
.map( e -> e.getKey())
.collect(Collectors.toSet());
}

@Override
Expand Down
Loading

0 comments on commit ee6e291

Please sign in to comment.