This is an automated email from the ASF dual-hosted git repository.
dongjoon-hyun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/spark-kubernetes-operator.git
The following commit(s) were added to refs/heads/main by this push:
new bd154658 [SPARK-59878] Exclude `(UWF|NP)_UNWRITTEN_FIELD` for test
classes in `spotbugs_exclude.xml`
bd154658 is described below
commit bd154658261b8f516844242108e698937ccaa2de
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Tue Sep 29 12:20:02 2026 -0700
[SPARK-59878] Exclude `(UWF|NP)_UNWRITTEN_FIELD` for test classes in
`spotbugs_exclude.xml`
### What changes were proposed in this pull request?
This PR aims to exclude `UWF_UNWRITTEN_FIELD` and `NP_UNWRITTEN_FIELD` for
test classes (`~.*Test(\$.*)?`) in `spotbugs_exclude.xml`, and to remove them
from the `SuppressFBWarnings` of 10 test classes.
### Why are the changes needed?
SpotBugs reports the fields injected by `EnableKubernetesMockClient` as
unwritten fields. However, SpotBugs drops these warnings by itself when more
than 8 classes have such a field with the same name. Then, the class-level
suppressions become useless and fail the build with
`US_USELESS_SUPPRESSION_ON_CLASS`.
Currently, 8 test classes have a `kubernetesClient` field. So, adding a new
one breaks `spotbugsTest` on 5 unrelated test classes. Since SpotBugs applies
`SuppressFBWarnings` before the exclude filter, this PR removes the class-level
suppressions too.
### Does this PR introduce _any_ user-facing change?
No. This only affects the SpotBugs analysis of test classes.
### How was this patch tested?
Pass the CIs. I also manually verified that adding a 9th test class with a
`kubernetesClient` field breaks `spotbugsTest` before this PR, but not after it.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Opus 5.5
Closes #911 from dongjoon-hyun/SPARK-59878.
Authored-by: Dongjoon Hyun <[email protected]>
Signed-off-by: Dongjoon Hyun <[email protected]>
---
config/spotbugs/spotbugs_exclude.xml | 9 +++++++++
.../operator/config/SparkOperatorConfigMapReconcilerTest.java | 4 ----
.../apache/spark/k8s/operator/kueue/KueueWorkloadUtilsTest.java | 4 ----
.../k8s/operator/metrics/healthcheck/SentinelManagerTest.java | 2 +-
.../metrics/source/KubernetesMetricsInterceptorTest.java | 4 ----
.../org/apache/spark/k8s/operator/probe/HealthProbeTest.java | 4 ----
.../reconciler/reconcilesteps/AppDriverPodCacheStaleTest.java | 2 +-
.../k8s/operator/reconciler/reconcilesteps/AppInitStepTest.java | 2 +-
.../operator/reconciler/reconcilesteps/ClusterInitStepTest.java | 4 ----
.../reconciler/reconcilesteps/ClusterSuspendStepTest.java | 4 ----
.../org/apache/spark/k8s/operator/utils/StatusRecorderTest.java | 4 ----
11 files changed, 12 insertions(+), 31 deletions(-)
diff --git a/config/spotbugs/spotbugs_exclude.xml
b/config/spotbugs/spotbugs_exclude.xml
index c492bc2b..73dafac2 100644
--- a/config/spotbugs/spotbugs_exclude.xml
+++ b/config/spotbugs/spotbugs_exclude.xml
@@ -22,4 +22,13 @@
<Bug pattern="EI_EXPOSE_REP2"/>
<Bug pattern="MS_EXPOSE_REP"/>
<Bug pattern="CT_CONSTRUCTOR_THROW"/>
+ <!--
+ Fields of test classes can be injected by JUnit extensions like
`@EnableKubernetesMockClient`.
+ Exclude their unwritten-field warnings here instead of per-class
`@SuppressFBWarnings`, which
+ SpotBugs reports as useless whenever its own heuristics already drop those
warnings.
+ -->
+ <Match>
+ <Class name="~.*Test(\$.*)?"/>
+ <Bug pattern="UWF_UNWRITTEN_FIELD,NP_UNWRITTEN_FIELD"/>
+ </Match>
</FindBugsFilter>
diff --git
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/config/SparkOperatorConfigMapReconcilerTest.java
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/config/SparkOperatorConfigMapReconcilerTest.java
index d8f81e79..709a3be7 100644
---
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/config/SparkOperatorConfigMapReconcilerTest.java
+++
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/config/SparkOperatorConfigMapReconcilerTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.mock;
import java.util.Map;
import java.util.function.Function;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.fabric8.kubeapitest.junit.EnableKubeAPIServer;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
@@ -37,9 +36,6 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-@SuppressFBWarnings(
- value = {"UWF_UNWRITTEN_FIELD"},
- justification = "Unwritten fields are covered by Kubernetes mock client")
@EnableKubeAPIServer
class SparkOperatorConfigMapReconcilerTest {
diff --git
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/kueue/KueueWorkloadUtilsTest.java
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/kueue/KueueWorkloadUtilsTest.java
index 662308ec..90efba4a 100644
---
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/kueue/KueueWorkloadUtilsTest.java
+++
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/kueue/KueueWorkloadUtilsTest.java
@@ -42,7 +42,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.function.BooleanSupplier;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.fabric8.kubernetes.api.model.Condition;
import io.fabric8.kubernetes.api.model.ConditionBuilder;
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
@@ -94,9 +93,6 @@ import org.apache.spark.k8s.operator.utils.EventUtils;
import org.apache.spark.k8s.operator.utils.TestUtils;
@EnableKubernetesMockClient(crud = true)
-@SuppressFBWarnings(
- value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"},
- justification = "Unwritten fields are covered by Kubernetes mock client")
class KueueWorkloadUtilsTest {
private static final String NAME = "sparkapplication-app-1";
private static final String STATUS_PATH =
diff --git
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/metrics/healthcheck/SentinelManagerTest.java
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/metrics/healthcheck/SentinelManagerTest.java
index 8b73485d..45cf394c 100644
---
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/metrics/healthcheck/SentinelManagerTest.java
+++
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/metrics/healthcheck/SentinelManagerTest.java
@@ -60,7 +60,7 @@ import org.apache.spark.k8s.operator.utils.Utils;
@EnableKubernetesMockClient(crud = true)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@SuppressFBWarnings(
- value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", "UUF_UNUSED_FIELD"},
+ value = {"UUF_UNUSED_FIELD"},
justification = "Unwritten fields are covered by Kubernetes mock client")
class SentinelManagerTest {
private static final String DEFAULT = "default";
diff --git
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/metrics/source/KubernetesMetricsInterceptorTest.java
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/metrics/source/KubernetesMetricsInterceptorTest.java
index 49b1d6ef..24c2e1cc 100644
---
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/metrics/source/KubernetesMetricsInterceptorTest.java
+++
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/metrics/source/KubernetesMetricsInterceptorTest.java
@@ -28,7 +28,6 @@ import java.util.Map;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metric;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.client.KubernetesClient;
@@ -48,9 +47,6 @@ import org.apache.spark.k8s.operator.spec.ApplicationSpec;
@EnableKubernetesMockClient(crud = true)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
-@SuppressFBWarnings(
- value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"},
- justification = "Unwritten fields are covered by Kubernetes mock client")
class KubernetesMetricsInterceptorTest {
private KubernetesMockServer mockServer;
diff --git
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/probe/HealthProbeTest.java
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/probe/HealthProbeTest.java
index 4f73a7cd..9043448e 100644
---
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/probe/HealthProbeTest.java
+++
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/probe/HealthProbeTest.java
@@ -30,7 +30,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
import io.javaoperatorsdk.operator.Operator;
@@ -46,9 +45,6 @@ import
org.apache.spark.k8s.operator.config.DynamicConfigMonitor;
import org.apache.spark.k8s.operator.metrics.healthcheck.SentinelManager;
@EnableKubernetesMockClient(crud = true)
-@SuppressFBWarnings(
- value = {"UWF_UNWRITTEN_FIELD"},
- justification = "Unwritten fields are covered by Kubernetes mock client")
class HealthProbeTest {
private static Operator operator;
private static Operator sparkConfMonitor;
diff --git
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/AppDriverPodCacheStaleTest.java
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/AppDriverPodCacheStaleTest.java
index 6751b783..cb00b433 100644
---
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/AppDriverPodCacheStaleTest.java
+++
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/AppDriverPodCacheStaleTest.java
@@ -55,7 +55,7 @@ import
org.apache.spark.k8s.operator.utils.SparkAppStatusRecorder;
@EnableKubernetesMockClient(crud = true)
@SuppressFBWarnings(
- value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", "UUF_UNUSED_FIELD"},
+ value = {"UUF_UNUSED_FIELD"},
justification = "Fields are written by the Kubernetes mock client
extension.")
class AppDriverPodCacheStaleTest {
private static final String APP_NAME = "stale-cache-app";
diff --git
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/AppInitStepTest.java
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/AppInitStepTest.java
index 11386785..2c95ff4a 100644
---
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/AppInitStepTest.java
+++
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/AppInitStepTest.java
@@ -104,7 +104,7 @@ import org.apache.spark.k8s.operator.utils.TestUtils;
@EnableKubernetesMockClient(crud = true)
@SuppressFBWarnings(
- value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", "UUF_UNUSED_FIELD"},
+ value = {"UUF_UNUSED_FIELD"},
justification = "Unwritten fields are covered by Kubernetes mock client")
class AppInitStepTest {
private KubernetesMockServer mockServer;
diff --git
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/ClusterInitStepTest.java
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/ClusterInitStepTest.java
index e65adf11..02afbd51 100644
---
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/ClusterInitStepTest.java
+++
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/ClusterInitStepTest.java
@@ -35,7 +35,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.fabric8.kubernetes.api.model.ConditionBuilder;
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
@@ -101,9 +100,6 @@ import
org.apache.spark.k8s.operator.utils.SparkClusterStatusRecorder;
import org.apache.spark.k8s.operator.utils.TestUtils;
@EnableKubernetesMockClient(crud = true)
-@SuppressFBWarnings(
- value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"},
- justification = "Unwritten fields are covered by Kubernetes mock client")
class ClusterInitStepTest {
private KubernetesClient kubernetesClient;
diff --git
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/ClusterSuspendStepTest.java
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/ClusterSuspendStepTest.java
index 278dc098..5e318ff7 100644
---
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/ClusterSuspendStepTest.java
+++
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/ClusterSuspendStepTest.java
@@ -36,7 +36,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.fabric8.kubernetes.api.model.ConditionBuilder;
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
@@ -95,9 +94,6 @@ import
org.apache.spark.k8s.operator.utils.SparkClusterStatusRecorder;
import org.apache.spark.k8s.operator.utils.TestUtils;
@EnableKubernetesMockClient(crud = true)
-@SuppressFBWarnings(
- value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"},
- justification = "Unwritten fields are covered by Kubernetes mock client")
class ClusterSuspendStepTest {
private static final String WORKLOAD_STATUS_PATH =
"/apis/kueue.x-k8s.io/v1beta2/namespaces/default/workloads/sparkcluster-cluster1/status";
diff --git
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/utils/StatusRecorderTest.java
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/utils/StatusRecorderTest.java
index f3fb8a25..57ba2e3c 100644
---
a/spark-operator/src/test/java/org/apache/spark/k8s/operator/utils/StatusRecorderTest.java
+++
b/spark-operator/src/test/java/org/apache/spark/k8s/operator/utils/StatusRecorderTest.java
@@ -32,7 +32,6 @@ import java.io.IOException;
import java.net.ServerSocket;
import java.util.List;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
@@ -52,9 +51,6 @@ import
org.apache.spark.k8s.operator.status.ApplicationStateSummary;
import org.apache.spark.k8s.operator.status.ApplicationStatus;
@EnableKubernetesMockClient
-@SuppressFBWarnings(
- value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"},
- justification = "Unwritten fields are covered by Kubernetes mock client")
class StatusRecorderTest {
static final String DEFAULT_NS = "default";
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]