Skip to content

Commit c6f2ec9

Browse files
authored
fix: Task does not fail if report generation fails (#929)
* test: reproduce the reported issue as #909 Signed-off-by: Kengo TODA <[email protected]> * test: fix test case Signed-off-by: Kengo TODA <[email protected]> * fix: Task does not fail if report generation fails Signed-off-by: Kengo TODA <[email protected]> * chore: format code by spotless Signed-off-by: Kengo TODA <[email protected]> * chore: resolve problems reported by SonarQube Signed-off-by: Kengo TODA <[email protected]> --------- Signed-off-by: Kengo TODA <[email protected]>
1 parent 4ebb221 commit c6f2ec9

File tree

5 files changed

+149
-2
lines changed

5 files changed

+149
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2023 SpotBugs team
3+
*
4+
* <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5+
* except in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* <p>http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
10+
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
* express or implied. See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package com.github.spotbugs.snom
15+
16+
import org.gradle.testkit.runner.GradleRunner
17+
import org.gradle.testkit.runner.TaskOutcome
18+
import spock.lang.Specification
19+
20+
import java.nio.file.Files
21+
import java.nio.file.Path
22+
23+
/**
24+
* @see <a href="https://github.com/spotbugs/spotbugs-gradle-plugin/issues/909">GitHub Issues</a>
25+
*/
26+
class Issue909 extends Specification {
27+
Path rootDir
28+
Path buildFile
29+
30+
def setup() {
31+
rootDir = Files.createTempDirectory("spotbugs-gradle-plugin")
32+
buildFile = rootDir.resolve("build.gradle")
33+
buildFile.toFile() << """
34+
plugins {
35+
id "java"
36+
id "com.github.spotbugs"
37+
}
38+
repositories {
39+
mavenCentral()
40+
}
41+
tasks.spotbugsMain {
42+
reports {
43+
html {
44+
required = true
45+
stylesheet = resources.text.fromString("I am not valid XSL")
46+
}
47+
}
48+
}
49+
"""
50+
Path sourceDir = rootDir.resolve("src").resolve("main").resolve("java")
51+
sourceDir.toFile().mkdirs()
52+
Path sourceFile = sourceDir.resolve("Foo.java")
53+
sourceFile.toFile() << """
54+
public class Foo {
55+
public static void main(String... args) {
56+
System.out.println("Hello, SpotBugs!");
57+
}
58+
}
59+
"""
60+
}
61+
62+
def "cannot generate HTML report if invalid XSL is provided"() {
63+
when:
64+
def result = GradleRunner.create()
65+
.withProjectDir(rootDir.toFile())
66+
.withArguments('spotbugsMain')
67+
.withPluginClasspath()
68+
.buildAndFail()
69+
70+
then:
71+
TaskOutcome.FAILED == result.task(":spotbugsMain").outcome
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2023 SpotBugs team
3+
*
4+
* <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5+
* except in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* <p>http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
10+
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
* express or implied. See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package com.github.spotbugs.snom.internal;
15+
16+
import java.io.ByteArrayOutputStream;
17+
import java.io.FilterOutputStream;
18+
import java.io.IOException;
19+
import java.io.OutputStream;
20+
import org.jetbrains.annotations.NotNull;
21+
22+
/**
23+
* Monitors the stdout of forked process, and report when it contains some problems reported by
24+
* SpotBugs core.
25+
*/
26+
class OutputScanner extends FilterOutputStream {
27+
private final ByteArrayOutputStream builder = new ByteArrayOutputStream();
28+
private boolean failedToReport = false;
29+
30+
public OutputScanner(OutputStream out) {
31+
super(out);
32+
}
33+
34+
boolean isFailedToReport() {
35+
return failedToReport;
36+
}
37+
38+
@Override
39+
public void write(byte @NotNull [] b, int off, int len) throws IOException {
40+
super.write(b, off, len);
41+
builder.write(b, off, len);
42+
}
43+
44+
@Override
45+
public void write(int b) throws IOException {
46+
super.write(b);
47+
builder.write(b);
48+
49+
if (b == '\n') {
50+
String line = builder.toString();
51+
if (line.contains("Could not generate HTML output")) {
52+
failedToReport = true;
53+
}
54+
55+
builder.reset();
56+
}
57+
}
58+
}

src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import com.github.spotbugs.snom.SpotBugsTask;
1818
import edu.umd.cs.findbugs.annotations.NonNull;
1919
import groovy.lang.Closure;
20-
import java.io.File;
20+
import java.io.*;
2121
import java.net.URI;
2222
import java.nio.file.Path;
2323
import java.util.ArrayList;
@@ -119,6 +119,8 @@ public abstract static class SpotBugsExecutor implements WorkAction<SpotBugsWork
119119
private final Logger log = LoggerFactory.getLogger(getClass());
120120
private final ExecOperations execOperations;
121121

122+
private OutputScanner stderrOutputScanner;
123+
122124
@Inject
123125
public SpotBugsExecutor(ExecOperations execOperations) {
124126
this.execOperations = Objects.requireNonNull(execOperations);
@@ -131,11 +133,15 @@ public void execute() {
131133

132134
final int exitValue =
133135
execOperations.javaexec(configureJavaExec(params)).rethrowFailure().getExitValue();
136+
final boolean ignoreFailures = params.getIgnoreFailures().getOrElse(Boolean.FALSE);
134137
if (ignoreMissingClassFlag(exitValue) == 0) {
138+
if (stderrOutputScanner.isFailedToReport() && !ignoreFailures) {
139+
throw new GradleException("SpotBugs analysis succeeded but report generation failed");
140+
}
135141
return;
136142
}
137143

138-
if (params.getIgnoreFailures().getOrElse(Boolean.FALSE)) {
144+
if (ignoreFailures) {
139145
log.warn("SpotBugs ended with exit code " + exitValue);
140146
return;
141147
}
@@ -180,6 +186,8 @@ private Action<? super JavaExecSpec> configureJavaExec(SpotBugsWorkParameters pa
180186
spec.setExecutable(params.getJavaToolchainExecutablePath().get());
181187
}
182188
spec.setIgnoreExitValue(true);
189+
this.stderrOutputScanner = new OutputScanner(System.err);
190+
spec.setErrorOutput(stderrOutputScanner);
183191
};
184192
}
185193
}

src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.java

+7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public class SpotBugsRunnerForJavaExec extends SpotBugsRunner {
3737
private final Logger log = LoggerFactory.getLogger(SpotBugsRunnerForJavaExec.class);
3838
private final Property<JavaLauncher> javaLauncher;
3939

40+
private OutputScanner stderrOutputScanner;
41+
4042
public SpotBugsRunnerForJavaExec(Property<JavaLauncher> javaLauncher) {
4143
this.javaLauncher = javaLauncher;
4244
}
@@ -46,6 +48,9 @@ public void run(@NonNull SpotBugsTask task) {
4648
// TODO print version of SpotBugs and Plugins
4749
try {
4850
task.getProject().javaexec(configureJavaExec(task)).rethrowFailure().assertNormalExitValue();
51+
if (stderrOutputScanner.isFailedToReport() && !task.getIgnoreFailures()) {
52+
throw new GradleException("SpotBugs analysis succeeded but report generation failed");
53+
}
4954
} catch (ExecException e) {
5055
if (task.getIgnoreFailures()) {
5156
log.warn("SpotBugs reported failures", task.getShowStackTraces() ? e : null);
@@ -81,6 +86,8 @@ private Action<? super JavaExecSpec> configureJavaExec(SpotBugsTask task) {
8186
if (maxHeapSize != null) {
8287
spec.setMaxHeapSize(maxHeapSize);
8388
}
89+
stderrOutputScanner = new OutputScanner(System.err);
90+
spec.setErrorOutput(stderrOutputScanner);
8491
if (javaLauncher.isPresent()) {
8592
log.info(
8693
"Spotbugs will be executed using Java Toolchain configuration: Vendor: {} | Version: {}",

src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.java

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.slf4j.Logger;
4141
import org.slf4j.LoggerFactory;
4242

43+
@Deprecated
4344
public class SpotBugsRunnerForWorker extends SpotBugsRunner {
4445
private final Logger log = LoggerFactory.getLogger(SpotBugsRunnerForWorker.class);
4546
private final WorkerExecutor workerExecutor;

0 commit comments

Comments
 (0)