• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 
3 // Copyright 2018 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #endregion
18 
19 using System.IO;
20 using Microsoft.Build.Framework;
21 using Moq;
22 using NUnit.Framework;
23 
24 namespace Grpc.Tools.Tests
25 {
26     public class ProtoCompileCommandLineGeneratorTest : ProtoCompileBasicTest
27     {
28         [SetUp]
SetUp()29         public new void SetUp()
30         {
31             _task.Generator = "csharp";
32             _task.OutputDir = "outdir";
33             _task.Protobuf = Utils.MakeSimpleItems("a.proto");
34         }
35 
ExecuteExpectSuccess()36         void ExecuteExpectSuccess()
37         {
38             _mockEngine
39               .Setup(me => me.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
40               .Callback((BuildErrorEventArgs e) =>
41                   Assert.Fail($"Error logged by build engine:\n{e.Message}"));
42             bool result = _task.Execute();
43             Assert.IsTrue(result);
44         }
45 
46         [Test]
MinimalCompile()47         public void MinimalCompile()
48         {
49             ExecuteExpectSuccess();
50             Assert.That(_task.LastPathToTool, Does.Match(@"protoc(.exe)?$"));
51             Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
52                 "--csharp_out=outdir", "--error_format=msvs", "a.proto" }));
53         }
54 
55         [Test]
CompileTwoFiles()56         public void CompileTwoFiles()
57         {
58             _task.Protobuf = Utils.MakeSimpleItems("a.proto", "foo/b.proto");
59             ExecuteExpectSuccess();
60             Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
61                 "--csharp_out=outdir", "--error_format=msvs", "a.proto", "foo/b.proto" }));
62         }
63 
64         [Test]
CompileWithProtoPaths()65         public void CompileWithProtoPaths()
66         {
67             _task.ProtoPath = new[] { "/path1", "/path2" };
68             ExecuteExpectSuccess();
69             Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
70                 "--csharp_out=outdir", "--proto_path=/path1",
71                 "--proto_path=/path2", "--error_format=msvs", "a.proto" }));
72         }
73 
74         [TestCase("Cpp")]
75         [TestCase("CSharp")]
76         [TestCase("Java")]
77         [TestCase("Javanano")]
78         [TestCase("Js")]
79         [TestCase("Objc")]
80         [TestCase("Php")]
81         [TestCase("Python")]
82         [TestCase("Ruby")]
CompileWithOptions(string gen)83         public void CompileWithOptions(string gen)
84         {
85             _task.Generator = gen;
86             _task.OutputOptions = new[] { "foo", "bar" };
87             ExecuteExpectSuccess();
88             gen = gen.ToLowerInvariant();
89             Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
90                 $"--{gen}_out=outdir", $"--{gen}_opt=foo,bar", "--error_format=msvs", "a.proto" }));
91         }
92 
93         [Test]
OutputDependencyFile()94         public void OutputDependencyFile()
95         {
96             _task.DependencyOut = "foo/my.protodep";
97             // Task fails trying to read the non-generated file; we ignore that.
98             _task.Execute();
99             Assert.That(_task.LastResponseFile,
100                 Does.Contain("--dependency_out=foo/my.protodep"));
101         }
102 
103         [Test]
OutputDependencyWithProtoDepDir()104         public void OutputDependencyWithProtoDepDir()
105         {
106             _task.ProtoDepDir = "foo";
107             // Task fails trying to read the non-generated file; we ignore that.
108             _task.Execute();
109             Assert.That(_task.LastResponseFile,
110                 Has.One.Match(@"^--dependency_out=foo[/\\].+_a.protodep$"));
111         }
112 
113         [Test]
GenerateGrpc()114         public void GenerateGrpc()
115         {
116             _task.GrpcPluginExe = "/foo/grpcgen";
117             ExecuteExpectSuccess();
118             Assert.That(_task.LastResponseFile, Is.SupersetOf(new[] {
119                 "--csharp_out=outdir", "--grpc_out=outdir",
120                 "--plugin=protoc-gen-grpc=/foo/grpcgen" }));
121         }
122 
123         [Test]
GenerateGrpcWithOutDir()124         public void GenerateGrpcWithOutDir()
125         {
126             _task.GrpcPluginExe = "/foo/grpcgen";
127             _task.GrpcOutputDir = "gen-out";
128             ExecuteExpectSuccess();
129             Assert.That(_task.LastResponseFile, Is.SupersetOf(new[] {
130                 "--csharp_out=outdir", "--grpc_out=gen-out" }));
131         }
132 
133         [Test]
GenerateGrpcWithOptions()134         public void GenerateGrpcWithOptions()
135         {
136             _task.GrpcPluginExe = "/foo/grpcgen";
137             _task.GrpcOutputOptions = new[] { "baz", "quux" };
138             ExecuteExpectSuccess();
139             Assert.That(_task.LastResponseFile,
140                         Does.Contain("--grpc_opt=baz,quux"));
141         }
142 
143         [Test]
DirectoryArgumentsSlashTrimmed()144         public void DirectoryArgumentsSlashTrimmed()
145         {
146             _task.GrpcPluginExe = "/foo/grpcgen";
147             _task.GrpcOutputDir = "gen-out/";
148             _task.OutputDir = "outdir/";
149             _task.ProtoPath = new[] { "/path1/", "/path2/" };
150             ExecuteExpectSuccess();
151             Assert.That(_task.LastResponseFile, Is.SupersetOf(new[] {
152         "--proto_path=/path1", "--proto_path=/path2",
153         "--csharp_out=outdir", "--grpc_out=gen-out" }));
154         }
155 
156         [TestCase(".", ".")]
157         [TestCase("/", "/")]
158         [TestCase("//", "/")]
159         [TestCase("/foo/", "/foo")]
160         [TestCase("/foo", "/foo")]
161         [TestCase("foo/", "foo")]
162         [TestCase("foo//", "foo")]
163         [TestCase("foo/\\", "foo")]
164         [TestCase("foo\\/", "foo")]
165         [TestCase("C:\\foo", "C:\\foo")]
166         [TestCase("C:", "C:")]
167         [TestCase("C:\\", "C:\\")]
168         [TestCase("C:\\\\", "C:\\")]
DirectorySlashTrimmingCases(string given, string expect)169         public void DirectorySlashTrimmingCases(string given, string expect)
170         {
171             if (Path.DirectorySeparatorChar == '/')
172                 expect = expect.Replace('\\', '/');
173             _task.OutputDir = given;
174             ExecuteExpectSuccess();
175             Assert.That(_task.LastResponseFile,
176                         Does.Contain("--csharp_out=" + expect));
177         }
178 
179         [TestCase(
180             "../Protos/greet.proto(19) : warning in column=5 : warning : When enum name is stripped and label is PascalCased (Zero) this value label conflicts with Zero.",
181             "../Protos/greet.proto",
182             19,
183             5,
184             "warning : When enum name is stripped and label is PascalCased (Zero) this value label conflicts with Zero.")]
185         [TestCase(
186             "../Protos/greet.proto: warning: Import google/protobuf/empty.proto but not used.",
187             "../Protos/greet.proto",
188             0,
189             0,
190             "Import google/protobuf/empty.proto but not used.")]
191         [TestCase("../Protos/greet.proto(14) : error in column=10: \"name\" is already defined in \"Greet.HelloRequest\".", null, 0, 0, null)]
192         [TestCase("../Protos/greet.proto: Import \"google / protobuf / empty.proto\" was listed twice.", null, 0, 0, null)]
WarningsParsed(string stderr, string file, int line, int col, string message)193         public void WarningsParsed(string stderr, string file, int line, int col, string message)
194         {
195             _task.StdErrMessages.Add(stderr);
196 
197             _mockEngine
198                 .Setup(me => me.LogWarningEvent(It.IsAny<BuildWarningEventArgs>()))
199                 .Callback((BuildWarningEventArgs e) => {
200                     if (file != null)
201                     {
202                         Assert.AreEqual(file, e.File);
203                         Assert.AreEqual(line, e.LineNumber);
204                         Assert.AreEqual(col, e.ColumnNumber);
205                         Assert.AreEqual(message, e.Message);
206                     }
207                     else
208                     {
209                         Assert.Fail($"Error logged by build engine:\n{e.Message}");
210                     }
211                 });
212 
213             bool result = _task.Execute();
214             Assert.IsFalse(result);
215         }
216 
217         [TestCase(
218             "../Protos/greet.proto(14) : error in column=10: \"name\" is already defined in \"Greet.HelloRequest\".",
219             "../Protos/greet.proto",
220             14,
221             10,
222             "\"name\" is already defined in \"Greet.HelloRequest\".")]
223         [TestCase(
224             "../Protos/greet.proto: Import \"google / protobuf / empty.proto\" was listed twice.",
225             "../Protos/greet.proto",
226             0,
227             0,
228             "Import \"google / protobuf / empty.proto\" was listed twice.")]
229         [TestCase("../Protos/greet.proto(19) : warning in column=5 : warning : When enum name is stripped and label is PascalCased (Zero) this value label conflicts with Zero.", null, 0, 0, null)]
230         [TestCase("../Protos/greet.proto: warning: Import google/protobuf/empty.proto but not used.", null, 0, 0, null)]
ErrorsParsed(string stderr, string file, int line, int col, string message)231         public void ErrorsParsed(string stderr, string file, int line, int col, string message)
232         {
233             _task.StdErrMessages.Add(stderr);
234 
235             _mockEngine
236                 .Setup(me => me.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
237                 .Callback((BuildErrorEventArgs e) => {
238                     if (file != null)
239                     {
240                         Assert.AreEqual(file, e.File);
241                         Assert.AreEqual(line, e.LineNumber);
242                         Assert.AreEqual(col, e.ColumnNumber);
243                         Assert.AreEqual(message, e.Message);
244                     }
245                     else
246                     {
247                         // Ignore expected error
248                         // "protoc/protoc.exe" existed with code -1.
249                         if (!e.Message.EndsWith("exited with code -1."))
250                         {
251                             Assert.Fail($"Error logged by build engine:\n{e.Message}");
252                         }
253                     }
254                 });
255 
256             bool result = _task.Execute();
257             Assert.IsFalse(result);
258         }
259     };
260 }
261