diff --git a/buildSrc/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts b/buildSrc/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts index 8b11975c11..92e58b6338 100644 --- a/buildSrc/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts +++ b/buildSrc/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts @@ -96,11 +96,11 @@ afterEvaluate { // If agent depends on some shared instrumentation module that is not a testing module, it will // be packaged into the testing jar so we need to make sure to exclude from the test classpath. - val libPath = it.absolutePath - val instrumentationPath = file("${rootDir}/instrumentation/").absolutePath - if (libPath.startsWith(instrumentationPath) && - libPath.endsWith(".jar") && - !libPath.substring(instrumentationPath.length).contains("testing")) { + val lib = it.absoluteFile + val instrumentationDir = file("${rootDir}/instrumentation/").absoluteFile + if (lib.startsWith(instrumentationDir) && + lib.extension == "jar" && + !lib.absolutePath.substring(instrumentationDir.absolutePath.length).contains("testing")) { return@filter false } return@filter true diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent-unit-tests/src/test/java/TracingRequestHandlerTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent-unit-tests/src/test/java/TracingRequestHandlerTest.java index e9a5da634d..821cb3d81e 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent-unit-tests/src/test/java/TracingRequestHandlerTest.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent-unit-tests/src/test/java/TracingRequestHandlerTest.java @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import static io.opentelemetry.instrumentation.testing.util.TraceUtils.withClientSpan; import static org.assertj.core.api.Assertions.assertThat; import com.amazonaws.DefaultRequest; @@ -12,12 +11,14 @@ import com.amazonaws.Response; import com.amazonaws.http.HttpResponse; import com.amazonaws.services.sqs.model.SendMessageRequest; import com.amazonaws.services.sqs.model.SendMessageResult; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.TracingRequestHandler; import java.net.URI; import org.apache.http.client.methods.HttpGet; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; -public class TracingRequestHandlerTest { +class TracingRequestHandlerTest { private static Response response(Request request) { return new Response<>(new SendMessageResult(), new HttpResponse(request, new HttpGet())); @@ -29,13 +30,16 @@ public class TracingRequestHandlerTest { return request; } + @RegisterExtension + static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create(); + @Test - public void shouldNotSetScopeAndNotFailIfClientSpanAlreadyPresent() { + void shouldNotSetScopeAndNotFailIfClientSpanAlreadyPresent() { // given TracingRequestHandler underTest = new TracingRequestHandler(); Request request = request(); - withClientSpan( + testing.runWithClientSpan( "test", () -> { // when @@ -47,7 +51,7 @@ public class TracingRequestHandlerTest { } @Test - public void shouldSetScopeIfClientSpanNotPresent() { + void shouldSetScopeIfClientSpanNotPresent() { // given TracingRequestHandler underTest = new TracingRequestHandler(); Request request = request(); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectTest.java index 54f780e16e..b7339c80a5 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectTest.java @@ -8,9 +8,6 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.aspects; import static io.opentelemetry.api.trace.SpanKind.CLIENT; import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.api.trace.SpanKind.SERVER; -import static io.opentelemetry.instrumentation.testing.util.TraceUtils.withClientSpan; -import static io.opentelemetry.instrumentation.testing.util.TraceUtils.withServerSpan; -import static io.opentelemetry.instrumentation.testing.util.TraceUtils.withSpan; import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -34,8 +31,7 @@ import org.springframework.aop.aspectj.annotation.AspectJProxyFactory; /** Spring AOP Test for {@link WithSpanAspect}. */ public class WithSpanAspectTest { @RegisterExtension - static final LibraryInstrumentationExtension instrumentation = - LibraryInstrumentationExtension.create(); + static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create(); static class WithSpanTester { @WithSpan @@ -79,7 +75,7 @@ public class WithSpanAspectTest { @BeforeEach void setup() { AspectJProxyFactory factory = new AspectJProxyFactory(new WithSpanTester()); - WithSpanAspect aspect = new WithSpanAspect(instrumentation.getOpenTelemetry()); + WithSpanAspect aspect = new WithSpanAspect(testing.getOpenTelemetry()); factory.addAspect(aspect); withSpanTester = factory.getProxy(); @@ -94,10 +90,10 @@ public class WithSpanAspectTest { @DisplayName("when method is annotated with @WithSpan should wrap method execution in a Span") void withSpanWithDefaults() throws Throwable { // when - withSpan("parent", () -> withSpanTester.testWithSpan()); + testing.runWithSpan("parent", withSpanTester::testWithSpan); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -116,10 +112,10 @@ public class WithSpanAspectTest { "when @WithSpan value is set should wrap method execution in a Span with custom name") void withSpanName() throws Throwable { // when - withSpan("parent", () -> withSpanTester.testWithSpanWithValue()); + testing.runWithSpan("parent", () -> withSpanTester.testWithSpanWithValue()); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -138,7 +134,7 @@ public class WithSpanAspectTest { assertThatThrownBy(() -> withSpanTester.testWithSpanWithException()) .isInstanceOf(Exception.class); - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -154,10 +150,10 @@ public class WithSpanAspectTest { "when method is annotated with @WithSpan(kind=CLIENT) should build span with the declared SpanKind") void withSpanKind() throws Throwable { // when - withSpan("parent", () -> withSpanTester.testWithClientSpan()); + testing.runWithSpan("parent", () -> withSpanTester.testWithClientSpan()); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -174,10 +170,10 @@ public class WithSpanAspectTest { "when method is annotated with @WithSpan(kind=CLIENT) and context already contains a CLIENT span should suppress span") void suppressClientSpan() throws Throwable { // when - withClientSpan("parent", () -> withSpanTester.testWithClientSpan()); + testing.runWithClientSpan("parent", withSpanTester::testWithClientSpan); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -190,10 +186,10 @@ public class WithSpanAspectTest { "when method is annotated with @WithSpan(kind=SERVER) and context already contains a SERVER span should suppress span") void suppressServerSpan() throws Throwable { // when - withServerSpan("parent", () -> withSpanTester.testWithServerSpan()); + testing.runWithServerSpan("parent", withSpanTester::testWithServerSpan); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -211,10 +207,10 @@ public class WithSpanAspectTest { CompletableFuture future = new CompletableFuture<>(); // when - withSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future)); + testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future)); // then - assertThat(instrumentation.waitForTraces(1)) + assertThat(testing.waitForTraces(1)) .hasTracesSatisfyingExactly( trace -> trace @@ -225,7 +221,7 @@ public class WithSpanAspectTest { future.complete("DONE"); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -243,10 +239,10 @@ public class WithSpanAspectTest { CompletableFuture future = new CompletableFuture<>(); // when - withSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future)); + testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future)); // then - assertThat(instrumentation.waitForTraces(1)) + assertThat(testing.waitForTraces(1)) .hasTracesSatisfyingExactly( trace -> trace @@ -257,7 +253,7 @@ public class WithSpanAspectTest { future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally")); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -274,10 +270,10 @@ public class WithSpanAspectTest { @DisplayName("should end Span on incompatible return value") void onIncompatibleReturnValue() throws Throwable { // when - withSpan("parent", () -> withSpanTester.testAsyncCompletionStage(null)); + testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(null)); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -300,10 +296,10 @@ public class WithSpanAspectTest { CompletableFuture future = new CompletableFuture<>(); // when - withSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future)); + testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future)); // then - assertThat(instrumentation.waitForTraces(1)) + assertThat(testing.waitForTraces(1)) .hasTracesSatisfyingExactly( trace -> trace @@ -314,7 +310,7 @@ public class WithSpanAspectTest { future.complete("DONE"); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -332,10 +328,10 @@ public class WithSpanAspectTest { CompletableFuture future = new CompletableFuture<>(); // when - withSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future)); + testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future)); // then - assertThat(instrumentation.waitForTraces(1)) + assertThat(testing.waitForTraces(1)) .hasTracesSatisfyingExactly( trace -> trace @@ -346,7 +342,7 @@ public class WithSpanAspectTest { future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally")); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -365,10 +361,10 @@ public class WithSpanAspectTest { CompletableFuture future = CompletableFuture.completedFuture("Done"); // when - withSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future)); + testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future)); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -387,10 +383,10 @@ public class WithSpanAspectTest { future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally")); // when - withSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future)); + testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future)); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> @@ -407,10 +403,10 @@ public class WithSpanAspectTest { @DisplayName("should end Span on incompatible return value") void onIncompatibleReturnValue() throws Throwable { // when - withSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(null)); + testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(null)); // then - List> traces = instrumentation.waitForTraces(1); + List> traces = testing.waitForTraces(1); assertThat(traces) .hasTracesSatisfyingExactly( trace -> diff --git a/instrumentation/spring/spring-web-3.1/library/src/test/java/io/opentelemetry/instrumentation/spring/httpclients/RestTemplateInterceptorTest.java b/instrumentation/spring/spring-web-3.1/library/src/test/java/io/opentelemetry/instrumentation/spring/httpclients/RestTemplateInterceptorTest.java index 147b99674c..ccec443168 100644 --- a/instrumentation/spring/spring-web-3.1/library/src/test/java/io/opentelemetry/instrumentation/spring/httpclients/RestTemplateInterceptorTest.java +++ b/instrumentation/spring/spring-web-3.1/library/src/test/java/io/opentelemetry/instrumentation/spring/httpclients/RestTemplateInterceptorTest.java @@ -5,7 +5,6 @@ package io.opentelemetry.instrumentation.spring.httpclients; -import static io.opentelemetry.instrumentation.testing.util.TraceUtils.withClientSpan; import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat; import static org.mockito.BDDMockito.then; @@ -22,8 +21,7 @@ import org.springframework.http.client.ClientHttpRequestExecution; @ExtendWith(MockitoExtension.class) class RestTemplateInterceptorTest { @RegisterExtension - static final LibraryInstrumentationExtension instrumentation = - LibraryInstrumentationExtension.create(); + static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create(); @Mock HttpRequest httpRequestMock; @Mock ClientHttpRequestExecution requestExecutionMock; @@ -32,11 +30,10 @@ class RestTemplateInterceptorTest { @Test void shouldSkipWhenContextHasClientSpan() throws Exception { // given - RestTemplateInterceptor interceptor = - new RestTemplateInterceptor(instrumentation.getOpenTelemetry()); + RestTemplateInterceptor interceptor = new RestTemplateInterceptor(testing.getOpenTelemetry()); // when - withClientSpan( + testing.runWithClientSpan( "parent", () -> { interceptor.intercept(httpRequestMock, requestBody, requestExecutionMock); @@ -45,7 +42,7 @@ class RestTemplateInterceptorTest { // then then(requestExecutionMock).should().execute(httpRequestMock, requestBody); - assertThat(instrumentation.waitForTraces(1)) + assertThat(testing.waitForTraces(1)) .hasTracesSatisfyingExactly( trace -> trace.hasSpansSatisfyingExactly( diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java index 2dd0af82c2..021734fd0b 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java @@ -6,9 +6,15 @@ package io.opentelemetry.instrumentation.testing.junit; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.testing.InstrumentationTestRunner; import io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil; +import io.opentelemetry.instrumentation.testing.util.ThrowingRunnable; +import io.opentelemetry.instrumentation.testing.util.ThrowingSupplier; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.trace.data.SpanData; import java.util.List; @@ -26,8 +32,21 @@ public abstract class InstrumentationExtension private final InstrumentationTestRunner testRunner; + private final Instrumenter testInstrumenter; + private final Instrumenter testClientInstrumenter; + private final Instrumenter testServerInstrumenter; + protected InstrumentationExtension(InstrumentationTestRunner testRunner) { this.testRunner = testRunner; + testInstrumenter = + Instrumenter.newBuilder(testRunner.getOpenTelemetry(), "test", name -> name) + .newInstrumenter(SpanKindExtractor.alwaysInternal()); + testClientInstrumenter = + Instrumenter.newBuilder(testRunner.getOpenTelemetry(), "test", name -> name) + .newInstrumenter(SpanKindExtractor.alwaysClient()); + testServerInstrumenter = + Instrumenter.newBuilder(testRunner.getOpenTelemetry(), "test", name -> name) + .newInstrumenter(SpanKindExtractor.alwaysServer()); } @Override @@ -106,4 +125,88 @@ public abstract class InstrumentationExtension throws TimeoutException, InterruptedException { return TelemetryDataUtil.waitForTraces(this::spans, numberOfTraces, timeout, unit); } + + /** + * Runs the provided {@code callback} inside the scope of an INTERNAL span with name {@code + * spanName}. + */ + public void runWithSpan(String spanName, ThrowingRunnable callback) + throws E { + runWithSpan( + spanName, + () -> { + callback.run(); + return null; + }); + } + + /** + * Runs the provided {@code callback} inside the scope of an INTERNAL span with name {@code + * spanName}. + */ + public T runWithSpan(String spanName, ThrowingSupplier callback) + throws E { + return runWithInstrumenter(spanName, testInstrumenter, callback); + } + + /** + * Runs the provided {@code callback} inside the scope of an CLIENT span with name {@code + * spanName}. + */ + public void runWithClientSpan(String spanName, ThrowingRunnable callback) + throws E { + runWithClientSpan( + spanName, + () -> { + callback.run(); + return null; + }); + } + + /** + * Runs the provided {@code callback} inside the scope of an CLIENT span with name {@code + * spanName}. + */ + public T runWithClientSpan( + String spanName, ThrowingSupplier callback) throws E { + return runWithInstrumenter(spanName, testClientInstrumenter, callback); + } + + /** + * Runs the provided {@code callback} inside the scope of an CLIENT span with name {@code + * spanName}. + */ + public void runWithServerSpan(String spanName, ThrowingRunnable callback) + throws E { + runWithServerSpan( + spanName, + () -> { + callback.run(); + return null; + }); + } + + /** + * Runs the provided {@code callback} inside the scope of an CLIENT span with name {@code + * spanName}. + */ + public T runWithServerSpan( + String spanName, ThrowingSupplier callback) throws E { + return runWithInstrumenter(spanName, testServerInstrumenter, callback); + } + + private static T runWithInstrumenter( + String spanName, Instrumenter instrumenter, ThrowingSupplier callback) + throws E { + Context context = instrumenter.start(Context.current(), spanName); + Throwable err = null; + try (Scope ignored = context.makeCurrent()) { + return callback.get(); + } catch (Throwable t) { + err = t; + throw t; + } finally { + instrumenter.end(context, spanName, null, err); + } + } } diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TraceUtils.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TraceUtils.java deleted file mode 100644 index 26067e22bf..0000000000 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TraceUtils.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.testing.util; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.tracer.BaseTracer; - -/** Utility class for creating spans in tests. */ -public final class TraceUtils { - - private static final TestTracer TRACER = new TestTracer(); - - public static void withSpan(String spanName, ThrowingRunnable callback) - throws E { - withSpan( - spanName, - () -> { - callback.run(); - return null; - }); - } - - public static T withSpan( - String spanName, ThrowingSupplier callback) throws E { - Context context = TRACER.startSpan(spanName); - try (Scope ignored = context.makeCurrent()) { - T result = callback.get(); - TRACER.end(context); - return result; - } catch (Throwable t) { - TRACER.endExceptionally(context, t); - throw t; - } - } - - public static void withClientSpan( - String spanName, ThrowingRunnable callback) throws E { - withClientSpan( - spanName, - () -> { - callback.run(); - return null; - }); - } - - public static T withClientSpan( - String spanName, ThrowingSupplier callback) throws E { - Context context = TRACER.startClientSpan(spanName); - try (Scope ignored = context.makeCurrent()) { - T result = callback.get(); - TRACER.end(context); - return result; - } catch (Throwable t) { - TRACER.endExceptionally(context, t); - throw t; - } - } - - public static void withServerSpan( - String spanName, ThrowingRunnable callback) throws E { - withServerSpan( - spanName, - () -> { - callback.run(); - return null; - }); - } - - public static T withServerSpan( - String spanName, ThrowingSupplier callback) throws E { - Context context = TRACER.startServerSpan(spanName); - try (Scope ignored = context.makeCurrent()) { - T result = callback.get(); - TRACER.end(context); - return result; - } catch (Throwable t) { - TRACER.endExceptionally(context, t); - throw t; - } - } - - private static final class TestTracer extends BaseTracer { - @Override - protected String getInstrumentationName() { - return "test"; - } - - Context startClientSpan(String name) { - Context parentContext = Context.current(); - Span span = spanBuilder(parentContext, name, SpanKind.CLIENT).startSpan(); - return withClientSpan(parentContext, span); - } - - Context startServerSpan(String name) { - Context parentContext = Context.current(); - Span span = spanBuilder(parentContext, name, SpanKind.SERVER).startSpan(); - return withServerSpan(parentContext, span); - } - } - - private TraceUtils() {} -}