package brave.internal.recorder;
import brave.Span;
import brave.Tracing;
import brave.internal.Platform;
import brave.propagation.TraceContext;
import org.junit.Test;
import zipkin.BinaryAnnotation;
import zipkin.Constants;
import zipkin.Endpoint;
import static brave.Span.Kind.CLIENT;
import static brave.Span.Kind.SERVER;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static zipkin.Constants.CLIENT_ADDR;
import static zipkin.Constants.CLIENT_RECV;
import static zipkin.Constants.CLIENT_SEND;
import static zipkin.Constants.LOCAL_COMPONENT;
import static zipkin.Constants.SERVER_ADDR;
import static zipkin.Constants.SERVER_RECV;
import static zipkin.Constants.SERVER_SEND;
public class MutableSpanTest {
Endpoint localEndpoint = Platform.get().localEndpoint();
TraceContext context = Tracing.newBuilder().build().tracer().newTrace().context();
// zipkin needs one annotation or binary annotation so that the local endpoint can be read
@Test public void addsDefaultBinaryAnnotation() {
MutableSpan span = newSpan();
span.start(1L);
span.finish(2L);
assertThat(span.toSpan().binaryAnnotations.get(0)).isEqualTo(
BinaryAnnotation.create(LOCAL_COMPONENT, "", localEndpoint)
);
}
@Test public void minimumDurationIsOne() {
MutableSpan span = newSpan();
span.start(1L).finish(1L);
assertThat(span.toSpan().duration).isEqualTo(1L);
}
@Test public void doesntAddDefaultBinaryAnnotation() {
MutableSpan span = newSpan();
span.start(1L);
span.annotate(2L, "foo"); // this has an endpoint, so don't add a default binary annotation
span.finish(2L);
assertThat(span.toSpan().binaryAnnotations).isEmpty();
}
@Test public void clientAnnotationsImplicitlySetKind() {
for (String annotation : asList(Constants.CLIENT_SEND, Constants.CLIENT_RECV)) {
assertThat(newSpan().annotate(1L, annotation).kind)
.isEqualTo(Span.Kind.CLIENT);
}
}
@Test public void serverAnnotationsImplicitlySetKind() {
for (String annotation : asList(Constants.SERVER_RECV, Constants.SERVER_SEND)) {
assertThat(newSpan().annotate(1L, annotation).kind)
.isEqualTo(Span.Kind.SERVER);
}
}
@Test public void whenKindIsClient_addsCsCr() {
MutableSpan span = newSpan();
span.kind(CLIENT);
span.start(1L);
span.finish(2L);
assertThat(span.toSpan().annotations).extracting(a -> a.value)
.containsExactly("cs", "cr");
}
@Test public void whenKindIsClient_addsCr() {
MutableSpan span = newSpan();
span.annotate(1L, CLIENT_SEND);
span.finish(2L);
assertThat(span.toSpan().annotations).extracting(a -> a.value)
.containsExactly("cs", "cr");
}
@Test public void whenKindIsClient_addsCs() {
MutableSpan span = newSpan();
span.start(1L);
span.annotate(2L, CLIENT_RECV);
span.finish(null);
assertThat(span.toSpan().annotations).extracting(a -> a.value)
.containsExactly("cs", "cr");
}
@Test public void whenKindIsClient_addsSa() {
MutableSpan span = newSpan();
Endpoint endpoint = Endpoint.create("server", 127 | 1);
span.kind(CLIENT);
span.remoteEndpoint(endpoint);
span.start(1L);
span.finish(2L);
assertThat(span.toSpan().binaryAnnotations.get(0)).isEqualTo(
BinaryAnnotation.address(SERVER_ADDR, endpoint)
);
}
// This prevents the server timestamp from overwriting the client one
@Test public void doesntReportServerTimestampOnSharedSpans() {
MutableSpan span = new MutableSpan(context.toBuilder().shared(true).build(), localEndpoint);
span.start(1L);
span.kind(SERVER);
span.finish(2L);
assertThat(span.toSpan()).extracting(s -> s.timestamp, s -> s.duration)
.allSatisfy(u -> assertThat(u).isNull());
}
@Test public void whenKindIsServer_addsSrSs() {
MutableSpan span = newSpan();
span.kind(SERVER);
span.start(1L);
span.finish(1L);
assertThat(span.toSpan().annotations).extracting(a -> a.value)
.containsExactly("sr", "ss");
}
@Test public void whenKindIsServer_addsSs() {
MutableSpan span = newSpan();
span.annotate(1L, SERVER_RECV);
span.finish(2L);
assertThat(span.toSpan().annotations).extracting(a -> a.value)
.containsExactly("sr", "ss");
}
@Test public void whenKindIsServer_addsSr() {
MutableSpan span = newSpan();
span.start(1L);
span.annotate(2L, SERVER_SEND);
span.finish(null);
assertThat(span.toSpan().annotations).extracting(a -> a.value)
.containsExactly("sr", "ss");
}
@Test public void whenKindIsServer_addsCa() {
MutableSpan span = newSpan();
Endpoint endpoint = Endpoint.create("caller", 127 | 1);
span.kind(SERVER);
span.remoteEndpoint(endpoint);
span.start(1L);
span.finish(2L);
assertThat(span.toSpan().binaryAnnotations.get(0)).isEqualTo(
BinaryAnnotation.address(CLIENT_ADDR, endpoint)
);
}
@Test public void flushUnstartedNeitherSetsTimestampNorDuration() {
zipkin.Span flushed = newSpan().finish(null).toSpan();
assertThat(flushed).extracting(s -> s.timestamp, s -> s.duration)
.allSatisfy(u -> assertThat(u).isNull());
}
/** We can't compute duration unless we started the span in the same tracer. */
@Test public void finishUnstartedIsSameAsFlush() {
assertThat(newSpan().finish(2L).toSpan())
.isEqualTo(newSpan().finish(null).toSpan());
}
@Test public void oneWaySpan() {
MutableSpan client = newSpan().kind(Span.Kind.CLIENT).start(1L).finish(null);
assertThat(client.toSpan()).satisfies(s -> {
assertThat(s.timestamp).isNull();
assertThat(s.annotations).extracting(a -> a.value)
.containsExactly("cs");
});
MutableSpan server = newSpan().kind(Span.Kind.SERVER).start(1L).finish(null);
assertThat(server.toSpan()).satisfies(s -> {
assertThat(s.timestamp).isNull();
assertThat(s.annotations).extracting(a -> a.value)
.containsExactly("sr");
});
}
MutableSpan newSpan() {
return new MutableSpan(context, localEndpoint);
}
}