Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optionally print a report URI when the Pitest task finishes #297

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ abstract class AbstractPitestFunctionalSpec extends IntegrationSpec {
""".stripIndent()
}

protected void writeFailingPitTest(String packageDotted = 'gradle.pitest.test.hello', File baseDir = getProjectDir()) {
String path = 'src/test/java/' + packageDotted.replace('.', '/') + '/HelloPitTest.java'
File javaFile = createFile(path, baseDir)
javaFile << """package ${packageDotted};
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class HelloPitTest {
@Test public void shouldReturnInputNumber() {
assertEquals(1, new HelloPit().returnInputNumber(5));
}
}
""".stripIndent()
}

protected void assertStdOutOrStdErrContainsGivenText(ExecutionResult result, String textToContain) {
//TODO: Simplify if possible - standardOutput for Gradle <5 and standardError for Gradle 5+
assert result.standardOutput.contains(textToContain) || result.standardError.contains(textToContain)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,93 @@ class PitestPluginGeneralFunctionalSpec extends AbstractPitestFunctionalSpec {
historyOutputLocation.size()
}

void "report URI is not printed if test suite is not green"() {
given:
buildFile << getBasicGradlePitestConfig()
and:
writeHelloPitClass()
writeFailingPitTest()
when:
ExecutionResult result = runTasksWithFailure('pitest')
then:
result.wasExecuted(':pitest')
!result.getStandardOutput().contains('index.html')
}

void "timestamped report URI is printed if pitest run fails due to unmet thresholds"() {
given:
buildFile << getBasicGradlePitestConfig()
buildFile << """
pitest {
mutationThreshold = 101
}
""".stripIndent()
and:
writeHelloPitClass()
writeHelloPitTest()
when:
ExecutionResult result = runTasksWithFailure('pitest')
then:
result.wasExecuted(':pitest')
result.getStandardOutput().find(/pitest.[0-9]{12}.index\.html/)
}

void "timestamped report URI is not printed if turned off and pitest run fails due to unmet thresholds"() {
given:
buildFile << getBasicGradlePitestConfig()
buildFile << """
pitest {
mutationThreshold = 101
printReportUri = 'never'
}
""".stripIndent()
and:
writeHelloPitClass()
writeHelloPitTest()
when:
ExecutionResult result = runTasksWithFailure('pitest')
then:
result.wasExecuted(':pitest')
!result.getStandardOutput().find(/pitest.[0-9]{12}.index\.html/)
}

void "timestamped report URI is printed if always requested and pitest run passes"() {
given:
buildFile << getBasicGradlePitestConfig()
buildFile << """
pitest {
printReportUri = 'always'
}
""".stripIndent()
and:
writeHelloPitClass()
writeHelloPitTest()
when:
ExecutionResult result = runTasksSuccessfully('pitest')
then:
result.wasExecuted(':pitest')
result.getStandardOutput().find(/pitest.[0-9]{12}.index\.html/)
}

void "non-timestamped report URI is printed if always requested and pitest run passes"() {
given:
buildFile << getBasicGradlePitestConfig()
buildFile << """
pitest {
printReportUri = 'always'
timestampedReports = false
}
""".stripIndent()
and:
writeHelloPitClass()
writeHelloPitTest()
when:
ExecutionResult result = runTasksSuccessfully('pitest')
then:
result.wasExecuted(':pitest')
result.getStandardOutput().contains(quoteBackslashesInWindowsPath(new File('reports/pitest/index.html')))
}

void "pass additional configured parameters that cannot be test with ProjectBuilder"() {
given:
buildFile << getBasicGradlePitestConfig()
Expand Down Expand Up @@ -114,7 +201,7 @@ class PitestPluginGeneralFunctionalSpec extends AbstractPitestFunctionalSpec {

private String quoteBackslashesInWindowsPath(File file) {
//There is problem with backslash within '' or "" while running this test on Windows: "unexpected char"
return file.absolutePath.replaceAll('\\\\', '\\\\\\\\')
return file.toString().replaceAll('\\\\', '\\\\\\\\')
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ class PitestPlugin implements Plugin<Project> {
task.maxSurviving.set(extension.maxSurviving)
task.useClasspathJar.set(extension.useClasspathJar)
task.features.set(extension.features)
task.printReportUri.set(extension.printReportUri)

configurePropertiesWithProblematicTypesForGradle5(task)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ class PitestPluginExtension {
@Incubating
final ListProperty<String> fileExtensionsToFilter

/**
* Controls when the report URI will be printed. Valid values are: 'never', 'on-failure' (default),
* or 'always'
*/
final Property<String> printReportUri

PitestPluginExtension(Project project) {
ObjectFactory of = project.objects
Project p = project
Expand Down Expand Up @@ -262,6 +268,7 @@ class PitestPluginExtension {
useClasspathJar = of.property(Boolean)
features = nullListPropertyOf(p, String)
fileExtensionsToFilter = nullListPropertyOf(p, String)
printReportUri = of.property(String)
}

void setReportDir(File reportDir) {
Expand Down
55 changes: 55 additions & 0 deletions src/main/groovy/info/solidsoft/gradle/pitest/PitestTask.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.options.Option
import org.gradle.process.ExecResult

/**
* Gradle task implementation for Pitest.
Expand Down Expand Up @@ -241,6 +242,10 @@ class PitestTask extends JavaExec {
@Optional
List<String> overriddenTargetTests //should be Set<String> or SetProperty but it's not supported in Gradle as of 5.6.1

@Input
@Optional
final Property<String> printReportUri

PitestTask() {
//setting during execution doesn't work in 6.4+:
//The value for task ':pitest' property 'mainClass' is final and cannot be changed any further.
Expand Down Expand Up @@ -294,6 +299,7 @@ class PitestTask extends JavaExec {
useAdditionalClasspathFile = of.property(Boolean)
additionalClasspathFile = of.fileProperty()
features = of.listProperty(String)
printReportUri = of.property(String)
}

@Input
Expand All @@ -319,12 +325,61 @@ class PitestTask extends JavaExec {
return jvmPath.isPresent() ? jvmPath.asFile.get().absolutePath : null
}

@SuppressWarnings('UnnecessarySetter')
@Override
void exec() {
args = argumentsForPit()
jvmArgs = ((List<String>) getMainProcessJvmArgs().getOrNull() ?: getJvmArgs())
classpath = getLaunchClasspath()

/*
* As it's up to Pitest to write the report somewhere in the report dir if
* timestamped reports are allowed, it is necessary to figure out which one
* is the latest one.
*/
Set<File> previousReportDirs = []
if (timestampedReports.getOrElse(true)) {
previousReportDirs = listTimestampedReportDirs(getReportDir().asFile.get())
}

setIgnoreExitValue(true)
super.exec()
ExecResult execResult = getExecutionResult().get()

printReportUriIfNecessary(execResult.exitValue, previousReportDirs)

execResult.assertNormalExitValue()
}

private void printReportUriIfNecessary(int exitValue, Set<File> previousReportDirs) {
String printReportUri = this.printReportUri.getOrElse('on-failure')

if ((exitValue != 0 &&
printReportUri in ['on-failure', 'always'])
|| printReportUri in ['always']) {
File indexFile = resolveReportIndexFile(previousReportDirs)

if (indexFile.exists()) {
getLogger().warn('See the Pitest report at: ' +
new URI('file', '', indexFile.toURI().getPath(), (String) null, (String) null))
}
}
}

private File resolveReportIndexFile(Set<File> previousReportDirs) {
File actualReportDir

if (timestampedReports.getOrElse(true)) {
actualReportDir = (listTimestampedReportDirs(getReportDir().asFile.get()) - previousReportDirs).first()
} else {
actualReportDir = getReportDir().asFile.get()
}

return new File(actualReportDir, 'index.html')
}

private static Set<File> listTimestampedReportDirs(File reportDir) {
return reportDir.listFiles().findAll { file -> file.isDirectory() }.toSet()
}

private List<String> argumentsForPit() {
Expand Down