/**
* Copyright 2015-2016 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package zipkin;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
public abstract class CodecTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
protected abstract Codec codec();
@Test
public void spanRoundTrip() throws IOException {
for (Span span : TestObjects.TRACE) {
byte[] bytes = codec().writeSpan(span);
assertThat(codec().readSpan(bytes))
.isEqualTo(span);
}
}
@Test
public void sizeInBytes() throws IOException {
for (Span span : TestObjects.TRACE) {
assertThat(codec().sizeInBytes(span))
.isEqualTo(codec().writeSpan(span).length);
}
}
@Test
public void spanRoundTrip_128bitTraceId() throws IOException {
for (Span span : TestObjects.TRACE) {
span = span.toBuilder().traceIdHigh(12345L).build();
byte[] bytes = codec().writeSpan(span);
assertThat(codec().readSpan(bytes))
.isEqualTo(span);
}
}
@Test
public void sizeInBytes_128bitTraceId() throws IOException {
for (Span span : TestObjects.TRACE) {
span = span.toBuilder().traceIdHigh(12345L).build();
assertThat(codec().sizeInBytes(span))
.isEqualTo(codec().writeSpan(span).length);
}
}
@Test
public void binaryAnnotation_long() throws IOException {
Span span = TestObjects.LOTS_OF_SPANS[0].toBuilder().binaryAnnotations(asList(
BinaryAnnotation.builder()
.key("Long.zero")
.type(BinaryAnnotation.Type.I64)
.value(ByteBuffer.allocate(8).putLong(0, 0L).array())
.build(),
BinaryAnnotation.builder()
.key("Long.negative")
.type(BinaryAnnotation.Type.I64)
.value(ByteBuffer.allocate(8).putLong(0, -1005656679588439279L).array())
.build(),
BinaryAnnotation.builder()
.key("Long.MIN_VALUE")
.type(BinaryAnnotation.Type.I64)
.value(ByteBuffer.allocate(8).putLong(0, Long.MIN_VALUE).array())
.build(),
BinaryAnnotation.builder()
.key("Long.MAX_VALUE")
.type(BinaryAnnotation.Type.I64)
.value(ByteBuffer.allocate(8).putLong(0, Long.MAX_VALUE).array())
.build()
)).build();
byte[] bytes = codec().writeSpan(span);
assertThat(codec().readSpan(bytes))
.isEqualTo(span);
}
/**
* This isn't a test of what we "should" accept as a span, rather that characters that
* trip-up json don't fail in codec.
*/
@Test
public void specialCharsInJson() throws IOException {
// service name is surrounded by control characters
Endpoint e = Endpoint.create(new String(new char[] {0, 'a', 1}), 0);
Span worstSpanInTheWorld = Span.builder().traceId(1L).id(1L)
// name is terrible
.name(new String(new char[] {'"', '\\', '\t', '\b', '\n', '\r', '\f'}))
// annotation value includes some json newline characters
.addAnnotation(Annotation.create(1L, "\u2028 and \u2029", e))
// binary annotation key includes a quote and value newlines
.addBinaryAnnotation(BinaryAnnotation.create("\"foo",
"Database error: ORA-00942:\u2028 and \u2029 table or view does not exist\n", e))
.build();
byte[] bytes = codec().writeSpan(worstSpanInTheWorld);
assertThat(codec().readSpan(bytes))
.isEqualTo(worstSpanInTheWorld);
}
@Test
public void binaryAnnotation_double() throws IOException {
Span span = TestObjects.LOTS_OF_SPANS[0].toBuilder().binaryAnnotations(asList(
BinaryAnnotation.builder()
.key("Double.zero")
.type(BinaryAnnotation.Type.DOUBLE)
.value(ByteBuffer.allocate(8).putDouble(0, 0.0).array())
.build(),
BinaryAnnotation.builder()
.key("Double.negative")
.type(BinaryAnnotation.Type.DOUBLE)
.value(ByteBuffer.allocate(8).putDouble(0, -1.005656679588439279).array())
.build(),
BinaryAnnotation.builder()
.key("Double.MIN_VALUE")
.type(BinaryAnnotation.Type.DOUBLE)
.value(ByteBuffer.allocate(8).putDouble(0, Double.MIN_VALUE).array())
.build(),
BinaryAnnotation.builder()
.key("Double.MAX_VALUE")
.type(BinaryAnnotation.Type.I64)
.value(ByteBuffer.allocate(8).putDouble(0, Double.MAX_VALUE).array())
.build()
)).build();
byte[] bytes = codec().writeSpan(span);
assertThat(codec().readSpan(bytes))
.isEqualTo(span);
}
@Test
public void spansRoundTrip() throws IOException {
byte[] bytes = codec().writeSpans(TestObjects.TRACE);
assertThat(codec().readSpans(bytes))
.isEqualTo(TestObjects.TRACE);
}
@Test
public void writeTraces() throws IOException {
byte[] bytes = codec().writeTraces(asList(TestObjects.TRACE, TestObjects.TRACE));
assertThat(codec().writeSpans(TestObjects.TRACE).length * 2)
.isLessThan(bytes.length);
}
@Test
public void dependencyLinkRoundTrip() throws IOException {
DependencyLink link = DependencyLink.create("foo", "bar", 2);
byte[] bytes = codec().writeDependencyLink(link);
assertThat(codec().readDependencyLink(bytes))
.isEqualTo(link);
}
@Test
public void dependencyLinksRoundTrip() throws IOException {
List<DependencyLink> links = asList(
DependencyLink.create("foo", "bar", 2),
DependencyLink.create("bar", "baz", 3)
);
byte[] bytes = codec().writeDependencyLinks(links);
assertThat(codec().readDependencyLinks(bytes))
.isEqualTo(links);
}
@Test
public void decentErrorMessageOnEmptyInput_span() throws IOException {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Empty input reading Span");
codec().readSpan(new byte[0]);
}
@Test
public void decentErrorMessageOnEmptyInput_spans() throws IOException {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Empty input reading List<Span>");
codec().readSpans(new byte[0]);
}
@Test
public void decentErrorMessageOnEmptyInput_dependencyLinks() throws IOException {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Empty input reading List<DependencyLink>");
codec().readDependencyLinks(new byte[0]);
}
@Test
public void decentErrorMessageOnMalformedInput_span() throws IOException {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Malformed reading Span from ");
codec().readSpan(new byte[] {'h', 'e', 'l', 'l', 'o'});
}
/**
* Particulary, thrift can mistake malformed content as a huge list. Let's not blow up.
*/
@Test
public void decentErrorMessageOnMalformedInput_spans() throws IOException {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Malformed reading List<Span> from ");
codec().readSpans(new byte[] {'h', 'e', 'l', 'l', 'o'});
}
@Test
public void decentErrorMessageOnMalformedInput_dependencyLinks() throws IOException {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Malformed reading List<DependencyLink> from ");
codec().readDependencyLinks(new byte[] {'h', 'e', 'l', 'l', 'o'});
}
}