
io.opentelemetry.instrumentation.testing.InstrumentationTestRunner Maven / Gradle / Ivy
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.testing;
import static org.awaitility.Awaitility.await;
import io.opentelemetry.api.OpenTelemetry;
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.logs.data.LogRecordData;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.testing.assertj.TraceAssert;
import io.opentelemetry.sdk.testing.assertj.TracesAssert;
import io.opentelemetry.sdk.trace.data.SpanData;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import org.awaitility.core.ConditionTimeoutException;
/**
* This interface defines a common set of operations for interaction with OpenTelemetry SDK and
* traces & metrics exporters.
*
* @see LibraryTestRunner
* @see AgentTestRunner
*/
public abstract class InstrumentationTestRunner {
private final TestInstrumenters testInstrumenters;
protected InstrumentationTestRunner(OpenTelemetry openTelemetry) {
testInstrumenters = new TestInstrumenters(openTelemetry);
}
public abstract void beforeTestClass();
public abstract void afterTestClass();
public abstract void clearAllExportedData();
public abstract OpenTelemetry getOpenTelemetry();
public abstract List getExportedSpans();
public abstract List getExportedMetrics();
public abstract List getExportedLogRecords();
public abstract boolean forceFlushCalled();
/** Return a list of all captured traces, where each trace is a sorted list of spans. */
public final List> traces() {
return TelemetryDataUtil.groupTraces(getExportedSpans());
}
public final List> waitForTraces(int numberOfTraces) {
try {
return TelemetryDataUtil.waitForTraces(
this::getExportedSpans, numberOfTraces, 20, TimeUnit.SECONDS);
} catch (TimeoutException | InterruptedException e) {
throw new AssertionError("Error waiting for " + numberOfTraces + " traces", e);
}
}
@SafeVarargs
@SuppressWarnings("varargs")
public final void waitAndAssertSortedTraces(
Comparator> traceComparator, Consumer... assertions) {
waitAndAssertTraces(traceComparator, Arrays.asList(assertions));
}
@SafeVarargs
@SuppressWarnings("varargs")
public final void waitAndAssertTraces(Consumer... assertions) {
waitAndAssertTraces(Arrays.asList(assertions));
}
public final > void waitAndAssertTraces(Iterable assertions) {
waitAndAssertTraces(null, assertions);
}
private > void waitAndAssertTraces(
@Nullable Comparator> traceComparator, Iterable assertions) {
List assertionsList = new ArrayList<>();
assertions.forEach(assertionsList::add);
try {
await().untilAsserted(() -> doAssertTraces(traceComparator, assertionsList));
} catch (ConditionTimeoutException e) {
// Don't throw this failure since the stack is the awaitility thread, causing confusion.
// Instead, just assert one more time on the test thread, which will fail with a better stack
// trace.
// TODO(anuraaga): There is probably a better way to do this.
doAssertTraces(traceComparator, assertionsList);
}
}
private > void doAssertTraces(
@Nullable Comparator> traceComparator, List assertionsList) {
List> traces = waitForTraces(assertionsList.size());
if (traceComparator != null) {
traces.sort(traceComparator);
}
TracesAssert.assertThat(traces).hasTracesSatisfyingExactly(assertionsList);
}
/**
* Runs the provided {@code callback} inside the scope of an INTERNAL span with name {@code
* spanName}.
*/
public final 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 final T runWithSpan(
String spanName, ThrowingSupplier callback) throws E {
return testInstrumenters.runWithSpan(spanName, callback);
}
/**
* Runs the provided {@code callback} inside the scope of an HTTP CLIENT span with name {@code
* spanName}.
*/
public final void runWithHttpClientSpan(
String spanName, ThrowingRunnable callback) throws E {
runWithHttpClientSpan(
spanName,
() -> {
callback.run();
return null;
});
}
/**
* Runs the provided {@code callback} inside the scope of an HTTP CLIENT span with name {@code
* spanName}.
*/
public final T runWithHttpClientSpan(
String spanName, ThrowingSupplier callback) throws E {
return testInstrumenters.runWithHttpClientSpan(spanName, callback);
}
/**
* Runs the provided {@code callback} inside the scope of an HTTP SERVER span with name {@code
* spanName}.
*/
public final void runWithHttpServerSpan(
String spanName, ThrowingRunnable callback) throws E {
runWithHttpServerSpan(
spanName,
() -> {
callback.run();
return null;
});
}
/**
* Runs the provided {@code callback} inside the scope of an HTTP SERVER span with name {@code
* spanName}.
*/
public final T runWithHttpServerSpan(
String spanName, ThrowingSupplier callback) throws E {
return testInstrumenters.runWithHttpServerSpan(spanName, callback);
}
/** Runs the provided {@code callback} inside the scope of a non-recording span. */
public final T runWithNonRecordingSpan(ThrowingSupplier callback)
throws E {
return testInstrumenters.runWithNonRecordingSpan(callback);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy