diff --git a/tests/spring-cloud-sleuth-instrumentation-async-tests/pom.xml b/tests/spring-cloud-sleuth-instrumentation-async-tests/pom.xml
index 947798f7f..b49903bec 100644
--- a/tests/spring-cloud-sleuth-instrumentation-async-tests/pom.xml
+++ b/tests/spring-cloud-sleuth-instrumentation-async-tests/pom.xml
@@ -59,6 +59,11 @@
org.springframework.cloud
spring-cloud-starter-sleuth
+
+ io.zipkin.brave
+ brave-tests
+ test
+
org.springframework.boot
spring-boot-starter-test
diff --git a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncIntegrationTests.java b/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncIntegrationTests.java
index 3ca91cd8f..a71d3df1f 100644
--- a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncIntegrationTests.java
+++ b/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncIntegrationTests.java
@@ -16,183 +16,102 @@
package org.springframework.cloud.sleuth.instrument.async;
-import java.util.AbstractMap;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
-
-import brave.Span;
-import brave.Tracer;
-import brave.Tracing;
-import brave.sampler.Sampler;
-import org.awaitility.Awaitility;
-import org.junit.Before;
+import brave.SpanCustomizer;
+import brave.handler.MutableSpan;
+import brave.handler.SpanHandler;
+import brave.propagation.CurrentTraceContext;
+import brave.propagation.CurrentTraceContext.Scope;
+import brave.propagation.TraceContext;
+import brave.test.IntegrationTestSpanHandler;
+import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.SpanName;
-import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.test.context.junit4.SpringRunner;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.assertj.core.api.BDDAssertions.then;
+import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(
+ webEnvironment = SpringBootTest.WebEnvironment.NONE,
classes = { TraceAsyncIntegrationTests.TraceAsyncITestConfiguration.class })
public class TraceAsyncIntegrationTests {
- @Autowired
- ClassPerformingAsyncLogic classPerformingAsyncLogic;
+ @ClassRule
+ public static IntegrationTestSpanHandler spans = new IntegrationTestSpanHandler();
+
+ TraceContext context = TraceContext.newBuilder().traceId(1).spanId(2).sampled(true)
+ .build();
@Autowired
- Tracing tracer;
+ AsyncLogic asyncLogic;
@Autowired
- ArrayListSpanReporter reporter;
-
- @Before
- public void cleanup() {
- this.classPerformingAsyncLogic.clear();
- this.reporter.clear();
- }
+ CurrentTraceContext currentTraceContext;
@Test
public void should_set_span_on_an_async_annotated_method() {
- whenAsyncProcessingTakesPlace();
+ asyncLogic.invokeAsync();
- thenANewAsyncSpanGetsCreated();
+ assertSpan_invokeAsync(takeDesirableSpan());
}
@Test
public void should_set_span_with_custom_method_on_an_async_annotated_method() {
- whenAsyncProcessingTakesPlaceWithCustomSpanName();
+ asyncLogic.invokeAsync_customName();
- thenAsyncSpanHasCustomName();
+ assertSpan_invokeAsync_customName(takeDesirableSpan());
}
@Test
public void should_continue_a_span_on_an_async_annotated_method() {
- Span span = givenASpanInCurrentThread();
+ try (Scope ws = currentTraceContext.maybeScope(context)) {
+ asyncLogic.invokeAsync();
- try (Tracer.SpanInScope ws = this.tracer.tracer().withSpanInScope(span.start())) {
- whenAsyncProcessingTakesPlace();
-
- thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(span);
- }
- finally {
- span.finish();
+ MutableSpan span = assertSpan_invokeAsync(takeDesirableSpan());
+ assertThat(span.traceId()).isEqualTo(context.traceIdString());
}
}
@Test
public void should_continue_a_span_with_custom_method_on_an_async_annotated_method() {
- Span span = givenASpanInCurrentThread();
+ try (Scope ws = currentTraceContext.maybeScope(context)) {
+ asyncLogic.invokeAsync_customName();
- try (Tracer.SpanInScope ws = this.tracer.tracer().withSpanInScope(span.start())) {
- whenAsyncProcessingTakesPlaceWithCustomSpanName();
-
- thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(
- span);
- }
- finally {
- span.finish();
+ MutableSpan span = assertSpan_invokeAsync_customName(takeDesirableSpan());
+ assertThat(span.traceId()).isEqualTo(context.traceIdString());
}
}
- private Span givenASpanInCurrentThread() {
- return this.tracer.tracer().nextSpan().name("http:existing");
+ static MutableSpan assertSpan_invokeAsync_customName(MutableSpan span) {
+ assertThat(span.name()).isEqualTo("foo");
+ assertThat(span.containsAnnotation("@Async")).isTrue();
+ assertThat(span.tags()).containsEntry("class", "AsyncLogic")
+ .containsEntry("method", "invokeAsync_customName");
+ return span;
}
- private void whenAsyncProcessingTakesPlace() {
- this.classPerformingAsyncLogic.invokeAsynchronousLogic();
+ static MutableSpan assertSpan_invokeAsync(MutableSpan span) {
+ assertThat(span.name()).isEqualTo("invoke-async");
+ assertThat(span.containsAnnotation("@Async")).isTrue();
+ assertThat(span.tags()).containsEntry("class", "AsyncLogic")
+ .containsEntry("method", "invokeAsync");
+ return span;
}
- private void whenAsyncProcessingTakesPlaceWithCustomSpanName() {
- this.classPerformingAsyncLogic.customNameInvokeAsynchronousLogic();
- }
-
- private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(final Span span) {
- Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
- Span asyncSpan = TraceAsyncIntegrationTests.this.classPerformingAsyncLogic
- .getSpan();
- then(asyncSpan.context().traceId()).isEqualTo(span.context().traceId());
- List spans = TraceAsyncIntegrationTests.this.reporter
- .getSpans();
- zipkin2.Span reportedAsyncSpan = spans.stream()
- .filter(span2 -> span2.name().equals("invoke-asynchronous-logic"))
- .findFirst().orElseThrow(() -> new AssertionError(
- "Should have a span with custom name"));
- then(reportedAsyncSpan.traceId()).isEqualTo(span.context().traceIdString());
- then(reportedAsyncSpan.name()).isEqualTo("invoke-asynchronous-logic");
- then(reportedAsyncSpan.tags())
- .contains(new AbstractMap.SimpleEntry<>("class",
- "ClassPerformingAsyncLogic"))
- .contains(new AbstractMap.SimpleEntry<>("method",
- "invokeAsynchronousLogic"));
- });
- }
-
- private void thenANewAsyncSpanGetsCreated() {
- Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
- List spans = TraceAsyncIntegrationTests.this.reporter
- .getSpans();
- then(spans).hasSize(2);
- zipkin2.Span reportedAsyncSpan = spans.stream()
- .filter(span -> span.name().equals("invoke-asynchronous-logic"))
- .findFirst().orElseThrow(() -> new AssertionError(
- "Should have a span with custom name"));
- then(reportedAsyncSpan.tags())
- .contains(new AbstractMap.SimpleEntry<>("class",
- "ClassPerformingAsyncLogic"))
- .contains(new AbstractMap.SimpleEntry<>("method",
- "invokeAsynchronousLogic"));
- });
- }
-
- private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(
- final Span span) {
- Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
- Span asyncSpan = TraceAsyncIntegrationTests.this.classPerformingAsyncLogic
- .getSpan();
- then(asyncSpan.context().traceId()).isEqualTo(span.context().traceId());
- List spans = TraceAsyncIntegrationTests.this.reporter
- .getSpans();
- then(spans).hasSize(2);
- zipkin2.Span reportedAsyncSpan = spans.stream()
- .filter(span2 -> span2.name().equals("foo")).findFirst()
- .orElseThrow(() -> new AssertionError(
- "Should have a span with custom name"));
- then(reportedAsyncSpan.traceId()).isEqualTo(span.context().traceIdString());
- then(reportedAsyncSpan.name()).isEqualTo("foo");
- then(reportedAsyncSpan.tags())
- .contains(new AbstractMap.SimpleEntry<>("class",
- "ClassPerformingAsyncLogic"))
- .contains(new AbstractMap.SimpleEntry<>("method",
- "customNameInvokeAsynchronousLogic"));
- });
- }
-
- private void thenAsyncSpanHasCustomName() {
- Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
- List spans = TraceAsyncIntegrationTests.this.reporter
- .getSpans();
- zipkin2.Span reportedAsyncSpan = spans.stream()
- .filter(span2 -> span2.name().equals("foo")).findFirst()
- .orElseThrow(() -> new AssertionError(
- "Should have a span with custom name"));
- then(reportedAsyncSpan.name()).isEqualTo("foo");
- then(reportedAsyncSpan.tags())
- .contains(new AbstractMap.SimpleEntry<>("class",
- "ClassPerformingAsyncLogic"))
- .contains(new AbstractMap.SimpleEntry<>("method",
- "customNameInvokeAsynchronousLogic"));
- });
+ // Sleuth adds spans named "async" with no tags when an executor is used.
+ // We don't want that one.
+ MutableSpan takeDesirableSpan() {
+ MutableSpan span1 = spans.takeLocalSpan();
+ MutableSpan span2 = spans.takeLocalSpan();
+ return span1.name().equals("async") ? span2 : span1;
}
@DefaultTestAutoConfiguration
@@ -201,49 +120,34 @@ public class TraceAsyncIntegrationTests {
static class TraceAsyncITestConfiguration {
@Bean
- ClassPerformingAsyncLogic asyncClass(Tracer tracer) {
- return new ClassPerformingAsyncLogic(tracer);
+ AsyncLogic asyncLogic(SpanCustomizer customizer) {
+ return new AsyncLogic(customizer);
}
@Bean
- Sampler defaultSampler() {
- return Sampler.ALWAYS_SAMPLE;
- }
-
- @Bean
- ArrayListSpanReporter reporter() {
- return new ArrayListSpanReporter();
+ SpanHandler spanHandler() {
+ return spans;
}
}
- static class ClassPerformingAsyncLogic {
+ static class AsyncLogic {
- private final Tracer tracer;
+ final SpanCustomizer customizer;
- AtomicReference span = new AtomicReference<>();
-
- ClassPerformingAsyncLogic(Tracer tracer) {
- this.tracer = tracer;
+ AsyncLogic(SpanCustomizer customizer) {
+ this.customizer = customizer;
}
@Async
- public void invokeAsynchronousLogic() {
- this.span.set(this.tracer.currentSpan());
+ public void invokeAsync() {
+ customizer.annotate("@Async"); // proves the handler is in scope
}
@Async
@SpanName("foo")
- public void customNameInvokeAsynchronousLogic() {
- this.span.set(this.tracer.currentSpan());
- }
-
- public Span getSpan() {
- return this.span.get();
- }
-
- public void clear() {
- this.span.set(null);
+ public void invokeAsync_customName() {
+ customizer.annotate("@Async"); // proves the handler is in scope
}
}