1#region Copyright notice and license
2// Protocol Buffers - Google's data interchange format
3// Copyright 2018 Google Inc.  All rights reserved.
4// https://developers.google.com/protocol-buffers/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10//     * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12//     * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16//     * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31#endregion
32
33using Google.Protobuf.Reflection;
34using NUnit.Framework;
35using System.Linq;
36using System.Reflection;
37
38namespace Google.Protobuf.Test.Reflection
39{
40    // In reality this isn't a test for DescriptorDeclaration so much as the way they're loaded.
41    public class DescriptorDeclarationTest
42    {
43        static readonly FileDescriptor unitTestProto3Descriptor = LoadProtos();
44
45        // Note: we don't expose a declaration for FileDescriptor as it doesn't have comments
46        // at the moment and the locations aren't terribly useful.
47
48        // The tests for most elements are quite basic: we don't test every aspect of every element.
49        // The code within the library falls into two categories:
50        // - Exposing the properties for *any* declaration
51        // - Finding the right declaration for an element from the descriptor data
52        // We have a per-element check to make sure we *are* finding the right declaration, and we
53        // check every property of declarations in at least one test, but we don't have a cross-product.
54        // That would effectively be testing protoc, which seems redundant here.
55
56        [Test]
57        public void ServiceComments()
58        {
59            var service = unitTestProto3Descriptor.FindTypeByName<ServiceDescriptor>("TestService");
60            Assert.NotNull(service.Declaration);
61            Assert.AreEqual(" This is a test service\n", service.Declaration.LeadingComments);
62        }
63
64        [Test]
65        public void MethodComments()
66        {
67            var service = unitTestProto3Descriptor.FindTypeByName<ServiceDescriptor>("TestService");
68            var method = service.FindMethodByName("Foo");
69            Assert.NotNull(method.Declaration);
70            Assert.AreEqual(" This is a test method\n", method.Declaration.LeadingComments);
71        }
72
73        [Test]
74        public void MessageComments()
75        {
76            var message = unitTestProto3Descriptor.FindTypeByName<MessageDescriptor>("CommentMessage");
77            Assert.NotNull(message.Declaration);
78            Assert.AreEqual(" This is a leading comment\n", message.Declaration.LeadingComments);
79            Assert.AreEqual(new[] { " This is leading detached comment 1\n", " This is leading detached comment 2\n" },
80                message.Declaration.LeadingDetachedComments);
81        }
82
83        // Note: this test is somewhat brittle; a change earlier in the proto will break it.
84        [Test]
85        public void MessageLocations()
86        {
87            var message = unitTestProto3Descriptor.FindTypeByName<MessageDescriptor>("CommentMessage");
88            Assert.NotNull(message.Declaration);
89            Assert.AreEqual(389, message.Declaration.StartLine);
90            Assert.AreEqual(1, message.Declaration.StartColumn);
91
92            Assert.AreEqual(404, message.Declaration.EndLine);
93            Assert.AreEqual(2, message.Declaration.EndColumn);
94        }
95
96        [Test]
97        public void EnumComments()
98        {
99            var descriptor = unitTestProto3Descriptor.FindTypeByName<EnumDescriptor>("CommentEnum");
100            Assert.NotNull(descriptor.Declaration);
101            Assert.AreEqual(" Leading enum comment\n", descriptor.Declaration.LeadingComments);
102        }
103
104        [Test]
105        public void NestedMessageComments()
106        {
107            var outer = unitTestProto3Descriptor.FindTypeByName<MessageDescriptor>("CommentMessage");
108            var nested = outer.FindDescriptor<MessageDescriptor>("NestedCommentMessage");
109            Assert.NotNull(nested.Declaration);
110            Assert.AreEqual(" Leading nested message comment\n", nested.Declaration.LeadingComments);
111        }
112
113        [Test]
114        public void NestedEnumComments()
115        {
116            var outer = unitTestProto3Descriptor.FindTypeByName<MessageDescriptor>("CommentMessage");
117            var nested = outer.FindDescriptor<EnumDescriptor>("NestedCommentEnum");
118            Assert.NotNull(nested.Declaration);
119            Assert.AreEqual(" Leading nested enum comment\n", nested.Declaration.LeadingComments);
120        }
121
122        [Test]
123        public void FieldComments()
124        {
125            var message = unitTestProto3Descriptor.FindTypeByName<MessageDescriptor>("CommentMessage");
126            var field = message.FindFieldByName("text");
127            Assert.NotNull(field.Declaration);
128            Assert.AreEqual(" Leading field comment\n", field.Declaration.LeadingComments);
129            Assert.AreEqual(" Trailing field comment\n", field.Declaration.TrailingComments);
130        }
131
132        [Test]
133        public void NestedMessageFieldComments()
134        {
135            var outer = unitTestProto3Descriptor.FindTypeByName<MessageDescriptor>("CommentMessage");
136            var nested = outer.FindDescriptor<MessageDescriptor>("NestedCommentMessage");
137            var field = nested.FindFieldByName("nested_text");
138            Assert.NotNull(field.Declaration);
139            Assert.AreEqual(" Leading nested message field comment\n", field.Declaration.LeadingComments);
140        }
141
142        [Test]
143        public void EnumValueComments()
144        {
145            var enumDescriptor = unitTestProto3Descriptor.FindTypeByName<EnumDescriptor>("CommentEnum");
146            var value = enumDescriptor.FindValueByName("ZERO_VALUE");
147            Assert.NotNull(value.Declaration);
148            Assert.AreEqual(" Zero value comment\n", value.Declaration.LeadingComments);
149        }
150
151        [Test]
152        public void NestedEnumValueComments()
153        {
154            var outer = unitTestProto3Descriptor.FindTypeByName<MessageDescriptor>("CommentMessage");
155            var nested = outer.FindDescriptor<EnumDescriptor>("NestedCommentEnum");
156            var value = nested.FindValueByName("ZERO_VALUE");
157            Assert.NotNull(value.Declaration);
158            Assert.AreEqual(" Zero value comment\n", value.Declaration.LeadingComments);
159        }
160
161        private static FileDescriptor LoadProtos()
162        {
163            var type = typeof(DescriptorDeclarationTest);
164            // TODO: Make this simpler :)
165            FileDescriptorSet descriptorSet;
166            using (var stream = type.GetTypeInfo().Assembly.GetManifestResourceStream($"Google.Protobuf.Test.testprotos.pb"))
167            {
168                descriptorSet = FileDescriptorSet.Parser.ParseFrom(stream);
169            }
170            var byteStrings = descriptorSet.File.Select(f => f.ToByteString()).ToList();
171            var descriptors = FileDescriptor.BuildFromByteStrings(byteStrings);
172            return descriptors.Single(d => d.Name == "unittest_proto3.proto");
173        }
174    }
175}
176