Skip to content

Commit b615ae8

Browse files
[GSoC 2026] Kafka Streams runner: enable ParDoTest in the ValidatesRunner suite (#39451)
1 parent fca1435 commit b615ae8

2 files changed

Lines changed: 36 additions & 1 deletion

File tree

runners/kafka-streams/build.gradle

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,15 @@ def sickbayTests = [
9292
'org.apache.beam.sdk.transforms.GroupByKeyTest$WindowTests',
9393
'org.apache.beam.sdk.transforms.GroupByKeyTest$BasicTests.testTimestampCombinerLatest',
9494
'org.apache.beam.sdk.transforms.GroupByKeyTest$BasicTests.testTimestampCombinerEarliest',
95+
// A DoFn whose @StartBundle throws never gets to report its error: SdkHarnessClient.newBundle
96+
// sends the ProcessBundleRequest and then blocks in GrpcDataService.createOutboundAggregator
97+
// waiting for the SDK harness to open its data stream, which a bundle that failed during setup
98+
// never does — so the run blocks for the data service's three-minute timeout instead of
99+
// surfacing the user's exception. This is shared java-fn-execution behaviour rather than
100+
// anything specific to this runner; the Flink runner sickbays all of LifecycleTests and the
101+
// Prism runner sickbays each of its three error tests. The @ProcessElement and @FinishBundle
102+
// variants do pass here, because by then the data stream is established.
103+
'org.apache.beam.sdk.transforms.ParDoTest$LifecycleTests.testParDoWithErrorInStartBatch',
95104
]
96105

97106
tasks.register("validatesRunner", Test) {
@@ -143,6 +152,7 @@ tasks.register("validatesRunner", Test) {
143152
includeTestsMatching 'org.apache.beam.sdk.transforms.CreateTest'
144153
includeTestsMatching 'org.apache.beam.sdk.transforms.FlattenTest'
145154
includeTestsMatching 'org.apache.beam.sdk.transforms.GroupByKeyTest*'
155+
includeTestsMatching 'org.apache.beam.sdk.transforms.ParDoTest*'
146156
for (String test : sickbayTests) {
147157
excludeTestsMatching test
148158
}

runners/kafka-streams/src/test/java/org/apache/beam/runners/kafka/streams/TestKafkaStreamsRunner.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.io.IOException;
2121
import java.util.UUID;
2222
import org.apache.beam.sdk.Pipeline;
23+
import org.apache.beam.sdk.Pipeline.PipelineExecutionException;
2324
import org.apache.beam.sdk.PipelineResult;
2425
import org.apache.beam.sdk.PipelineRunner;
2526
import org.apache.beam.sdk.metrics.MetricResults;
@@ -47,6 +48,10 @@
4748
* PAssert} that silently never runs is caught by {@code TestPipeline.verifyPAssertsSucceeded}
4849
* comparing the success-counter metric against the number of assertions in the pipeline.
4950
*
51+
* <p>Any other failed run is restated as a {@link PipelineExecutionException} carrying the
52+
* exception the user's code threw, which is the failure Beam's contract for {@link #run} — and the
53+
* tests that assert on a DoFn throwing — expect to see.
54+
*
5055
* <p>Select it with {@code --runner=org.apache.beam.runners.kafka.streams.TestKafkaStreamsRunner}
5156
* in {@code beamTestPipelineOptions}.
5257
*/
@@ -84,11 +89,31 @@ public PipelineResult run(Pipeline pipeline) {
8489
throw (AssertionError) current;
8590
}
8691
}
87-
throw t;
92+
throw pipelineExecutionExceptionFor(t);
8893
}
8994
return new TestKafkaStreamsPipelineResult(metrics);
9095
}
9196

97+
/**
98+
* Restates a failed run as the {@link PipelineExecutionException} Beam's contract for {@link
99+
* #run} expects, carrying the exception the user's code threw.
100+
*
101+
* <p>What surfaces from the run is the outermost layer of runner plumbing — a Kafka Streams
102+
* {@code StreamsException} naming the processor node that failed — wrapping several layers of
103+
* bundle- and Fn-API-level wrappers, with the user's exception at the bottom. Callers assert on
104+
* the user's failure, so the root cause is what {@link PipelineExecutionException} is given:
105+
* because {@code RuntimeException(Throwable)} derives its message from the cause, the user's
106+
* message ends up on the exception that {@link #run} throws. Other runners restate failures the
107+
* same way (e.g. {@code SparkPipelineResult}, {@code DirectRunner}).
108+
*/
109+
private static PipelineExecutionException pipelineExecutionExceptionFor(Throwable t) {
110+
Throwable rootCause = t;
111+
while (rootCause.getCause() != null && rootCause.getCause() != rootCause) {
112+
rootCause = rootCause.getCause();
113+
}
114+
return new PipelineExecutionException(rootCause);
115+
}
116+
92117
/** Terminal result of a test run: state {@code DONE} with the harness-reported metrics. */
93118
private static final class TestKafkaStreamsPipelineResult implements PipelineResult {
94119
private final MetricResults metrics;

0 commit comments

Comments
 (0)