1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ptypes
6
7import (
8	"reflect"
9	"testing"
10
11	"github.com/golang/protobuf/proto"
12
13	descriptorpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
14	anypb "github.com/golang/protobuf/ptypes/any"
15)
16
17func TestMarshalUnmarshal(t *testing.T) {
18	orig := &anypb.Any{Value: []byte("test")}
19
20	packed, err := MarshalAny(orig)
21	if err != nil {
22		t.Errorf("MarshalAny(%+v): got: _, %v exp: _, nil", orig, err)
23	}
24
25	unpacked := &anypb.Any{}
26	err = UnmarshalAny(packed, unpacked)
27	if err != nil || !proto.Equal(unpacked, orig) {
28		t.Errorf("got: %v, %+v; want nil, %+v", err, unpacked, orig)
29	}
30}
31
32func TestIs(t *testing.T) {
33	a, err := MarshalAny(&descriptorpb.FileDescriptorProto{})
34	if err != nil {
35		t.Fatal(err)
36	}
37	if Is(a, &descriptorpb.DescriptorProto{}) {
38		// No spurious match for message names of different length.
39		t.Error("FileDescriptorProto is not a DescriptorProto, but Is says it is")
40	}
41	if Is(a, &descriptorpb.EnumDescriptorProto{}) {
42		// No spurious match for message names of equal length.
43		t.Error("FileDescriptorProto is not an EnumDescriptorProto, but Is says it is")
44	}
45	if !Is(a, &descriptorpb.FileDescriptorProto{}) {
46		t.Error("FileDescriptorProto is indeed a FileDescriptorProto, but Is says it is not")
47	}
48}
49
50func TestIsDifferentUrlPrefixes(t *testing.T) {
51	m := &descriptorpb.FileDescriptorProto{}
52	a := &anypb.Any{TypeUrl: "foo/bar/" + proto.MessageName(m)}
53	if !Is(a, m) {
54		t.Errorf("message with type url %q didn't satisfy Is for type %q", a.TypeUrl, proto.MessageName(m))
55	}
56}
57
58func TestIsCornerCases(t *testing.T) {
59	m := &descriptorpb.FileDescriptorProto{}
60	if Is(nil, m) {
61		t.Errorf("message with nil type url incorrectly claimed to be %q", proto.MessageName(m))
62	}
63	noPrefix := &anypb.Any{TypeUrl: proto.MessageName(m)}
64	if !Is(noPrefix, m) {
65		t.Errorf("message with type url %q didn't satisfy Is for type %q", noPrefix.TypeUrl, proto.MessageName(m))
66	}
67	shortPrefix := &anypb.Any{TypeUrl: "/" + proto.MessageName(m)}
68	if !Is(shortPrefix, m) {
69		t.Errorf("message with type url %q didn't satisfy Is for type %q", shortPrefix.TypeUrl, proto.MessageName(m))
70	}
71}
72
73func TestUnmarshalDynamic(t *testing.T) {
74	want := &descriptorpb.FileDescriptorProto{Name: proto.String("foo")}
75	a, err := MarshalAny(want)
76	if err != nil {
77		t.Fatal(err)
78	}
79	var got DynamicAny
80	if err := UnmarshalAny(a, &got); err != nil {
81		t.Fatal(err)
82	}
83	if !proto.Equal(got.Message, want) {
84		t.Errorf("invalid result from UnmarshalAny, got %q want %q", got.Message, want)
85	}
86}
87
88func TestEmpty(t *testing.T) {
89	want := &descriptorpb.FileDescriptorProto{}
90	a, err := MarshalAny(want)
91	if err != nil {
92		t.Fatal(err)
93	}
94	got, err := Empty(a)
95	if err != nil {
96		t.Fatal(err)
97	}
98	if !proto.Equal(got, want) {
99		t.Errorf("unequal empty message, got %q, want %q", got, want)
100	}
101
102	// that's a valid type_url for a message which shouldn't be linked into this
103	// test binary. We want an error.
104	a.TypeUrl = "type.googleapis.com/google.protobuf.FieldMask"
105	if _, err := Empty(a); err == nil {
106		t.Errorf("got no error for an attempt to create a message of type %q, which shouldn't be linked in", a.TypeUrl)
107	}
108}
109
110func TestEmptyCornerCases(t *testing.T) {
111	_, err := Empty(nil)
112	if err == nil {
113		t.Error("expected Empty for nil to fail")
114	}
115	want := &descriptorpb.FileDescriptorProto{}
116	noPrefix := &anypb.Any{TypeUrl: proto.MessageName(want)}
117	got, err := Empty(noPrefix)
118	if err != nil {
119		t.Errorf("Empty for any type %q failed: %s", noPrefix.TypeUrl, err)
120	}
121	if !proto.Equal(got, want) {
122		t.Errorf("Empty for any type %q differs, got %q, want %q", noPrefix.TypeUrl, got, want)
123	}
124	shortPrefix := &anypb.Any{TypeUrl: "/" + proto.MessageName(want)}
125	got, err = Empty(shortPrefix)
126	if err != nil {
127		t.Errorf("Empty for any type %q failed: %s", shortPrefix.TypeUrl, err)
128	}
129	if !proto.Equal(got, want) {
130		t.Errorf("Empty for any type %q differs, got %q, want %q", shortPrefix.TypeUrl, got, want)
131	}
132}
133
134func TestAnyReflect(t *testing.T) {
135	want := &descriptorpb.FileDescriptorProto{Name: proto.String("foo")}
136	a, err := MarshalAny(want)
137	if err != nil {
138		t.Fatal(err)
139	}
140	var got DynamicAny
141	if err := UnmarshalAny(a, &got); err != nil {
142		t.Fatal(err)
143	}
144	wantName := want.ProtoReflect().Descriptor().FullName()
145	gotName := got.ProtoReflect().Descriptor().FullName()
146	if gotName != wantName {
147		t.Errorf("name mismatch: got %v, want %v", gotName, wantName)
148	}
149	wantType := reflect.TypeOf(got)
150	gotType := reflect.TypeOf(got.ProtoReflect().Interface())
151	if gotType != wantType {
152		t.Errorf("ProtoReflect().Interface() round-trip type mismatch: got %v, want %v", gotType, wantType)
153	}
154	gotType = reflect.TypeOf(got.ProtoReflect().New().Interface())
155	if gotType != wantType {
156		t.Errorf("ProtoReflect().New().Interface() type mismatch: got %v, want %v", gotType, wantType)
157	}
158	gotType = reflect.TypeOf(got.ProtoReflect().Type().New().Interface())
159	if gotType != wantType {
160		t.Errorf("ProtoReflect().Type().New().Interface() type mismatch: got %v, want %v", gotType, wantType)
161	}
162	gotType = reflect.TypeOf(got.ProtoReflect().Type().Zero().Interface())
163	if gotType != wantType {
164		t.Errorf("ProtoReflect().Type().Zero().Interface() type mismatch: got %v, want %v", gotType, wantType)
165	}
166}
167