From e36be9c3f6829e6b1093a1b930ca09216d210368 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Fri, 13 Sep 2024 16:50:30 +0200 Subject: [PATCH] convert spring batch tests to java (#12004) Co-authored-by: Steve Rao Co-authored-by: Lauri Tulmin --- .../test/groovy/ApplicationConfigTrait.groovy | 42 -- .../src/test/groovy/ChunkRootSpanTest.groovy | 94 ---- .../test/groovy/CustomSpanEventTest.groovy | 219 --------- .../src/test/groovy/ItemLevelSpanTest.groovy | 421 ------------------ .../test/groovy/JavaxBatchConfigTrait.groovy | 36 -- .../test/groovy/SpringBatchApplication.groovy | 304 ------------- .../jsr/CustomEventChunkListener.groovy | 27 -- .../jsr/CustomEventItemProcessListener.groovy | 27 -- .../jsr/CustomEventItemReadListener.groovy | 27 -- .../jsr/CustomEventItemWriteListener.groovy | 27 -- .../groovy/jsr/CustomEventJobListener.groovy | 22 - .../groovy/jsr/CustomEventStepListener.groovy | 22 - .../test/groovy/jsr/SingleItemReader.groovy | 31 -- .../src/test/groovy/jsr/TestBatchlet.groovy | 28 -- .../src/test/groovy/jsr/TestDecider.groovy | 16 - .../test/groovy/jsr/TestItemProcessor.groovy | 15 - .../src/test/groovy/jsr/TestItemReader.groovy | 38 -- .../src/test/groovy/jsr/TestItemWriter.groovy | 32 -- .../jsr/TestPartitionedItemReader.groovy | 45 -- .../CustomEventChunkListener.groovy | 27 -- .../CustomEventItemProcessListener.groovy | 26 -- .../CustomEventItemReadListener.groovy | 26 -- .../CustomEventItemWriteListener.groovy | 26 -- .../springbatch/CustomEventJobListener.groovy | 22 - .../CustomEventStepListener.groovy | 24 - .../springbatch/SingleItemReader.groovy | 19 - .../groovy/springbatch/TestDecider.groovy | 18 - .../springbatch/TestItemProcessor.groovy | 15 - .../groovy/springbatch/TestItemReader.groovy | 17 - .../groovy/springbatch/TestItemWriter.groovy | 17 - .../TestPartitionedItemReader.groovy | 43 -- .../groovy/springbatch/TestPartitioner.groovy | 23 - .../springbatch/TestSyncItemReader.groovy | 26 -- .../groovy/springbatch/TestTasklet.groovy | 21 - .../v3_0/chunk/AbstractChunkRootSpanTest.java | 70 +++ .../chunk/JavaConfigChunkRootSpanTest.java | 23 + .../chunk/JsrConfigChunkRootSpanTest.java | 18 + .../chunk/XmlConfigChunkRootSpanTest.java | 21 + .../batch/v3_0/event/CustomSpanEventTest.java | 123 +++++ .../event/JavaConfigCustomSpanEventTest.java | 23 + .../event/JsrConfigCustomSpanEventTest.java | 57 +++ .../event/XmlConfigCustomSpanEventTest.java | 21 + .../batch/v3_0/item/ItemLevelSpanTest.java | 320 +++++++++++++ .../item/JavaConfigItemLevelSpanTest.java | 25 ++ .../v3_0/item/JsrConfigItemLevelSpanTest.java | 135 ++++++ .../v3_0/item/XmlConfigItemLevelSpanTest.java | 26 ++ 46 files changed, 862 insertions(+), 1823 deletions(-) delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ApplicationConfigTrait.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ChunkRootSpanTest.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/CustomSpanEventTest.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ItemLevelSpanTest.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/JavaxBatchConfigTrait.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/SpringBatchApplication.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventChunkListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemProcessListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemReadListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemWriteListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventJobListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventStepListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/SingleItemReader.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestBatchlet.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestDecider.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemProcessor.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemReader.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemWriter.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestPartitionedItemReader.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventChunkListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemProcessListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemReadListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemWriteListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventJobListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventStepListener.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/SingleItemReader.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestDecider.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemProcessor.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemReader.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemWriter.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestPartitionedItemReader.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestPartitioner.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestSyncItemReader.groovy delete mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestTasklet.groovy create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/AbstractChunkRootSpanTest.java create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/JavaConfigChunkRootSpanTest.java create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/JsrConfigChunkRootSpanTest.java create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/XmlConfigChunkRootSpanTest.java create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/CustomSpanEventTest.java create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/JavaConfigCustomSpanEventTest.java create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/JsrConfigCustomSpanEventTest.java create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/XmlConfigCustomSpanEventTest.java create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/ItemLevelSpanTest.java create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/JavaConfigItemLevelSpanTest.java create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/JsrConfigItemLevelSpanTest.java create mode 100644 instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/XmlConfigItemLevelSpanTest.java diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ApplicationConfigTrait.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ApplicationConfigTrait.groovy deleted file mode 100644 index 61a812f03b..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ApplicationConfigTrait.groovy +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import org.springframework.batch.core.Job -import org.springframework.batch.core.JobParameter -import org.springframework.batch.core.JobParameters -import org.springframework.batch.core.launch.JobLauncher -import org.springframework.context.ConfigurableApplicationContext - -trait ApplicationConfigTrait { - static ConfigurableApplicationContext applicationContext - static JobLauncher jobLauncher - - abstract ConfigurableApplicationContext createApplicationContext() - - def setupSpec() { - applicationContext = createApplicationContext() - applicationContext.start() - - jobLauncher = applicationContext.getBean(JobLauncher) - } - - def cleanupSpec() { - applicationContext.stop() - applicationContext.close() - - additionalCleanup() - } - - def additionalCleanup() {} - - def runJob(String jobName, Map params) { - def job = applicationContext.getBean(jobName, Job) - postProcessJob(jobName, job) - jobLauncher.run(job, new JobParameters(params)) - } - - def postProcessJob(String jobName, Job job) { - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ChunkRootSpanTest.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ChunkRootSpanTest.groovy deleted file mode 100644 index be12c26ba9..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ChunkRootSpanTest.groovy +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import org.springframework.batch.core.JobParameter -import org.springframework.context.ConfigurableApplicationContext -import org.springframework.context.annotation.AnnotationConfigApplicationContext -import org.springframework.context.support.ClassPathXmlApplicationContext - -import static io.opentelemetry.api.trace.SpanKind.INTERNAL -import static java.util.Collections.emptyMap - -abstract class ChunkRootSpanTest extends AgentInstrumentationSpecification { - - abstract runJob(String jobName, Map params = emptyMap()) - - def "should create separate traces for each chunk"() { - when: - runJob("itemsAndTaskletJob") - - then: - assertTraces(5) { - def itemStepSpan = null - def taskletStepSpan = null - - trace(0, 3) { - itemStepSpan = span(1) - taskletStepSpan = span(2) - - span(0) { - name "BatchJob itemsAndTaskletJob" - kind INTERNAL - } - span(1) { - name "BatchJob itemsAndTaskletJob.itemStep" - kind INTERNAL - childOf span(0) - } - span(2) { - name "BatchJob itemsAndTaskletJob.taskletStep" - kind INTERNAL - childOf span(0) - } - } - trace(1, 1) { - span(0) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - hasLink itemStepSpan - } - } - trace(2, 1) { - span(0) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - hasLink itemStepSpan - } - } - trace(3, 1) { - span(0) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - hasLink itemStepSpan - } - } - trace(4, 1) { - span(0) { - name "BatchJob itemsAndTaskletJob.taskletStep.Tasklet" - kind INTERNAL - hasLink taskletStepSpan - } - } - } - } -} - -class JavaConfigChunkRootSpanTest extends ChunkRootSpanTest implements ApplicationConfigTrait { - @Override - ConfigurableApplicationContext createApplicationContext() { - new AnnotationConfigApplicationContext(SpringBatchApplication) - } -} - -class XmlConfigChunkRootSpanTest extends ChunkRootSpanTest implements ApplicationConfigTrait { - @Override - ConfigurableApplicationContext createApplicationContext() { - new ClassPathXmlApplicationContext("spring-batch.xml") - } -} - -class JsrConfigChunkRootSpanTest extends ChunkRootSpanTest implements JavaxBatchConfigTrait { -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/CustomSpanEventTest.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/CustomSpanEventTest.groovy deleted file mode 100644 index a2fbc428f8..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/CustomSpanEventTest.groovy +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.instrumentation.test.asserts.TraceAssert -import org.springframework.batch.core.JobParameter -import org.springframework.context.ConfigurableApplicationContext -import org.springframework.context.annotation.AnnotationConfigApplicationContext -import org.springframework.context.support.ClassPathXmlApplicationContext - -import static io.opentelemetry.api.trace.SpanKind.INTERNAL -import static java.util.Collections.emptyMap - -abstract class CustomSpanEventTest extends AgentInstrumentationSpecification { - static final boolean VERSION_GREATER_THAN_4_0 = Boolean.getBoolean("testLatestDeps") - - abstract runJob(String jobName, Map params = emptyMap()) - - def "should be able to call Span.current() and add custom info to spans"() { - when: - runJob("customSpanEventsItemsJob") - - then: - assertTraces(1) { - trace(0, 7) { - span(0) { - name "BatchJob customSpanEventsItemsJob" - kind INTERNAL - events(2) - event(0) { - eventName "job.before" - } - event(1) { - eventName "job.after" - } - } - span(1) { - name "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep" - kind INTERNAL - childOf span(0) - - // CompositeChunkListener has broken ordering that causes listeners that do not override order() to appear first at all times - // because of that a custom ChunkListener will always see a Step span when using spring-batch versions [3, 4) - // that bug was fixed in 4.0 - if (VERSION_GREATER_THAN_4_0) { - events(2) - event(0) { - eventName "step.before" - } - event(1) { - eventName "step.after" - } - } else { - events(4) - event(0) { - eventName "step.before" - } - event(1) { - eventName "chunk.before" - } - event(2) { - eventName "chunk.after" - } - event(3) { - eventName "step.after" - } - } - } - span(2) { - name "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.Chunk" - kind INTERNAL - childOf span(1) - - // CompositeChunkListener has broken ordering that causes listeners that do not override order() to appear first at all times - // because of that a custom ChunkListener will always see a Step span when using spring-batch versions [3, 4) - // that bug was fixed in 4.0 - if (VERSION_GREATER_THAN_4_0) { - events(2) - event(0) { - eventName "chunk.before" - } - event(1) { - eventName "chunk.after" - } - } else { - events(0) - } - } - - itemSpans(it) - } - } - } - - // Spring Batch Java & XML configs have slightly different ordering from JSR config - protected void itemSpans(TraceAssert trace) { - trace.with { - span(3) { - name "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemRead" - kind INTERNAL - childOf span(2) - events(2) - event(0) { - eventName "item.read.before" - } - event(1) { - eventName "item.read.after" - } - } - // second read that returns null and signifies end of stream - span(4) { - name "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemRead" - kind INTERNAL - childOf span(2) - // spring batch does not call ItemReadListener after() methods when read() returns end-of-stream - events(1) - event(0) { - eventName "item.read.before" - } - } - span(5) { - name "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemProcess" - kind INTERNAL - childOf span(2) - events(2) - event(0) { - eventName "item.process.before" - } - event(1) { - eventName "item.process.after" - } - } - span(6) { - name "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemWrite" - kind INTERNAL - childOf span(2) - events(2) - event(0) { - eventName "item.write.before" - } - event(1) { - eventName "item.write.after" - } - } - } - } -} - -class JavaConfigCustomSpanEventTest extends CustomSpanEventTest implements ApplicationConfigTrait { - @Override - ConfigurableApplicationContext createApplicationContext() { - new AnnotationConfigApplicationContext(SpringBatchApplication) - } -} - -class XmlConfigCustomSpanEventTest extends CustomSpanEventTest implements ApplicationConfigTrait { - @Override - ConfigurableApplicationContext createApplicationContext() { - new ClassPathXmlApplicationContext("spring-batch.xml") - } -} - -class JsrConfigCustomSpanEventTest extends CustomSpanEventTest implements JavaxBatchConfigTrait { - - // JSR config has different item span ordering - protected void itemSpans(TraceAssert trace) { - trace.with { - span(3) { - name "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemRead" - kind INTERNAL - childOf span(2) - events(2) - event(0) { - eventName "item.read.before" - } - event(1) { - eventName "item.read.after" - } - } - span(4) { - name "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemProcess" - kind INTERNAL - childOf span(2) - events(2) - event(0) { - eventName "item.process.before" - } - event(1) { - eventName "item.process.after" - } - } - // second read that returns null and signifies end of stream - span(5) { - name "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemRead" - kind INTERNAL - childOf span(2) - // spring batch does not call ItemReadListener after() methods when read() returns end-of-stream - events(1) - event(0) { - eventName "item.read.before" - } - } - span(6) { - name "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemWrite" - kind INTERNAL - childOf span(2) - events(2) - event(0) { - eventName "item.write.before" - } - event(1) { - eventName "item.write.after" - } - } - } - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ItemLevelSpanTest.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ItemLevelSpanTest.groovy deleted file mode 100644 index 418dbd2eef..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/ItemLevelSpanTest.groovy +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.sdk.trace.data.SpanData -import org.springframework.batch.core.Job -import org.springframework.batch.core.JobParameter -import org.springframework.batch.core.Step -import org.springframework.batch.core.job.AbstractJob -import org.springframework.batch.core.step.tasklet.TaskletStep -import org.springframework.batch.repeat.policy.SimpleCompletionPolicy -import org.springframework.batch.repeat.support.TaskExecutorRepeatTemplate -import org.springframework.context.ConfigurableApplicationContext -import org.springframework.context.annotation.AnnotationConfigApplicationContext -import org.springframework.context.support.ClassPathXmlApplicationContext - -import static io.opentelemetry.api.trace.SpanKind.INTERNAL -import static java.util.Collections.emptyMap - -abstract class ItemLevelSpanTest extends AgentInstrumentationSpecification { - abstract runJob(String jobName, Map params = emptyMap()) - - def "should trace item read, process and write calls"() { - when: - runJob("itemsAndTaskletJob") - - then: - assertTraces(1) { - trace(0, 37) { - span(0) { - name "BatchJob itemsAndTaskletJob" - kind INTERNAL - } - - // item step - span(1) { - name "BatchJob itemsAndTaskletJob.itemStep" - kind INTERNAL - childOf span(0) - } - - // chunk 1, items 0-5 - span(2) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - childOf span(1) - } - (3..7).forEach { - span(it) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemRead" - kind INTERNAL - childOf span(2) - } - } - (8..12).forEach { - span(it) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemProcess" - kind INTERNAL - childOf span(2) - } - } - span(13) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemWrite" - kind INTERNAL - childOf span(2) - } - - // chunk 2, items 5-10 - span(14) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - childOf span(1) - } - (15..19).forEach { - span(it) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemRead" - kind INTERNAL - childOf span(14) - } - } - (20..24).forEach { - span(it) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemProcess" - kind INTERNAL - childOf span(14) - } - } - span(25) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemWrite" - kind INTERNAL - childOf span(14) - } - - // chunk 3, items 10-13 - span(26) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - childOf span(1) - } - // +1 for last read returning end of stream marker - (27..30).forEach { - span(it) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemRead" - kind INTERNAL - childOf span(26) - } - } - (31..33).forEach { - span(it) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemProcess" - kind INTERNAL - childOf span(26) - } - } - span(34) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemWrite" - kind INTERNAL - childOf span(26) - } - - // tasklet step - span(35) { - name "BatchJob itemsAndTaskletJob.taskletStep" - kind INTERNAL - childOf span(0) - } - span(36) { - name "BatchJob itemsAndTaskletJob.taskletStep.Tasklet" - kind INTERNAL - childOf span(35) - } - } - } - } - - def "should trace all item operations on a parallel items job"() { - when: - runJob("parallelItemsJob") - - then: - assertTraces(1) { - trace(0, 19) { - // as chunks are processed in parallel we need to sort them to guarantee that they are - // in the expected order - // firstly compute child span count for each chunk, we'll sort chunks from larger to smaller - // based on child count - def childCount = new HashMap() - spans.forEach { span -> - if (span.name == "BatchJob parallelItemsJob.parallelItemsStep.Chunk") { - childCount.put(span, spans.count { it.parentSpanId == span.spanId }) - } - } - // sort spans with a ranking function - spans.sort({ - // job span is first - if (it.name == "BatchJob parallelItemsJob") { - return 0 - } - // step span is second - if (it.name == "BatchJob parallelItemsJob.parallelItemsStep") { - return 1 - } - - // find the chunk this span belongs to - def chunkSpan = it - while (chunkSpan != null && chunkSpan.name != "BatchJob parallelItemsJob.parallelItemsStep.Chunk") { - chunkSpan = spans.find { it.spanId == chunkSpan.parentSpanId } - } - if (chunkSpan != null) { - // sort larger chunks first - return 100 - childCount.get(chunkSpan) - } - throw new IllegalStateException("item spans should have a parent chunk span") - }) - - span(0) { - name "BatchJob parallelItemsJob" - kind INTERNAL - } - span(1) { - name "BatchJob parallelItemsJob.parallelItemsStep" - kind INTERNAL - childOf span(0) - } - - // chunk 1, first two items; thread 1 - span(2) { - name "BatchJob parallelItemsJob.parallelItemsStep.Chunk" - kind INTERNAL - childOf span(1) - } - [3, 4].forEach { - span(it) { - name "BatchJob parallelItemsJob.parallelItemsStep.ItemRead" - kind INTERNAL - childOf span(2) - } - } - [5, 6].forEach { - span(it) { - name "BatchJob parallelItemsJob.parallelItemsStep.ItemProcess" - kind INTERNAL - childOf span(2) - } - } - span(7) { - name "BatchJob parallelItemsJob.parallelItemsStep.ItemWrite" - kind INTERNAL - childOf span(2) - } - - // chunk 2, items 3 & 4; thread 2 - span(8) { - name "BatchJob parallelItemsJob.parallelItemsStep.Chunk" - kind INTERNAL - childOf span(1) - } - [9, 10].forEach { - span(it) { - name "BatchJob parallelItemsJob.parallelItemsStep.ItemRead" - kind INTERNAL - childOf span(8) - } - } - [11, 12].forEach { - span(it) { - name "BatchJob parallelItemsJob.parallelItemsStep.ItemProcess" - kind INTERNAL - childOf span(8) - } - } - span(13) { - name "BatchJob parallelItemsJob.parallelItemsStep.ItemWrite" - kind INTERNAL - childOf span(8) - } - - // chunk 3, 5th item; thread 1 - span(14) { - name "BatchJob parallelItemsJob.parallelItemsStep.Chunk" - kind INTERNAL - childOf span(1) - } - // +1 for last read returning end of stream marker - [15, 16].forEach { - span(it) { - name "BatchJob parallelItemsJob.parallelItemsStep.ItemRead" - kind INTERNAL - childOf span(14) - } - } - span(17) { - name "BatchJob parallelItemsJob.parallelItemsStep.ItemProcess" - kind INTERNAL - childOf span(14) - } - span(18) { - name "BatchJob parallelItemsJob.parallelItemsStep.ItemWrite" - kind INTERNAL - childOf span(14) - } - } - } - } - - def postProcessParallelItemsJob(String jobName, Job job) { - if ("parallelItemsJob" == jobName) { - Step step = ((AbstractJob) job).getStep("parallelItemsStep") - TaskletStep taskletStep = (TaskletStep) step - // explicitly set the number of chunks we expect from this test to ensure we always get - // the same number of spans - ((TaskExecutorRepeatTemplate) taskletStep.stepOperations).completionPolicy = new SimpleCompletionPolicy(3) - } - } -} - -class JavaConfigItemLevelSpanTest extends ItemLevelSpanTest implements ApplicationConfigTrait { - @Override - def postProcessJob(String jobName, Job job) { - postProcessParallelItemsJob(jobName, job) - } - - @Override - ConfigurableApplicationContext createApplicationContext() { - new AnnotationConfigApplicationContext(SpringBatchApplication) - } -} - -class XmlConfigItemLevelSpanTest extends ItemLevelSpanTest implements ApplicationConfigTrait { - @Override - def postProcessJob(String jobName, Job job) { - postProcessParallelItemsJob(jobName, job) - } - - @Override - ConfigurableApplicationContext createApplicationContext() { - new ClassPathXmlApplicationContext("spring-batch.xml") - } -} - -// JsrChunkProcessor works a bit differently than the "standard" one and does not read the whole -// chunk at once, it reads every item separately; it results in a different span ordering, that's -// why it has a completely separate test class -class JsrConfigItemLevelSpanTest extends AgentInstrumentationSpecification implements JavaxBatchConfigTrait { - def "should trace item read, process and write calls"() { - when: - runJob("itemsAndTaskletJob", [:]) - - then: - assertTraces(1) { - trace(0, 37) { - span(0) { - name "BatchJob itemsAndTaskletJob" - kind INTERNAL - } - - // item step - span(1) { - name "BatchJob itemsAndTaskletJob.itemStep" - kind INTERNAL - childOf span(0) - } - - // chunk 1, items 0-5 - span(2) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - childOf span(1) - } - (3..11).step(2) { - println it - span(it) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemRead" - kind INTERNAL - childOf span(2) - } - span(it + 1) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemProcess" - kind INTERNAL - childOf span(2) - } - } - span(13) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemWrite" - kind INTERNAL - childOf span(2) - } - - // chunk 2, items 5-10 - span(14) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - childOf span(1) - } - (15..23).step(2) { - println it - span(it) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemRead" - kind INTERNAL - childOf span(14) - } - span(it + 1) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemProcess" - kind INTERNAL - childOf span(14) - } - } - span(25) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemWrite" - kind INTERNAL - childOf span(14) - } - - // chunk 3, items 10-13 - span(26) { - name "BatchJob itemsAndTaskletJob.itemStep.Chunk" - kind INTERNAL - childOf span(1) - } - (27..32).step(2) { - println it - span(it) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemRead" - kind INTERNAL - childOf span(26) - } - span(it + 1) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemProcess" - kind INTERNAL - childOf span(26) - } - } - // last read returning end of stream marker - span(33) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemRead" - kind INTERNAL - childOf span(26) - } - span(34) { - name "BatchJob itemsAndTaskletJob.itemStep.ItemWrite" - kind INTERNAL - childOf span(26) - } - - // tasklet step - span(35) { - name "BatchJob itemsAndTaskletJob.taskletStep" - kind INTERNAL - childOf span(0) - } - span(36) { - name "BatchJob itemsAndTaskletJob.taskletStep.Tasklet" - kind INTERNAL - childOf span(35) - } - } - } - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/JavaxBatchConfigTrait.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/JavaxBatchConfigTrait.groovy deleted file mode 100644 index 7f59ff00e8..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/JavaxBatchConfigTrait.groovy +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import org.springframework.batch.core.JobParameter - -import javax.batch.operations.JobOperator -import javax.batch.runtime.BatchRuntime -import java.util.concurrent.atomic.AtomicInteger - -trait JavaxBatchConfigTrait { - static JobOperator jobOperator - static AtomicInteger counter = new AtomicInteger() - - def setupSpec() { - jobOperator = BatchRuntime.jobOperator - } - - // just for consistency with ApplicationConfigTrait - def cleanupSpec() { - additionalCleanup() - } - - def additionalCleanup() {} - - def runJob(String jobName, Map params) { - def jobParams = new Properties() - params.forEach({ k, v -> - jobParams.setProperty(k, v.toString()) - }) - // each job instance with the same name needs to be unique - jobParams.setProperty("uniqueJobIdCounter", counter.getAndIncrement().toString()) - jobOperator.start(jobName, jobParams) - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/SpringBatchApplication.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/SpringBatchApplication.groovy deleted file mode 100644 index cfd15a2425..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/SpringBatchApplication.groovy +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import org.springframework.batch.core.Job -import org.springframework.batch.core.Step -import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing -import org.springframework.batch.core.configuration.annotation.JobBuilderFactory -import org.springframework.batch.core.configuration.annotation.StepBuilderFactory -import org.springframework.batch.core.job.builder.FlowBuilder -import org.springframework.batch.core.job.flow.Flow -import org.springframework.batch.core.job.flow.support.SimpleFlow -import org.springframework.batch.core.launch.JobLauncher -import org.springframework.batch.core.launch.support.SimpleJobLauncher -import org.springframework.batch.core.partition.support.Partitioner -import org.springframework.batch.core.repository.JobRepository -import org.springframework.batch.item.ItemProcessor -import org.springframework.batch.item.ItemReader -import org.springframework.batch.item.ItemWriter -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.core.task.AsyncTaskExecutor -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor -import springbatch.CustomEventChunkListener -import springbatch.CustomEventItemProcessListener -import springbatch.CustomEventItemReadListener -import springbatch.CustomEventItemWriteListener -import springbatch.CustomEventJobListener -import springbatch.CustomEventStepListener -import springbatch.SingleItemReader -import springbatch.TestDecider -import springbatch.TestItemProcessor -import springbatch.TestItemReader -import springbatch.TestItemWriter -import springbatch.TestPartitionedItemReader -import springbatch.TestPartitioner -import springbatch.TestSyncItemReader -import springbatch.TestTasklet - -@Configuration -@EnableBatchProcessing -class SpringBatchApplication { - - @Autowired - JobBuilderFactory jobs - @Autowired - StepBuilderFactory steps - @Autowired - JobRepository jobRepository - - @Bean - AsyncTaskExecutor asyncTaskExecutor() { - def executor = new ThreadPoolTaskExecutor() - executor.corePoolSize = 10 - executor.maxPoolSize = 10 - executor - } - - @Bean - JobLauncher jobLauncher() { - def launcher = new SimpleJobLauncher() - launcher.jobRepository = jobRepository - launcher.taskExecutor = asyncTaskExecutor() - launcher - } - - // common - @Bean - ItemReader itemReader() { - new TestItemReader() - } - - @Bean - ItemProcessor itemProcessor() { - new TestItemProcessor() - } - - @Bean - ItemWriter itemWriter() { - new TestItemWriter() - } - - // simple tasklet job - @Bean - Job taskletJob() { - jobs.get("taskletJob") - .start(step()) - .build() - } - - @Bean - Step step() { - steps.get("step") - .tasklet(new TestTasklet()) - .build() - } - - // 2-step tasklet + chunked items job - @Bean - Job itemsAndTaskletJob() { - jobs.get("itemsAndTaskletJob") - .start(itemStep()) - .next(taskletStep()) - .build() - } - - @Bean - Step taskletStep() { - steps.get("taskletStep") - .tasklet(new TestTasklet()) - .build() - } - - @Bean - Step itemStep() { - steps.get("itemStep") - .chunk(5) - .reader(itemReader()) - .processor(itemProcessor()) - .writer(itemWriter()) - .build() - } - - // parallel items job - @Bean - Job parallelItemsJob() { - jobs.get("parallelItemsJob") - .start(parallelItemsStep()) - .build() - } - - @Bean - Step parallelItemsStep() { - steps.get("parallelItemsStep") - .chunk(2) - .reader(new TestSyncItemReader(5)) - .processor(itemProcessor()) - .writer(itemWriter()) - .taskExecutor(asyncTaskExecutor()) - .throttleLimit(2) - .build() - } - - // job using a flow - @Bean - Job flowJob() { - jobs.get("flowJob") - .start(flow()) - .build() - .build() - } - - @Bean - Flow flow() { - new FlowBuilder("flow") - .start(flowStep1()) - .on("*") - .to(flowStep2()) - .build() - } - - @Bean - Step flowStep1() { - steps.get("flowStep1") - .tasklet(new TestTasklet()) - .build() - } - - @Bean - Step flowStep2() { - steps.get("flowStep2") - .tasklet(new TestTasklet()) - .build() - } - - // split job - @Bean - Job splitJob() { - jobs.get("splitJob") - .start(splitFlowStep1()) - .split(asyncTaskExecutor()) - .add(splitFlow2()) - .build() - .build() - } - - @Bean - Step splitFlowStep1() { - steps.get("splitFlowStep1") - .tasklet(new TestTasklet()) - .build() - } - - @Bean - Flow splitFlow2() { - new FlowBuilder("splitFlow2") - .start(splitFlowStep2()) - .build() - } - - @Bean - Step splitFlowStep2() { - steps.get("splitFlowStep2") - .tasklet(new TestTasklet()) - .build() - } - - // job with decisions - @Bean - Job decisionJob() { - jobs.get("decisionJob") - .start(decisionStepStart()) - .next(new TestDecider()) - .on("LEFT").to(decisionStepLeft()) - .on("RIGHT").to(decisionStepRight()) - .end() - .build() - } - - @Bean - Step decisionStepStart() { - steps.get("decisionStepStart") - .tasklet(new TestTasklet()) - .build() - } - - @Bean - Step decisionStepLeft() { - steps.get("decisionStepLeft") - .tasklet(new TestTasklet()) - .build() - } - - @Bean - Step decisionStepRight() { - steps.get("decisionStepRight") - .tasklet(new TestTasklet()) - .build() - } - - // partitioned job - @Bean - Job partitionedJob() { - jobs.get("partitionedJob") - .start(partitionManagerStep()) - .build() - } - - @Bean - Step partitionManagerStep() { - steps.get("partitionManagerStep") - .partitioner("partitionWorkerStep", partitioner()) - .step(partitionWorkerStep()) - .gridSize(2) - .taskExecutor(asyncTaskExecutor()) - .build() - } - - @Bean - Partitioner partitioner() { - new TestPartitioner() - } - - @Bean - Step partitionWorkerStep() { - steps.get("partitionWorkerStep") - .chunk(5) - .reader(partitionedItemReader()) - .processor(itemProcessor()) - .writer(itemWriter()) - .build() - } - - @Bean - ItemReader partitionedItemReader() { - new TestPartitionedItemReader() - } - - // custom span events items job - @Bean - Job customSpanEventsItemsJob() { - jobs.get("customSpanEventsItemsJob") - .start(customSpanEventsItemStep()) - .listener(new CustomEventJobListener()) - .build() - } - - @Bean - Step customSpanEventsItemStep() { - steps.get("customSpanEventsItemStep") - .chunk(5) - .reader(new SingleItemReader()) - .processor(itemProcessor()) - .writer(itemWriter()) - .listener(new CustomEventStepListener()) - .listener(new CustomEventChunkListener()) - .listener(new CustomEventItemReadListener()) - .listener(new CustomEventItemProcessListener()) - .listener(new CustomEventItemWriteListener()) - .build() - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventChunkListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventChunkListener.groovy deleted file mode 100644 index 976fb17401..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventChunkListener.groovy +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import io.opentelemetry.api.trace.Span - -import javax.batch.api.chunk.listener.ChunkListener - -class CustomEventChunkListener implements ChunkListener { - @Override - void beforeChunk() throws Exception { - Span.current().addEvent("chunk.before") - } - - @Override - void onError(Exception e) throws Exception { - Span.current().addEvent("chunk.error") - } - - @Override - void afterChunk() throws Exception { - Span.current().addEvent("chunk.after") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemProcessListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemProcessListener.groovy deleted file mode 100644 index 1977a4eb6d..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemProcessListener.groovy +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import io.opentelemetry.api.trace.Span - -import javax.batch.api.chunk.listener.ItemProcessListener - -class CustomEventItemProcessListener implements ItemProcessListener { - @Override - void beforeProcess(Object o) throws Exception { - Span.current().addEvent("item.process.before") - } - - @Override - void afterProcess(Object o, Object o1) throws Exception { - Span.current().addEvent("item.process.after") - } - - @Override - void onProcessError(Object o, Exception e) throws Exception { - Span.current().addEvent("item.process.error") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemReadListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemReadListener.groovy deleted file mode 100644 index 6ed343fb34..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemReadListener.groovy +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import io.opentelemetry.api.trace.Span - -import javax.batch.api.chunk.listener.ItemReadListener - -class CustomEventItemReadListener implements ItemReadListener { - @Override - void beforeRead() throws Exception { - Span.current().addEvent("item.read.before") - } - - @Override - void afterRead(Object o) throws Exception { - Span.current().addEvent("item.read.after") - } - - @Override - void onReadError(Exception e) throws Exception { - Span.current().addEvent("item.read.error") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemWriteListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemWriteListener.groovy deleted file mode 100644 index f7866bfa06..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventItemWriteListener.groovy +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import io.opentelemetry.api.trace.Span - -import javax.batch.api.chunk.listener.ItemWriteListener - -class CustomEventItemWriteListener implements ItemWriteListener { - @Override - void beforeWrite(List list) throws Exception { - Span.current().addEvent("item.write.before") - } - - @Override - void afterWrite(List list) throws Exception { - Span.current().addEvent("item.write.after") - } - - @Override - void onWriteError(List list, Exception e) throws Exception { - Span.current().addEvent("item.write.error") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventJobListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventJobListener.groovy deleted file mode 100644 index 9bdacd8584..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventJobListener.groovy +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import io.opentelemetry.api.trace.Span - -import javax.batch.api.listener.JobListener - -class CustomEventJobListener implements JobListener { - @Override - void beforeJob() throws Exception { - Span.current().addEvent("job.before") - } - - @Override - void afterJob() throws Exception { - Span.current().addEvent("job.after") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventStepListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventStepListener.groovy deleted file mode 100644 index 41513aeaf7..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/CustomEventStepListener.groovy +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import io.opentelemetry.api.trace.Span - -import javax.batch.api.listener.StepListener - -class CustomEventStepListener implements StepListener { - @Override - void beforeStep() throws Exception { - Span.current().addEvent("step.before") - } - - @Override - void afterStep() throws Exception { - Span.current().addEvent("step.after") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/SingleItemReader.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/SingleItemReader.groovy deleted file mode 100644 index 830ec6b2e2..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/SingleItemReader.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import javax.batch.api.chunk.ItemReader -import java.util.concurrent.atomic.AtomicReference - -class SingleItemReader implements ItemReader { - final AtomicReference item = new AtomicReference<>("42") - - @Override - void open(Serializable serializable) throws Exception { - } - - @Override - void close() throws Exception { - } - - @Override - Object readItem() throws Exception { - return item.getAndSet(null) - } - - @Override - Serializable checkpointInfo() throws Exception { - return null - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestBatchlet.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestBatchlet.groovy deleted file mode 100644 index d8d8b9b680..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestBatchlet.groovy +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import javax.batch.api.BatchProperty -import javax.batch.api.Batchlet -import javax.inject.Inject - -class TestBatchlet implements Batchlet { - @Inject - @BatchProperty(name = "fail") - String fail - - @Override - String process() throws Exception { - if (fail != null && Integer.valueOf(fail) == 1) { - throw new IllegalStateException("fail") - } - return "FINISHED" - } - - @Override - void stop() throws Exception { - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestDecider.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestDecider.groovy deleted file mode 100644 index 4401f1cefd..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestDecider.groovy +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import javax.batch.api.Decider -import javax.batch.runtime.StepExecution - -class TestDecider implements Decider { - @Override - String decide(StepExecution[] stepExecutions) throws Exception { - "LEFT" - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemProcessor.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemProcessor.groovy deleted file mode 100644 index d6ec593ddb..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemProcessor.groovy +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import javax.batch.api.chunk.ItemProcessor - -class TestItemProcessor implements ItemProcessor { - @Override - Object processItem(Object item) throws Exception { - Integer.parseInt(item as String) - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemReader.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemReader.groovy deleted file mode 100644 index 35484b3dc4..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemReader.groovy +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import javax.batch.api.chunk.ItemReader -import java.util.stream.Collectors -import java.util.stream.IntStream - -class TestItemReader implements ItemReader { - private final List items = IntStream.range(0, 13).mapToObj(String.&valueOf).collect(Collectors.toList()) - private Iterator itemsIt - - @Override - void open(Serializable serializable) throws Exception { - itemsIt = items.iterator() - } - - @Override - void close() throws Exception { - itemsIt = null - } - - @Override - Object readItem() throws Exception { - if (itemsIt == null) { - return null - } - return itemsIt.hasNext() ? itemsIt.next() : null - } - - @Override - Serializable checkpointInfo() throws Exception { - return null - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemWriter.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemWriter.groovy deleted file mode 100644 index d3c34d14bd..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestItemWriter.groovy +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import javax.batch.api.chunk.ItemWriter - -class TestItemWriter implements ItemWriter { - final List items = new ArrayList() - - @Override - void open(Serializable checkpoint) throws Exception { - } - - @Override - void close() throws Exception { - } - - @Override - void writeItems(List items) throws Exception { - for (item in items) { - this.items.add(item as Integer) - } - } - - @Override - Serializable checkpointInfo() throws Exception { - return null - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestPartitionedItemReader.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestPartitionedItemReader.groovy deleted file mode 100644 index 722c1646bd..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/jsr/TestPartitionedItemReader.groovy +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package jsr - -import javax.batch.api.BatchProperty -import javax.batch.api.chunk.ItemReader -import javax.inject.Inject - -class TestPartitionedItemReader implements ItemReader { - @Inject - @BatchProperty(name = "start") - String startStr - @Inject - @BatchProperty(name = "end") - String endStr - - int start - int end - - @Override - void open(Serializable checkpoint) throws Exception { - start = Integer.parseInt(startStr) - end = Integer.parseInt(endStr) - } - - @Override - void close() throws Exception { - } - - @Override - Object readItem() throws Exception { - if (start >= end) { - return null - } - return String.valueOf(start++) - } - - @Override - Serializable checkpointInfo() throws Exception { - return null - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventChunkListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventChunkListener.groovy deleted file mode 100644 index 9eec04b8fc..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventChunkListener.groovy +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import io.opentelemetry.api.trace.Span -import org.springframework.batch.core.ChunkListener -import org.springframework.batch.core.scope.context.ChunkContext - -class CustomEventChunkListener implements ChunkListener { - @Override - void beforeChunk(ChunkContext context) { - Span.current().addEvent("chunk.before") - } - - @Override - void afterChunk(ChunkContext context) { - Span.current().addEvent("chunk.after") - } - - @Override - void afterChunkError(ChunkContext context) { - Span.current().addEvent("chunk.error") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemProcessListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemProcessListener.groovy deleted file mode 100644 index 21b0939908..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemProcessListener.groovy +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import io.opentelemetry.api.trace.Span -import org.springframework.batch.core.ItemProcessListener - -class CustomEventItemProcessListener implements ItemProcessListener { - @Override - void beforeProcess(String item) { - Span.current().addEvent("item.process.before") - } - - @Override - void afterProcess(String item, Integer result) { - Span.current().addEvent("item.process.after") - } - - @Override - void onProcessError(String item, Exception e) { - Span.current().addEvent("item.process.error") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemReadListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemReadListener.groovy deleted file mode 100644 index c24631118b..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemReadListener.groovy +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import io.opentelemetry.api.trace.Span -import org.springframework.batch.core.ItemReadListener - -class CustomEventItemReadListener implements ItemReadListener { - @Override - void beforeRead() { - Span.current().addEvent("item.read.before") - } - - @Override - void afterRead(String item) { - Span.current().addEvent("item.read.after") - } - - @Override - void onReadError(Exception ex) { - Span.current().addEvent("item.read.error") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemWriteListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemWriteListener.groovy deleted file mode 100644 index 761645eceb..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventItemWriteListener.groovy +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import io.opentelemetry.api.trace.Span -import org.springframework.batch.core.ItemWriteListener - -class CustomEventItemWriteListener implements ItemWriteListener { - @Override - void beforeWrite(List items) { - Span.current().addEvent("item.write.before") - } - - @Override - void afterWrite(List items) { - Span.current().addEvent("item.write.after") - } - - @Override - void onWriteError(Exception exception, List items) { - Span.current().addEvent("item.write.error") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventJobListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventJobListener.groovy deleted file mode 100644 index 1c691448c9..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventJobListener.groovy +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import io.opentelemetry.api.trace.Span -import org.springframework.batch.core.JobExecution -import org.springframework.batch.core.JobExecutionListener - -class CustomEventJobListener implements JobExecutionListener { - @Override - void beforeJob(JobExecution jobExecution) { - Span.current().addEvent("job.before") - } - - @Override - void afterJob(JobExecution jobExecution) { - Span.current().addEvent("job.after") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventStepListener.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventStepListener.groovy deleted file mode 100644 index a55deb7bc8..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/CustomEventStepListener.groovy +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import io.opentelemetry.api.trace.Span -import org.springframework.batch.core.ExitStatus -import org.springframework.batch.core.StepExecution -import org.springframework.batch.core.StepExecutionListener - -class CustomEventStepListener implements StepExecutionListener { - @Override - void beforeStep(StepExecution stepExecution) { - Span.current().addEvent("step.before") - } - - @Override - ExitStatus afterStep(StepExecution stepExecution) { - Span.current().addEvent("step.after") - return null - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/SingleItemReader.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/SingleItemReader.groovy deleted file mode 100644 index c2952effbf..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/SingleItemReader.groovy +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import org.springframework.batch.item.ItemReader - -import java.util.concurrent.atomic.AtomicReference - -class SingleItemReader implements ItemReader { - final AtomicReference item = new AtomicReference<>("42") - - @Override - String read() { - return item.getAndSet(null) - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestDecider.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestDecider.groovy deleted file mode 100644 index b7ab851505..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestDecider.groovy +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import org.springframework.batch.core.JobExecution -import org.springframework.batch.core.StepExecution -import org.springframework.batch.core.job.flow.FlowExecutionStatus -import org.springframework.batch.core.job.flow.JobExecutionDecider - -class TestDecider implements JobExecutionDecider { - @Override - FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) { - new FlowExecutionStatus("LEFT") - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemProcessor.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemProcessor.groovy deleted file mode 100644 index 07cac9b689..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemProcessor.groovy +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import org.springframework.batch.item.ItemProcessor - -class TestItemProcessor implements ItemProcessor { - @Override - Integer process(String item) throws Exception { - Integer.parseInt(item) - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemReader.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemReader.groovy deleted file mode 100644 index a5b5403c4c..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemReader.groovy +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import org.springframework.batch.item.support.ListItemReader - -import java.util.stream.Collectors -import java.util.stream.IntStream - -class TestItemReader extends ListItemReader { - TestItemReader() { - super(IntStream.range(0, 13).mapToObj(String.&valueOf).collect(Collectors.toList()) as List) - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemWriter.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemWriter.groovy deleted file mode 100644 index ba7d2f2710..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestItemWriter.groovy +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import org.springframework.batch.item.ItemWriter - -class TestItemWriter implements ItemWriter { - final List items = Collections.synchronizedList(new ArrayList()) - - @Override - void write(List items) throws Exception { - this.items.addAll(items) - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestPartitionedItemReader.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestPartitionedItemReader.groovy deleted file mode 100644 index bce1797cba..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestPartitionedItemReader.groovy +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import org.springframework.batch.item.ExecutionContext -import org.springframework.batch.item.ItemReader -import org.springframework.batch.item.ItemStream -import org.springframework.batch.item.ItemStreamException -import org.springframework.batch.item.NonTransientResourceException -import org.springframework.batch.item.ParseException -import org.springframework.batch.item.UnexpectedInputException - -class TestPartitionedItemReader implements ItemReader, ItemStream { - ThreadLocal start = new ThreadLocal<>() - ThreadLocal end = new ThreadLocal<>() - - @Override - String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException { - if (start.get() >= end.get()) { - return null - } - def value = start.get() - start.set(value + 1) - return String.valueOf(value) - } - - @Override - void open(ExecutionContext executionContext) throws ItemStreamException { - start.set(executionContext.getInt("start")) - end.set(executionContext.getInt("end")) - } - - @Override - void update(ExecutionContext executionContext) throws ItemStreamException { - } - - @Override - void close() throws ItemStreamException { - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestPartitioner.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestPartitioner.groovy deleted file mode 100644 index aaea9a104b..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestPartitioner.groovy +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import org.springframework.batch.core.partition.support.Partitioner -import org.springframework.batch.item.ExecutionContext - -class TestPartitioner implements Partitioner { - @Override - Map partition(int gridSize) { - return [ - "partition0": new ExecutionContext([ - "start": 0, "end": 8 - ]), - "partition1": new ExecutionContext([ - "start": 8, "end": 13 - ]) - ] - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestSyncItemReader.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestSyncItemReader.groovy deleted file mode 100644 index 36c3df8fa5..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestSyncItemReader.groovy +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import org.springframework.batch.item.ItemReader - -import java.util.stream.Collectors -import java.util.stream.IntStream - -class TestSyncItemReader implements ItemReader { - private final Iterator items - - TestSyncItemReader(int max) { - items = IntStream.range(0, max).mapToObj(String.&valueOf).collect(Collectors.toList()).iterator() - } - - synchronized String read() { - if (items.hasNext()) { - return items.next() - } - return null - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestTasklet.groovy b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestTasklet.groovy deleted file mode 100644 index 0c544821ac..0000000000 --- a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/groovy/springbatch/TestTasklet.groovy +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package springbatch - -import org.springframework.batch.core.StepContribution -import org.springframework.batch.core.scope.context.ChunkContext -import org.springframework.batch.core.step.tasklet.Tasklet -import org.springframework.batch.repeat.RepeatStatus - -class TestTasklet implements Tasklet { - @Override - RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { - if (chunkContext.stepContext.stepExecution.jobParameters.getLong("fail") == 1) { - throw new IllegalStateException("fail") - } - RepeatStatus.FINISHED - } -} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/AbstractChunkRootSpanTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/AbstractChunkRootSpanTest.java new file mode 100644 index 0000000000..6e330a0ce1 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/AbstractChunkRootSpanTest.java @@ -0,0 +1,70 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.chunk; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.JobRunner; +import io.opentelemetry.sdk.testing.assertj.TraceAssert; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.data.SpanData; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +abstract class AbstractChunkRootSpanTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + private final JobRunner jobRunner; + + AbstractChunkRootSpanTest(JobRunner jobRunner) { + this.jobRunner = jobRunner; + } + + @Test + void shouldCreateSeparateTracesForEachChunk() { + jobRunner.runJob("itemsAndTaskletJob"); + AtomicReference itemStepSpan = new AtomicReference<>(); + AtomicReference taskletStepSpan = new AtomicReference<>(); + + Consumer chunk = + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasLinks(LinkData.create(itemStepSpan.get().getSpanContext()))); + testing.waitAndAssertTraces( + trace -> { + itemStepSpan.set(trace.getSpan(1)); + taskletStepSpan.set(trace.getSpan(2)); + + trace.hasSpansSatisfyingExactly( + span -> span.hasName("BatchJob itemsAndTaskletJob").hasKind(SpanKind.INTERNAL), + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)), + span -> + span.hasName("BatchJob itemsAndTaskletJob.taskletStep") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0))); + }, + chunk, + chunk, + chunk, + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("BatchJob itemsAndTaskletJob.taskletStep.Tasklet") + .hasKind(SpanKind.INTERNAL) + .hasLinks(LinkData.create(taskletStepSpan.get().getSpanContext())))); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/JavaConfigChunkRootSpanTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/JavaConfigChunkRootSpanTest.java new file mode 100644 index 0000000000..bc8b00e26f --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/JavaConfigChunkRootSpanTest.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.chunk; + +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.ApplicationConfigRunner; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.SpringBatchApplication; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +class JavaConfigChunkRootSpanTest extends AbstractChunkRootSpanTest { + + @RegisterExtension + static final ApplicationConfigRunner runner = + new ApplicationConfigRunner( + () -> new AnnotationConfigApplicationContext(SpringBatchApplication.class)); + + JavaConfigChunkRootSpanTest() { + super(runner); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/JsrConfigChunkRootSpanTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/JsrConfigChunkRootSpanTest.java new file mode 100644 index 0000000000..625674f770 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/JsrConfigChunkRootSpanTest.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.chunk; + +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.JavaxBatchConfigRunner; +import org.junit.jupiter.api.extension.RegisterExtension; + +class JsrConfigChunkRootSpanTest extends AbstractChunkRootSpanTest { + + @RegisterExtension static final JavaxBatchConfigRunner runner = new JavaxBatchConfigRunner(); + + JsrConfigChunkRootSpanTest() { + super(runner); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/XmlConfigChunkRootSpanTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/XmlConfigChunkRootSpanTest.java new file mode 100644 index 0000000000..1c1b6314ee --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/chunk/XmlConfigChunkRootSpanTest.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.chunk; + +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.ApplicationConfigRunner; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +class XmlConfigChunkRootSpanTest extends AbstractChunkRootSpanTest { + + @RegisterExtension + static final ApplicationConfigRunner runner = + new ApplicationConfigRunner(() -> new ClassPathXmlApplicationContext("spring-batch.xml")); + + XmlConfigChunkRootSpanTest() { + super(runner); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/CustomSpanEventTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/CustomSpanEventTest.java new file mode 100644 index 0000000000..1c8bd53d80 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/CustomSpanEventTest.java @@ -0,0 +1,123 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.event; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.JobRunner; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.testing.assertj.TraceAssert; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +abstract class CustomSpanEventTest { + + private static final boolean VERSION_GREATER_THAN_4_0 = Boolean.getBoolean("testLatestDeps"); + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + private final JobRunner runner; + + CustomSpanEventTest(JobRunner runner) { + this.runner = runner; + } + + @Test + void shouldBeAbleToCallSpanCurrentAndAddCustomInfoToSpans() { + runner.runJob("customSpanEventsItemsJob"); + + testing.waitAndAssertTraces( + trace -> { + List> assertions = + new ArrayList<>( + Arrays.asList( + span -> + span.hasName("BatchJob customSpanEventsItemsJob") + .hasKind(SpanKind.INTERNAL) + .hasEventsSatisfyingExactly( + event -> event.hasName("job.before"), + event -> event.hasName("job.after")), + span -> + span.hasName("BatchJob customSpanEventsItemsJob.customSpanEventsItemStep") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)) + .satisfies( + spanData -> { + // CompositeChunkListener has broken ordering that causes + // listeners that do not override order() to appear first at all + // times because of that a custom ChunkListener will always see + // a Step span when using spring-batch versions [3, 4) + // that bug was fixed in 4.0 + if (VERSION_GREATER_THAN_4_0) { + assertThat(spanData) + .hasEventsSatisfyingExactly( + event -> event.hasName("step.before"), + event -> event.hasName("step.after")); + } else { + assertThat(spanData) + .hasEventsSatisfyingExactly( + event -> event.hasName("step.before"), + event -> event.hasName("chunk.before"), + event -> event.hasName("chunk.after"), + event -> event.hasName("step.after")); + } + }), + span -> + span.hasName( + "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1)) + .satisfies( + spanData -> { + // CompositeChunkListener has broken ordering that causes + // listeners that do not override order() to appear first at all + // times because of that a custom ChunkListener will always see + // a Step span when using spring-batch versions [3, 4) + // that bug was fixed in 4.0 + if (VERSION_GREATER_THAN_4_0) { + assertThat(spanData) + .hasEventsSatisfyingExactly( + event -> event.hasName("chunk.before"), + event -> event.hasName("chunk.after")); + } else { + assertThat(spanData.getEvents()).isEmpty(); + } + }))); + itemSpans(trace, assertions); + trace.hasSpansSatisfyingExactly(assertions); + }); + } + + protected void itemSpans(TraceAssert trace, List> assertions) { + assertions.addAll( + Arrays.asList( + span -> + span.hasName("BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2)), + span -> + span.hasName("BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2)), + span -> + span.hasName( + "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemProcess") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2)), + span -> + span.hasName("BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemWrite") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2)))); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/JavaConfigCustomSpanEventTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/JavaConfigCustomSpanEventTest.java new file mode 100644 index 0000000000..174f876ee8 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/JavaConfigCustomSpanEventTest.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.event; + +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.ApplicationConfigRunner; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.SpringBatchApplication; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +class JavaConfigCustomSpanEventTest extends CustomSpanEventTest { + + @RegisterExtension + static final ApplicationConfigRunner runner = + new ApplicationConfigRunner( + () -> new AnnotationConfigApplicationContext(SpringBatchApplication.class)); + + JavaConfigCustomSpanEventTest() { + super(runner); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/JsrConfigCustomSpanEventTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/JsrConfigCustomSpanEventTest.java new file mode 100644 index 0000000000..ec3222881c --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/JsrConfigCustomSpanEventTest.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.event; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.JavaxBatchConfigRunner; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.testing.assertj.TraceAssert; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import org.junit.jupiter.api.extension.RegisterExtension; + +class JsrConfigCustomSpanEventTest extends CustomSpanEventTest { + + @RegisterExtension static final JavaxBatchConfigRunner runner = new JavaxBatchConfigRunner(); + + JsrConfigCustomSpanEventTest() { + super(runner); + } + + @Override + protected void itemSpans(TraceAssert trace, List> assertions) { + assertions.addAll( + Arrays.asList( + span -> + span.hasName("BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2)) + .hasEventsSatisfyingExactly( + event -> event.hasName("item.read.before"), + event -> event.hasName("item.read.after")), + span -> + span.hasName( + "BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemProcess") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2)) + .hasEventsSatisfyingExactly( + event -> event.hasName("item.process.before"), + event -> event.hasName("item.process.after")), + span -> + span.hasName("BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2)) + .hasEventsSatisfyingExactly(event -> event.hasName("item.read.before")), + span -> + span.hasName("BatchJob customSpanEventsItemsJob.customSpanEventsItemStep.ItemWrite") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2)) + .hasEventsSatisfyingExactly( + event -> event.hasName("item.write.before"), + event -> event.hasName("item.write.after")))); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/XmlConfigCustomSpanEventTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/XmlConfigCustomSpanEventTest.java new file mode 100644 index 0000000000..4063196e7a --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/event/XmlConfigCustomSpanEventTest.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.event; + +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.ApplicationConfigRunner; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +class XmlConfigCustomSpanEventTest extends CustomSpanEventTest { + + @RegisterExtension + static final ApplicationConfigRunner runner = + new ApplicationConfigRunner(() -> new ClassPathXmlApplicationContext("spring-batch.xml")); + + XmlConfigCustomSpanEventTest() { + super(runner); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/ItemLevelSpanTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/ItemLevelSpanTest.java new file mode 100644 index 0000000000..a55715ad17 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/ItemLevelSpanTest.java @@ -0,0 +1,320 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.JobRunner; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.trace.data.SpanData; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import org.assertj.core.api.AssertAccess; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.job.AbstractJob; +import org.springframework.batch.core.step.tasklet.TaskletStep; +import org.springframework.batch.repeat.policy.SimpleCompletionPolicy; +import org.springframework.batch.repeat.support.TaskExecutorRepeatTemplate; + +abstract class ItemLevelSpanTest { + private final JobRunner runner; + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + ItemLevelSpanTest(JobRunner runner) { + this.runner = runner; + } + + @Test + void shouldTraceItemReadProcessAndWriteCalls() { + runner.runJob("itemsAndTaskletJob"); + + testing.waitAndAssertTraces( + trace -> { + List> assertions = new ArrayList<>(); + assertions.add( + span -> span.hasName("BatchJob itemsAndTaskletJob").hasKind(SpanKind.INTERNAL)); + + // item step + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0))); + + // chunk 1, items 0-5 + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))); + for (int i = 3; i <= 7; i++) { + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2))); + } + for (int i = 8; i <= 12; i++) { + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemProcess") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2))); + } + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemWrite") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2))); + + // chunk 2, items 5-10 + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))); + for (int i = 15; i <= 19; i++) { + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(14))); + } + for (int i = 20; i <= 24; i++) { + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemProcess") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(14))); + } + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemWrite") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(14))); + + // chunk 3, items 10-13 + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))); + // +1 for last read returning end of stream marker + for (int i = 27; i <= 30; i++) { + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(26))); + } + for (int i = 31; i <= 33; i++) { + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemProcess") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(26))); + } + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemWrite") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(26))); + + // tasklet step + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.taskletStep") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0))); + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.taskletStep.Tasklet") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(35))); + + trace.hasSpansSatisfyingExactly(assertions); + }); + } + + @Test + void shouldTraceAllItemOperationsOnAparallelItemsJob() { + runner.runJob("parallelItemsJob"); + + testing.waitAndAssertTraces( + trace -> { + // as chunks are processed in parallel we need to sort them to guarantee that they are + // in the expected order + // firstly compute child span count for each chunk, we'll sort chunks from larger to + // smaller based on child count + + List spans = AssertAccess.getActual(trace); + Map childCount = new HashMap<>(); + + for (SpanData span : spans) { + if (span.getName().equals("BatchJob parallelItemsJob.parallelItemsStep.Chunk")) { + childCount.put( + span, + spans.stream() + .filter(it -> it.getParentSpanId().equals(span.getSpanId())) + .count()); + } + } + + spans.sort( + Comparator.comparingLong( + it -> { + // job span is first + if (it.getName().equals("BatchJob parallelItemsJob")) { + return 0; + } + // step span is second + if (it.getName().equals("BatchJob parallelItemsJob.parallelItemsStep")) { + return 1; + } + + // find the chunk this span belongs to + SpanData chunkSpan = it; + while (chunkSpan != null + && !chunkSpan + .getName() + .equals("BatchJob parallelItemsJob.parallelItemsStep.Chunk")) { + SpanData currentChunkSpan = chunkSpan; + chunkSpan = + spans.stream() + .filter( + candidate -> + candidate + .getSpanId() + .equals(currentChunkSpan.getParentSpanId())) + .findFirst() + .orElse(null); + } + if (chunkSpan != null) { + // sort larger chunks first + return 100 - childCount.get(chunkSpan); + } + throw new IllegalStateException("item spans should have a parent chunk span"); + })); + + List> assertions = new ArrayList<>(); + assertions.add( + span -> span.hasName("BatchJob parallelItemsJob").hasKind(SpanKind.INTERNAL)); + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0))); + + // chunk 1, first two items; thread 1 + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))); + for (int i = 3; i <= 4; i++) { + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2))); + } + for (int i = 5; i <= 6; i++) { + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.ItemProcess") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2))); + } + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.ItemWrite") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2))); + + // chunk 2, items 3 & 4; thread 2 + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))); + for (int i = 9; i <= 10; i++) { + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(8))); + } + for (int i = 11; i <= 12; i++) { + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.ItemProcess") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(8))); + } + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.ItemWrite") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(8))); + + // chunk 3, 5th item; thread 1 + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))); + // +1 for last read returning end of stream marker + for (int i = 15; i <= 16; i++) { + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(14))); + } + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.ItemProcess") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(14))); + assertions.add( + span -> + span.hasName("BatchJob parallelItemsJob.parallelItemsStep.ItemWrite") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(14))); + + trace.hasSpansSatisfyingExactly(assertions); + }); + } + + protected void postProcessParallelItemsJob(String jobName, Job job) { + if ("parallelItemsJob".equals(jobName)) { + Step step = ((AbstractJob) job).getStep("parallelItemsStep"); + TaskletStep taskletStep = (TaskletStep) step; + // explicitly set the number of chunks we expect from this test to ensure we always get + // the same number of spans + try { + Field field = taskletStep.getClass().getDeclaredField("stepOperations"); + field.setAccessible(true); + TaskExecutorRepeatTemplate stepOperations = + (TaskExecutorRepeatTemplate) field.get(taskletStep); + stepOperations.setCompletionPolicy(new SimpleCompletionPolicy(3)); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/JavaConfigItemLevelSpanTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/JavaConfigItemLevelSpanTest.java new file mode 100644 index 0000000000..8b12e7e0ae --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/JavaConfigItemLevelSpanTest.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item; + +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.ApplicationConfigRunner; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +class JavaConfigItemLevelSpanTest extends ItemLevelSpanTest { + static JavaConfigItemLevelSpanTest instance; + + @RegisterExtension + static final ApplicationConfigRunner runner = + new ApplicationConfigRunner( + () -> new ClassPathXmlApplicationContext("spring-batch.xml"), + (jobName, job) -> instance.postProcessParallelItemsJob(jobName, job)); + + JavaConfigItemLevelSpanTest() { + super(runner); + instance = this; + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/JsrConfigItemLevelSpanTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/JsrConfigItemLevelSpanTest.java new file mode 100644 index 0000000000..471d00af4f --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/JsrConfigItemLevelSpanTest.java @@ -0,0 +1,135 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.JavaxBatchConfigRunner; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class JsrConfigItemLevelSpanTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @RegisterExtension static final JavaxBatchConfigRunner runner = new JavaxBatchConfigRunner(); + + @Test + void shouldTraceItemReadProcessAndWriteCalls() { + runner.runJob("itemsAndTaskletJob"); + + testing.waitAndAssertTraces( + trace -> { + List> assertions = new ArrayList<>(); + assertions.add( + span -> span.hasName("BatchJob itemsAndTaskletJob").hasKind(SpanKind.INTERNAL)); + + // item step + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0))); + + // chunk 1, items 0-5 + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))); + + for (int i = 3; i <= 11; i += 2) { + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2))); + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemProcess") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2))); + } + + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemWrite") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2))); + + // chunk 2, items 6-10 + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))); + for (int i = 15; i <= 23; i += 2) { + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(14))); + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemProcess") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(14))); + } + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemWrite") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(14))); + + // chunk 3, items 11-13 + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.Chunk") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))); + for (int i = 27; i <= 32; i += 2) { + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemRead") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(26))); + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemProcess") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(26))); + } + + // last read returning end of stream marker + assertions.add(span -> span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemRead")); + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.itemStep.ItemWrite") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(26))); + + // tasklet step + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.taskletStep") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0))); + assertions.add( + span -> + span.hasName("BatchJob itemsAndTaskletJob.taskletStep.Tasklet") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(35))); + + trace.hasSpansSatisfyingExactly(assertions); + }); + } +} diff --git a/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/XmlConfigItemLevelSpanTest.java b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/XmlConfigItemLevelSpanTest.java new file mode 100644 index 0000000000..a18b5abc84 --- /dev/null +++ b/instrumentation/spring/spring-batch-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/batch/v3_0/item/XmlConfigItemLevelSpanTest.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.item; + +import io.opentelemetry.javaagent.instrumentation.spring.batch.v3_0.runner.ApplicationConfigRunner; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +class XmlConfigItemLevelSpanTest extends ItemLevelSpanTest { + + static XmlConfigItemLevelSpanTest instance; + + @RegisterExtension + static final ApplicationConfigRunner runner = + new ApplicationConfigRunner( + () -> new ClassPathXmlApplicationContext("spring-batch.xml"), + (jobName, job) -> instance.postProcessParallelItemsJob(jobName, job)); + + XmlConfigItemLevelSpanTest() { + super(runner); + instance = this; + } +}