Refactor AgentTestRunner: extract a common interface, convert to Java class (#2223)
This commit is contained in:
parent
fc410706d0
commit
49206212cf
|
@ -43,6 +43,12 @@
|
|||
<Bug pattern="SE_BAD_FIELD_INNER_CLASS"/>
|
||||
</Match>
|
||||
|
||||
<Match>
|
||||
<!-- writing to static field in a non-static method (clearAllExportedData()) -->
|
||||
<Class name="io.opentelemetry.instrumentation.testing.LibraryTestRunner"/>
|
||||
<Bug pattern="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD"/>
|
||||
</Match>
|
||||
|
||||
<Match>
|
||||
<Class name="io.opentelemetry.instrumentation.api.tracer.utils.NetPeerUtils"/>
|
||||
<Method name="setNetPeer"
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.test;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import groovy.lang.Closure;
|
||||
import groovy.lang.DelegatesTo;
|
||||
import groovy.transform.stc.ClosureParams;
|
||||
import groovy.transform.stc.SimpleType;
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.instrumentation.test.asserts.InMemoryExporterAssert;
|
||||
import io.opentelemetry.javaagent.testing.common.AgentTestingExporterAccess;
|
||||
import io.opentelemetry.javaagent.testing.common.TestAgentListenerAccess;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class AgentTestRunner {
|
||||
/**
|
||||
* For test runs, agent's global tracer will report to this list writer.
|
||||
*
|
||||
* <p>Before the start of each test the reported traces will be reset.
|
||||
*/
|
||||
public static final InMemoryExporter TEST_WRITER = new InMemoryExporter();
|
||||
|
||||
static {
|
||||
((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(Level.WARN);
|
||||
((Logger) LoggerFactory.getLogger("io.opentelemetry")).setLevel(Level.DEBUG);
|
||||
}
|
||||
|
||||
public void setupBeforeTests() {
|
||||
TestAgentListenerAccess.reset();
|
||||
}
|
||||
|
||||
public void beforeTest() {
|
||||
assert !Span.current().getSpanContext().isValid()
|
||||
: "Span is active before test has started: " + Span.current();
|
||||
AgentTestingExporterAccess.reset();
|
||||
}
|
||||
|
||||
public static synchronized void agentCleanup() {
|
||||
// Cleanup before assertion.
|
||||
assert TestAgentListenerAccess.getInstrumentationErrorCount() == 0
|
||||
: TestAgentListenerAccess.getInstrumentationErrorCount()
|
||||
+ " Instrumentation errors during test";
|
||||
assert TestAgentListenerAccess.getIgnoredButTransformedClassNames().isEmpty()
|
||||
: "Transformed classes match global libraries ignore matcher: "
|
||||
+ TestAgentListenerAccess.getIgnoredButTransformedClassNames();
|
||||
}
|
||||
|
||||
public static void assertTraces(
|
||||
int size,
|
||||
@ClosureParams(
|
||||
value = SimpleType.class,
|
||||
options = "io.opentelemetry.instrumentation.test.asserts.ListWriterAssert")
|
||||
@DelegatesTo(value = InMemoryExporterAssert.class, strategy = Closure.DELEGATE_FIRST)
|
||||
Closure spec) {
|
||||
InMemoryExporterAssert.assertTraces(AgentTestingExporterAccess::getExportedSpans, size, spec);
|
||||
}
|
||||
}
|
|
@ -5,10 +5,8 @@
|
|||
|
||||
package io.opentelemetry.instrumentation.test
|
||||
|
||||
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.SimpleType
|
||||
import io.opentelemetry.instrumentation.test.asserts.InMemoryExporterAssert
|
||||
import io.opentelemetry.instrumentation.testing.AgentTestRunner
|
||||
import io.opentelemetry.instrumentation.testing.InstrumentationTestRunner
|
||||
|
||||
/**
|
||||
* A trait which initializes agent tests, including bytecode manipulation and a test span exporter.
|
||||
|
@ -16,31 +14,10 @@ import io.opentelemetry.instrumentation.test.asserts.InMemoryExporterAssert
|
|||
*/
|
||||
trait AgentTestTrait {
|
||||
|
||||
static AgentTestRunner agentTestRunner
|
||||
static InMemoryExporter testWriter
|
||||
static InstrumentationTestRunner agentTestRunner = AgentTestRunner.instance()
|
||||
static InMemoryExporter testWriter = new InMemoryExporter()
|
||||
|
||||
void runnerSetupSpec() {
|
||||
agentTestRunner = new AgentTestRunner()
|
||||
testWriter = AgentTestRunner.TEST_WRITER
|
||||
|
||||
agentTestRunner.setupBeforeTests()
|
||||
InstrumentationTestRunner testRunner() {
|
||||
agentTestRunner
|
||||
}
|
||||
|
||||
void runnerSetup() {
|
||||
agentTestRunner.beforeTest()
|
||||
}
|
||||
|
||||
void runnerCleanupSpec() {
|
||||
AgentTestRunner.agentCleanup()
|
||||
}
|
||||
|
||||
void assertTraces(final int size,
|
||||
@ClosureParams(
|
||||
value = SimpleType,
|
||||
options = "io.opentelemetry.instrumentation.test.asserts.ListWriterAssert")
|
||||
@DelegatesTo(value = InMemoryExporterAssert, strategy = Closure.DELEGATE_FIRST)
|
||||
final Closure spec) {
|
||||
AgentTestRunner.assertTraces(size, spec)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
|
||||
package io.opentelemetry.instrumentation.test
|
||||
|
||||
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.SimpleType
|
||||
import io.opentelemetry.api.trace.Span
|
||||
import io.opentelemetry.instrumentation.test.asserts.InMemoryExporterAssert
|
||||
import io.opentelemetry.instrumentation.testing.InstrumentationTestRunner
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
|
@ -17,29 +18,35 @@ import spock.lang.Specification
|
|||
* {@link LibraryTestTrait}.
|
||||
*/
|
||||
abstract class InstrumentationSpecification extends Specification {
|
||||
abstract InstrumentationTestRunner testRunner()
|
||||
|
||||
def setupSpec() {
|
||||
runnerSetupSpec()
|
||||
testRunner().beforeTestClass()
|
||||
}
|
||||
|
||||
abstract void runnerSetupSpec()
|
||||
|
||||
/**
|
||||
* Clears all data exported during a test.
|
||||
*/
|
||||
def setup() {
|
||||
runnerSetup()
|
||||
assert !Span.current().getSpanContext().isValid(): "Span is active before test has started: " + Span.current()
|
||||
testRunner().clearAllExportedData()
|
||||
}
|
||||
|
||||
abstract void runnerSetup()
|
||||
|
||||
def cleanupSpec() {
|
||||
runnerCleanupSpec()
|
||||
testRunner().afterTestClass()
|
||||
}
|
||||
|
||||
abstract void runnerCleanupSpec()
|
||||
boolean forceFlushCalled() {
|
||||
return testRunner().forceFlushCalled()
|
||||
}
|
||||
|
||||
abstract void assertTraces(
|
||||
void assertTraces(
|
||||
final int size,
|
||||
@ClosureParams(
|
||||
value = SimpleType,
|
||||
options = "io.opentelemetry.instrumentation.test.asserts.ListWriterAssert")
|
||||
@DelegatesTo(value = InMemoryExporterAssert, strategy = Closure.DELEGATE_FIRST)
|
||||
final Closure spec)
|
||||
final Closure spec) {
|
||||
InMemoryExporterAssert.assertTraces({ testRunner().getExportedSpans() }, size, spec)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.test
|
||||
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.SimpleType
|
||||
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator
|
||||
import io.opentelemetry.context.Context
|
||||
import io.opentelemetry.context.propagation.ContextPropagators
|
||||
import io.opentelemetry.instrumentation.test.asserts.InMemoryExporterAssert
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk
|
||||
import io.opentelemetry.sdk.common.CompletableResultCode
|
||||
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter
|
||||
import io.opentelemetry.sdk.trace.ReadWriteSpan
|
||||
import io.opentelemetry.sdk.trace.ReadableSpan
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProvider
|
||||
import io.opentelemetry.sdk.trace.SpanProcessor
|
||||
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor
|
||||
import org.junit.Before
|
||||
import spock.lang.Specification
|
||||
/**
|
||||
* A spock test runner which automatically initializes an in-memory exporter that can be used to
|
||||
* verify traces.
|
||||
*/
|
||||
abstract class InstrumentationTestRunner extends Specification {
|
||||
|
||||
protected static final InMemorySpanExporter testExporter
|
||||
|
||||
private static boolean forceFlushCalled
|
||||
|
||||
static {
|
||||
testExporter = InMemorySpanExporter.create()
|
||||
OpenTelemetrySdk.builder()
|
||||
.setTracerProvider(SdkTracerProvider.builder()
|
||||
.addSpanProcessor(new FlushTrackingSpanProcessor())
|
||||
.addSpanProcessor(SimpleSpanProcessor.create(testExporter))
|
||||
.build())
|
||||
.setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
|
||||
.buildAndRegisterGlobal()
|
||||
}
|
||||
|
||||
@Before
|
||||
void beforeTest() {
|
||||
testExporter.reset()
|
||||
forceFlushCalled = false
|
||||
}
|
||||
|
||||
protected static boolean forceFlushCalled() {
|
||||
return forceFlushCalled
|
||||
}
|
||||
|
||||
protected static void assertTraces(
|
||||
final int size,
|
||||
@ClosureParams(
|
||||
value = SimpleType,
|
||||
options = "io.opentelemetry.instrumentation.test.asserts.ListWriterAssert")
|
||||
@DelegatesTo(value = InMemoryExporterAssert, strategy = Closure.DELEGATE_FIRST)
|
||||
final Closure spec) {
|
||||
InMemoryExporterAssert.assertTraces({ testExporter.getFinishedSpanItems() }, size, spec)
|
||||
}
|
||||
|
||||
static class FlushTrackingSpanProcessor implements SpanProcessor {
|
||||
@Override
|
||||
void onStart(Context parentContext, ReadWriteSpan span) {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isStartRequired() {
|
||||
return false
|
||||
}
|
||||
|
||||
@Override
|
||||
void onEnd(ReadableSpan span) {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isEndRequired() {
|
||||
return false
|
||||
}
|
||||
|
||||
@Override
|
||||
CompletableResultCode forceFlush() {
|
||||
forceFlushCalled = true
|
||||
return CompletableResultCode.ofSuccess()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,11 +5,8 @@
|
|||
|
||||
package io.opentelemetry.instrumentation.test
|
||||
|
||||
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.SimpleType
|
||||
import io.opentelemetry.instrumentation.test.asserts.InMemoryExporterAssert
|
||||
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter
|
||||
import io.opentelemetry.instrumentation.testing.InstrumentationTestRunner
|
||||
import io.opentelemetry.instrumentation.testing.LibraryTestRunner
|
||||
|
||||
/**
|
||||
* A trait which initializes instrumentation library tests, including a test span exporter. All
|
||||
|
@ -17,33 +14,13 @@ import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter
|
|||
*/
|
||||
trait LibraryTestTrait {
|
||||
|
||||
static InstrumentationTestRunner instrumentationTestRunner
|
||||
static InMemorySpanExporter testWriter
|
||||
static InstrumentationTestRunner instrumentationTestRunner = LibraryTestRunner.instance()
|
||||
|
||||
void runnerSetupSpec() {
|
||||
instrumentationTestRunner = new InstrumentationTestRunnerImpl()
|
||||
testWriter = InstrumentationTestRunner.testExporter
|
||||
static {
|
||||
instrumentationTestRunner = LibraryTestRunner.instance()
|
||||
}
|
||||
|
||||
void runnerSetup() {
|
||||
instrumentationTestRunner.beforeTest()
|
||||
InstrumentationTestRunner testRunner() {
|
||||
instrumentationTestRunner
|
||||
}
|
||||
|
||||
void runnerCleanupSpec() {
|
||||
}
|
||||
|
||||
boolean forceFlushCalled() {
|
||||
return instrumentationTestRunner.forceFlushCalled()
|
||||
}
|
||||
|
||||
void assertTraces(final int size,
|
||||
@ClosureParams(
|
||||
value = SimpleType,
|
||||
options = "io.opentelemetry.instrumentation.test.asserts.ListWriterAssert")
|
||||
@DelegatesTo(value = InMemoryExporterAssert, strategy = Closure.DELEGATE_FIRST)
|
||||
final Closure spec) {
|
||||
instrumentationTestRunner.assertTraces(size, spec)
|
||||
}
|
||||
|
||||
static class InstrumentationTestRunnerImpl extends InstrumentationTestRunner {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.testing;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import io.opentelemetry.javaagent.testing.common.AgentTestingExporterAccess;
|
||||
import io.opentelemetry.javaagent.testing.common.TestAgentListenerAccess;
|
||||
import io.opentelemetry.sdk.metrics.data.MetricData;
|
||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||
import java.util.List;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* An implementation of {@link InstrumentationTestRunner} that delegates most of its calls to the
|
||||
* OpenTelemetry Javaagent that this process runs with. It uses the {@link
|
||||
* AgentTestingExporterAccess} bridge class to retrieve exported traces and metrics data from the
|
||||
* agent classloader.
|
||||
*/
|
||||
public final class AgentTestRunner implements InstrumentationTestRunner {
|
||||
static {
|
||||
((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(Level.WARN);
|
||||
((Logger) LoggerFactory.getLogger("io.opentelemetry")).setLevel(Level.DEBUG);
|
||||
}
|
||||
|
||||
private static final AgentTestRunner INSTANCE = new AgentTestRunner();
|
||||
|
||||
public static InstrumentationTestRunner instance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTestClass() {
|
||||
TestAgentListenerAccess.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTestClass() {
|
||||
// Cleanup before assertion.
|
||||
assert TestAgentListenerAccess.getInstrumentationErrorCount() == 0
|
||||
: TestAgentListenerAccess.getInstrumentationErrorCount()
|
||||
+ " Instrumentation errors during test";
|
||||
assert TestAgentListenerAccess.getIgnoredButTransformedClassNames().isEmpty()
|
||||
: "Transformed classes match global libraries ignore matcher: "
|
||||
+ TestAgentListenerAccess.getIgnoredButTransformedClassNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllExportedData() {
|
||||
AgentTestingExporterAccess.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SpanData> getExportedSpans() {
|
||||
return AgentTestingExporterAccess.getExportedSpans();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetricData> getExportedMetrics() {
|
||||
return AgentTestingExporterAccess.getExportedMetrics();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceFlushCalled() {
|
||||
return AgentTestingExporterAccess.forceFlushCalled();
|
||||
}
|
||||
|
||||
private AgentTestRunner() {}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.testing;
|
||||
|
||||
import io.opentelemetry.sdk.metrics.data.MetricData;
|
||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This interface defines a common set of operations for interaction with OpenTelemetry SDK and
|
||||
* traces & metrics exporters.
|
||||
*
|
||||
* @see LibraryTestRunner
|
||||
* @see AgentTestRunner
|
||||
*/
|
||||
public interface InstrumentationTestRunner {
|
||||
void beforeTestClass();
|
||||
|
||||
void afterTestClass();
|
||||
|
||||
void clearAllExportedData();
|
||||
|
||||
List<SpanData> getExportedSpans();
|
||||
|
||||
List<MetricData> getExportedMetrics();
|
||||
|
||||
boolean forceFlushCalled();
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.testing;
|
||||
|
||||
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.common.CompletableResultCode;
|
||||
import io.opentelemetry.sdk.metrics.data.MetricData;
|
||||
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
|
||||
import io.opentelemetry.sdk.trace.ReadWriteSpan;
|
||||
import io.opentelemetry.sdk.trace.ReadableSpan;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
||||
import io.opentelemetry.sdk.trace.SpanProcessor;
|
||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An implementation of {@link InstrumentationTestRunner} that initializes OpenTelemetry SDK and
|
||||
* uses in-memory exporter to collect traces and metrics.
|
||||
*/
|
||||
public final class LibraryTestRunner implements InstrumentationTestRunner {
|
||||
|
||||
protected static final InMemorySpanExporter testExporter;
|
||||
private static boolean forceFlushCalled;
|
||||
private static final LibraryTestRunner INSTANCE = new LibraryTestRunner();
|
||||
|
||||
static {
|
||||
testExporter = InMemorySpanExporter.create();
|
||||
OpenTelemetrySdk.builder()
|
||||
.setTracerProvider(
|
||||
SdkTracerProvider.builder()
|
||||
.addSpanProcessor(new FlushTrackingSpanProcessor())
|
||||
.addSpanProcessor(SimpleSpanProcessor.create(testExporter))
|
||||
.build())
|
||||
.setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
|
||||
.buildAndRegisterGlobal();
|
||||
}
|
||||
|
||||
public static InstrumentationTestRunner instance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTestClass() {}
|
||||
|
||||
@Override
|
||||
public void afterTestClass() {}
|
||||
|
||||
@Override
|
||||
public void clearAllExportedData() {
|
||||
testExporter.reset();
|
||||
forceFlushCalled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SpanData> getExportedSpans() {
|
||||
return testExporter.getFinishedSpanItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetricData> getExportedMetrics() {
|
||||
// no metrics support yet
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceFlushCalled() {
|
||||
return forceFlushCalled;
|
||||
}
|
||||
|
||||
private LibraryTestRunner() {}
|
||||
|
||||
private static class FlushTrackingSpanProcessor implements SpanProcessor {
|
||||
@Override
|
||||
public void onStart(Context parentContext, ReadWriteSpan span) {}
|
||||
|
||||
@Override
|
||||
public boolean isStartRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnd(ReadableSpan span) {}
|
||||
|
||||
@Override
|
||||
public boolean isEndRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableResultCode forceFlush() {
|
||||
forceFlushCalled = true;
|
||||
return CompletableResultCode.ofSuccess();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue