Skip to content

Commit 5d447fd

Browse files
authored
set backslash as path separator for Protobuf item (#1323)
* set backslash as path separator for Protobuf item * add tests * remove unused method
1 parent dce1ef7 commit 5d447fd

File tree

4 files changed

+166
-30
lines changed

4 files changed

+166
-30
lines changed

src/dotnet-grpc/Commands/CommandBase.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,17 @@ public void AddProtobufReference(Services services, string additionalImportDirs,
179179
throw new CLIToolException(string.Format(CultureInfo.CurrentCulture, CoreStrings.ErrorReferenceDoesNotExist, file));
180180
}
181181

182-
if (!Project.GetItems(ProtobufElement).Any(i => string.Equals(i.UnevaluatedInclude, file, StringComparison.OrdinalIgnoreCase)))
182+
var normalizedFile = NormalizePath(file);
183+
var normalizedAdditionalImportDirs = string.Join(';', additionalImportDirs.Split(';', StringSplitOptions.RemoveEmptyEntries).Select(NormalizePath));
184+
185+
if (!Project.GetItems(ProtobufElement).Any(i => string.Equals(NormalizePath(i.UnevaluatedInclude), normalizedFile, StringComparison.OrdinalIgnoreCase)))
183186
{
184187
if (!string.Equals(Path.GetExtension(file), ".proto", StringComparison.OrdinalIgnoreCase))
185188
{
186189
Console.LogWarning(CoreStrings.LogWarningReferenceNotProto, file);
187190
}
188191

189-
var newItem = Project.AddItem(ProtobufElement, file).Single();
192+
var newItem = Project.AddItem(ProtobufElement, normalizedFile).Single();
190193

191194
if (services != Services.Both)
192195
{
@@ -198,9 +201,9 @@ public void AddProtobufReference(Services services, string additionalImportDirs,
198201
newItem.Xml.AddMetadata(AccessElement, access.ToString(), expressAsAttribute: true);
199202
}
200203

201-
if (!string.IsNullOrEmpty(additionalImportDirs))
204+
if (!string.IsNullOrEmpty(normalizedAdditionalImportDirs))
202205
{
203-
newItem.Xml.AddMetadata(AdditionalImportDirsElement, additionalImportDirs, expressAsAttribute: true);
206+
newItem.Xml.AddMetadata(AdditionalImportDirsElement, normalizedAdditionalImportDirs, expressAsAttribute: true);
204207
}
205208

206209
if (!string.IsNullOrEmpty(url))
@@ -211,7 +214,7 @@ public void AddProtobufReference(Services services, string additionalImportDirs,
211214
// If file is outside of the project, display the file under Protos/ directory
212215
if (!Path.GetFullPath(resolvedPath).StartsWith(Project.DirectoryPath, StringComparison.OrdinalIgnoreCase))
213216
{
214-
newItem.Xml.AddMetadata(LinkElement, Path.Combine(ProtosFolder, Path.GetFileName(file)!), expressAsAttribute: true);
217+
newItem.Xml.AddMetadata(LinkElement, $"{ProtosFolder}\\{Path.GetFileName(file)}", expressAsAttribute: true);
215218
}
216219
}
217220
}
@@ -447,5 +450,14 @@ private static byte[] GetHash(Stream stream)
447450
return algorithm.ComputeHash(stream);
448451
}
449452
}
453+
454+
private string NormalizePath(string path)
455+
{
456+
path = !Path.IsPathRooted(path)
457+
? Path.GetRelativePath(Project.DirectoryPath, Path.GetFullPath(Path.Combine(Project.DirectoryPath, path)))
458+
: Path.GetFullPath(path);
459+
460+
return path.Replace('/', '\\');
461+
}
450462
}
451463
}

test/dotnet-grpc.Tests/AddFileCommandTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ public async Task AddFileCommand_AddsPackagesAndReferences()
5353

5454
var protoRefs = command.Project.GetItems(CommandBase.ProtobufElement);
5555
Assert.AreEqual(2, protoRefs.Count);
56-
Assert.NotNull(protoRefs.SingleOrDefault(r => r.UnevaluatedInclude == Path.Combine("Proto", "a.proto")));
57-
Assert.NotNull(protoRefs.SingleOrDefault(r => r.UnevaluatedInclude == Path.Combine("Proto", "b.proto")));
56+
Assert.NotNull(protoRefs.SingleOrDefault(r => r.UnevaluatedInclude == "Proto\\a.proto"));
57+
Assert.NotNull(protoRefs.SingleOrDefault(r => r.UnevaluatedInclude == "Proto\\b.proto"));
5858
foreach (var protoRef in protoRefs)
5959
{
6060
Assert.AreEqual("Server", protoRef.GetMetadataValue(CommandBase.GrpcServicesElement));

test/dotnet-grpc.Tests/AddUrlCommandTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public async Task AddUrlCommand_AddsPackagesAndReferences()
5555
var protoRefs = command.Project.GetItems(CommandBase.ProtobufElement);
5656
Assert.AreEqual(1, protoRefs.Count);
5757
var protoRef = protoRefs.Single();
58-
Assert.AreEqual(Path.Combine("Proto", "c.proto"), protoRef.UnevaluatedInclude);
58+
Assert.AreEqual("Proto\\c.proto", protoRef.UnevaluatedInclude);
5959
Assert.AreEqual("Server", protoRef.GetMetadataValue(CommandBase.GrpcServicesElement));
6060
Assert.AreEqual("ImportDir", protoRef.GetMetadataValue(CommandBase.AdditionalImportDirsElement));
6161
Assert.AreEqual("Internal", protoRef.GetMetadataValue(CommandBase.AccessElement));

test/dotnet-grpc.Tests/CommandBaseTests.cs

Lines changed: 146 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using System.IO;
2222
using System.Linq;
2323
using System.Reflection;
24+
using System.Runtime.InteropServices;
2425
using System.Threading.Tasks;
2526
using Grpc.Dotnet.Cli.Commands;
2627
using Grpc.Dotnet.Cli.Internal;
@@ -142,15 +143,69 @@ public void AddProtobufReference_ThrowsIfFileNotFound()
142143
Assert.Throws<CLIToolException>(() => commandBase.AddProtobufReference(Services.Both, string.Empty, Access.Public, "NonExistentFile", string.Empty));
143144
}
144145

146+
static object[] ReferenceCases()
147+
{
148+
var cases = new List<object>
149+
{
150+
new object[] {"Proto/a.proto", "Proto\\a.proto", ""},
151+
new object[] {"./Proto/a.proto", "Proto\\a.proto", ""},
152+
new object[] {"../ProjectWithReference/Proto/a.proto", "..\\ProjectWithReference\\Proto\\a.proto", "Protos\\a.proto"},
153+
new object[] {"./../ProjectWithReference/Proto/a.proto", "..\\ProjectWithReference\\Proto\\a.proto", "Protos\\a.proto"},
154+
};
155+
156+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
157+
{
158+
cases.Add(new object[] {"Proto\\a.proto", "Proto\\a.proto", ""});
159+
cases.Add(new object[] {".\\Proto/a.proto", "Proto\\a.proto", ""});
160+
cases.Add(new object[] {"..\\ProjectWithReference\\Proto\\a.proto", "..\\ProjectWithReference\\Proto\\a.proto", "Protos\\a.proto"});
161+
cases.Add(new object[] {".\\..\\ProjectWithReference\\Proto\\a.proto", "..\\ProjectWithReference\\Proto\\a.proto", "Protos\\a.proto"});
162+
}
163+
164+
return cases.ToArray();
165+
}
166+
145167
[Test]
146-
public void AddProtobufReference_AddsRelativeReference()
168+
[TestCaseSource(nameof(ReferenceCases))]
169+
public void AddProtobufReference_AddsRelativeReference(string path, string normalizedPath, string link)
147170
{
148171
// Arrange
149172
var commandBase = new CommandBase(
150173
new TestConsole(),
151174
CreateIsolatedProject(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets", "EmptyProject", "test.csproj")));
152175

153-
var referencePath = Path.Combine("Proto", "a.proto");
176+
// Act
177+
commandBase.AddProtobufReference(Services.Server, "ImportDir", Access.Internal, path, SourceUrl);
178+
commandBase.Project.ReevaluateIfNecessary();
179+
180+
// Assert
181+
var protoRefs = commandBase.Project.GetItems(CommandBase.ProtobufElement);
182+
Assert.AreEqual(1, protoRefs.Count);
183+
var protoRef = protoRefs.Single();
184+
Assert.AreEqual(normalizedPath, protoRef.UnevaluatedInclude);
185+
Assert.AreEqual("Server", protoRef.GetMetadataValue(CommandBase.GrpcServicesElement));
186+
Assert.AreEqual("ImportDir", protoRef.GetMetadataValue(CommandBase.AdditionalImportDirsElement));
187+
Assert.AreEqual("Internal", protoRef.GetMetadataValue(CommandBase.AccessElement));
188+
Assert.AreEqual(SourceUrl, protoRef.GetMetadataValue(CommandBase.SourceUrlElement));
189+
Assert.AreEqual(link, protoRef.GetMetadataValue(CommandBase.LinkElement));
190+
}
191+
192+
[Test]
193+
[TestCaseSource(nameof(ReferenceCases))]
194+
public void AddProtobufReference_AddsAbsoluteReference(string path, string normalizedPath, string link)
195+
{
196+
// Arrange
197+
var commandBase = new CommandBase(
198+
new TestConsole(),
199+
CreateIsolatedProject(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets", "EmptyProject", "test.csproj")));
200+
201+
var referencePath = Path.Combine(Directory.GetCurrentDirectory(), "TestAssets", "EmptyProject", path);
202+
var normalizedReferencePath = Path.GetFullPath(
203+
Path.Combine(
204+
Directory.GetCurrentDirectory(),
205+
"TestAssets",
206+
"EmptyProject",
207+
normalizedPath.Replace('\\', Path.DirectorySeparatorChar)))
208+
.Replace('/', '\\');
154209

155210
// Act
156211
commandBase.AddProtobufReference(Services.Server, "ImportDir", Access.Internal, referencePath, SourceUrl);
@@ -160,89 +215,158 @@ public void AddProtobufReference_AddsRelativeReference()
160215
var protoRefs = commandBase.Project.GetItems(CommandBase.ProtobufElement);
161216
Assert.AreEqual(1, protoRefs.Count);
162217
var protoRef = protoRefs.Single();
163-
Assert.AreEqual(referencePath, protoRef.UnevaluatedInclude);
218+
Assert.AreEqual(normalizedReferencePath, protoRef.UnevaluatedInclude);
164219
Assert.AreEqual("Server", protoRef.GetMetadataValue(CommandBase.GrpcServicesElement));
165220
Assert.AreEqual("ImportDir", protoRef.GetMetadataValue(CommandBase.AdditionalImportDirsElement));
166221
Assert.AreEqual("Internal", protoRef.GetMetadataValue(CommandBase.AccessElement));
167222
Assert.AreEqual(SourceUrl, protoRef.GetMetadataValue(CommandBase.SourceUrlElement));
168-
Assert.False(protoRef.HasMetadata(CommandBase.LinkElement));
223+
Assert.AreEqual(link, protoRef.GetMetadataValue(CommandBase.LinkElement));
224+
}
225+
226+
static object[] AdditionalImportDirsCases()
227+
{
228+
var cases = new List<object>
229+
{
230+
new object[] {"ImportDir", "ImportDir"},
231+
new object[] {"ImportDir;./ImportDir2", "ImportDir;ImportDir2"},
232+
new object[] {"../ImportDir;./../ImportDir2", "../ImportDir;../ImportDir2"},
233+
new object[] {"ImportDir;;ImportDir2;", "ImportDir;ImportDir2"},
234+
};
235+
236+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
237+
{
238+
cases.Add(new object[] {".\\ImportDir;ImportDir2", "ImportDir;ImportDir2"});
239+
cases.Add(new object[] {"../ImportDir;..\\ImportDir2", "..\\ImportDir;..\\ImportDir2"});
240+
cases.Add(new object[] {"./../ImportDir;.\\..\\ImportDir2", "..\\ImportDir;..\\ImportDir2"});
241+
}
242+
243+
return cases.ToArray();
169244
}
170245

171246
[Test]
172-
public void AddProtobufReference_AddsAbsoluteReference()
247+
[TestCaseSource(nameof(AdditionalImportDirsCases))]
248+
public void AddProtobufReference_AdditionalImportDirs(string additionalImportDir, string normalizedAdditionalImportDir)
173249
{
174250
// Arrange
175-
var commandBase = new CommandBase(new TestConsole(), new Project());
176-
var referencePath = Path.Combine(Directory.GetCurrentDirectory(), "TestAssets", "EmptyProject", "Proto", "a.proto");
251+
var commandBase = new CommandBase(
252+
new TestConsole(),
253+
CreateIsolatedProject(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets", "EmptyProject", "test.csproj")));
177254

255+
const string proto = "Proto/a.proto";
256+
178257
// Act
179-
commandBase.AddProtobufReference(Services.Server, "ImportDir", Access.Internal, referencePath, SourceUrl);
258+
commandBase.AddProtobufReference(Services.Server, "ImportDir", Access.Internal, proto, SourceUrl);
180259
commandBase.Project.ReevaluateIfNecessary();
181260

182261
// Assert
183262
var protoRefs = commandBase.Project.GetItems(CommandBase.ProtobufElement);
184263
Assert.AreEqual(1, protoRefs.Count);
185264
var protoRef = protoRefs.Single();
186-
Assert.AreEqual(referencePath, protoRef.UnevaluatedInclude);
265+
Assert.AreEqual(proto.Replace('/', '\\'), protoRef.UnevaluatedInclude);
187266
Assert.AreEqual("Server", protoRef.GetMetadataValue(CommandBase.GrpcServicesElement));
188267
Assert.AreEqual("ImportDir", protoRef.GetMetadataValue(CommandBase.AdditionalImportDirsElement));
189268
Assert.AreEqual("Internal", protoRef.GetMetadataValue(CommandBase.AccessElement));
190269
Assert.AreEqual(SourceUrl, protoRef.GetMetadataValue(CommandBase.SourceUrlElement));
191270
Assert.False(protoRef.HasMetadata(CommandBase.LinkElement));
192271
}
193272

273+
static object[] DoesNotOverwriteCases()
274+
{
275+
var cases = new List<object>
276+
{
277+
new object[] { "Proto/a.proto", "Proto/a.proto", "Proto\\a.proto" },
278+
new object[] { "./Proto/a.proto", "Proto/a.proto", "Proto\\a.proto" },
279+
new object[] { "../ProjectWithReference/Proto/a.proto", "../ProjectWithReference/Proto/a.proto", "..\\ProjectWithReference\\Proto\\a.proto" },
280+
new object[] { "../ProjectWithReference/Proto/a.proto", "./../ProjectWithReference/Proto/a.proto", "..\\ProjectWithReference\\Proto\\a.proto" },
281+
new object[] { "./../ProjectWithReference/Proto/a.proto", "./../ProjectWithReference/Proto/a.proto", "..\\ProjectWithReference\\Proto\\a.proto" },
282+
};
283+
284+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
285+
{
286+
cases.Add(new object[] { "Proto/a.proto", "Proto\\a.proto", "Proto\\a.proto" });
287+
cases.Add(new object[] { ".\\..\\ProjectWithReference\\Proto\\a.proto", "../ProjectWithReference/Proto/a.proto", "..\\ProjectWithReference\\Proto\\a.proto" });
288+
cases.Add(new object[] { ".\\..\\ProjectWithReference\\Proto\\a.proto", "./../ProjectWithReference/Proto/a.proto", "..\\ProjectWithReference\\Proto\\a.proto" });
289+
}
290+
291+
return cases.ToArray();
292+
}
293+
194294
[Test]
195-
public void AddProtobufReference_DoesNotOverwriteReference()
295+
[TestCaseSource(nameof(DoesNotOverwriteCases))]
296+
public void AddProtobufReference_DoesNotOverwriteReference(string path, string altPath, string normalizedPath)
196297
{
197298
// Arrange
198299
var commandBase = new CommandBase(new TestConsole(), new Project());
199-
var referencePath = Path.Combine(Directory.GetCurrentDirectory(), "TestAssets", "EmptyProject", "Proto", "a.proto");
300+
var referencePath = Path.Combine(Directory.GetCurrentDirectory(), "TestAssets", "EmptyProject", path);
301+
var altReferencePath = Path.Combine(Directory.GetCurrentDirectory(), "TestAssets", "EmptyProject", altPath);
302+
var normalizedReferencePath = Path.GetFullPath(
303+
Path.Combine(
304+
Directory.GetCurrentDirectory(),
305+
"TestAssets",
306+
"EmptyProject",
307+
normalizedPath.Replace('\\', '/')))
308+
.Replace('/', '\\');
200309

201310
// Act
202311
commandBase.AddProtobufReference(Services.Server, "ImportDir", Access.Internal, referencePath, SourceUrl);
203-
commandBase.AddProtobufReference(Services.Client, "ImportDir2", Access.Public, referencePath, SourceUrl + ".proto");
312+
commandBase.AddProtobufReference(Services.Client, "ImportDir2", Access.Public, altReferencePath, SourceUrl + ".proto");
204313
commandBase.Project.ReevaluateIfNecessary();
205314

206315
// Assert
207316
var protoRefs = commandBase.Project.GetItems(CommandBase.ProtobufElement);
208317
Assert.AreEqual(1, protoRefs.Count);
209318
var protoRef = protoRefs.Single();
210-
Assert.AreEqual(referencePath, protoRef.UnevaluatedInclude);
319+
Assert.AreEqual(normalizedReferencePath, protoRef.UnevaluatedInclude);
211320
Assert.AreEqual("Server", protoRef.GetMetadataValue(CommandBase.GrpcServicesElement));
212321
Assert.AreEqual("ImportDir", protoRef.GetMetadataValue(CommandBase.AdditionalImportDirsElement));
213322
Assert.AreEqual("Internal", protoRef.GetMetadataValue(CommandBase.AccessElement));
214323
Assert.AreEqual(SourceUrl, protoRef.GetMetadataValue(CommandBase.SourceUrlElement));
215324
}
216325

217-
static object[] ProtosOutsideProject =
326+
static object[] ProtosOutsideProject()
218327
{
219-
new object[] { Path.Combine(Directory.GetCurrentDirectory(), "TestAssets", "ProjectWithReference", "Proto", "a.proto") },
220-
new object[] { Path.Combine("..", "ProjectWithReference", "Proto", "a.proto") },
221-
};
328+
var cases = new List<object>
329+
{
330+
Case(Directory.GetCurrentDirectory(), "TestAssets", "ProjectWithReference", "Proto", "a.proto"),
331+
Case("..", "ProjectWithReference", "Proto", "a.proto")
332+
};
333+
334+
return cases.ToArray();
335+
336+
static object Case(params string[] segments)
337+
{
338+
var path = Path.Combine(segments);
339+
return new object[]
340+
{
341+
path,
342+
path.Replace('/', '\\')
343+
};
344+
}
345+
}
222346

223347
[Test]
224-
[TestCaseSource("ProtosOutsideProject")]
225-
public void AddProtobufReference_AddsLinkElementIfFileOutsideProject(string reference)
348+
[TestCaseSource(nameof(ProtosOutsideProject))]
349+
public void AddProtobufReference_AddsLinkElementIfFileOutsideProject(string path, string normalizedPath)
226350
{
227351
// Arrange
228352
var commandBase = new CommandBase(
229353
new TestConsole(),
230354
CreateIsolatedProject(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets", "EmptyProject", "test.csproj")));
231355

232356
// Act
233-
commandBase.AddProtobufReference(Services.Server, "ImportDir", Access.Internal, reference, SourceUrl);
357+
commandBase.AddProtobufReference(Services.Server, "ImportDir", Access.Internal, path, SourceUrl);
234358
commandBase.Project.ReevaluateIfNecessary();
235359

236360
// Assert
237361
var protoRefs = commandBase.Project.GetItems(CommandBase.ProtobufElement);
238362
Assert.AreEqual(1, protoRefs.Count);
239363
var protoRef = protoRefs.Single();
240-
Assert.AreEqual(reference, protoRef.UnevaluatedInclude);
364+
Assert.AreEqual(normalizedPath, protoRef.UnevaluatedInclude);
241365
Assert.AreEqual("Server", protoRef.GetMetadataValue(CommandBase.GrpcServicesElement));
242366
Assert.AreEqual("ImportDir", protoRef.GetMetadataValue(CommandBase.AdditionalImportDirsElement));
243367
Assert.AreEqual("Internal", protoRef.GetMetadataValue(CommandBase.AccessElement));
244368
Assert.AreEqual(SourceUrl, protoRef.GetMetadataValue(CommandBase.SourceUrlElement));
245-
Assert.AreEqual(Path.Combine(CommandBase.ProtosFolder, "a.proto"), protoRef.GetMetadataValue(CommandBase.LinkElement));
369+
Assert.AreEqual($"{CommandBase.ProtosFolder}\\a.proto", protoRef.GetMetadataValue(CommandBase.LinkElement));
246370
}
247371

248372
[Test]

0 commit comments

Comments
 (0)