func TestRoundTripProto3(t *testing.T) { m := &pb.Message{ Name: "David", // (2 | 1<<3): 0x0a 0x05 "David" Hilarity: pb.Message_PUNS, // (0 | 2<<3): 0x10 0x01 HeightInCm: 178, // (0 | 3<<3): 0x18 0xb2 0x01 Data: []byte("roboto"), // (2 | 4<<3): 0x20 0x06 "roboto" ResultCount: 47, // (0 | 7<<3): 0x38 0x2f TrueScotsman: true, // (0 | 8<<3): 0x40 0x01 Score: 8.1, // (5 | 9<<3): 0x4d <8.1> Key: []uint64{1, 0xdeadbeef}, Nested: &pb.Nested{ Bunny: "Monty", }, } t.Logf(" m: %v", m) b, err := proto.Marshal(m) if err != nil { t.Fatalf("proto.Marshal: %v", err) } t.Logf(" b: %q", b) m2 := new(pb.Message) if err := proto.Unmarshal(b, m2); err != nil { t.Fatalf("proto.Unmarshal: %v", err) } t.Logf("m2: %v", m2) if !proto.Equal(m, m2) { t.Errorf("proto.Equal returned false:\n m: %v\nm2: %v", m, m2) } }
func TestGetExtensionStability(t *testing.T) { check := func(m *pb.MyMessage) bool { ext1, err := proto.GetExtension(m, pb.E_Ext_More) if err != nil { t.Fatalf("GetExtension() failed: %s", err) } ext2, err := proto.GetExtension(m, pb.E_Ext_More) if err != nil { t.Fatalf("GetExtension() failed: %s", err) } return ext1 == ext2 } msg := &pb.MyMessage{Count: proto.Int32(4)} ext0 := &pb.Ext{} if err := proto.SetExtension(msg, pb.E_Ext_More, ext0); err != nil { t.Fatalf("Could not set ext1: %s", ext0) } if !check(msg) { t.Errorf("GetExtension() not stable before marshaling") } bb, err := proto.Marshal(msg) if err != nil { t.Fatalf("Marshal() failed: %s", err) } msg1 := &pb.MyMessage{} err = proto.Unmarshal(bb, msg1) if err != nil { t.Fatalf("Unmarshal() failed: %s", err) } if !check(msg1) { t.Errorf("GetExtension() not stable after unmarshaling") } }
func newTestMessage() *pb.MyMessage { msg := &pb.MyMessage{ Count: proto.Int32(42), Name: proto.String("Dave"), Quote: proto.String(`"I didn't want to go."`), Pet: []string{"bunny", "kitty", "horsey"}, Inner: &pb.InnerMessage{ Host: proto.String("footrest.syd"), Port: proto.Int32(7001), Connected: proto.Bool(true), }, Others: []*pb.OtherMessage{ { Key: proto.Int64(0xdeadbeef), Value: []byte{1, 65, 7, 12}, }, { Weight: proto.Float32(6.022), Inner: &pb.InnerMessage{ Host: proto.String("lesha.mtv"), Port: proto.Int32(8002), }, }, }, Bikeshed: pb.MyMessage_BLUE.Enum(), Somegroup: &pb.MyMessage_SomeGroup{ GroupField: proto.Int32(8), }, // One normally wouldn't do this. // This is an undeclared tag 13, as a varint (wire type 0) with value 4. XXX_unrecognized: []byte{13<<3 | 0, 4}, } ext := &pb.Ext{ Data: proto.String("Big gobs for big rats"), } if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil { panic(err) } greetings := []string{"adg", "easy", "cow"} if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil { panic(err) } // Add an unknown extension. We marshal a pb.Ext, and fake the ID. b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")}) if err != nil { panic(err) } b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...) proto.SetRawExtension(msg, 201, b) // Extensions can be plain fields, too, so let's test that. b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19) proto.SetRawExtension(msg, 202, b) return msg }
// WriteDelimited encodes and dumps a message to the provided writer prefixed // with a 32-bit varint indicating the length of the encoded message, producing // a length-delimited record stream, which can be used to chain together // encoded messages of the same type together in a file. It returns the total // number of bytes written and any applicable error. This is roughly // equivalent to the companion Java API's MessageLite#writeDelimitedTo. func WriteDelimited(w io.Writer, m proto.Message) (n int, err error) { buffer, err := proto.Marshal(m) if err != nil { return 0, err } buf := make([]byte, binary.MaxVarintLen32) encodedLength := binary.PutUvarint(buf, uint64(len(buffer))) sync, err := w.Write(buf[:encodedLength]) if err != nil { return sync, err } n, err = w.Write(buffer) return n + sync, err }
func TestProto3ZeroValues(t *testing.T) { tests := []struct { desc string m proto.Message }{ {"zero message", &pb.Message{}}, {"empty bytes field", &pb.Message{Data: []byte{}}}, } for _, test := range tests { b, err := proto.Marshal(test.m) if err != nil { t.Errorf("%s: proto.Marshal: %v", test.desc, err) continue } if len(b) > 0 { t.Errorf("%s: Encoding is non-empty: %q", test.desc, b) } } }
func testHandler(t testing.TB) { metricVec := NewCounterVec( CounterOpts{ Name: "name", Help: "docstring", ConstLabels: Labels{"constname": "constvalue"}, }, []string{"labelname"}, ) metricVec.WithLabelValues("val1").Inc() metricVec.WithLabelValues("val2").Inc() varintBuf := make([]byte, binary.MaxVarintLen32) externalMetricFamily := &dto.MetricFamily{ Name: proto.String("externalname"), Help: proto.String("externaldocstring"), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{ { Label: []*dto.LabelPair{ { Name: proto.String("externalconstname"), Value: proto.String("externalconstvalue"), }, { Name: proto.String("externallabelname"), Value: proto.String("externalval1"), }, }, Counter: &dto.Counter{ Value: proto.Float64(1), }, }, }, } marshaledExternalMetricFamily, err := proto.Marshal(externalMetricFamily) if err != nil { t.Fatal(err) } var externalBuf bytes.Buffer l := binary.PutUvarint(varintBuf, uint64(len(marshaledExternalMetricFamily))) _, err = externalBuf.Write(varintBuf[:l]) if err != nil { t.Fatal(err) } _, err = externalBuf.Write(marshaledExternalMetricFamily) if err != nil { t.Fatal(err) } externalMetricFamilyAsBytes := externalBuf.Bytes() externalMetricFamilyAsText := []byte(`# HELP externalname externaldocstring # TYPE externalname counter externalname{externalconstname="externalconstvalue",externallabelname="externalval1"} 1 `) externalMetricFamilyAsProtoText := []byte(`name: "externalname" help: "externaldocstring" type: COUNTER metric: < label: < name: "externalconstname" value: "externalconstvalue" > label: < name: "externallabelname" value: "externalval1" > counter: < value: 1 > > `) externalMetricFamilyAsProtoCompactText := []byte(`name:"externalname" help:"externaldocstring" type:COUNTER metric:<label:<name:"externalconstname" value:"externalconstvalue" > label:<name:"externallabelname" value:"externalval1" > counter:<value:1 > > `) expectedMetricFamily := &dto.MetricFamily{ Name: proto.String("name"), Help: proto.String("docstring"), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{ { Label: []*dto.LabelPair{ { Name: proto.String("constname"), Value: proto.String("constvalue"), }, { Name: proto.String("labelname"), Value: proto.String("val1"), }, }, Counter: &dto.Counter{ Value: proto.Float64(1), }, }, { Label: []*dto.LabelPair{ { Name: proto.String("constname"), Value: proto.String("constvalue"), }, { Name: proto.String("labelname"), Value: proto.String("val2"), }, }, Counter: &dto.Counter{ Value: proto.Float64(1), }, }, }, } marshaledExpectedMetricFamily, err := proto.Marshal(expectedMetricFamily) if err != nil { t.Fatal(err) } var buf bytes.Buffer l = binary.PutUvarint(varintBuf, uint64(len(marshaledExpectedMetricFamily))) _, err = buf.Write(varintBuf[:l]) if err != nil { t.Fatal(err) } _, err = buf.Write(marshaledExpectedMetricFamily) if err != nil { t.Fatal(err) } expectedMetricFamilyAsBytes := buf.Bytes() expectedMetricFamilyAsText := []byte(`# HELP name docstring # TYPE name counter name{constname="constvalue",labelname="val1"} 1 name{constname="constvalue",labelname="val2"} 1 `) expectedMetricFamilyAsProtoText := []byte(`name: "name" help: "docstring" type: COUNTER metric: < label: < name: "constname" value: "constvalue" > label: < name: "labelname" value: "val1" > counter: < value: 1 > > metric: < label: < name: "constname" value: "constvalue" > label: < name: "labelname" value: "val2" > counter: < value: 1 > > `) expectedMetricFamilyAsProtoCompactText := []byte(`name:"name" help:"docstring" type:COUNTER metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"val1" > counter:<value:1 > > metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"val2" > counter:<value:1 > > `) externalMetricFamilyWithSameName := &dto.MetricFamily{ Name: proto.String("name"), Help: proto.String("inconsistent help string does not matter here"), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{ { Label: []*dto.LabelPair{ { Name: proto.String("constname"), Value: proto.String("constvalue"), }, { Name: proto.String("labelname"), Value: proto.String("different_val"), }, }, Counter: &dto.Counter{ Value: proto.Float64(42), }, }, }, } expectedMetricFamilyMergedWithExternalAsProtoCompactText := []byte(`name:"name" help:"docstring" type:COUNTER metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"different_val" > counter:<value:42 > > metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"val1" > counter:<value:1 > > metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"val2" > counter:<value:1 > > `) type output struct { headers map[string]string body []byte } var scenarios = []struct { headers map[string]string out output collector Collector externalMF []*dto.MetricFamily }{ { // 0 headers: map[string]string{ "Accept": "foo/bar;q=0.2, dings/bums;q=0.8", }, out: output{ headers: map[string]string{ "Content-Type": `text/plain; version=0.0.4`, }, body: []byte{}, }, }, { // 1 headers: map[string]string{ "Accept": "foo/bar;q=0.2, application/quark;q=0.8", }, out: output{ headers: map[string]string{ "Content-Type": `text/plain; version=0.0.4`, }, body: []byte{}, }, }, { // 2 headers: map[string]string{ "Accept": "foo/bar;q=0.2, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.8", }, out: output{ headers: map[string]string{ "Content-Type": `text/plain; version=0.0.4`, }, body: []byte{}, }, }, { // 3 headers: map[string]string{ "Accept": "text/plain;q=0.2, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.8", }, out: output{ headers: map[string]string{ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`, }, body: []byte{}, }, }, { // 4 headers: map[string]string{ "Accept": "application/json", }, out: output{ headers: map[string]string{ "Content-Type": `text/plain; version=0.0.4`, }, body: expectedMetricFamilyAsText, }, collector: metricVec, }, { // 5 headers: map[string]string{ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited", }, out: output{ headers: map[string]string{ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`, }, body: expectedMetricFamilyAsBytes, }, collector: metricVec, }, { // 6 headers: map[string]string{ "Accept": "application/json", }, out: output{ headers: map[string]string{ "Content-Type": `text/plain; version=0.0.4`, }, body: externalMetricFamilyAsText, }, externalMF: []*dto.MetricFamily{externalMetricFamily}, }, { // 7 headers: map[string]string{ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited", }, out: output{ headers: map[string]string{ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`, }, body: externalMetricFamilyAsBytes, }, externalMF: []*dto.MetricFamily{externalMetricFamily}, }, { // 8 headers: map[string]string{ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited", }, out: output{ headers: map[string]string{ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`, }, body: bytes.Join( [][]byte{ externalMetricFamilyAsBytes, expectedMetricFamilyAsBytes, }, []byte{}, ), }, collector: metricVec, externalMF: []*dto.MetricFamily{externalMetricFamily}, }, { // 9 headers: map[string]string{ "Accept": "text/plain", }, out: output{ headers: map[string]string{ "Content-Type": `text/plain; version=0.0.4`, }, body: []byte{}, }, }, { // 10 headers: map[string]string{ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.2, text/plain;q=0.5", }, out: output{ headers: map[string]string{ "Content-Type": `text/plain; version=0.0.4`, }, body: expectedMetricFamilyAsText, }, collector: metricVec, }, { // 11 headers: map[string]string{ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.2, text/plain;q=0.5;version=0.0.4", }, out: output{ headers: map[string]string{ "Content-Type": `text/plain; version=0.0.4`, }, body: bytes.Join( [][]byte{ externalMetricFamilyAsText, expectedMetricFamilyAsText, }, []byte{}, ), }, collector: metricVec, externalMF: []*dto.MetricFamily{externalMetricFamily}, }, { // 12 headers: map[string]string{ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.2, text/plain;q=0.5;version=0.0.2", }, out: output{ headers: map[string]string{ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`, }, body: bytes.Join( [][]byte{ externalMetricFamilyAsBytes, expectedMetricFamilyAsBytes, }, []byte{}, ), }, collector: metricVec, externalMF: []*dto.MetricFamily{externalMetricFamily}, }, { // 13 headers: map[string]string{ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=text;q=0.5, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.4", }, out: output{ headers: map[string]string{ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=text`, }, body: bytes.Join( [][]byte{ externalMetricFamilyAsProtoText, expectedMetricFamilyAsProtoText, }, []byte{}, ), }, collector: metricVec, externalMF: []*dto.MetricFamily{externalMetricFamily}, }, { // 14 headers: map[string]string{ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=compact-text", }, out: output{ headers: map[string]string{ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`, }, body: bytes.Join( [][]byte{ externalMetricFamilyAsProtoCompactText, expectedMetricFamilyAsProtoCompactText, }, []byte{}, ), }, collector: metricVec, externalMF: []*dto.MetricFamily{externalMetricFamily}, }, { // 15 headers: map[string]string{ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=compact-text", }, out: output{ headers: map[string]string{ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`, }, body: bytes.Join( [][]byte{ externalMetricFamilyAsProtoCompactText, expectedMetricFamilyMergedWithExternalAsProtoCompactText, }, []byte{}, ), }, collector: metricVec, externalMF: []*dto.MetricFamily{ externalMetricFamily, externalMetricFamilyWithSameName, }, }, } for i, scenario := range scenarios { registry := newRegistry() registry.collectChecksEnabled = true if scenario.collector != nil { registry.Register(scenario.collector) } if scenario.externalMF != nil { registry.metricFamilyInjectionHook = func() []*dto.MetricFamily { return scenario.externalMF } } writer := &fakeResponseWriter{ header: http.Header{}, } handler := InstrumentHandler("prometheus", registry) request, _ := http.NewRequest("GET", "/", nil) for key, value := range scenario.headers { request.Header.Add(key, value) } handler(writer, request) for key, value := range scenario.out.headers { if writer.Header().Get(key) != value { t.Errorf( "%d. expected %q for header %q, got %q", i, value, key, writer.Header().Get(key), ) } } if !bytes.Equal(scenario.out.body, writer.body.Bytes()) { t.Errorf( "%d. expected %q for body, got %q", i, scenario.out.body, writer.body.Bytes(), ) } } }