diff options
| -rw-r--r-- | BUILD | 1 | ||||
| -rw-r--r-- | core/src/test/java/com/google/instrumentation/stats/StatsContextFactoryTest.java | 85 | ||||
| -rw-r--r-- | core_impl/src/main/java/com/google/instrumentation/stats/StatsSerializer.java | 46 |
3 files changed, 120 insertions, 12 deletions
@@ -257,6 +257,7 @@ java_test( name = "StatsContextFactoryTest", srcs = ["core/src/test/java/com/google/instrumentation/stats/StatsContextFactoryTest.java"], deps = [ + ":shared", ":stats-core", ":stats-core_impl", "@guava//jar", diff --git a/core/src/test/java/com/google/instrumentation/stats/StatsContextFactoryTest.java b/core/src/test/java/com/google/instrumentation/stats/StatsContextFactoryTest.java index 12efa98f..004d8030 100644 --- a/core/src/test/java/com/google/instrumentation/stats/StatsContextFactoryTest.java +++ b/core/src/test/java/com/google/instrumentation/stats/StatsContextFactoryTest.java @@ -13,8 +13,13 @@ package com.google.instrumentation.stats; +import static com.google.common.truth.Truth.assertThat; + +import com.google.io.base.VarInt; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -24,8 +29,84 @@ import org.junit.runners.JUnit4; */ @RunWith(JUnit4.class) public class StatsContextFactoryTest { + private static final int VALUE_TYPE_STRING = 0; + private static final int VALUE_TYPE_INTEGER = 1; + private static final int VALUE_TYPE_BOOLEAN = 2; + + private static final String KEY1 = "Key"; + private static final String VALUE_STRING = "String"; + private static final int VALUE_INT = 10; + private static final boolean VALUE_BOOL = true; + + @Test + public void testDeserializeEmptyReturnDefaultStatsContext() throws Exception { + StatsContext expected = Stats.getStatsContextFactory().getDefault(); + StatsContext actual = testDeserialize(new ByteArrayInputStream(new byte[0])); + assertThat(actual).isEqualTo(expected); + } + + @Test(expected = IOException.class) + public void testDeserializeValueTypeInteger() throws Exception { + // TODO(songya): test should pass after we add support for type integer + testDeserialize(constructInputStream(VALUE_TYPE_INTEGER)); + } + + @Test(expected = IOException.class) + public void testDeserializeValueTypeBoolean() throws Exception { + // TODO(songya): test should pass after we add support for type boolean + testDeserialize(constructInputStream(VALUE_TYPE_BOOLEAN)); + } + @Test(expected = IOException.class) - public void testDeserializeEmpty() throws Exception { - Stats.getStatsContextFactory().deserialize(new ByteArrayInputStream(new byte[0])); + public void testDeserializeWrongFormat() throws Exception { + // encoded tags should follow the format [tag_type key_len key_bytes value_len value_bytes]* + testDeserialize(new ByteArrayInputStream(new byte[1])); + } + + private StatsContext testDeserialize(InputStream inputStream) throws IOException { + return Stats.getStatsContextFactory().deserialize(inputStream); + } + + /* + * TODO(songya): after supporting serialize integer and boolean, + * remove this method and use StatsContext.serialize() instead. + * Currently StatsContext.serialize() can only serialize strings. + */ + private InputStream constructInputStream(int valueType) { + // TODO(songya): please note that ByteBuffer will overflow if > 1024 bytes were appended. + ByteBuffer buffer = ByteBuffer.allocate(1024); + VarInt.putVarInt(valueType, buffer); + encodeString(KEY1, buffer); + switch (valueType) { + case VALUE_TYPE_STRING: + encodeString(VALUE_STRING, buffer); + break; + case VALUE_TYPE_INTEGER: + encodeInteger(VALUE_INT, buffer); + break; + case VALUE_TYPE_BOOLEAN: + encodeBoolean(VALUE_BOOL, buffer); + break; + default: + return null; + } + return new ByteArrayInputStream(buffer.array()); + } + + private void encodeString(String input, ByteBuffer buffer) { + VarInt.putVarInt(input.length(), buffer); + for (int i = 0; i < input.length(); i++) { + buffer.put((byte) input.charAt(i)); + } + } + + private void encodeInteger(int input, ByteBuffer buffer) { + VarInt.putVarInt(Integer.valueOf(input).toString().length(), buffer); + buffer.put((byte) input); + } + + private void encodeBoolean(boolean input, ByteBuffer buffer) { + VarInt.putVarInt(Boolean.valueOf(input).toString().length(), buffer); + buffer.put((byte) (input? 1 : 0)); } } diff --git a/core_impl/src/main/java/com/google/instrumentation/stats/StatsSerializer.java b/core_impl/src/main/java/com/google/instrumentation/stats/StatsSerializer.java index 62c4ca66..4a1e08be 100644 --- a/core_impl/src/main/java/com/google/instrumentation/stats/StatsSerializer.java +++ b/core_impl/src/main/java/com/google/instrumentation/stats/StatsSerializer.java @@ -30,14 +30,30 @@ import java.util.Set; * Native implementation {@link StatsContext} serialization. */ final class StatsSerializer { + // * tag_metadata is one byte and is for encoding metadata about the tag. The + // low 2 bits of this byte are used to encode the type of the value bytes. + // The high 6 bits are reserved for future use. The value_bytes type is + // encoded as: + // 00 (value 0): string (UTF-8) encoding + // 01 (value 1): integer (varint int64 encoding). + // 10 (value 2): boolean format. + // 11 (value 3): reserved for future use. + // TODO(songya): Currently we only support encoding on string type. + private static final int VALUE_TYPE_STRING = 0; + private static final int VALUE_TYPE_INTEGER = 1; + private static final int VALUE_TYPE_BOOLEAN = 2; + // Serializes a StatsContext by transforming it into a StatsContextProto. The // encoded tags are of the form: - // num_tags [key_len key_bytes value_len value_bytes]* + // [tag_metadata key_len key_bytes value_len value_bytes]* static void serialize(StatsContextImpl context, OutputStream output) throws IOException { + // TODO(songya): please note that ByteBuffer will overflow if > 1024 bytes were appended ByteBuffer buffer = ByteBuffer.allocate(1024); + + // TODO(songya): add support for value types integer and boolean Set<Entry<String, String>> tags = context.tags.entrySet(); - VarInt.putVarInt(tags.size(), buffer); for (Entry<String, String> tag : tags) { + VarInt.putVarInt(VALUE_TYPE_STRING, buffer); encode(tag.getKey(), buffer); encode(tag.getValue(), buffer); } @@ -48,17 +64,27 @@ final class StatsSerializer { } // Deserializes based on an serialized StatsContextProto. The encoded tags are of the form: - // num_tags [key_len key_bytes value_len value_bytes]* + // [tag_metadata key_len key_bytes value_len value_bytes]* static StatsContextImpl deserialize(InputStream input) throws IOException { try { StatsContextProto.StatsContext context = StatsContextProto.StatsContext.parseFrom(input); ByteBuffer buffer = context.getTags().asReadOnlyByteBuffer(); - int numTags = VarInt.getVarInt(buffer); - HashMap<String, String> tags = new HashMap<String, String>(numTags); - for (int i = 0; i < numTags; i++) { - String key = decode(buffer); - String val = decode(buffer); - tags.put(key, val); + HashMap<String, String> tags = new HashMap<String, String>(); + int limit = buffer.limit(); + while (buffer.position() < limit) { + int type = VarInt.getVarInt(buffer); + switch (type) { + case VALUE_TYPE_STRING: + String key = decode(buffer); + String val = decode(buffer); + tags.put(key, val); + break; + case VALUE_TYPE_INTEGER: + case VALUE_TYPE_BOOLEAN: + default: + // TODO(songya): add support for value types integer and boolean + throw new IOException("Unsupported tag value type."); + } } return new StatsContextImpl(tags); } catch (BufferUnderflowException exn) { @@ -81,4 +107,4 @@ final class StatsSerializer { } return builder.toString(); } -} +}
\ No newline at end of file |
