Skip to content

Commit

Permalink
Added test resource for Kamon test span reporters.
Browse files Browse the repository at this point in the history
Reporters should become a unique registration name and the registration should be canceled after the test. A test resource helps with the latter and to reduce redundancy for TestSpanReporter creation and registration.

Signed-off-by: Juergen Fickel <juergen.fickel@bosch.io>
  • Loading branch information
Juergen Fickel committed Nov 2, 2022
1 parent 2d4c21f commit 7f3817c
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
import org.eclipse.ditto.internal.utils.tracing.config.DefaultTracingConfig;
import org.eclipse.ditto.internal.utils.tracing.config.TracingConfig;
import org.eclipse.ditto.internal.utils.tracing.filter.AcceptAllTracingFilter;
import org.eclipse.ditto.internal.utils.tracing.span.KamonTestSpanReporterResource;
import org.eclipse.ditto.internal.utils.tracing.span.KamonTracingInitResource;
import org.eclipse.ditto.internal.utils.tracing.span.SpanOperationName;
import org.eclipse.ditto.internal.utils.tracing.span.SpanTagKey;
import org.eclipse.ditto.internal.utils.tracing.span.TestSpanReporter;
import org.eclipse.ditto.internal.utils.tracing.span.TracingSpans;
import org.junit.After;
import org.junit.Before;
Expand All @@ -48,8 +48,6 @@

import com.typesafe.config.ConfigFactory;

import kamon.Kamon;

/**
* Unit test for {@link DittoTracing}.
*/
Expand All @@ -58,14 +56,16 @@ public final class DittoTracingTest {
@ClassRule
public static final KamonTracingInitResource KAMON_TRACING_INIT_RESOURCE = KamonTracingInitResource.newInstance(
KamonTracingInitResource.KamonTracingConfig.defaultValues()
.withIdentifierSchemeDouble()
.withSamplerAlways()
.withTickInterval(Duration.ofMillis(100L))
);

@Rule
public final TestName testName = new TestName();

@Rule
public final KamonTestSpanReporterResource testSpanReporterResource = KamonTestSpanReporterResource.newInstance();

private TracingConfig tracingConfigMock;

@Before
Expand Down Expand Up @@ -246,12 +246,12 @@ public void newPreparedSpanWhenTracingIsEnabledReturnsExpectedPreparedSpan() {
@Test
public void newStartedSpanByTimerWhenTracingIsEnabledReturnsExpectedStartedSpan() {
DittoTracing.init(tracingConfigMock);
final var operationName = SpanOperationName.of(testName.getMethodName());
final var testMethodName = testName.getMethodName();
final var operationName = SpanOperationName.of(testMethodName);
final var timerTags = TagSet.ofTagCollection(List.of(Tag.of("foo", "bar"), Tag.of("marco", "polo")));
final var startedTimer = Timers.newTimer(operationName.toString()).tags(timerTags).start();
final var dittoHeaders = DittoHeaders.newBuilder().correlationId(operationName).build();
final var testSpanReporter = TestSpanReporter.newInstance();
Kamon.addReporter("mySpanReporter", testSpanReporter);
final var testSpanReporter = testSpanReporterResource.registerTestSpanReporter(testMethodName);

final var startedSpan = DittoTracing.newStartedSpanByTimer(dittoHeaders, startedTimer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
public final class KamonHttpContextPropagationTest {

@ClassRule
public static final KamonTracingInitResource KAMON_INIT_RESOURCE = KamonTracingInitResource.newInstance(
KamonTracingInitResource.KamonTracingConfig.defaultValues().withIdentifierSchemeDouble()
public static final KamonTracingInitResource KAMON_TRACING_INIT_RESOURCE = KamonTracingInitResource.newInstance(
KamonTracingInitResource.KamonTracingConfig.defaultValues()
);

private static final String DEFAULT_CHANNEL_NAME = "default";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.internal.utils.tracing.span;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.annotation.concurrent.NotThreadSafe;

import org.eclipse.ditto.base.model.common.ConditionChecker;
import org.junit.rules.ExternalResource;

import kamon.Kamon;
import kamon.module.Module;

/**
* This ExternalResource helps to create instances of {@link TestSpanReporter} and to add them Kamon.
* After the test the registrations are canceled.
* Each {@code TestSpanReporter} must be registered with a unique name.
* Both, cancelling registrations after test and enforcing a unique name stabilises tests which rely on span reporting.
*/
@NotThreadSafe
public final class KamonTestSpanReporterResource extends ExternalResource {

private final Map<String, Module.Registration> reporterRegistrations;

private KamonTestSpanReporterResource() {
reporterRegistrations = new HashMap<>(3);
}

/**
* Returns a new instance of {@code KamonTestSpanReporterResource}.
*
* @return the new instance.
*/
public static KamonTestSpanReporterResource newInstance() {
return new KamonTestSpanReporterResource();
}

/**
* Creates a {@code TestSpanReporter} and registers it under the specified name argument at Kamon.
*
* @param reporterName the registration name of the returned reporter.
* @return the new {@code TestSpanReporter} instance.
* @throws NullPointerException if {@code reporterName} is {@code null}.
* @throws IllegalArgumentException if a reporter with name {@code reporterName} was already registered.
*/
public TestSpanReporter registerTestSpanReporter(final CharSequence reporterName) {
final var reporterNameAsString = validateReporterName(reporterName);
final var result = TestSpanReporter.newInstance();
reporterRegistrations.put(reporterNameAsString, Kamon.addReporter(reporterNameAsString, result));
return result;
}

private String validateReporterName(final CharSequence reporterName) {
ConditionChecker.checkNotNull(reporterName, "reporterName");
final var result = reporterName.toString();
if (reporterRegistrations.containsKey(result)) {
throw new IllegalArgumentException(
MessageFormat.format("A reporter with name <{0}> was already registered.", result)
);
}
return result;
}

@Override
protected void after() {
final var registrationEntryIterator = getRegistrationEntryIterator();
while (registrationEntryIterator.hasNext()) {
cancelRegistration(registrationEntryIterator.next());
registrationEntryIterator.remove();
}
super.after();
}

private Iterator<Map.Entry<String, Module.Registration>> getRegistrationEntryIterator() {
final var reporterRegistrationEntries = reporterRegistrations.entrySet();
return reporterRegistrationEntries.iterator();
}

private static void cancelRegistration(final Map.Entry<String, Module.Registration> registrationEntry) {
final var registration = registrationEntry.getValue();
registration.cancel();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,29 @@
import org.eclipse.ditto.internal.utils.metrics.instruments.timer.StartInstant;
import org.eclipse.ditto.internal.utils.metrics.instruments.timer.Timers;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;

import kamon.Kamon;

/**
* Unit test for {@link PreparedKamonSpan}.
*/
public final class PreparedKamonSpanTest {

@Rule
public final KamonTracingInitResource kamonTracingInitResource = KamonTracingInitResource.newInstance(
@ClassRule
public static final KamonTracingInitResource KAMON_TRACING_INIT_RESOURCE = KamonTracingInitResource.newInstance(
KamonTracingInitResource.KamonTracingConfig.defaultValues()
.withIdentifierSchemeDouble()
.withSamplerAlways()
.withTickInterval(Duration.ofMillis(100L))
);

@Rule
public final TestName testName = new TestName();

@Rule
public final KamonTestSpanReporterResource testSpanReporterResource = KamonTestSpanReporterResource.newInstance();

private PreparedKamonSpan underTest;

@Before
Expand Down Expand Up @@ -135,8 +136,7 @@ public void startReturnsStartedSpanWithExpectedTags() {
underTest.tags(TagSet.ofTagCollection(
List.of(Tag.of("foo", "bar"), Tag.of("ping", "pong"), Tag.of("marco", "polo"))
));
final var testSpanReporter = TestSpanReporter.newInstance();
Kamon.addReporter("mySpanReporter", testSpanReporter);
final var testSpanReporter = testSpanReporterResource.registerTestSpanReporter(testName.getMethodName());

final var startedSpan = underTest.start();

Expand All @@ -156,8 +156,7 @@ public void startAtReturnsStartedSpanWithExpectedTagsAndStartInstant() throws In
underTest.tags(TagSet.ofTagCollection(
List.of(Tag.of("foo", "bar"), Tag.of("ping", "pong"), Tag.of("marco", "polo"))
));
final var testSpanReporter = TestSpanReporter.newInstance();
Kamon.addReporter("mySpanReporter", testSpanReporter);
final var testSpanReporter = testSpanReporterResource.registerTestSpanReporter(testName.getMethodName());
Thread.sleep(150L);

final var startedSpan = underTest.startAt(startInstant);
Expand All @@ -177,9 +176,9 @@ public void startAtReturnsStartedSpanWithExpectedTagsAndStartInstant() throws In

@Test
public void startByStartedTimerReturnsStartedSpanWithTagsOfTimer() {
final var testSpanReporter = TestSpanReporter.newInstance();
Kamon.addReporter("mySpanReporter", testSpanReporter);
final var startedTimer = Timers.newTimer(testName.getMethodName())
final var testMethodName = testName.getMethodName();
final var testSpanReporter = testSpanReporterResource.registerTestSpanReporter(testMethodName);
final var startedTimer = Timers.newTimer(testMethodName)
.tags(TagSet.ofTagCollection(
List.of(Tag.of("foo", "bar"), Tag.of("ping", "pong"), Tag.of("marco", "polo"))
))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import org.eclipse.ditto.internal.utils.metrics.instruments.tag.KamonTagSetConverter;
import org.eclipse.ditto.internal.utils.metrics.instruments.tag.Tag;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
Expand All @@ -44,25 +46,33 @@
*/
public final class StartedKamonSpanTest {

private static final Identifier TRACE_ID = Kamon.identifierScheme().traceIdFactory().generate();

@Rule
public final KamonTracingInitResource kamonTracingInitResource = KamonTracingInitResource.newInstance(
@ClassRule
public static final KamonTracingInitResource KAMON_TRACING_INIT_RESOURCE = KamonTracingInitResource.newInstance(
KamonTracingInitResource.KamonTracingConfig.defaultValues()
.withSamplerAlways()
.withTickInterval(Duration.ofMillis(100L))
);

private static Identifier traceId;

@Rule
public final TestName testName = new TestName();

@Rule
public final KamonTestSpanReporterResource testSpanReporterResource = KamonTestSpanReporterResource.newInstance();

private StartedSpan underTest;

@BeforeClass
public static void beforeClass() {
traceId = Kamon.identifierScheme().traceIdFactory().generate();
}

@Before
public void setup() {
underTest = StartedKamonSpan.newInstance(
Kamon.spanBuilder(testName.getMethodName())
.asChildOf(Kamon.spanBuilder("/").traceId(TRACE_ID).start())
.asChildOf(Kamon.spanBuilder("/").traceId(traceId).start())
.start(),
KamonHttpContextPropagation.newInstanceForChannelName("default")
);
Expand Down Expand Up @@ -110,9 +120,8 @@ public void markWithNullKeyThrowsNullPointerException() {
@Test
public void markWithKeyAsOnlyArgumentCreatesMarkWithSpecifiedKeyCloseToCurrentInstant() {
final var key = "successful";
final var testSpanReporter = TestSpanReporter.newInstance();
final var testSpanReporter = registerKamonTestSpanReporter();
final var finishedSpanFuture = testSpanReporter.getFinishedSpanForSpanWithId(underTest.getSpanId());
Kamon.addReporter("mySpanReporter", testSpanReporter);
final var nowInstant = Instant.now();

underTest.mark(key);
Expand All @@ -130,6 +139,10 @@ public void markWithKeyAsOnlyArgumentCreatesMarkWithSpecifiedKeyCloseToCurrentIn
}));
}

private TestSpanReporter registerKamonTestSpanReporter() {
return testSpanReporterResource.registerTestSpanReporter(testName.getMethodName());
}

@Test
public void markWithNullKeyButWithInstantThrowsNullPointerException() {
assertThatNullPointerException()
Expand All @@ -149,9 +162,8 @@ public void markWithNullInstantThrowsNullPointerException() {
@Test
public void markWithInstantProducesExpectedMark() {
final var key = "successful";
final var testSpanReporter = TestSpanReporter.newInstance();
final var testSpanReporter = registerKamonTestSpanReporter();
final var finishedSpanFuture = testSpanReporter.getFinishedSpanForSpanWithId(underTest.getSpanId());
Kamon.addReporter("mySpanReporter", testSpanReporter);
final var mark = new Span.Mark(Instant.now(), key);

underTest.mark(mark.key(), mark.instant());
Expand All @@ -177,9 +189,8 @@ public void tagAsFailedWithNullErrorMessageThrowsNullPointerException() {
@Test
public void tagAsFailedCreatesTagWithSpecifiedErrorMessage() {
final var errorMessage = "A foo is not allowed to bar.";
final var testSpanReporter = TestSpanReporter.newInstance();
final var testSpanReporter = registerKamonTestSpanReporter();
final var finishedSpanFuture = testSpanReporter.getFinishedSpanForSpanWithId(underTest.getSpanId());
Kamon.addReporter("mySpanReporter", testSpanReporter);

underTest.tagAsFailed(errorMessage);
underTest.finish();
Expand All @@ -205,9 +216,8 @@ public void tagAsFailedWithNullThrowableThrowsNullPointerException() {
@Test
public void tagAsFailedWithThrowableCreatesExpectedTags() {
final var throwable = new NoSuchElementException("Hypermatter");
final var testSpanReporter = TestSpanReporter.newInstance();
final var testSpanReporter = registerKamonTestSpanReporter();
final var finishedSpanFuture = testSpanReporter.getFinishedSpanForSpanWithId(underTest.getSpanId());
Kamon.addReporter("mySpanReporter", testSpanReporter);

underTest.tagAsFailed(throwable);
underTest.finish();
Expand All @@ -231,9 +241,8 @@ public void tagAsFailedWithThrowableCreatesExpectedTags() {
public void tagAsFailedWithErrorMessageAndThrowableCreatesExpectedTags() {
final var errorMessage = "Failed to fire tachyon pulses.";
final var throwable = new NoSuchElementException("Tachyons");
final var testSpanReporter = TestSpanReporter.newInstance();
final var testSpanReporter = registerKamonTestSpanReporter();
final var finishedSpanFuture = testSpanReporter.getFinishedSpanForSpanWithId(underTest.getSpanId());
Kamon.addReporter("mySpanReporter", testSpanReporter);

underTest.tagAsFailed(errorMessage, throwable);
underTest.finish();
Expand Down Expand Up @@ -276,7 +285,7 @@ public void propagateContextToDittoHeadersPutsW3cTraceparent() {
}

private String getExpectedTraceparentValue() {
return MessageFormat.format("00-0000000000000000{0}-{1}-01", TRACE_ID.string(), underTest.getSpanId());
return MessageFormat.format("00-{0}-{1}-01", traceId.string(), underTest.getSpanId());
}

@Test
Expand Down

0 comments on commit 7f3817c

Please sign in to comment.