Merge pull request #2767 from dotnet/prkrishn/filesystemglobber

Copy FileSystemGlobbing and HashCodeCombiner sources
This commit is contained in:
Pranav K 2016-04-29 19:57:47 -07:00
commit 9e768d9cad
75 changed files with 3999 additions and 43 deletions

View file

@ -0,0 +1,100 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.IO;
using System.Linq;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.TestUtility;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests
{
public class FileAbstractionsTests
{
[Fact]
public void TempFolderStartsInitiallyEmpty()
{
using (var scenario = new DisposableFileSystem())
{
var contents = scenario.DirectoryInfo.EnumerateFileSystemInfos();
Assert.Equal(Path.GetFileName(scenario.RootPath), scenario.DirectoryInfo.Name);
Assert.Equal(scenario.RootPath, scenario.DirectoryInfo.FullName);
Assert.Equal(0, contents.Count());
}
}
[Fact]
public void FilesAreEnumerated()
{
using (var scenario = new DisposableFileSystem()
.CreateFile("alpha.txt"))
{
var contents = new DirectoryInfoWrapper(scenario.DirectoryInfo).EnumerateFileSystemInfos();
var alphaTxt = contents.OfType<FileInfoBase>().Single();
Assert.Equal(1, contents.Count());
Assert.Equal("alpha.txt", alphaTxt.Name);
}
}
[Fact]
public void FoldersAreEnumerated()
{
using (var scenario = new DisposableFileSystem()
.CreateFolder("beta"))
{
var contents1 = new DirectoryInfoWrapper(scenario.DirectoryInfo).EnumerateFileSystemInfos();
var beta = contents1.OfType<DirectoryInfoBase>().Single();
var contents2 = beta.EnumerateFileSystemInfos();
Assert.Equal(1, contents1.Count());
Assert.Equal("beta", beta.Name);
Assert.Equal(0, contents2.Count());
}
}
[Fact]
public void SubFoldersAreEnumerated()
{
using (var scenario = new DisposableFileSystem()
.CreateFolder("beta")
.CreateFile(Path.Combine("beta", "alpha.txt")))
{
var contents1 = new DirectoryInfoWrapper(scenario.DirectoryInfo).EnumerateFileSystemInfos();
var beta = contents1.OfType<DirectoryInfoBase>().Single();
var contents2 = beta.EnumerateFileSystemInfos();
var alphaTxt = contents2.OfType<FileInfoBase>().Single();
Assert.Equal(1, contents1.Count());
Assert.Equal("beta", beta.Name);
Assert.Equal(1, contents2.Count());
Assert.Equal("alpha.txt", alphaTxt.Name);
}
}
[Fact]
public void GetDirectoryCanTakeDotDot()
{
using (var scenario = new DisposableFileSystem()
.CreateFolder("gamma")
.CreateFolder("beta")
.CreateFile(Path.Combine("beta", "alpha.txt")))
{
var directoryInfoBase = new DirectoryInfoWrapper(scenario.DirectoryInfo);
var gamma = directoryInfoBase.GetDirectory("gamma");
var dotdot = gamma.GetDirectory("..");
var contents1 = dotdot.EnumerateFileSystemInfos();
var beta = dotdot.GetDirectory("beta");
var contents2 = beta.EnumerateFileSystemInfos();
var alphaTxt = contents2.OfType<FileInfoBase>().Single();
Assert.Equal("..", dotdot.Name);
Assert.Equal(2, contents1.Count());
Assert.Equal("beta", beta.Name);
Assert.Equal(1, contents2.Count());
Assert.Equal("alpha.txt", alphaTxt.Name);
}
}
}
}

View file

@ -0,0 +1,469 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
using System.Linq;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.TestUtility;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests
{
public class FunctionalTests : IDisposable
{
private readonly DisposableFileSystem _context;
public FunctionalTests()
{
_context = CreateContext();
}
public void Dispose()
{
_context.Dispose();
}
[Theory]
[InlineData("sub/source2.cs", "sub/source2.cs")]
[InlineData("sub\\source2.cs", "sub\\source2.cs")]
[InlineData("sub/source2.cs", "sub\\source2.cs")]
public void DuplicatePatterns(string pattern1, string pattern2)
{
var matcher = new Matcher();
matcher.AddInclude(pattern1);
matcher.AddInclude(pattern2);
ExecuteAndVerify(matcher, @"src/project",
"src/project/sub/source2.cs");
}
[Theory]
[InlineData("src/project", "source1.cs", new string[] { "source1.cs" })]
[InlineData("src/project", "Source1.cs", new string[] { })]
[InlineData("src/project", "compiler/preprocess/**/*.cs", new string[] { "compiler/preprocess/preprocess-source1.cs",
"compiler/preprocess/sub/preprocess-source2.cs",
"compiler/preprocess/sub/sub/preprocess-source3.cs" })]
[InlineData("src/project", "compiler/Preprocess/**.cs", new string[] { })]
public void IncludeCaseSensitive(string root, string includePattern, string[] expectedFiles)
{
var matcher = new Matcher(StringComparison.Ordinal);
matcher.AddInclude(includePattern);
ExecuteAndVerify(matcher, root, expectedFiles.Select(f => root + "/" + f).ToArray());
}
[Theory]
[InlineData("src/project", "source1.cs", new string[] { "source1.cs" })]
[InlineData("src/project", "Source1.cs", new string[] { "Source1.cs" })]
[InlineData("src/project", "compiler/preprocess/**/*.cs", new string[] { "compiler/preprocess/preprocess-source1.cs",
"compiler/preprocess/sub/preprocess-source2.cs",
"compiler/preprocess/sub/sub/preprocess-source3.cs" })]
[InlineData("src/project", "compiler/Preprocess/**.cs", new string[] { "compiler/Preprocess/preprocess-source1.cs",
"compiler/Preprocess/sub/preprocess-source2.cs",
"compiler/Preprocess/sub/sub/preprocess-source3.cs" })]
public void IncludeCaseInsensitive(string root, string includePattern, string[] expectedFiles)
{
var matcher = new Matcher(StringComparison.OrdinalIgnoreCase);
matcher.AddInclude(includePattern);
ExecuteAndVerify(matcher, root, expectedFiles.Select(f => root + "/" + f).ToArray());
}
[Theory]
[InlineData("src/project/compiler/preprocess/", "source.cs", new string[] { "preprocess-source1.cs",
"sub/preprocess-source2.cs",
"sub/sub/preprocess-source3.cs",
"sub/sub/preprocess-source3.txt" })]
[InlineData("src/project/compiler/preprocess/", "preprocess-source1.cs", new string[] {
"sub/preprocess-source2.cs",
"sub/sub/preprocess-source3.cs",
"sub/sub/preprocess-source3.txt" })]
[InlineData("src/project/compiler/preprocess/", "preprocesS-source1.cs", new string[] {
"preprocess-source1.cs",
"sub/preprocess-source2.cs",
"sub/sub/preprocess-source3.cs",
"sub/sub/preprocess-source3.txt" })]
[InlineData("src/project/compiler/preprocess/", "**/Preprocess*", new string[] { "preprocess-source1.cs",
"sub/preprocess-source2.cs",
"sub/sub/preprocess-source3.cs",
"sub/sub/preprocess-source3.txt" })]
[InlineData("src/project/compiler/preprocess/", "**/preprocess*", new string[] { })]
[InlineData("src/project/compiler/preprocess/", "**/*source*.cs", new string[] { "sub/sub/preprocess-source3.txt" })]
[InlineData("src/project/compiler/preprocess/", "**/*Source*.cs", new string[] {
"preprocess-source1.cs",
"sub/preprocess-source2.cs",
"sub/sub/preprocess-source3.cs",
"sub/sub/preprocess-source3.txt" })]
[InlineData("src/project/compiler/preprocess/", "sub/sub/*", new string[] { "preprocess-source1.cs",
"sub/preprocess-source2.cs" })]
[InlineData("src/project/compiler/preprocess/", "sub/Sub/*", new string[] { "preprocess-source1.cs",
"sub/preprocess-source2.cs",
"sub/sub/preprocess-source3.cs",
"sub/sub/preprocess-source3.txt" })]
public void ExcludeCaseSensitive(string root, string excludePattern, string[] expectedFiles)
{
var matcher = new Matcher(StringComparison.Ordinal);
matcher.AddInclude("**/*.*");
matcher.AddExclude(excludePattern);
ExecuteAndVerify(matcher, root, expectedFiles.Select(f => root + "/" + f).ToArray());
}
[Theory]
[InlineData("src/project/compiler/preprocess/", "source.cs", new string[] { "preprocess-source1.cs",
"sub/preprocess-source2.cs",
"sub/sub/preprocess-source3.cs",
"sub/sub/preprocess-source3.txt" })]
[InlineData("src/project/compiler/preprocess/", "preprocess-source1.cs", new string[] {
"sub/preprocess-source2.cs",
"sub/sub/preprocess-source3.cs",
"sub/sub/preprocess-source3.txt" })]
[InlineData("src/project/compiler/preprocess/", "preprocesS-source1.cs", new string[] {
"sub/preprocess-source2.cs",
"sub/sub/preprocess-source3.cs",
"sub/sub/preprocess-source3.txt" })]
[InlineData("src/project/compiler/preprocess/", "**/Preprocess*", new string[] { })]
[InlineData("src/project/compiler/preprocess/", "**/preprocess*", new string[] { })]
[InlineData("src/project/compiler/preprocess/", "**/*source*.cs", new string[] { "sub/sub/preprocess-source3.txt" })]
[InlineData("src/project/compiler/preprocess/", "**/*Source*.cs", new string[] { "sub/sub/preprocess-source3.txt" })]
[InlineData("src/project/compiler/preprocess/", "sub/sub/*", new string[] { "preprocess-source1.cs",
"sub/preprocess-source2.cs" })]
[InlineData("src/project/compiler/preprocess/", "sub/Sub/*", new string[] { "preprocess-source1.cs",
"sub/preprocess-source2.cs" })]
public void ExcludeCaseInsensitive(string root, string excludePattern, string[] expectedFiles)
{
var matcher = new Matcher(StringComparison.OrdinalIgnoreCase);
matcher.AddInclude("**/*.*");
matcher.AddExclude(excludePattern);
ExecuteAndVerify(matcher, root, expectedFiles.Select(f => root + "/" + f).ToArray());
}
[Fact]
public void RecursiveAndDoubleParentsWithRecursiveSearch()
{
var matcher = new Matcher();
matcher.AddInclude("**/*.cs")
.AddInclude(@"../../lib/**/*.cs");
ExecuteAndVerify(matcher, @"src/project",
"src/project/source1.cs",
"src/project/sub/source2.cs",
"src/project/sub/source3.cs",
"src/project/sub2/source4.cs",
"src/project/sub2/source5.cs",
"src/project/compiler/preprocess/preprocess-source1.cs",
"src/project/compiler/preprocess/sub/preprocess-source2.cs",
"src/project/compiler/preprocess/sub/sub/preprocess-source3.cs",
"src/project/compiler/shared/shared1.cs",
"src/project/compiler/shared/sub/shared2.cs",
"src/project/compiler/shared/sub/sub/sharedsub.cs",
"lib/source6.cs",
"lib/sub3/source7.cs",
"lib/sub4/source8.cs");
}
[Fact]
public void RecursiveAndDoubleParentsSearch()
{
var matcher = new Matcher();
matcher.AddInclude("**/*.cs")
.AddInclude(@"../../lib/*.cs");
ExecuteAndVerify(matcher, @"src/project",
"src/project/source1.cs",
"src/project/sub/source2.cs",
"src/project/sub/source3.cs",
"src/project/sub2/source4.cs",
"src/project/sub2/source5.cs",
"src/project/compiler/preprocess/preprocess-source1.cs",
"src/project/compiler/preprocess/sub/preprocess-source2.cs",
"src/project/compiler/preprocess/sub/sub/preprocess-source3.cs",
"src/project/compiler/shared/shared1.cs",
"src/project/compiler/shared/sub/shared2.cs",
"src/project/compiler/shared/sub/sub/sharedsub.cs",
"lib/source6.cs");
}
[Fact]
public void WildcardAndDoubleParentWithRecursiveSearch()
{
var matcher = new Matcher();
matcher.AddInclude(@"..\..\lib\**\*.cs");
matcher.AddInclude(@"*.cs");
ExecuteAndVerify(matcher, @"src/project",
"src/project/source1.cs",
"lib/source6.cs",
"lib/sub3/source7.cs",
"lib/sub4/source8.cs");
}
[Fact]
public void WildcardAndDoubleParentsSearch()
{
var matcher = new Matcher();
matcher.AddInclude(@"..\..\lib\*.cs");
matcher.AddInclude(@"*.cs");
ExecuteAndVerify(matcher, @"src/project",
"src/project/source1.cs",
"lib/source6.cs");
}
[Fact]
public void DoubleParentsWithRecursiveSearch()
{
var matcher = new Matcher();
matcher.AddInclude(@"..\..\lib\**\*.cs");
ExecuteAndVerify(matcher, @"src/project",
"lib/source6.cs",
"lib/sub3/source7.cs",
"lib/sub4/source8.cs");
}
[Fact]
public void OneLevelParentAndRecursiveSearch()
{
var matcher = new Matcher();
matcher.AddInclude(@"../project2/**/*.cs");
ExecuteAndVerify(matcher, @"src/project",
"src/project2/source1.cs",
"src/project2/sub/source2.cs",
"src/project2/sub/source3.cs",
"src/project2/sub2/source4.cs",
"src/project2/sub2/source5.cs",
"src/project2/compiler/preprocess/preprocess-source1.cs",
"src/project2/compiler/preprocess/sub/preprocess-source2.cs",
"src/project2/compiler/preprocess/sub/sub/preprocess-source3.cs",
"src/project2/compiler/shared/shared1.cs",
"src/project2/compiler/shared/sub/shared2.cs",
"src/project2/compiler/shared/sub/sub/sharedsub.cs");
}
[Fact]
public void RecursiveSuffixSearch()
{
var matcher = new Matcher();
matcher.AddInclude(@"**.txt");
ExecuteAndVerify(matcher, @"src/project",
"src/project/compiler/preprocess/sub/sub/preprocess-source3.txt",
"src/project/compiler/shared/shared1.txt",
"src/project/compiler/shared/sub/shared2.txt",
"src/project/content1.txt");
}
[Fact]
public void FolderExclude()
{
var matcher = new Matcher();
matcher.AddInclude(@"**/*.*");
matcher.AddExclude(@"obj");
matcher.AddExclude(@"bin");
matcher.AddExclude(@".*");
ExecuteAndVerify(matcher, @"src/project",
"src/project/source1.cs",
"src/project/sub/source2.cs",
"src/project/sub/source3.cs",
"src/project/sub2/source4.cs",
"src/project/sub2/source5.cs",
"src/project/compiler/preprocess/preprocess-source1.cs",
"src/project/compiler/preprocess/sub/preprocess-source2.cs",
"src/project/compiler/preprocess/sub/sub/preprocess-source3.cs",
"src/project/compiler/preprocess/sub/sub/preprocess-source3.txt",
"src/project/compiler/shared/shared1.cs",
"src/project/compiler/shared/shared1.txt",
"src/project/compiler/shared/sub/shared2.cs",
"src/project/compiler/shared/sub/shared2.txt",
"src/project/compiler/shared/sub/sub/sharedsub.cs",
"src/project/compiler/resources/resource.res",
"src/project/compiler/resources/sub/resource2.res",
"src/project/compiler/resources/sub/sub/resource3.res",
"src/project/content1.txt");
}
[Fact]
public void FolderInclude()
{
var matcher = new Matcher();
matcher.AddInclude(@"compiler/");
ExecuteAndVerify(matcher, @"src/project",
"src/project/compiler/preprocess/preprocess-source1.cs",
"src/project/compiler/preprocess/sub/preprocess-source2.cs",
"src/project/compiler/preprocess/sub/sub/preprocess-source3.cs",
"src/project/compiler/preprocess/sub/sub/preprocess-source3.txt",
"src/project/compiler/shared/shared1.cs",
"src/project/compiler/shared/shared1.txt",
"src/project/compiler/shared/sub/shared2.cs",
"src/project/compiler/shared/sub/shared2.txt",
"src/project/compiler/shared/sub/sub/sharedsub.cs",
"src/project/compiler/resources/resource.res",
"src/project/compiler/resources/sub/resource2.res",
"src/project/compiler/resources/sub/sub/resource3.res");
}
[Theory]
[InlineData("source1.cs", "src/project/source1.cs")]
[InlineData("../project2/source1.cs", "src/project2/source1.cs")]
public void SingleFile(string pattern, string expect)
{
var matcher = new Matcher();
matcher.AddInclude(pattern);
ExecuteAndVerify(matcher, "src/project", expect);
}
[Fact]
public void SingleFileAndRecursive()
{
var matcher = new Matcher();
matcher.AddInclude("**/*.cs");
matcher.AddInclude("../project2/source1.cs");
ExecuteAndVerify(matcher, "src/project",
"src/project/source1.cs",
"src/project/sub/source2.cs",
"src/project/sub/source3.cs",
"src/project/sub2/source4.cs",
"src/project/sub2/source5.cs",
"src/project/compiler/preprocess/preprocess-source1.cs",
"src/project/compiler/preprocess/sub/preprocess-source2.cs",
"src/project/compiler/preprocess/sub/sub/preprocess-source3.cs",
"src/project/compiler/shared/shared1.cs",
"src/project/compiler/shared/sub/shared2.cs",
"src/project/compiler/shared/sub/sub/sharedsub.cs",
"src/project2/source1.cs");
}
[Fact]
public void StemCorrectWithDifferentWildCards()
{
var matcher = new Matcher();
matcher.AddInclude("sub/*.cs");
matcher.AddInclude("**/*.cs");
var directoryPath = Path.Combine(_context.RootPath, "src/project");
var results = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(directoryPath)));
var actual = results.Files.Select(match => match.Stem);
var expected = new string[] {
"source1.cs",
"source2.cs",
"source3.cs",
"sub2/source4.cs",
"sub2/source5.cs",
"compiler/preprocess/preprocess-source1.cs",
"compiler/preprocess/sub/preprocess-source2.cs",
"compiler/preprocess/sub/sub/preprocess-source3.cs",
"compiler/shared/shared1.cs",
"compiler/shared/sub/shared2.cs",
"compiler/shared/sub/sub/sharedsub.cs"
};
Assert.Equal(
expected.OrderBy(e => e),
actual.OrderBy(e => e),
StringComparer.OrdinalIgnoreCase);
}
[Fact]
public void MultipleSubDirsAfterFirstWildcardMatch_HasCorrectStem()
{
var matcher = new Matcher();
matcher.AddInclude("compiler/**/*.cs");
var directoryPath = Path.Combine(_context.RootPath, "src/project");
var results = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(directoryPath)));
var actual = results.Files.Select(match => match.Stem);
var expected = new string[] {
"preprocess/preprocess-source1.cs",
"preprocess/sub/preprocess-source2.cs",
"preprocess/sub/sub/preprocess-source3.cs",
"shared/shared1.cs",
"shared/sub/shared2.cs",
"shared/sub/sub/sharedsub.cs"
};
Assert.Equal(
expected.OrderBy(e => e),
actual.OrderBy(e => e),
StringComparer.OrdinalIgnoreCase);
}
private DisposableFileSystem CreateContext()
{
var context = new DisposableFileSystem();
context.CreateFiles(
"src/project/source1.cs",
"src/project/sub/source2.cs",
"src/project/sub/source3.cs",
"src/project/sub2/source4.cs",
"src/project/sub2/source5.cs",
"src/project/compiler/preprocess/preprocess-source1.cs",
"src/project/compiler/preprocess/sub/preprocess-source2.cs",
"src/project/compiler/preprocess/sub/sub/preprocess-source3.cs",
"src/project/compiler/preprocess/sub/sub/preprocess-source3.txt",
"src/project/compiler/shared/shared1.cs",
"src/project/compiler/shared/shared1.txt",
"src/project/compiler/shared/sub/shared2.cs",
"src/project/compiler/shared/sub/shared2.txt",
"src/project/compiler/shared/sub/sub/sharedsub.cs",
"src/project/compiler/resources/resource.res",
"src/project/compiler/resources/sub/resource2.res",
"src/project/compiler/resources/sub/sub/resource3.res",
"src/project/content1.txt",
"src/project/obj/object.o",
"src/project/bin/object",
"src/project/.hidden/file1.hid",
"src/project/.hidden/sub/file2.hid",
"src/project2/source1.cs",
"src/project2/sub/source2.cs",
"src/project2/sub/source3.cs",
"src/project2/sub2/source4.cs",
"src/project2/sub2/source5.cs",
"src/project2/compiler/preprocess/preprocess-source1.cs",
"src/project2/compiler/preprocess/sub/preprocess-source2.cs",
"src/project2/compiler/preprocess/sub/sub/preprocess-source3.cs",
"src/project2/compiler/preprocess/sub/sub/preprocess-source3.txt",
"src/project2/compiler/shared/shared1.cs",
"src/project2/compiler/shared/shared1.txt",
"src/project2/compiler/shared/sub/shared2.cs",
"src/project2/compiler/shared/sub/shared2.txt",
"src/project2/compiler/shared/sub/sub/sharedsub.cs",
"src/project2/compiler/resources/resource.res",
"src/project2/compiler/resources/sub/resource2.res",
"src/project2/compiler/resources/sub/sub/resource3.res",
"src/project2/content1.txt",
"src/project2/obj/object.o",
"src/project2/bin/object",
"lib/source6.cs",
"lib/sub3/source7.cs",
"lib/sub4/source8.cs",
"res/resource1.text",
"res/resource2.text",
"res/resource3.text",
".hidden/file1.hid",
".hidden/sub/file2.hid");
return context;
}
private void ExecuteAndVerify(Matcher matcher, string directoryPath, params string[] expectFiles)
{
directoryPath = Path.Combine(_context.RootPath, directoryPath);
var results = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(directoryPath)));
var actual = results.Files.Select(match => Path.GetFullPath(Path.Combine(_context.RootPath, directoryPath, match.Path)));
var expected = expectFiles.Select(relativePath => Path.GetFullPath(Path.Combine(_context.RootPath, relativePath)));
Assert.Equal(
expected.OrderBy(e => e),
actual.OrderBy(e => e),
StringComparer.OrdinalIgnoreCase);
}
}
}

View file

@ -0,0 +1,168 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PatternContexts;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.TestUtility;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.PatternContexts
{
public class PatternContextLinearIncludeTests
{
[Fact]
public void PredictBeforeEnterDirectoryShouldThrow()
{
var pattern = MockLinearPatternBuilder.New().Add("a").Build();
var context = new PatternContextLinearInclude(pattern);
Assert.Throws<InvalidOperationException>(() =>
{
context.Declare((segment, last) =>
{
Assert.False(true, "No segment should be declared.");
});
});
}
[Theory]
[InlineData(new string[] { "a", "b" }, new string[] { "root" }, "a", false)]
[InlineData(new string[] { "a", "b" }, new string[] { "root", "a" }, "b", true)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root" }, "a", false)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a" }, "b", false)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "b" }, "c", true)]
public void PredictReturnsCorrectResult(string[] testSegments, string[] pushDirectory, string expectSegment, bool expectLast)
{
var pattern = MockLinearPatternBuilder.New().Add(testSegments).Build();
var context = new PatternContextLinearInclude(pattern);
PatternContextHelper.PushDirectory(context, pushDirectory);
context.Declare((segment, last) =>
{
var literal = segment as MockNonRecursivePathSegment;
Assert.NotNull(segment);
Assert.Equal(expectSegment, literal.Value);
Assert.Equal(expectLast, last);
});
}
[Theory]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "b" })]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "c" })]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "b", "d" })]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "b", "c" })]
public void PredictNotCallBackWhenEnterUnmatchDirectory(string[] testSegments, string[] pushDirectory)
{
var pattern = MockLinearPatternBuilder.New().Add(testSegments).Build();
var context = new PatternContextLinearInclude(pattern);
PatternContextHelper.PushDirectory(context, pushDirectory);
context.Declare((segment, last) =>
{
Assert.False(true, "No segment should be declared.");
});
}
[Theory]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", }, "b", false)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "b" }, "d", false)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "b" }, "c", true)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "b", "c" }, "d", false)]
public void TestFileForIncludeReturnsCorrectResult(string[] testSegments, string[] pushDirectory, string filename, bool expectResult)
{
var pattern = MockLinearPatternBuilder.New().Add(testSegments).Build();
var context = new PatternContextLinearInclude(pattern);
PatternContextHelper.PushDirectory(context, pushDirectory);
var result = context.Test(new FakeFileInfo(filename));
Assert.Equal(expectResult, result.IsSuccessful);
}
[Theory]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", }, "b", false)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "b" }, "c", true)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "b" }, "d", false)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "b", "c" }, "d", false)]
public void TestFileForExcludeReturnsCorrectResult(string[] testSegments, string[] pushDirectory, string filename, bool expectResult)
{
var pattern = MockLinearPatternBuilder.New().Add(testSegments).Build();
var context = new PatternContextLinearExclude(pattern);
PatternContextHelper.PushDirectory(context, pushDirectory);
var result = context.Test(new FakeFileInfo(filename));
Assert.Equal(expectResult, result.IsSuccessful);
}
[Theory]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root" }, "a", true)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a" }, "b", true)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a" }, "c", false)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "b" }, "c", false)]
public void TestDirectoryForIncludeReturnsCorrectResult(string[] testSegments, string[] pushDirectory, string directoryName, bool expectResult)
{
var pattern = MockLinearPatternBuilder.New().Add(testSegments).Build();
var context = new PatternContextLinearInclude(pattern);
PatternContextHelper.PushDirectory(context, pushDirectory);
var result = context.Test(new FakeDirectoryInfo(directoryName));
Assert.Equal(expectResult, result);
}
[Theory]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root" }, "a", false)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a" }, "b", false)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a" }, "c", false)]
[InlineData(new string[] { "a", "b", "c" }, new string[] { "root", "a", "b" }, "c", true)]
public void TestDirectoryForExcludeReturnsCorrectResult(string[] testSegments, string[] pushDirectory, string directoryName, bool expectResult)
{
var pattern = MockLinearPatternBuilder.New().Add(testSegments).Build();
var context = new PatternContextLinearExclude(pattern);
PatternContextHelper.PushDirectory(context, pushDirectory);
var result = context.Test(new FakeDirectoryInfo(directoryName));
Assert.Equal(expectResult, result);
}
private class FakeDirectoryInfo : DirectoryInfoBase
{
public FakeDirectoryInfo(string name)
{
Name = name;
}
public override string FullName { get { throw new NotImplementedException(); } }
public override string Name { get; }
public override DirectoryInfoBase ParentDirectory { get { throw new NotImplementedException(); } }
public override IEnumerable<FileSystemInfoBase> EnumerateFileSystemInfos() { throw new NotImplementedException(); }
public override DirectoryInfoBase GetDirectory(string path) { throw new NotImplementedException(); }
public override FileInfoBase GetFile(string path) { throw new NotImplementedException(); }
}
private class FakeFileInfo : FileInfoBase
{
public FakeFileInfo(string name)
{
Name = name;
}
public override string FullName { get { throw new NotImplementedException(); } }
public override string Name { get; }
public override DirectoryInfoBase ParentDirectory { get { throw new NotImplementedException(); } }
}
}
}

View file

@ -0,0 +1,80 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PatternContexts;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.Patterns;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.TestUtility;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.PatternContexts
{
public class PatternContextRaggedIncludeTests
{
[Fact]
public void PredictBeforeEnterDirectoryShouldThrow()
{
var builder = new PatternBuilder();
var pattern = builder.Build("**") as IRaggedPattern;
var context = new PatternContextRaggedInclude(pattern);
Assert.Throws<InvalidOperationException>(() =>
{
context.Declare((segment, last) =>
{
Assert.False(true, "No segment should be declared.");
});
});
}
[Theory]
[InlineData("/a/b/**/c/d", new string[] { "root" }, "a", false)]
[InlineData("/a/b/**/c/d", new string[] { "root", "a" }, "b", false)]
[InlineData("/a/b/**/c/d", new string[] { "root", "a", "b" }, null, false)]
[InlineData("/a/b/**/c/d", new string[] { "root", "a", "b", "whatever" }, null, false)]
[InlineData("/a/b/**/c/d", new string[] { "root", "a", "b", "whatever", "anything" }, null, false)]
public void PredictReturnsCorrectResult(string patternString, string[] pushDirectory, string expectSegment, bool expectWildcard)
{
var builder = new PatternBuilder();
var pattern = builder.Build(patternString) as IRaggedPattern;
Assert.NotNull(pattern);
var context = new PatternContextRaggedInclude(pattern);
PatternContextHelper.PushDirectory(context, pushDirectory);
context.Declare((segment, last) =>
{
if (expectSegment != null)
{
var mockSegment = segment as LiteralPathSegment;
Assert.NotNull(mockSegment);
Assert.Equal(false, last);
Assert.Equal(expectSegment, mockSegment.Value);
}
else
{
Assert.Equal(Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments.WildcardPathSegment.MatchAll, segment);
}
});
}
[Theory]
[InlineData("/a/b/**/c/d", new string[] { "root", "b" })]
[InlineData("/a/b/**/c/d", new string[] { "root", "a", "c" })]
public void PredictNotCallBackWhenEnterUnmatchDirectory(string patternString, string[] pushDirectory)
{
var builder = new PatternBuilder();
var pattern = builder.Build(patternString) as IRaggedPattern;
var context = new PatternContextRaggedInclude(pattern);
PatternContextHelper.PushDirectory(context, pushDirectory);
context.Declare((segment, last) =>
{
Assert.False(true, "No segment should be declared.");
});
}
}
}

View file

@ -0,0 +1,472 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Linq;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.TestUtility;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests
{
public class PatternMatchingTests
{
[Fact]
public void EmptyCollectionWhenNoFilesPresent()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("alpha.txt")
.Execute();
scenario.AssertExact();
}
[Fact]
public void MatchingFileIsFound()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("alpha.txt")
.Files("alpha.txt")
.Execute();
scenario.AssertExact("alpha.txt");
}
[Fact]
public void MismatchedFileIsIgnored()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("alpha.txt")
.Files("omega.txt")
.Execute();
scenario.AssertExact();
}
[Fact]
public void FolderNamesAreTraversed()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("beta/alpha.txt")
.Files("beta/alpha.txt")
.Execute();
scenario.AssertExact("beta/alpha.txt");
}
[Theory]
[InlineData(@"beta/alpha.txt", @"beta/alpha.txt")]
[InlineData(@"beta\alpha.txt", @"beta/alpha.txt")]
[InlineData(@"beta/alpha.txt", @"beta\alpha.txt")]
[InlineData(@"beta\alpha.txt", @"beta\alpha.txt")]
[InlineData(@"\beta\alpha.txt", @"beta\alpha.txt")]
public void SlashPolarityIsIgnored(string includePattern, string filePath)
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include(includePattern)
.Files("one/two.txt", filePath, "three/four.txt")
.Execute();
scenario.AssertExact("beta/alpha.txt");
}
[Theory]
[InlineData(@"*.txt", new[] { "alpha.txt", "beta.txt" })]
[InlineData(@"alpha.*", new[] { "alpha.txt" })]
[InlineData(@"*.*", new[] { "alpha.txt", "beta.txt", "gamma.dat" })]
[InlineData(@"*", new[] { "alpha.txt", "beta.txt", "gamma.dat" })]
[InlineData(@"*et*", new[] { "beta.txt" })]
[InlineData(@"b*et*t", new[] { "beta.txt" })]
[InlineData(@"b*et*x", new string[0])]
public void PatternMatchingWorks(string includePattern, string[] matchesExpected)
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include(includePattern)
.Files("alpha.txt", "beta.txt", "gamma.dat")
.Execute();
scenario.AssertExact(matchesExpected);
}
[Theory]
[InlineData(@"1234*5678", new[] { "12345678" })]
[InlineData(@"12345*5678", new string[0])]
[InlineData(@"12*3456*78", new[] { "12345678" })]
[InlineData(@"12*23*", new string[0])]
[InlineData(@"*67*78", new string[0])]
[InlineData(@"*45*56", new string[0])]
public void PatternBeginAndEndCantOverlap(string includePattern, string[] matchesExpected)
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include(includePattern)
.Files("12345678")
.Execute();
scenario.AssertExact(matchesExpected);
}
[Theory]
[InlineData(@"*mm*/*", new[] { "gamma/hello.txt" })]
[InlineData(@"/*mm*/*", new[] { "gamma/hello.txt" })]
[InlineData(@"*alpha*/*", new[] { "alpha/hello.txt" })]
[InlineData(@"/*alpha*/*", new[] { "alpha/hello.txt" })]
[InlineData(@"*/*", new[] { "alpha/hello.txt", "beta/hello.txt", "gamma/hello.txt" })]
[InlineData(@"/*/*", new[] { "alpha/hello.txt", "beta/hello.txt", "gamma/hello.txt" })]
[InlineData(@"*.*/*", new[] { "alpha/hello.txt", "beta/hello.txt", "gamma/hello.txt" })]
[InlineData(@"/*.*/*", new[] { "alpha/hello.txt", "beta/hello.txt", "gamma/hello.txt" })]
public void PatternMatchingWorksInFolders(string includePattern, string[] matchesExpected)
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include(includePattern)
.Files("alpha/hello.txt", "beta/hello.txt", "gamma/hello.txt")
.Execute();
scenario.AssertExact(matchesExpected);
}
[Theory]
[InlineData(@"", new string[] { })]
[InlineData(@"./", new string[] { "alpha/hello.txt", "beta/hello.txt", "gamma/hello.txt" })]
[InlineData(@"./alpha/hello.txt", new string[] { "alpha/hello.txt" })]
[InlineData(@"./**/hello.txt", new string[] { "alpha/hello.txt", "beta/hello.txt", "gamma/hello.txt" })]
[InlineData(@"././**/hello.txt", new string[] { "alpha/hello.txt", "beta/hello.txt", "gamma/hello.txt" })]
[InlineData(@"././**/./hello.txt", new string[] { "alpha/hello.txt", "beta/hello.txt", "gamma/hello.txt" })]
[InlineData(@"././**/./**/hello.txt", new string[] { "alpha/hello.txt", "beta/hello.txt", "gamma/hello.txt" })]
[InlineData(@"./*mm*/hello.txt", new string[] { "gamma/hello.txt" })]
[InlineData(@"./*mm*/*", new string[] { "gamma/hello.txt" })]
public void PatternMatchingCurrent(string includePattern, string[] matchesExpected)
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include(includePattern)
.Files("alpha/hello.txt", "beta/hello.txt", "gamma/hello.txt")
.Execute();
scenario.AssertExact(matchesExpected);
}
[Fact]
public void StarDotStarIsSameAsStar()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("*.*")
.Files("alpha.txt", "alpha.", ".txt", ".", "alpha", "txt")
.Execute();
scenario.AssertExact("alpha.txt", "alpha.", ".txt", ".", "alpha", "txt");
}
[Fact]
public void IncompletePatternsDoNotInclude()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("*/*.txt")
.Files("one/x.txt", "two/x.txt", "x.txt")
.Execute();
scenario.AssertExact("one/x.txt", "two/x.txt");
}
[Fact]
public void IncompletePatternsDoNotExclude()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("*/*.txt")
.Exclude("one/hello.txt")
.Files("one/x.txt", "two/x.txt")
.Execute();
scenario.AssertExact("one/x.txt", "two/x.txt");
}
[Fact]
public void TrailingRecursiveWildcardMatchesAllFiles()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("one/**")
.Files("one/x.txt", "two/x.txt", "one/x/y.txt")
.Execute();
scenario.AssertExact("one/x.txt", "one/x/y.txt");
}
[Fact]
public void LeadingRecursiveWildcardMatchesAllLeadingPaths()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("**/*.cs")
.Files("one/x.cs", "two/x.cs", "one/two/x.cs", "x.cs")
.Files("one/x.txt", "two/x.txt", "one/two/x.txt", "x.txt")
.Execute();
scenario.AssertExact("one/x.cs", "two/x.cs", "one/two/x.cs", "x.cs");
}
[Fact]
public void InnerRecursiveWildcardMuseStartWithAndEndWith()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("one/**/*.cs")
.Files("one/x.cs", "two/x.cs", "one/two/x.cs", "x.cs")
.Files("one/x.txt", "two/x.txt", "one/two/x.txt", "x.txt")
.Execute();
scenario.AssertExact("one/x.cs", "one/two/x.cs");
}
[Fact]
public void ExcludeMayEndInDirectoryName()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("*.cs", "*/*.cs", "*/*/*.cs")
.Exclude("bin", "one/two")
.Files("one/x.cs", "two/x.cs", "one/two/x.cs", "x.cs", "bin/x.cs", "bin/two/x.cs")
.Execute();
scenario.AssertExact("one/x.cs", "two/x.cs", "x.cs");
}
[Fact]
public void RecursiveWildcardSurroundingContainsWith()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("**/x/**")
.Files("x/1", "1/x/2", "1/x", "x", "1", "1/2")
.Execute();
scenario.AssertExact("x/1", "1/x/2");
}
[Fact]
public void SequentialFoldersMayBeRequired()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("a/b/**/1/2/**/2/3/**")
.Files("1/2/2/3/x", "1/2/3/y", "a/1/2/4/2/3/b", "a/2/3/1/2/b")
.Files("a/b/1/2/2/3/x", "a/b/1/2/3/y", "a/b/a/1/2/4/2/3/b", "a/b/a/2/3/1/2/b")
.Execute();
scenario.AssertExact("a/b/1/2/2/3/x", "a/b/a/1/2/4/2/3/b");
}
[Fact]
public void RecursiveAloneIncludesEverything()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("**")
.Files("1/2/2/3/x", "1/2/3/y")
.Execute();
scenario.AssertExact("1/2/2/3/x", "1/2/3/y");
}
[Fact]
public void ExcludeCanHaveSurroundingRecursiveWildcards()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("**")
.Exclude("**/x/**")
.Files("x/1", "1/x/2", "1/x", "x", "1", "1/2")
.Execute();
scenario.AssertExact("1/x", "x", "1", "1/2");
}
[Fact]
public void LeadingDotDotCanComeThroughPattern()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("*.cs")
.Include("../2/*.cs")
.Files("1/x.cs", "1/x.txt", "2/x.cs", "2/x.txt")
.SubDirectory("1")
.Execute();
scenario.AssertExact("x.cs", "../2/x.cs");
}
[Fact]
public void LeadingDotDotWithRecursiveCanComeThroughPattern()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("*.cs")
.Include("../2/**/*.cs")
.Files("1/x.cs", "1/x.txt", "2/x.cs", "2/x.txt", "2/3/x.cs", "2/3/4/z.cs", "2/3/x.txt")
.SubDirectory("1")
.Execute();
scenario.AssertExact("x.cs", "../2/x.cs", "../2/3/x.cs", "../2/3/4/z.cs");
}
[Fact]
public void ExcludeFolderRecursively()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("*.*")
.Include("../sibling/**/*.*")
.Exclude("../sibling/exc/**/*.*")
.Exclude("../sibling/inc/2.txt")
.Files("main/1.txt", "main/2.txt", "sibling/1.txt", "sibling/inc/1.txt", "sibling/inc/2.txt", "sibling/exc/1.txt", "sibling/exc/2.txt")
.SubDirectory("main")
.Execute();
scenario.AssertExact("1.txt", "2.txt", "../sibling/1.txt", "../sibling/inc/1.txt");
}
[Fact]
public void ExcludeFolderByName()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("*.*")
.Include("../sibling/**/*.*")
.Exclude("../sibling/exc/")
.Exclude("../sibling/inc/2.txt")
.Files("main/1.txt", "main/2.txt", "sibling/1.txt", "sibling/inc/1.txt", "sibling/inc/2.txt", "sibling/exc/1.txt", "sibling/exc/2.txt")
.SubDirectory("main")
.Execute();
scenario.AssertExact("1.txt", "2.txt", "../sibling/1.txt", "../sibling/inc/1.txt");
}
[Fact]
public void MultipleRecursiveWildcardStemMatch()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("sub/**/bar/**/*.txt")
.Files("root.txt", "sub/one.txt", "sub/two.txt", "sub/sub2/bar/baz/three.txt", "sub/sub3/sub4/bar/three.txt")
.Execute();
// Check the stem of the matched items
Assert.Equal(new[] {
new FilePatternMatch(path: "sub/sub2/bar/baz/three.txt", stem: "sub2/bar/baz/three.txt"),
new FilePatternMatch(path: "sub/sub3/sub4/bar/three.txt", stem: "sub3/sub4/bar/three.txt")
}, scenario.Result.Files.ToArray());
}
[Fact]
public void RecursiveWildcardStemMatch()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("sub/**/*.txt")
.Files("root.txt", "sub/one.txt", "sub/two.txt", "sub/sub2/three.txt")
.Execute();
// Check the stem of the matched items
Assert.Equal(new[] {
new FilePatternMatch(path: "sub/one.txt", stem: "one.txt"),
new FilePatternMatch(path: "sub/two.txt", stem: "two.txt"),
new FilePatternMatch(path: "sub/sub2/three.txt", stem: "sub2/three.txt")
}, scenario.Result.Files.ToArray());
}
[Fact]
public void WildcardMidSegmentMatch()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("sub/w*.txt")
.Files("root.txt", "sub/woah.txt", "sub/wow.txt", "sub/blah.txt")
.Execute();
// Check the stem of the matched items
Assert.Equal(new[] {
new FilePatternMatch(path: "sub/woah.txt", stem: "woah.txt"),
new FilePatternMatch(path: "sub/wow.txt", stem: "wow.txt")
}, scenario.Result.Files.ToArray());
}
[Fact]
public void StemMatchOnExactFile()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("sub/sub/three.txt")
.Files("root.txt", "sub/one.txt", "sub/two.txt", "sub/sub/three.txt")
.Execute();
// Check the stem of the matched items
Assert.Equal(new[] {
new FilePatternMatch(path: "sub/sub/three.txt", stem: "three.txt"),
}, scenario.Result.Files.ToArray());
}
[Fact]
public void SimpleStemMatching()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("sub/*")
.Files("root.txt", "sub/one.txt", "sub/two.txt", "sub/sub/three.txt")
.Execute();
// Check the stem of the matched items
Assert.Equal(new[] {
new FilePatternMatch(path: "sub/one.txt", stem: "one.txt"),
new FilePatternMatch(path: "sub/two.txt", stem: "two.txt")
}, scenario.Result.Files.ToArray());
}
[Fact]
public void StemMatchingWithFileExtension()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("sub/*.txt")
.Files("root.txt", "sub/one.txt", "sub/two.txt", "sub/three.dat")
.Execute();
// Check the stem of the matched items
Assert.Equal(new[] {
new FilePatternMatch(path: "sub/one.txt", stem: "one.txt"),
new FilePatternMatch(path: "sub/two.txt", stem: "two.txt")
}, scenario.Result.Files.ToArray());
}
[Fact]
public void StemMatchingWithParentDir()
{
var matcher = new Matcher();
var scenario = new FileSystemGlobbingTestContext(@"c:\files\", matcher)
.Include("../files/sub/*.txt")
.Files("root.txt", "sub/one.txt", "sub/two.txt", "sub/three.dat")
.Execute();
// Check the stem of the matched items
Assert.Equal(new[] {
new FilePatternMatch(path: "../files/sub/one.txt", stem: "one.txt"),
new FilePatternMatch(path: "../files/sub/two.txt", stem: "two.txt")
}, scenario.Result.Files.ToArray());
}
// exclude: **/.*/**
// exclude: node_modules/*
// exclude: **/.cs
}
}

View file

@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.PatternSegments
{
public class CurrentPathSegmentTests
{
[Theory]
[InlineData("anything")]
[InlineData("")]
[InlineData(null)]
public void Match(string testSample)
{
var pathSegment = new CurrentPathSegment();
Assert.False(pathSegment.Match(testSample));
}
}
}

View file

@ -0,0 +1,42 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.PatternSegments
{
public class LiteralPathSegmentTests
{
[Fact]
public void ThrowArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() =>
{
var pathSegment = new LiteralPathSegment(value: null, comparisonType: StringComparison.OrdinalIgnoreCase);
});
}
[Fact]
public void AllowEmptyInDefaultConstructor()
{
var pathSegment = new LiteralPathSegment(string.Empty, comparisonType: StringComparison.Ordinal);
Assert.NotNull(pathSegment);
}
[Theory]
[InlineData("something", "anything", StringComparison.Ordinal, false)]
[InlineData("something", "Something", StringComparison.Ordinal, false)]
[InlineData("something", "something", StringComparison.Ordinal, true)]
[InlineData("something", "anything", StringComparison.OrdinalIgnoreCase, false)]
[InlineData("something", "Something", StringComparison.OrdinalIgnoreCase, true)]
[InlineData("something", "something", StringComparison.OrdinalIgnoreCase, true)]
public void Match(string initialValue, string testSample, StringComparison comparisonType, bool expectation)
{
var pathSegment = new LiteralPathSegment(initialValue, comparisonType);
Assert.Equal(initialValue, pathSegment.Value);
Assert.Equal(expectation, pathSegment.Match(testSample));
}
}
}

View file

@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.PatternSegments
{
public class ParentPathSegmentTests
{
[Theory]
[InlineData(".", false)]
[InlineData("..", true)]
[InlineData("...", false)]
public void Match(string testSample, bool expectation)
{
var pathSegment = new ParentPathSegment();
Assert.Equal(expectation, pathSegment.Match(testSample));
}
}
}

View file

@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.PatternSegments
{
public class RecursiveWildcardSegmentTests
{
[Fact]
public void Match()
{
var pathSegment = new RecursiveWildcardSegment();
Assert.False(pathSegment.Match("Anything"));
}
}
}

View file

@ -0,0 +1,166 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.PatternSegments
{
public class WildcardPathSegmentTests
{
[Theory]
[InlineData(StringComparison.Ordinal)]
[InlineData(StringComparison.OrdinalIgnoreCase)]
public void DefaultConstructor(StringComparison comparisonType)
{
var paramBegin = "begin";
var paramContains = new List<string> { "1", "2", "three" };
var paramEnd = "end";
var segment = new WildcardPathSegment(paramBegin, paramContains, paramEnd, comparisonType);
Assert.Equal(paramBegin, segment.BeginsWith);
Assert.Equal<string>(paramContains, segment.Contains);
Assert.Equal(paramEnd, segment.EndsWith);
}
[Theory]
[MemberData("GetPositiveOrdinalIgnoreCaseDataSample")]
public void PositiveOrdinalIgnoreCaseMatch(string testSample, object segment)
{
var wildcardPathSegment = (WildcardPathSegment)segment;
Assert.True(
wildcardPathSegment.Match(testSample),
string.Format("[TestSample: {0}] [Wildcard: {1}]", testSample, Serialize(wildcardPathSegment)));
}
[Theory]
[MemberData("GetNegativeOrdinalIgnoreCaseDataSample")]
public void NegativeOrdinalIgnoreCaseMatch(string testSample, object segment)
{
var wildcardPathSegment = (WildcardPathSegment)segment;
Assert.False(
wildcardPathSegment.Match(testSample),
string.Format("[TestSample: {0}] [Wildcard: {1}]", testSample, Serialize(wildcardPathSegment)));
}
[Theory]
[MemberData("GetPositiveOrdinalDataSample")]
public void PositiveOrdinalMatch(string testSample, object segment)
{
var wildcardPathSegment = (WildcardPathSegment)segment;
Assert.True(
wildcardPathSegment.Match(testSample),
string.Format("[TestSample: {0}] [Wildcard: {1}]", testSample, Serialize(wildcardPathSegment)));
}
[Theory]
[MemberData("GetNegativeOrdinalDataSample")]
public void NegativeOrdinalMatch(string testSample, object segment)
{
var wildcardPathSegment = (WildcardPathSegment)segment;
Assert.False(
wildcardPathSegment.Match(testSample),
string.Format("[TestSample: {0}] [Wildcard: {1}]", testSample, Serialize(wildcardPathSegment)));
}
public static IEnumerable<object[]> GetPositiveOrdinalIgnoreCaseDataSample()
{
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "abc", "a", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "abBb123c", "a", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "aaac", "a", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "acccc", "a", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "aacc", "a", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "aacc", "aa", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "acc", "ac", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "abcdefgh", "ab", "cd", "ef", "gh");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "abCDEfgh", "ab", "cd", "ef", "gh");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "ab123cd321ef123gh", "ab", "cd", "ef", "gh");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "abcd321ef123gh", "ab", "cd", "ef", "gh");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "ababcd321ef123gh", "ab", "cd", "ef", "gh");
}
public static IEnumerable<object[]> GetNegativeOrdinalIgnoreCaseDataSample()
{
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "aa", "a", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "cc", "a", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "ab", "a", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "ab", "a", "b", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "bc", "a", "b", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "ac", "a", "b", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "abc", "a", "b", "b", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "ab", "ab", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "ab", "abb", "c");
yield return WrapResult(StringComparison.OrdinalIgnoreCase, "ac", "ac", "c");
}
public static IEnumerable<object[]> GetPositiveOrdinalDataSample()
{
yield return WrapResult(StringComparison.Ordinal, "abc", "a", "c");
yield return WrapResult(StringComparison.Ordinal, "abBb123c", "a", "c");
yield return WrapResult(StringComparison.Ordinal, "aaac", "a", "c");
yield return WrapResult(StringComparison.Ordinal, "Aaac", "A", "c");
yield return WrapResult(StringComparison.Ordinal, "acccC", "a", "C");
yield return WrapResult(StringComparison.Ordinal, "aacc", "a", "c");
yield return WrapResult(StringComparison.Ordinal, "aAcc", "aA", "c");
yield return WrapResult(StringComparison.Ordinal, "acc", "ac", "c");
yield return WrapResult(StringComparison.Ordinal, "abcDefgh", "ab", "cD", "ef", "gh");
yield return WrapResult(StringComparison.Ordinal, "aB123cd321ef123gh", "aB", "cd", "ef", "gh");
yield return WrapResult(StringComparison.Ordinal, "abcd321ef123gh", "ab", "cd", "ef", "gh");
yield return WrapResult(StringComparison.Ordinal, "ababcdCD321ef123gh", "ab", "cd", "ef", "gh");
yield return WrapResult(StringComparison.Ordinal, "ababcdCD321ef123gh", "ab", "CD", "ef", "gh");
yield return WrapResult(StringComparison.Ordinal, "ababcd321eF123gh", "ab", "cd", "eF", "gh");
}
public static IEnumerable<object[]> GetNegativeOrdinalDataSample()
{
yield return WrapResult(StringComparison.Ordinal, "aa", "a", "c");
yield return WrapResult(StringComparison.Ordinal, "abc", "A", "c");
yield return WrapResult(StringComparison.Ordinal, "cc", "a", "c");
yield return WrapResult(StringComparison.Ordinal, "ab", "a", "c");
yield return WrapResult(StringComparison.Ordinal, "ab", "a", "b", "c");
yield return WrapResult(StringComparison.Ordinal, "bc", "a", "b", "c");
yield return WrapResult(StringComparison.Ordinal, "ac", "a", "b", "c");
yield return WrapResult(StringComparison.Ordinal, "abc", "a", "b", "b", "c");
yield return WrapResult(StringComparison.Ordinal, "ab", "ab", "c");
yield return WrapResult(StringComparison.Ordinal, "ab", "abb", "c");
yield return WrapResult(StringComparison.Ordinal, "ac", "ac", "c");
yield return WrapResult(StringComparison.Ordinal, "abBb123C", "a", "c");
yield return WrapResult(StringComparison.Ordinal, "Aaac", "a", "c");
yield return WrapResult(StringComparison.Ordinal, "aAac", "A", "c");
yield return WrapResult(StringComparison.Ordinal, "aCc", "a", "C");
yield return WrapResult(StringComparison.Ordinal, "aacc", "aA", "c");
yield return WrapResult(StringComparison.Ordinal, "acc", "aC", "c");
yield return WrapResult(StringComparison.Ordinal, "abcDefgh", "ab", "cd", "ef", "gh");
yield return WrapResult(StringComparison.Ordinal, "aB123cd321ef123gh", "aB", "cd", "EF", "gh");
yield return WrapResult(StringComparison.Ordinal, "abcd321ef123gh", "ab", "cd", "efF", "gh");
yield return WrapResult(StringComparison.Ordinal, "ababcdCD321ef123gh", "AB", "cd", "ef", "gh");
yield return WrapResult(StringComparison.Ordinal, "ababcdCD321ef123gh", "ab", "CD", "EF", "gh");
}
private static object[] WrapResult(StringComparison comparisonType, params string[] values)
{
if (values == null || values.Length < 3)
{
throw new InvalidOperationException("At least three values are required to create a data sample");
}
var beginWith = values[1];
var endWith = values[values.Length - 1];
var contains = values.Skip(2).Take(values.Length - 3);
return new object[] { values[0], new WildcardPathSegment(beginWith, contains.ToList(), endWith, comparisonType) };
}
private static string Serialize(WildcardPathSegment segment)
{
return string.Format("{0}:{1}:{2}",
segment.BeginsWith,
string.Join(",", segment.Contains.ToArray()),
segment.EndsWith);
}
}
}

View file

@ -0,0 +1,139 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.Patterns;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.Patterns
{
public class PatternTests
{
[Theory]
[InlineData("abc", 1)]
[InlineData("/abc", 1)]
[InlineData("abc/efg", 2)]
[InlineData("abc/efg/h*j", 3)]
[InlineData("abc/efg/h*j/*.*", 4)]
[InlineData("abc/efg/hij", 3)]
[InlineData("abc/efg/hij/klm", 4)]
[InlineData("../abc/efg/hij/klm", 5)]
[InlineData("../../abc/efg/hij/klm", 6)]
public void BuildLinearPattern(string sample, int segmentCount)
{
var builder = new PatternBuilder();
var pattern = builder.Build(sample);
Assert.True(pattern is ILinearPattern);
Assert.Equal(segmentCount, (pattern as ILinearPattern).Segments.Count);
}
[Theory]
[InlineData("abc/efg/**")]
[InlineData("/abc/efg/**")]
[InlineData("abc/efg/**/hij/klm")]
[InlineData("abc/efg/**/hij/**/klm")]
[InlineData("abc/efg/**/hij/**/klm/**")]
[InlineData("abc/efg/**/hij/**/klm/**/")]
[InlineData("**/hij/**/klm")]
[InlineData("**/hij/**")]
[InlineData("/**/hij/**")]
[InlineData("**/**/hij/**")]
[InlineData("ab/**/**/hij/**")]
[InlineData("ab/**/**/hij/**/")]
[InlineData("/ab/**/**/hij/**/")]
[InlineData("/ab/**/**/hij/**")]
public void BuildLinearPatternNegative(string sample)
{
var builder = new PatternBuilder();
var pattern = builder.Build(sample) as ILinearPattern;
Assert.Null(pattern);
}
[Theory]
[InlineData("/abc/", 2, 1, 0, 0)]
[InlineData("abc/", 2, 1, 0, 0)]
[InlineData("abc/efg/", 3, 2, 0, 0)]
[InlineData("abc/efg/h*j/*.*/", 5, 4, 0, 0)]
[InlineData("abc/efg/**", 3, 2, 0, 0)]
[InlineData("/abc/efg/**", 3, 2, 0, 0)]
[InlineData("abc/efg/**/hij/klm", 5, 2, 0, 2)]
[InlineData("abc/efg/**/hij/**/klm", 6, 2, 1, 1)]
[InlineData("abc/efg/**/hij/**/klm/**", 7, 2, 2, 0)]
[InlineData("abc/efg/**/hij/**/klm/**/", 8, 2, 2, 0)]
[InlineData("**/hij/**/klm", 4, 0, 1, 1)]
[InlineData("**/hij/**", 3, 0, 1, 0)]
[InlineData("/**/hij/**", 3, 0, 1, 0)]
[InlineData("**/**/hij/**", 4, 0, 1, 0)]
[InlineData("ab/**/**/hij/**", 5, 1, 1, 0)]
[InlineData("ab/**/**/hij/**/", 6, 1, 1, 0)]
[InlineData("/ab/**/**/hij/**/", 6, 1, 1, 0)]
[InlineData("/ab/**/**/hij/**", 5, 1, 1, 0)]
[InlineData("**/*.suffix", 2, 0, 0, 1)]
[InlineData("**.suffix", 2, 0, 0, 1)]
[InlineData("ab/**.suffix", 3, 1, 0, 1)]
public void BuildRaggedPattern(string sample,
int segmentCount,
int startSegmentsCount,
int containSegmentCount,
int endSegmentCount)
{
var builder = new PatternBuilder();
var pattern = builder.Build(sample) as IRaggedPattern;
Assert.NotNull(pattern);
Assert.Equal(segmentCount, pattern.Segments.Count);
Assert.Equal(startSegmentsCount, pattern.StartsWith.Count);
Assert.Equal(endSegmentCount, pattern.EndsWith.Count);
Assert.Equal(containSegmentCount, pattern.Contains.Count);
}
[Theory]
[InlineData("abc")]
[InlineData("/abc")]
[InlineData("abc/efg")]
[InlineData("abc/efg/h*j")]
[InlineData("abc/efg/h*j/*.*")]
[InlineData("abc/efg/hij")]
[InlineData("abc/efg/hij/klm")]
public void BuildRaggedPatternNegative(string sample)
{
var builder = new PatternBuilder();
var pattern = builder.Build(sample) as IRaggedPattern;
Assert.Null(pattern);
}
[Theory]
[InlineData("a/../")]
[InlineData("a/..")]
[InlineData("/a/../")]
[InlineData("./a/../")]
[InlineData("**/../")]
[InlineData("*.cs/../")]
public void ThrowExceptionForInvalidParentsPath(string sample)
{
// parent segment is only allowed at the beginning of the pattern
Assert.Throws<ArgumentException>(() =>
{
var builder = new PatternBuilder();
var pattern = builder.Build(sample);
Assert.Null(pattern);
});
}
[Fact]
public void ThrowExceptionForNull()
{
Assert.Throws<ArgumentNullException>(() =>
{
var builder = new PatternBuilder();
builder.Build(null);
});
}
}
}

View file

@ -0,0 +1,61 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.TestUtility
{
public class DisposableFileSystem : IDisposable
{
public DisposableFileSystem()
{
RootPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(RootPath);
DirectoryInfo = new DirectoryInfo(RootPath);
}
public string RootPath { get; }
public DirectoryInfo DirectoryInfo { get; }
public DisposableFileSystem CreateFolder(string path)
{
Directory.CreateDirectory(Path.Combine(RootPath, path));
return this;
}
public DisposableFileSystem CreateFile(string path)
{
File.WriteAllText(Path.Combine(RootPath, path), "temp");
return this;
}
public DisposableFileSystem CreateFiles(params string[] fileRelativePaths)
{
foreach (var path in fileRelativePaths)
{
var fullPath = Path.Combine(RootPath, path);
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
File.WriteAllText(
fullPath,
string.Format("Automatically generated for testing on {0:yyyy}/{0:MM}/{0:dd} {0:hh}:{0:mm}:{0:ss}", DateTime.UtcNow));
}
return this;
}
public void Dispose()
{
try
{
Directory.Delete(RootPath, true);
}
catch
{
// Don't throw if this fails.
}
}
}
}

View file

@ -0,0 +1,85 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Linq;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.TestUtility
{
internal class FileSystemGlobbingTestContext
{
private readonly string _basePath;
private readonly FileSystemOperationRecorder _recorder;
private readonly Matcher _patternMatching;
private MockDirectoryInfo _directoryInfo;
public PatternMatchingResult Result { get; private set; }
public FileSystemGlobbingTestContext(string basePath, Matcher matcher)
{
_basePath = basePath;
_recorder = new FileSystemOperationRecorder();
_patternMatching = matcher;
_directoryInfo = new MockDirectoryInfo(
recorder: _recorder,
parentDirectory: null,
fullName: _basePath,
name: ".",
paths: new string[0]);
}
public FileSystemGlobbingTestContext Include(params string[] patterns)
{
foreach (var pattern in patterns)
{
_patternMatching.AddInclude(pattern);
}
return this;
}
public FileSystemGlobbingTestContext Exclude(params string[] patterns)
{
foreach (var pattern in patterns)
{
_patternMatching.AddExclude(pattern);
}
return this;
}
public FileSystemGlobbingTestContext Files(params string[] files)
{
_directoryInfo = new MockDirectoryInfo(
_directoryInfo.Recorder,
_directoryInfo.ParentDirectory,
_directoryInfo.FullName,
_directoryInfo.Name,
_directoryInfo.Paths.Concat(files.Select(file => _basePath + file)).ToArray());
return this;
}
public FileSystemGlobbingTestContext Execute()
{
Result = _patternMatching.Execute(_directoryInfo);
return this;
}
public FileSystemGlobbingTestContext AssertExact(params string[] files)
{
Assert.Equal(files.OrderBy(file => file), Result.Files.OrderBy(file => file.Path).Select(file => file.Path));
return this;
}
public FileSystemGlobbingTestContext SubDirectory(string name)
{
_directoryInfo = (MockDirectoryInfo)_directoryInfo.GetDirectory(name);
return this;
}
}
}

View file

@ -0,0 +1,28 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Reflection;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.TestUtility
{
internal class FileSystemOperationRecorder
{
public IList<IDictionary<string, object>> Records = new List<IDictionary<string, object>>();
public void Add(string action, object values)
{
var record = new Dictionary<string, object>
{
{"action", action }
};
foreach (var p in values.GetType().GetTypeInfo().DeclaredProperties)
{
record[p.Name] = p.GetValue(values);
}
Records.Add(record);
}
}
}

View file

@ -0,0 +1,116 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.TestUtility
{
internal class MockDirectoryInfo : DirectoryInfoBase
{
public MockDirectoryInfo(
FileSystemOperationRecorder recorder,
DirectoryInfoBase parentDirectory,
string fullName,
string name,
string[] paths)
{
ParentDirectory = parentDirectory;
Recorder = recorder;
FullName = fullName;
Name = name;
Paths = paths;
}
public FileSystemOperationRecorder Recorder { get; }
public override string FullName { get; }
public override string Name { get; }
public override DirectoryInfoBase ParentDirectory { get; }
public string[] Paths { get; }
public override IEnumerable<FileSystemInfoBase> EnumerateFileSystemInfos()
{
Recorder.Add("EnumerateFileSystemInfos", new { FullName, Name });
var names = new HashSet<string>();
foreach (var path in Paths)
{
if (!path.Replace('\\', '/').StartsWith(FullName.Replace('\\', '/')))
{
continue;
}
var beginPath = FullName.Length;
var endPath = path.Length;
var beginSegment = beginPath;
var endSegment = NextIndex(path, new[] { '/', '\\' }, beginSegment, path.Length);
if (endPath == endSegment)
{
yield return new MockFileInfo(
recorder: Recorder,
parentDirectory: this,
fullName: path,
name: path.Substring(beginSegment, endSegment - beginSegment));
}
else
{
var name = path.Substring(beginSegment, endSegment - beginSegment);
if (!names.Contains(name))
{
names.Add(name);
yield return new MockDirectoryInfo(
recorder: Recorder,
parentDirectory: this,
fullName: path.Substring(0, endSegment + 1),
name: name,
paths: Paths);
}
}
}
}
private int NextIndex(string pattern, char[] anyOf, int startIndex, int endIndex)
{
var index = pattern.IndexOfAny(anyOf, startIndex, endIndex - startIndex);
return index == -1 ? endIndex : index;
}
public override DirectoryInfoBase GetDirectory(string name)
{
if (string.Equals(name, "..", StringComparison.Ordinal))
{
var indexOfPenultimateSlash = FullName.LastIndexOf('\\', FullName.Length - 2);
var fullName = FullName.Substring(0, indexOfPenultimateSlash + 1);
return new MockDirectoryInfo(
recorder: Recorder,
parentDirectory: this,
fullName: FullName.Substring(0, indexOfPenultimateSlash + 1),
name: name,
paths: Paths);
}
return new MockDirectoryInfo(
recorder: Recorder,
parentDirectory: this,
fullName: FullName + name + "\\",
name: name,
paths: Paths);
}
public override FileInfoBase GetFile(string name)
{
return new MockFileInfo(
recorder: Recorder,
parentDirectory: this,
fullName: FullName + name,
name: name);
}
}
}

View file

@ -0,0 +1,29 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.TestUtility
{
internal class MockFileInfo : FileInfoBase
{
public MockFileInfo(
FileSystemOperationRecorder recorder,
DirectoryInfoBase parentDirectory,
string fullName,
string name)
{
Recorder = recorder;
FullName = fullName;
Name = name;
}
public FileSystemOperationRecorder Recorder { get; }
public override DirectoryInfoBase ParentDirectory { get; }
public override string FullName { get; }
public override string Name { get; }
}
}

View file

@ -0,0 +1,64 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.PatternContexts
{
internal class MockLinearPatternBuilder
{
private List<IPathSegment> _segments;
public static MockLinearPatternBuilder New()
{
return new MockLinearPatternBuilder();
}
private MockLinearPatternBuilder()
{
_segments = new List<IPathSegment>();
}
public MockLinearPatternBuilder Add(string value)
{
_segments.Add(new MockNonRecursivePathSegment(value));
return this;
}
public MockLinearPatternBuilder Add(string[] values)
{
_segments.AddRange(values.Select(v => new MockNonRecursivePathSegment(v)));
return this;
}
public ILinearPattern Build()
{
return new MockLinearPattern(_segments);
}
class MockLinearPattern : ILinearPattern
{
public MockLinearPattern(List<IPathSegment> segments)
{
Segments = segments;
}
public IList<IPathSegment> Segments { get; }
public IPatternContext CreatePatternContextForExclude()
{
throw new NotImplementedException();
}
public IPatternContext CreatePatternContextForInclude()
{
throw new NotImplementedException();
}
}
}
}

View file

@ -0,0 +1,32 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.PatternContexts
{
internal class MockNonRecursivePathSegment : IPathSegment
{
private readonly StringComparison _comparisonType;
public MockNonRecursivePathSegment(StringComparison comparisonType)
{
_comparisonType = comparisonType;
}
public MockNonRecursivePathSegment(string value)
{
Value = value;
}
public bool CanProduceStem { get { return false; } }
public string Value { get; }
public bool Match(string value)
{
return string.Compare(Value, value, _comparisonType) == 0;
}
}
}

View file

@ -0,0 +1,104 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.PatternContexts
{
internal class MockRaggedPatternBuilder
{
private MockRaggedPattern _result;
public static MockRaggedPatternBuilder New()
{
return new MockRaggedPatternBuilder();
}
private MockRaggedPatternBuilder()
{
_result = new MockRaggedPattern();
}
public MockRaggedPatternBuilder AddStart(params string[] values)
{
foreach (var value in values)
{
var segment = new MockNonRecursivePathSegment(value);
_result.StartsWith.Add(segment);
_result.Segments.Add(segment);
}
return this;
}
public MockRaggedPatternBuilder AddContainsGroup(params string[] values)
{
var last = _result.Segments.Last();
if (!(last is MockRecursivePathSegment))
{
AddRecursive();
}
var containSegment = new List<IPathSegment>();
foreach (var value in values)
{
var segment = new MockNonRecursivePathSegment(value);
containSegment.Add(segment);
_result.Segments.Add(segment);
}
_result.Contains.Add(containSegment);
AddRecursive();
return this;
}
public MockRaggedPatternBuilder AddEnd(params string[] values)
{
foreach (var value in values)
{
_result.EndsWith.Add(new MockNonRecursivePathSegment(value));
}
return this;
}
public MockRaggedPatternBuilder AddRecursive()
{
_result.Segments.Add(new MockRecursivePathSegment());
return this;
}
public IRaggedPattern Build()
{
return _result;
}
class MockRaggedPattern : IRaggedPattern
{
public IList<IPathSegment> Segments { get; } = new List<IPathSegment>();
public IList<IPathSegment> StartsWith { get; } = new List<IPathSegment>();
public IList<IList<IPathSegment>> Contains { get; } = new List<IList<IPathSegment>>();
public IList<IPathSegment> EndsWith { get; } = new List<IPathSegment>();
public IPatternContext CreatePatternContextForExclude()
{
throw new NotImplementedException();
}
public IPatternContext CreatePatternContextForInclude()
{
throw new NotImplementedException();
}
}
}
}

View file

@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.PatternContexts
{
internal class MockRecursivePathSegment : IPathSegment
{
public bool CanProduceStem { get { return false; } }
public bool Match(string value)
{
return false;
}
}
}

View file

@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Tests.TestUtility
{
internal static class PatternContextHelper
{
public static void PushDirectory(IPatternContext context, params string[] directoryNames)
{
foreach (var each in directoryNames)
{
var directory = new MockDirectoryInfo(null, null, string.Empty, each, null);
context.PushDirectory(directory);
}
}
}
}

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Concurrent;

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Net;

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using Newtonsoft.Json.Linq;

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Newtonsoft.Json.Linq;

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Newtonsoft.Json.Linq;

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Newtonsoft.Json.Linq;