public override IEnumerator <Tag> GetEnumerator() { return(new List <Tag>() { Tag.Create(TagKey.Create("key"), TagValue.Create("value")) }.GetEnumerator()); }
public void TestSerializeTooLargeTagContext() { var builder = tagger.EmptyBuilder; for (var i = 0; i < SerializationUtils.TagContextSerializedSizeLimit / 8 - 1; i++) { // Each tag will be with format {key : "0123", value : "0123"}, so the length of it is 8. String str; if (i < 10) { str = "000" + i; } else if (i < 100) { str = "00" + i; } else if (i < 1000) { str = "0" + i; } else { str = i.ToString(); } builder.Put(TagKey.Create(str), TagValue.Create(str)); } // The last tag will be of size 9, so the total size of the TagContext (8193) will be one byte // more than limit. builder.Put(TagKey.Create("last"), TagValue.Create("last1")); var tagContext = builder.Build(); Assert.Throws <TagContextSerializationException>(() => serializer.ToByteArray(tagContext)); }
public void TestRoundtrip_TagContextWithMaximumSize() { ITagContextBuilder builder = tagger.EmptyBuilder; for (int i = 0; i < SerializationUtils.TagContextSerializedSizeLimit / 8; i++) { // Each tag will be with format {key : "0123", value : "0123"}, so the length of it is 8. // Add 1024 tags, the total size should just be 8192. String str; if (i < 10) { str = "000" + i; } else if (i < 100) { str = "00" + i; } else if (i < 1000) { str = "0" + i; } else { str = "" + i; } builder.Put(TagKey.Create(str), TagValue.Create(str)); } TestRoundtripSerialization(builder.Build()); }
public void TestRecordWithTagsThatDoNotMatchViewData() { viewManager.RegisterView( CreateCumulativeView(VIEW_NAME, MEASURE_DOUBLE, DISTRIBUTION, new List <ITagKey>() { KEY })); statsRecorder .NewMeasureMap() .Put(MEASURE_DOUBLE, 10.0) .Record(tagger.EmptyBuilder.Put(TagKey.Create("wrong key"), VALUE).Build()); statsRecorder .NewMeasureMap() .Put(MEASURE_DOUBLE, 50.0) .Record(tagger.EmptyBuilder.Put(TagKey.Create("another wrong key"), VALUE).Build()); IViewData viewData = viewManager.GetView(VIEW_NAME); var tv = TagValues.Create(new List <ITagValue>() { MutableViewData.UnknownTagValue }); StatsTestUtil.AssertAggregationMapEquals( viewData.AggregationMap, new Dictionary <TagValues, IAggregationData>() { // Won't Record the unregistered tag key, for missing registered keys will use default // tag value : "unknown/not set". { tv, // Should Record stats with default tag value: "KEY" : "unknown/not set". StatsTestUtil.CreateAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 10.0, 50.0) }, }, EPSILON); }
public void GetMetricName_ReturnsExpected() { var opts = new CloudFoundryForwarderOptions(); var stats = new OpenCensusStats(); var ep = new SpringBootMetricWriter(opts, stats); IList <ITagKey> keys = new List <ITagKey>() { TagKey.Create("status"), TagKey.Create("exception"), TagKey.Create("method"), TagKey.Create("uri") }; IList <ITagValue> values = new List <ITagValue>() { TagValue.Create("200"), TagValue.Create("None"), TagValue.Create("GET"), TagValue.Create("/foo/bar") }; var tagDict = ep.GetTagKeysAndValues(keys, values); Assert.Equal("http.server.requests.mean.GET.200.foo.bar", ep.GetMetricName("http.server.requests", "mean", tagDict)); Assert.Equal("http.server.requests.mean", ep.GetMetricName("http.server.requests", "mean", new Dictionary <string, string>())); keys = new List <ITagKey>() { TagKey.Create("foo"), TagKey.Create("bar") }; values = new List <ITagValue>() { TagValue.Create("foo"), TagValue.Create("bar") }; tagDict = ep.GetTagKeysAndValues(keys, values); Assert.Equal("http.server.requests.bar.foo", ep.GetMetricName("http.server.requests", null, tagDict)); }
// TODO(sebright): Consider exposing a TagKey name validation method to avoid needing to catch an // IllegalArgumentException here. private static ITagKey CreateTagKey(String name) { try { return(TagKey.Create(name)); } catch (Exception e) { throw new TagContextDeserializationException("Invalid tag key: " + name, e); } }
public void CreateMetrics_CountAgg_ReturnsExpected() { var opts = new CloudFoundryForwarderOptions(); var stats = new OpenCensusStats(); var tagsComponent = new TagsComponent(); var tagger = tagsComponent.Tagger; var ep = new MicrometerMetricWriter(opts, stats); IMeasureDouble testMeasure = MeasureDouble.Create("test.total", "test", MeasureUnit.Bytes); SetupTestView(stats, Count.Create(), testMeasure, "test.test1"); ITagContext context1 = tagger .EmptyBuilder .Put(TagKey.Create("a"), TagValue.Create("v1")) .Put(TagKey.Create("b"), TagValue.Create("v1")) .Put(TagKey.Create("c"), TagValue.Create("v1")) .Build(); long allKeyssum = 0; for (int i = 0; i < 10; i++) { allKeyssum = allKeyssum + i; stats.StatsRecorder.NewMeasureMap().Put(testMeasure, i).Record(context1); } var viewData = stats.ViewManager.GetView(ViewName.Create("test.test1")); Assert.NotNull(viewData); var aggMap = viewData.AggregationMap; Assert.Single(aggMap); var tagValues = aggMap.Keys.Single(); var data = aggMap.Values.Single(); Assert.NotNull(tagValues); Assert.NotNull(data); var result = ep.CreateMetrics(viewData, data, tagValues, 1L); Assert.NotNull(result); Assert.Single(result); var metric = result[0]; Assert.Equal("test.test1", metric.Name); Assert.Equal(1L, metric.Timestamp); Assert.Equal("gauge", metric.Type); Assert.Equal("bytes", metric.Unit); Assert.Equal(10, metric.Value); var tags = metric.Tags; Assert.Equal("count", tags["statistic"]); Assert.Equal("v1", tags["a"]); Assert.Equal("v1", tags["b"]); Assert.Equal("v1", tags["c"]); }
public void GetTagKeysAndValues_ReturnsExpected() { var opts = new CloudFoundryForwarderOptions(); var stats = new OpenCensusStats(); var ep = new SpringBootMetricWriter(opts, stats); IList <ITagKey> keys = new List <ITagKey>() { TagKey.Create("status"), TagKey.Create("exception"), TagKey.Create("method"), TagKey.Create("uri") }; IList <ITagValue> values = new List <ITagValue>() { TagValue.Create("v1"), TagValue.Create("v2"), TagValue.Create("v3"), TagValue.Create("v4") }; var result = ep.GetTagKeysAndValues(keys, values); Assert.Equal("v1", result["status"]); Assert.Equal("v2", result["exception"]); Assert.Equal("v3", result["method"]); Assert.Equal("v4", result["uri"]); // Verify sorted var sortedKeys = result.Keys.ToList(); Assert.Equal("exception", sortedKeys[0]); Assert.Equal("method", sortedKeys[1]); Assert.Equal("status", sortedKeys[2]); Assert.Equal("uri", sortedKeys[3]); values = new List <ITagValue>() { TagValue.Create("v1"), null, null, null }; result = ep.GetTagKeysAndValues(keys, values); Assert.Equal("v1", result["status"]); Assert.Single(result); values = new List <ITagValue>() { null, TagValue.Create("v2"), null, null }; result = ep.GetTagKeysAndValues(keys, values); Assert.Equal("v2", result["exception"]); Assert.Single(result); values = new List <ITagValue>() { TagValue.Create("v1"), }; result = ep.GetTagKeysAndValues(keys, values); Assert.Empty(result); }
public void TestDeserializeOneTag() { var output = new MemoryStream(); output.WriteByte(SerializationUtils.VersionId); EncodeTagToOutPut("Key", "Value", output); var expected = tagger.EmptyBuilder.Put(TagKey.Create("Key"), TagValue.Create("Value")).Build(); Assert.Equal(expected, serializer.FromByteArray(output.ToArray())); }
public void TestTagKeyEquals() { var key1 = TagKey.Create("foo"); var key2 = TagKey.Create("foo"); var key3 = TagKey.Create("bar"); Assert.Equal(key1, key2); Assert.NotEqual(key3, key1); Assert.NotEqual(key3, key2); }
public void Invoke_WithMetricsRequest_ReturnsExpected() { var opts = new MetricsEndpointOptions(); var stats = new OpenCensusStats(); var tagsComponent = new TagsComponent(); var tagger = tagsComponent.Tagger; var ep = new MetricsEndpoint(opts, stats); var testMeasure = MeasureDouble.Create("test.total", "test", MeasureUnit.Bytes); SetupTestView(stats, Sum.Create(), testMeasure, "test.test1"); var context1 = tagger .EmptyBuilder .Put(TagKey.Create("a"), TagValue.Create("v1")) .Put(TagKey.Create("b"), TagValue.Create("v1")) .Put(TagKey.Create("c"), TagValue.Create("v1")) .Build(); long allKeyssum = 0; for (var i = 0; i < 10; i++) { allKeyssum += i; stats.StatsRecorder.NewMeasureMap().Put(testMeasure, i).Record(context1); } var alltags = new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("a", "v1"), new KeyValuePair <string, string>("b", "v1"), new KeyValuePair <string, string>("c", "v1") }; var req = new MetricsRequest("test.test1", alltags); var resp = ep.Invoke(req) as MetricsResponse; Assert.NotNull(resp); Assert.Equal("test.test1", resp.Name); Assert.NotNull(resp.Measurements); Assert.Single(resp.Measurements); var sample = resp.Measurements[0]; Assert.Equal(MetricStatistic.TOTAL, sample.Statistic); Assert.Equal(allKeyssum, sample.Value); Assert.NotNull(resp.AvailableTags); Assert.Equal(3, resp.AvailableTags.Count); req = new MetricsRequest("foo.bar", alltags); resp = ep.Invoke(req) as MetricsResponse; Assert.Null(resp); }
public void Create_DisallowTagKeyNameOverMaxLength() { char[] chars = new char[TagKey.MAX_LENGTH + 1]; for (int i = 0; i < chars.Length; i++) { chars[i] = 'k'; } String key = new String(chars); Assert.Throws <ArgumentOutOfRangeException>(() => TagKey.Create(key)); }
public void Create_AllowTagKeyNameWithMaxLength() { char[] chars = new char[TagKey.MAX_LENGTH]; for (int i = 0; i < chars.Length; i++) { chars[i] = 'k'; } String key = new String(chars); Assert.Equal(key, TagKey.Create(key).Name); }
public void TestDeserializeDuplicateTags() { MemoryStream output = new MemoryStream(); output.WriteByte(SerializationUtils.VERSION_ID); EncodeTagToOutPut("Key1", "Value1", output); EncodeTagToOutPut("Key1", "Value1", output); ITagContext expected = tagger.EmptyBuilder.Put(TagKey.Create("Key1"), TagValue.Create("Value1")).Build(); Assert.Equal(expected, serializer.FromByteArray(output.ToArray())); }
public void PreventDuplicateColumns() { Assert.Throws <ArgumentException>(() => View.Create( NAME, DESCRIPTION, MEASURE, MEAN, new List <ITagKey>() { TagKey.Create("duplicate"), TagKey.Create("duplicate") })); }
public void PreventDuplicateColumns() { Assert.Throws <ArgumentException>(() => View.Create( Name, Description, Measure, Mean, new List <TagKey>() { TagKey.Create("duplicate"), TagKey.Create("duplicate") })); }
public void GetTagValuesInColumnOrder_ReturnsExpected() { var tags = new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("foo", "bar"), new KeyValuePair <string, string>("aaa", "bbb") }; var columns = new List <ITagKey>() { TagKey.Create("foo"), TagKey.Create("aaa") }; var opts = new MetricsEndpointOptions(); var stats = new OpenCensusStats(); var ep = new MetricsEndpoint(opts, stats); var result = ep.GetTagValuesInColumnOrder(columns, tags); Assert.NotNull(result); Assert.Equal(2, result.Count); Assert.Equal(TagValue.Create("bar"), result[0]); Assert.Equal(TagValue.Create("bbb"), result[1]); // Invalid tagkey provided columns = new List <ITagKey>() { TagKey.Create("bar"), TagKey.Create("aaa") }; result = ep.GetTagValuesInColumnOrder(columns, tags); Assert.Null(result); // aaa column not provided columns = new List <ITagKey>() { TagKey.Create("foo"), TagKey.Create("aaa"), TagKey.Create("bbb"), }; tags = new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("foo", "bar"), new KeyValuePair <string, string>("bbb", "bbb") }; result = ep.GetTagValuesInColumnOrder(columns, tags); Assert.Equal(3, result.Count); Assert.Equal(TagValue.Create("bar"), result[0]); Assert.Null(result[1]); Assert.Equal(TagValue.Create("bbb"), result[2]); }
public void Create_AllowTagKeyNameWithMaxLength() { var chars = new char[TagKey.MaxLength]; for (var i = 0; i < chars.Length; i++) { chars[i] = 'k'; } var key = new String(chars); Assert.Equal(key, TagKey.Create(key).Name); }
public void TestTagEquals() { var tag1 = Tag.Create(TagKey.Create("Key"), TagValue.Create("foo")); var tag2 = Tag.Create(TagKey.Create("Key"), TagValue.Create("foo")); var tag3 = Tag.Create(TagKey.Create("Key"), TagValue.Create("bar")); var tag4 = Tag.Create(TagKey.Create("Key2"), TagValue.Create("foo")); Assert.Equal(tag1, tag2); Assert.NotEqual(tag1, tag3); Assert.NotEqual(tag1, tag4); Assert.NotEqual(tag2, tag3); Assert.NotEqual(tag2, tag4); Assert.NotEqual(tag3, tag4); }
public void ParseResourceLabels_ValueWithParenthesis_StrippedValue() { // Arrange string resourceLabels = "k1=\"v1\""; // Act var tags = Resource.ParseResourceLabels(resourceLabels); // Assert Assert.NotNull(tags); Assert.NotEmpty(tags); Assert.Equal(TagKey.Create("k1"), tags[0].Key); Assert.Equal(TagValue.Create("v1"), tags[0].Value); }
public void GetMetricsForExportedViews_ReturnsExpected() { var opts = new CloudFoundryForwarderOptions() { MicrometerMetricWriter = true }; var stats = new OpenCensusStats(); var tagsComponent = new TagsComponent(); var tagger = tagsComponent.Tagger; var ep = new CloudFoundryForwarderExporter(opts, stats); IMeasureDouble testMeasure = MeasureDouble.Create("test.total", "test", MeasureUnit.Bytes); SetupTestView(stats, Sum.Create(), testMeasure, "test.test1"); ITagContext context1 = tagger .EmptyBuilder .Put(TagKey.Create("a"), TagValue.Create("v1")) .Put(TagKey.Create("b"), TagValue.Create("v1")) .Put(TagKey.Create("c"), TagValue.Create("v1")) .Build(); long allKeyssum = 0; for (int i = 0; i < 10; i++) { allKeyssum = allKeyssum + i; stats.StatsRecorder.NewMeasureMap().Put(testMeasure, i).Record(context1); } var result = ep.GetMetricsForExportedViews(stats.ViewManager.AllExportedViews, 1L); Assert.NotNull(result); Assert.Single(result); var metric = result[0]; Assert.Equal("test.test1", metric.Name); Assert.Equal(1L, metric.Timestamp); Assert.Equal("gauge", metric.Type); Assert.Equal("bytes", metric.Unit); Assert.Equal(allKeyssum, metric.Value); var tags = metric.Tags; Assert.Equal("total", tags["statistic"]); Assert.Equal("v1", tags["a"]); Assert.Equal("v1", tags["b"]); Assert.Equal("v1", tags["c"]); }
public void ParseResourceLabels_WrongKeyValueDelimiter_PairIgnored() { // Arrange string resourceLabels = "k1:v1,k2=v2"; // Act var tags = Resource.ParseResourceLabels(resourceLabels); // Assert Assert.NotNull(tags); Assert.NotEmpty(tags); Assert.Single(tags); Assert.Equal(TagKey.Create("k2"), tags[0].Key); Assert.Equal(TagValue.Create("v2"), tags[0].Value); }
public void GetTagContext_ReturnsExpected() { var options = new MetricsEndpointOptions(); var stats = new OpenCensusStats(); var tags = new OpenCensusTags(); var observer = new HttpClientDesktopObserver(options, stats, tags, null); var req = GetHttpRequestMessage(); var tagContext = observer.GetTagContext(req, HttpStatusCode.InternalServerError); var tagValues = tagContext.ToList(); tagValues.Contains(Tag.Create(TagKey.Create("clientName"), TagValue.Create("localhost:5555"))); tagValues.Contains(Tag.Create(TagKey.Create("uri"), TagValue.Create("/foo/bar"))); tagValues.Contains(Tag.Create(TagKey.Create("status"), TagValue.Create("500"))); tagValues.Contains(Tag.Create(TagKey.Create("method"), TagValue.Create("GET"))); }
public void ParseResourceLabels_CommaSeparated_MapReturned() { // Arrange string resourceLabels = "key1=val1,key2=val2"; // Act var tags = Resource.ParseResourceLabels(resourceLabels); // Assert Assert.NotNull(tags); Assert.Equal(2, tags.Length); Assert.Equal(TagKey.Create("key1"), tags[0].Key); Assert.Equal(TagKey.Create("key2"), tags[1].Key); Assert.Equal(TagValue.Create("val1"), tags[0].Value); Assert.Equal(TagValue.Create("val2"), tags[1].Value); }
public void GetTagKeysAndValues_ReturnsExpected() { var opts = new CloudFoundryForwarderOptions(); var stats = new OpenCensusStats(); var ep = new MicrometerMetricWriter(opts, stats); IList <ITagKey> keys = new List <ITagKey>() { TagKey.Create("key1"), TagKey.Create("key2") }; IList <ITagValue> values = new List <ITagValue>() { TagValue.Create("v1"), TagValue.Create("v2") }; var result = ep.GetTagKeysAndValues(keys, values); Assert.Equal("v1", result["key1"]); Assert.Equal("v2", result["key2"]); values = new List <ITagValue>() { TagValue.Create("v1"), null }; result = ep.GetTagKeysAndValues(keys, values); Assert.Equal("v1", result["key1"]); Assert.Single(result); values = new List <ITagValue>() { null, TagValue.Create("v2"), }; result = ep.GetTagKeysAndValues(keys, values); Assert.Equal("v2", result["key2"]); Assert.Single(result); values = new List <ITagValue>() { TagValue.Create("v1"), }; result = ep.GetTagKeysAndValues(keys, values); Assert.Empty(result); }
private void SetupStats(OpenCensusStats stats) { var exceptionKey = TagKey.Create("exception"); var methodKey = TagKey.Create("method"); var uriKey = TagKey.Create("uri"); var statusKey = TagKey.Create("status"); var httpServerRquestMeasure = MeasureDouble.Create("server.totalTime", "server request times", MeasureUnit.MilliSeconds); var httpServerRequestsViewName = ViewName.Create("http.server.requests"); var httpServerRequestsView = View.Create( httpServerRequestsViewName, "server request times", httpServerRquestMeasure, Distribution.Create(BucketBoundaries.Create(new List <double>() { 0.0, 1.0, 2.0 })), new List <ITagKey>() { exceptionKey, methodKey, uriKey, statusKey }); stats.ViewManager.RegisterView(httpServerRequestsView); var area = TagKey.Create("area"); var id = TagKey.Create("id"); var memoryUsageMeasure = MeasureDouble.Create("memory.value", "memory usage", MeasureUnit.Bytes); var memoryUsageName = ViewName.Create("jvm.memory.used"); var memoryUsageView = View.Create( memoryUsageName, "memory usage", memoryUsageMeasure, Distribution.Create(BucketBoundaries.Create(new List <double>() { 0.0, 1000.0, 10000.0, 100000.0, 1000000.0, 10000000.0, 100000000.0 })), new List <ITagKey>() { area, id }); stats.ViewManager.RegisterView(memoryUsageView); }
/// <summary> /// Creates a label/tag map from the OC_RESOURCE_LABELS environment variable. /// OC_RESOURCE_LABELS: A comma-separated list of labels describing the source in more detail, /// e.g. “key1=val1,key2=val2”. Domain names and paths are accepted as label keys. /// Values may be quoted or unquoted in general. If a value contains whitespaces, =, or " characters, it must /// always be quoted. /// </summary> /// <param name="rawEnvironmentTags">Environment tags as a raw, comma separated string</param> /// <returns>Environment Tags as a list.</returns> internal static ITag[] ParseResourceLabels(string rawEnvironmentTags) { if (rawEnvironmentTags == null) { return(new ITag[0] { }); } else { var labels = new List <ITag>(); string[] rawLabels = rawEnvironmentTags.Split(LabelListSplitter); Regex regex = new Regex("^\"|\"$", RegexOptions.Compiled); foreach (var rawLabel in rawLabels) { string[] keyValuePair = rawLabel.Split(LabelKeyValueSplitter); if (keyValuePair.Length != 2) { continue; } string key = keyValuePair[0].Trim(); string value = Regex.Replace(keyValuePair[1].Trim(), "^\"|\"$", string.Empty); if (!IsValidAndNotEmpty(key)) { Log.InvalidCharactersInResourceElement("Label key"); return(new ITag[0] { }); } if (!IsValid(value)) { Log.InvalidCharactersInResourceElement("Label key"); return(new ITag[0] { }); } labels.Add(new Tag(TagKey.Create(key), TagValue.Create(value))); } return(labels.ToArray()); } }
protected internal List <ITagValue> GetTagValuesInColumnOrder(IList <ITagKey> columns, List <KeyValuePair <string, string> > tags) { ITagValue[] tagValues = new ITagValue[columns.Count]; foreach (var kvp in tags) { var key = TagKey.Create(kvp.Key); var value = TagValue.Create(kvp.Value); int indx = columns.IndexOf(key); if (indx < 0) { return(null); } tagValues[indx] = value; } return(new List <ITagValue>(tagValues)); }
public void GetMessage_ReturnsExpected() { var configsource = new Dictionary <string, string> { { "application:InstanceId", "InstanceId" }, { "application:InstanceIndex", "1" }, { "application:ApplicationId", "ApplicationId" }, { "management:metrics:exporter:cloudfoundry:MicrometerMetricWriter", "true" } }; var config = TestHelpers.GetConfigurationFromDictionary(configsource); var opts = new CloudFoundryForwarderOptions(new ApplicationInstanceInfo(config), new ServicesOptions(config), config); var stats = new OpenCensusStats(); var tagsComponent = new TagsComponent(); var tagger = tagsComponent.Tagger; var ep = new CloudFoundryForwarderExporter(opts, stats); var testMeasure = MeasureDouble.Create("test.total", "test", MeasureUnit.Bytes); SetupTestView(stats, Sum.Create(), testMeasure, "test.test1"); var context1 = tagger .EmptyBuilder .Put(TagKey.Create("a"), TagValue.Create("v1")) .Put(TagKey.Create("b"), TagValue.Create("v1")) .Put(TagKey.Create("c"), TagValue.Create("v1")) .Build(); long allKeyssum = 0; for (var i = 0; i < 10; i++) { allKeyssum = allKeyssum + i; stats.StatsRecorder.NewMeasureMap().Put(testMeasure, i).Record(context1); } var message = ep.GetMessage(stats.ViewManager.AllExportedViews, 1L); Assert.NotNull(message); var result = Serialize(message); Assert.Equal("{\"applications\":[{\"id\":\"ApplicationId\",\"instances\":[{\"id\":\"InstanceId\",\"index\":\"1\",\"metrics\":[{\"name\":\"test.test1\",\"tags\":{\"a\":\"v1\",\"b\":\"v1\",\"c\":\"v1\",\"statistic\":\"total\"},\"timestamp\":1,\"type\":\"gauge\",\"unit\":\"bytes\",\"value\":45.0}]}]}]}", result); }
public void TestDeserializeNonConsecutiveDuplicateTags() { var output = new MemoryStream(); output.WriteByte(SerializationUtils.VersionId); EncodeTagToOutPut("Key1", "Value1", output); EncodeTagToOutPut("Key2", "Value2", output); EncodeTagToOutPut("Key3", "Value3", output); EncodeTagToOutPut("Key1", "Value1", output); EncodeTagToOutPut("Key2", "Value2", output); var expected = tagger .EmptyBuilder .Put(TagKey.Create("Key1"), TagValue.Create("Value1")) .Put(TagKey.Create("Key2"), TagValue.Create("Value2")) .Put(TagKey.Create("Key3"), TagValue.Create("Value3")) .Build(); Assert.Equal(expected, serializer.FromByteArray(output.ToArray())); }