Skip to content

Commit

Permalink
Refactor: Expand path before parsing glob pattern. Added test cases f…
Browse files Browse the repository at this point in the history
…or expanding both relative and absolute glob patterns.
  • Loading branch information
peters committed Jun 16, 2020
1 parent 623bfce commit 04fd2b4
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 77 deletions.
69 changes: 42 additions & 27 deletions src/GprTool/GlobExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
Expand All @@ -14,55 +15,69 @@ public static bool IsGlobPattern(this Glob glob)
return glob.Tokens.Any(x => !(x is PathSeparatorToken || x is LiteralToken));
}

public static string BuildBasePathFromGlob(this Glob glob, string baseDirectory = null)
public static string BuildBasePathFromGlob(this Glob glob, string baseDirectory)
{
if (glob == null) throw new ArgumentNullException(nameof(glob));
if (baseDirectory == null) throw new ArgumentNullException(nameof(baseDirectory));

var tokensLength = glob.Tokens.Length;
var globTokens = glob.Tokens.ToList();
var pathTokens = new List<IGlobToken>();

var path = new StringBuilder();

for (var index = 0; index < tokensLength; index++)
for (var index = 0; index < globTokens.Count; index++)
{
var token = glob.Tokens[index];
var tokenNext = index + 1 < tokensLength ? glob.Tokens[index + 1] : null;
var tokenPrevious = index - 1 >= 0 ? glob.Tokens[index - 1] : null;
var tokenPreviousPrevious = index - 2 >= 0 ? glob.Tokens[index - 2] : null;

var tokenNext = index + 1 < globTokens.Count ? glob.Tokens[index + 1] : null;

switch (token)
{
case PathSeparatorToken pathSeparatorToken when(tokenNext is LiteralToken):
path.Append(pathSeparatorToken.Value);
break;
case LiteralToken literalToken:

if (tokenPrevious is WildcardToken
|| tokenPreviousPrevious is WildcardDirectoryToken)
case LiteralToken _:
pathTokens.Add(token);
if (tokenNext is AnyCharacterToken
|| tokenNext is INegatableToken
&& pathTokens.Any())
{
pathTokens.RemoveAt(pathTokens.Count - 1);
goto done;
}
continue;
case PathSeparatorToken _ when (tokenNext is LiteralToken):
pathTokens.Add(token);
continue;
}

path.Append(literalToken.Value);
goto done;
}

if (tokenNext is WildcardToken
|| tokenNext is WildcardDirectoryToken)
{
goto done;
}
done:

var pathStringBuilder = new StringBuilder();

foreach (var token in pathTokens)
{
switch (token)
{
case LiteralToken literalToken:
pathStringBuilder.Append(literalToken.Value);
break;
case PathSeparatorToken _: // xplat
pathStringBuilder.Append("/");
break;
default:
throw new NotSupportedException(token.GetType().FullName);
}
}

done:
var pathStr = pathStringBuilder.ToString();

var pathStr = path.ToString();
if (baseDirectory != null && !Path.IsPathRooted(pathStr))
// Remove trailing backward/forward slash
var lastAppendedGlobToken = pathTokens.LastOrDefault();
if (lastAppendedGlobToken is PathSeparatorToken
&& pathStr.Sum(x => x == '/' ? 1 : 0) > 1)
{
return Path.GetFullPath(pathStr, baseDirectory);
pathStr = pathStr.Substring(0, pathStr.Length - 1);
}

return Path.GetFullPath(pathStr);
return !Path.IsPathRooted(pathStr) ? Path.GetFullPath(pathStr, baseDirectory) : Path.GetFullPath(pathStr);
}
}
}
37 changes: 36 additions & 1 deletion src/GprTool/IoExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,44 @@
using System.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DotNet.Globbing;

namespace GprTool
{
public static class IoExtensions
{
public static IEnumerable<string> GetFilesByGlobPattern(this string baseDirectory, string globPattern, out Glob outGlob)
{
var baseDirectoryGlobPattern = Path.GetFullPath(Path.Combine(baseDirectory, globPattern));

if (string.Equals(".", globPattern))
{
globPattern = Path.GetFullPath(Path.Combine(baseDirectory, "*.*"));
} else if (Directory.Exists(baseDirectoryGlobPattern))
{
globPattern = Path.GetFullPath(Path.Combine(baseDirectoryGlobPattern, "*.*"));
} else if (File.Exists(baseDirectoryGlobPattern))
{
globPattern = Path.GetFullPath(baseDirectoryGlobPattern);
}

var basePathFromGlob = Path.GetDirectoryName(Glob.Parse(globPattern).BuildBasePathFromGlob(baseDirectory));

var glob = Path.IsPathRooted(globPattern)
? Glob.Parse(globPattern)
: Glob.Parse(baseDirectoryGlobPattern);

outGlob = glob;

return Directory
.GetFiles(basePathFromGlob, "*.*", SearchOption.AllDirectories)
.Where(filename =>
filename.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)
|| filename.EndsWith(".snupkg", StringComparison.OrdinalIgnoreCase))
.Where(filename => glob.IsMatch(filename));
}

public static FileStream OpenReadShared(this string filename)
{
return File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
Expand Down
34 changes: 9 additions & 25 deletions src/GprTool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -384,11 +384,6 @@ protected override async Task<int> OnExecuteAsyncImpl(CommandLineApplication app
CancellationToken cancellationToken)
{
var packageFiles = new List<PackageFile>();
var packageFullPath = Path.GetFullPath(PackageFilename);
var glob = Glob.Parse(packageFullPath);
var isGlobPattern = glob.IsGlobPattern();
var currentDirectory = Directory.GetCurrentDirectory();
var isPackageFilenameADirectory = !isGlobPattern && Directory.Exists(PackageFilename);

NuGetVersion nuGetVersion = null;
if (Version != null && !NuGetVersion.TryParse(Version, out nuGetVersion))
Expand All @@ -397,27 +392,16 @@ protected override async Task<int> OnExecuteAsyncImpl(CommandLineApplication app
return 1;
}

if (isGlobPattern || isPackageFilenameADirectory)
{
packageFiles.AddRange(Directory
.GetFiles(currentDirectory, "*.*", SearchOption.AllDirectories)
.Where(filename =>
filename.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)
|| filename.EndsWith(".snupkg", StringComparison.OrdinalIgnoreCase))
.Where(filename => isPackageFilenameADirectory || glob.IsMatch(filename))
.Select(filename => NuGetUtilities.BuildPackageFile(filename, RepositoryUrl)));
var currentDirectory = Directory.GetCurrentDirectory();
packageFiles.AddRange(
currentDirectory
.GetFilesByGlobPattern(GlobPattern, out var glob)
.Select(x => NuGetUtilities.BuildPackageFile(x, RepositoryUrl)));

if (!packageFiles.Any())
{
Console.WriteLine(isPackageFilenameADirectory
? $"Unable to find any packages in directory {currentDirectory}. Valid filename extensions are .nupkg, .snupkg."
: $"Unable to find any packages in directory {currentDirectory} matching glob pattern: {glob}. Valid filename extensions are .nupkg, .snupkg.");
return 1;
}
}
else
if (!packageFiles.Any())
{
packageFiles.Add(NuGetUtilities.BuildPackageFile(packageFullPath, RepositoryUrl));
Console.WriteLine($"Unable to find any packages matching glob pattern: {glob}. Valid filename extensions are .nupkg, .snupkg.");
return 1;
}

Console.WriteLine($"Found {packageFiles.Count} package{(packageFiles.Count > 1 ? "s" : string.Empty)}.");
Expand Down Expand Up @@ -555,7 +539,7 @@ static async Task<IRestResponse> UploadPackageAsyncImpl(PackageFile packageFile,
}

[Argument(0, Description = "Path to the package file")]
public string PackageFilename { get; set; }
public string GlobPattern { get; set; }

[Option("-r|--repository", Description = "Override current nupkg repository url. Format: owner/repository. E.g: jcansdale/gpr")]
public string RepositoryUrl { get; set; }
Expand Down
41 changes: 17 additions & 24 deletions test/GprTool.Tests/GlobExtensionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,24 @@ public void IsGlobPattern(string path, bool isGlobPattern)
Assert.That(glob.IsGlobPattern(), Is.EqualTo(isGlobPattern));
}

[TestCase("./packages/**/*.nupkg", "c:\\test\\packages")]
[TestCase(".\\packages/**/*.nupkg", "c:\\test\\packages")]
[TestCase("packages", "c:\\test\\packages")]
[TestCase("c:\\test.nupkg", "c:\\test.nupkg")]
[TestCase("c:\\test", "c:\\test")]
[TestCase("c:\\test?", "c:\\test")]
[TestCase("c:\\test?\\**", "c:\\test")]
[TestCase("c:\\test?\\[abc]\\**", "c:\\test")]
[TestCase("c:\\test\\*.*", "c:\\test")]
[TestCase("c:\\test\\**", "c:\\test")]
[TestCase("c:\\test\\**\\*.nupkg", "c:\\test")]
[TestCase("c:\\test\\subdirectory\\**\\*.nupkg", "c:\\test\\subdirectory")]
[TestCase("c:\\test\\**\\subdirectory\\**\\*.nupkg", "c:\\test")]
public void BuildBasePathFromGlob(string path, string expectedBaseDirectory)
[TestCase(".", "c:\\", "c:\\", Description = "Relative path is directory")]
[TestCase("packages", "c:\\", "c:\\packages", Description = "Relative path is directory")]
[TestCase("test.nupkg", "c:\\", "c:\\test.nupkg", Description = "Relative path is filename")]
[TestCase("packages\\**\\*.nupkg", "c:\\", "c:\\packages", Description = "Relative path")]
[TestCase("packages/**/*.nupkg", "c:\\", "c:\\packages", Description = "Relative path")]
[TestCase("./packages/**/*.nupkg", "c:\\", "c:\\packages", Description = "Relative path")]
[TestCase(".\\packages/**/*.nupkg", "c:\\", "c:\\packages", Description = "Relative path")]
[TestCase("c:\\test?", "c:\\", "c:\\")]
[TestCase("c:\\test?\\**", "c:\\", "c:\\")]
[TestCase("c:\\test?\\[abc]\\**", "c:\\", "c:\\")]
[TestCase("c:\\test\\*.*", "c:\\", "c:\\test")]
[TestCase("c:\\test\\**", "c:\\", "c:\\test")]
[TestCase("c:\\test\\**\\*.nupkg", "c:\\", "c:\\test")]
[TestCase("c:\\test\\subdirectory\\**\\*.nupkg", "c:\\", "c:\\test\\subdirectory")]
[TestCase("c:\\test\\**\\subdirectory\\**\\*.nupkg", "c:\\", "c:\\test")]
public void BuildBasePathFromGlob(string path, string baseDirectory, string expectedBaseDirectory)
{
var glob = Glob.Parse(path);
Assert.That(glob.BuildBasePathFromGlob("c:\\test"), Is.EqualTo(expectedBaseDirectory));
}

[TestCase("packages/**/*.nupkg", "c:\\test", "c:\\test\\packages")]
[TestCase("packages\\**\\*.nupkg", "c:\\test", "c:\\test\\packages")]
[TestCase("packages", "c:\\test", "c:\\test\\packages")]
public void BuildBasePathFromGlob_Uses_BaseDirectory_When_Path_Is_Not_Rooted(string relativePath, string baseDirectory, string expectedPath)
{
var glob = Glob.Parse(relativePath);
Assert.That(glob.BuildBasePathFromGlob(baseDirectory), Is.EqualTo(expectedPath));
Assert.That(glob.BuildBasePathFromGlob(baseDirectory), Is.EqualTo(expectedBaseDirectory));
}
}
139 changes: 139 additions & 0 deletions test/GprTool.Tests/IoExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using System;
using System.IO;
using System.Linq;
using NUnit.Framework;

namespace GprTool.Tests
{
[TestFixture]
class IoExtensionsTests
{
[TestCase("nupkg", "*.*", 2)]
[TestCase("nupkg/**/*.nupkg", "**/*.nupkg", 1)]
[TestCase("nupkg/**/*.snupkg", "**/*.snupkg", 1)]
[TestCase("nupkg/**/*.*", "**/*.*", 2)]
[TestCase("nupkg/**/**", "**/**", 2)]
public void GetFilesByGlobPattern_Is_Relative_Directory(string globPattern, string expectedGlobPattern, int expectedFilesCount)
{
using var tmpDirectory = new DisposableDirectory(Path.Combine(Directory.GetCurrentDirectory(), Guid.NewGuid().ToString("N")));

var nupkgDirectory = Path.Combine(tmpDirectory, "nupkg");
Directory.CreateDirectory(nupkgDirectory);

var nupkgAbsoluteFilename = Path.Combine(nupkgDirectory, "test.nupkg");
var snupkgAbsoluteFilename = Path.Combine(nupkgDirectory, "test.snupkg");

File.WriteAllText(nupkgAbsoluteFilename, string.Empty);
File.WriteAllText(snupkgAbsoluteFilename, string.Empty);

var packages = tmpDirectory.WorkingDirectory.GetFilesByGlobPattern(globPattern, out var glob).ToList();
var pattern = glob.ToString().Substring(nupkgDirectory.Length).Replace("\\", "/");
if (pattern[0] == '/')
{
pattern = pattern.Substring(1);
}
Assert.That(pattern, Is.EqualTo(expectedGlobPattern));
Assert.That(packages.Count, Is.EqualTo(expectedFilesCount));
}

[TestCase("nupkg", "*.*", 2)]
[TestCase("nupkg/**/*.nupkg", "**/*.nupkg", 1)]
[TestCase("nupkg/**/*.snupkg", "**/*.snupkg", 1)]
[TestCase("nupkg/**/*.*", "**/*.*", 2)]
[TestCase("nupkg/**/**", "**/**", 2)]
public void GetFilesByGlobPattern_Is_FullPath_Directory(string globPattern, string expectedGlobPattern, int expectedFilesCount)
{
using var tmpDirectory = new DisposableDirectory(Path.Combine(Directory.GetCurrentDirectory(), Guid.NewGuid().ToString("N")));

var nupkgDirectory = Path.Combine(tmpDirectory, "nupkg");
Directory.CreateDirectory(nupkgDirectory);

var nupkgAbsoluteFilename = Path.Combine(nupkgDirectory, "test.nupkg");
var snupkgAbsoluteFilename = Path.Combine(nupkgDirectory, "test.snupkg");

File.WriteAllText(nupkgAbsoluteFilename, string.Empty);
File.WriteAllText(snupkgAbsoluteFilename, string.Empty);

var packages = tmpDirectory.WorkingDirectory.GetFilesByGlobPattern(Path.Combine(tmpDirectory, globPattern), out var glob).ToList();
var pattern = glob.ToString().Substring(nupkgDirectory.Length).Replace("\\", "/");
if (pattern[0] == '/')
{
pattern = pattern.Substring(1);
}
Assert.That(pattern, Is.EqualTo(expectedGlobPattern));
Assert.That(packages.Count, Is.EqualTo(expectedFilesCount));
}

[Test]
public void GetFilesByGlobPattern_Is_Dot_Directory()
{
using var tmpDirectory = new DisposableDirectory(Path.Combine(Directory.GetCurrentDirectory(), Guid.NewGuid().ToString("N")));

var nupkgAbsoluteFilename = Path.Combine(tmpDirectory, "test.nupkg");
var snupkgAbsoluteFilename = Path.Combine(tmpDirectory, "test.snupkg");
File.WriteAllText(nupkgAbsoluteFilename, string.Empty);
File.WriteAllText(snupkgAbsoluteFilename, string.Empty);

var packages = tmpDirectory.WorkingDirectory.GetFilesByGlobPattern(".", out var glob).ToList();

var globPattern = glob.ToString().Substring(tmpDirectory.WorkingDirectory.Length).Replace("\\", "/");
if (globPattern[0] == '/')
{
globPattern = globPattern.Substring(1);
}

Assert.That(globPattern, Is.EqualTo(globPattern));
Assert.That(packages.Count, Is.EqualTo(2));
Assert.That(packages[0], Is.EqualTo(nupkgAbsoluteFilename));
Assert.That(packages[1], Is.EqualTo(snupkgAbsoluteFilename));
}

[Test]
public void GetFilesByGlobPattern_Is_Relative_Filename()
{
using var tmpDirectory = new DisposableDirectory(Path.Combine(Directory.GetCurrentDirectory(), Guid.NewGuid().ToString("N")));

var nupkgAbsoluteFilename = Path.Combine(tmpDirectory, "test.nupkg");
var snupkgAbsoluteFilename = Path.Combine(tmpDirectory, "test.snupkg");

File.WriteAllText(nupkgAbsoluteFilename, string.Empty);
File.WriteAllText(snupkgAbsoluteFilename, string.Empty);

var packages = tmpDirectory.WorkingDirectory.GetFilesByGlobPattern("test.nupkg", out var glob).ToList();

var globPattern = glob.ToString().Substring(tmpDirectory.WorkingDirectory.Length).Replace("\\", "/");
if (globPattern[0] == '/')
{
globPattern = globPattern.Substring(1);
}

Assert.That(globPattern, Is.EqualTo("test.nupkg"));
Assert.That(packages.Count, Is.EqualTo(1));
Assert.That(packages[0], Is.EqualTo(nupkgAbsoluteFilename));
}

[Test]
public void GetFilesByGlobPattern_Is_FullPath_Filename()
{
using var tmpDirectory = new DisposableDirectory(Path.Combine(Directory.GetCurrentDirectory(), Guid.NewGuid().ToString("N")));

var nupkgAbsoluteFilename = Path.Combine(tmpDirectory, "test.nupkg");
var snupkgAbsoluteFilename = Path.Combine(tmpDirectory, "test.snupkg");

File.WriteAllText(nupkgAbsoluteFilename, string.Empty);
File.WriteAllText(snupkgAbsoluteFilename, string.Empty);

var packages = tmpDirectory.WorkingDirectory.GetFilesByGlobPattern(nupkgAbsoluteFilename, out var glob).ToList();

var globPattern = glob.ToString().Substring(tmpDirectory.WorkingDirectory.Length).Replace("\\", "/");
if (globPattern[0] == '/')
{
globPattern = globPattern.Substring(1);
}

Assert.That(globPattern, Is.EqualTo("test.nupkg"));
Assert.That(packages.Count, Is.EqualTo(1));
Assert.That(packages[0], Is.EqualTo(nupkgAbsoluteFilename));
}
}
}

0 comments on commit 04fd2b4

Please sign in to comment.