package com.yammer.telemetry.tracing;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import java.math.BigInteger;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.*;
public class SpanUsageTest {
@Rule
public SpanContextRule spanContextRule = new SpanContextRule();
private InMemorySpanSinkSource sink;
private Sampling defaultSampler;
@Before
public void setUp() throws Exception {
sink = new InMemorySpanSinkSource();
SpanSinkRegistry.register(sink);
defaultSampler = SpanHelper.getSampler();
}
@After
public void tearDown() {
SpanSinkRegistry.clear();
SpanHelper.setSampler(defaultSampler);
}
@Test
public void testAttachSpan() {
Trace trace = testSpan(new Strategy() {
@Override
public Span createSpan() {
return SpanHelper.attachSpan(BigInteger.ONE, BigInteger.TEN, "wubba");
}
});
SpanData rootSpan = trace.getRoot();
assertEquals("wubba", rootSpan.getName());
assertNotNull(rootSpan.getHost());
List<AnnotationData> annotations = trace.getAnnotations(BigInteger.TEN);
assertEquals(3, annotations.size());
assertEquals(ImmutableList.of(AnnotationNames.SERVER_RECEIVED, AnnotationNames.SERVICE_NAME + "_testing", AnnotationNames.SERVER_SENT), ImmutableList.copyOf(Iterables.transform(annotations, new Function<AnnotationData, String>() {
@Override
public String apply(AnnotationData input) {
String message = input.getMessage() == null ? "" : "_" + input.getMessage();
return input.getName() + message;
}
})));
}
@Test
public void testStartSpan() {
Trace trace = testSpan(new Strategy() {
@Override
public Span createSpan() {
return SpanHelper.startSpan("Tracing");
}
});
assertNotNull(trace.getRoot());
List<AnnotationData> annotations = trace.getAnnotations(trace.getRoot().getSpanId());
assertEquals(3, annotations.size());
assertEquals(ImmutableList.of(AnnotationNames.SERVER_RECEIVED, AnnotationNames.SERVICE_NAME + "_testing", AnnotationNames.SERVER_SENT), ImmutableList.copyOf(Iterables.transform(annotations, new Function<AnnotationData, String>() {
@Override
public String apply(AnnotationData input) {
String message = input.getMessage() == null ? "" : "_" + input.getMessage();
return input.getName() + message;
}
})));
}
@Test(expected = NullPointerException.class)
public void testCannotAttachWithoutTraceId() {
SpanHelper.attachSpan(null, BigInteger.ONE, "name");
}
@Test(expected = NullPointerException.class)
public void testCannotAttachWithoutSpanId() {
SpanHelper.attachSpan(BigInteger.ONE, null, "name");
}
@Test(expected = NullPointerException.class)
public void testCannotAttachWithoutTraceAndSpanIds() {
SpanHelper.attachSpan(null, null, "name");
}
@Test
public void testDefaultSamplerIsOn() {
assertEquals(Sampling.ON, defaultSampler);
}
@Test
public void testStartTraceRecordsWhenSamplingIsOn() {
SpanHelper.setSampler(Sampling.ON);
Span trace = SpanHelper.startTrace("Foof");
trace.end();
assertFalse(sink.getTraces().isEmpty());
}
@Test
public void testStartTraceRecordsNothingWhenSamplingIsOff() {
SpanHelper.setSampler(Sampling.OFF);
Span trace = SpanHelper.startTrace("Foof");
trace.end();
assertTrue(sink.getTraces().isEmpty());
}
@Test
public void testStartSpanRecordsWhenSamplingIsOn() {
SpanHelper.setSampler(Sampling.ON);
Span trace = SpanHelper.startSpan("Foof");
trace.end();
assertFalse(sink.getTraces().isEmpty());
}
@Test
public void testStartSpanRecordsNothingWhenSamplingIsOff() {
SpanHelper.setSampler(Sampling.OFF);
Span trace = SpanHelper.startSpan("Foof");
trace.end();
assertTrue(sink.getTraces().isEmpty());
}
@Test
public void testStartSpanWithTraceAndSpanIdRecordsWhenSamplingIsOn() {
SpanHelper.setSampler(Sampling.ON);
Span trace = SpanHelper.startSpan(BigInteger.ONE, BigInteger.TEN, "Foof");
trace.end();
assertFalse(sink.getTraces().isEmpty());
Trace recordedTrace = sink.getTraces().iterator().next();
assertEquals(BigInteger.ONE, recordedTrace.getTraceId());
assertEquals("Foof", recordedTrace.getChildren(BigInteger.TEN).get(0).getName());
}
@Test
public void testStartSpanWithTraceAndSpanIdRecordsEvenWhenSamplingIsOff() {
SpanHelper.setSampler(Sampling.OFF);
Span trace = SpanHelper.startSpan(BigInteger.ONE, BigInteger.TEN, "Foof");
trace.end();
assertFalse(sink.getTraces().isEmpty());
Trace recordedTrace = sink.getTraces().iterator().next();
assertEquals(BigInteger.ONE, recordedTrace.getTraceId());
assertEquals("Foof", recordedTrace.getChildren(BigInteger.TEN).get(0).getName());
}
@Test
public void testSpanRecordsAfterAttachSpanIfSamplingIsOn() {
SpanHelper.setSampler(Sampling.ON);
Span foof = SpanHelper.attachSpan(BigInteger.ONE, BigInteger.TEN, "Foof");
Span subFoof = SpanHelper.startSpan("SubFoof");
subFoof.addAnnotation("A");
subFoof.end();
foof.end();
assertEquals(1, sink.recordedTraceCount());
Trace trace = sink.getTrace(BigInteger.ONE);
SpanData rootSpan = trace.getRoot();
assertNotNull(rootSpan);
assertEquals(BigInteger.ONE, rootSpan.getTraceId());
assertEquals(BigInteger.TEN, rootSpan.getSpanId());
assertEquals("Foof", rootSpan.getName());
assertNotNull(rootSpan.getHost());
assertEquals(ImmutableList.<SpanData>of(subFoof), trace.getChildren(foof.getSpanId()));
}
@Test
public void testSpanRecordsAfterAttachSpanEvenIfSamplingIsOff() {
SpanHelper.setSampler(Sampling.OFF);
Span foof = SpanHelper.attachSpan(BigInteger.ONE, BigInteger.TEN, "Foof");
Span subFoof = SpanHelper.startSpan("SubFoof");
subFoof.addAnnotation("A");
subFoof.end();
foof.end();
assertEquals(1, sink.recordedTraceCount());
Trace trace = sink.getTrace(BigInteger.ONE);
SpanData rootSpan = trace.getRoot();
assertEquals("Foof", rootSpan.getName());
assertNotNull(rootSpan.getHost());
assertEquals(ImmutableList.<SpanData>of(subFoof), trace.getChildren(foof.getSpanId()));
}
@Test
public void testSpanRecordsAfterStartTraceIfSamplingIsOn() {
SpanHelper.setSampler(Sampling.ON);
Span foof = SpanHelper.startTrace("Foof");
Span subFoof = SpanHelper.startSpan("SubFoof");
subFoof.addAnnotation("A");
subFoof.end();
foof.end();
assertEquals(1, sink.recordedTraceCount());
Trace trace = sink.getTrace(foof.getTraceId());
assertEquals(foof, trace.getRoot());
assertEquals(ImmutableList.<SpanData>of(subFoof), trace.getChildren(foof.getSpanId()));
}
@Test
public void testSpanDoesNotRecordAfterStartTraceIfSamplingIsOff() {
SpanHelper.setSampler(Sampling.OFF);
Span foof = SpanHelper.startTrace("Foof");
Span subFoof = SpanHelper.startSpan("SubFoof");
subFoof.addAnnotation("A");
subFoof.end();
foof.end();
assertEquals(0, sink.recordedTraceCount());
Trace trace = sink.getTrace(foof.getTraceId());
assertNull(trace);
}
@Test
public void testAnnotationRecordedAfterSpanEnded() {
SpanHelper.setSampler(Sampling.ON);
Span trace = SpanHelper.startTrace("trace");
trace.addAnnotation("During");
trace.end();
trace.addAnnotation("After");
assertEquals(1, sink.recordedTraceCount());
assertNotNull(sink.getTrace(trace.getTraceId()));
}
private Trace testSpan(Strategy strategy) {
final AtomicReference<BigInteger> traceId = new AtomicReference<>();
final AtomicReference<BigInteger> spanId = new AtomicReference<>();
try (Span span = strategy.createSpan()) {
traceId.set(span.getTraceId());
spanId.set(span.getSpanId());
span.addAnnotation(AnnotationNames.SERVER_RECEIVED);
span.addAnnotation(AnnotationNames.SERVICE_NAME, "testing");
SpanHelper.startSpan("here").end();
SpanHelper.startSpan("there").end();
span.addAnnotation(AnnotationNames.SERVER_SENT);
} catch (Exception e) {
e.printStackTrace();
}
assertEquals(1, sink.recordedTraceCount());
assertEquals(traceId.get(), sink.getTraces().iterator().next().getTraceId());
Trace trace = sink.getTrace(traceId.get());
List<SpanData> children = trace.getChildren(spanId.get());
assertEquals(2, children.size());
assertEquals(ImmutableList.of("here", "there"), ImmutableList.copyOf(Iterables.transform(children, new Function<SpanData, String>() {
@Override
public String apply(SpanData input) {
return input.getName();
}
})));
assertEquals(3, trace.getAnnotations(spanId.get()).size());
return trace;
}
private static interface Strategy {
Span createSpan();
}
}