Copy FileSystemGlobbing and HashCodeCombiner sources

This commit is contained in:
Pranav K 2016-04-29 12:11:27 -07:00
parent f8631fa4b7
commit 734c9fc43b
75 changed files with 3999 additions and 43 deletions

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.IO;

View file

@ -0,0 +1,58 @@
// 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.Runtime.CompilerServices;
namespace Microsoft.DotNet.InternalAbstractions
{
public struct HashCodeCombiner
{
private long _combinedHash64;
public int CombinedHash
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _combinedHash64.GetHashCode(); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private HashCodeCombiner(long seed)
{
_combinedHash64 = seed;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(int i)
{
_combinedHash64 = ((_combinedHash64 << 5) + _combinedHash64) ^ i;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(string s)
{
var hashCode = (s != null) ? s.GetHashCode() : 0;
Add(hashCode);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(object o)
{
var hashCode = (o != null) ? o.GetHashCode() : 0;
Add(hashCode);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add<TValue>(TValue value, IEqualityComparer<TValue> comparer)
{
var hashCode = value != null ? comparer.GetHashCode(value) : 0;
Add(hashCode);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HashCodeCombiner Start()
{
return new HashCodeCombiner(0x1505L);
}
}
}

View file

@ -5,8 +5,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.InternalAbstractions;
using Microsoft.DotNet.ProjectModel.Utilities;
using Microsoft.Extensions.Internal;
namespace Microsoft.DotNet.ProjectModel.Compilation
{

View file

@ -0,0 +1,16 @@
// 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;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions
{
public abstract class DirectoryInfoBase : FileSystemInfoBase
{
public abstract IEnumerable<FileSystemInfoBase> EnumerateFileSystemInfos();
public abstract DirectoryInfoBase GetDirectory(string path);
public abstract FileInfoBase GetFile(string path);
}
}

View file

@ -0,0 +1,89 @@
// 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;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions
{
public class DirectoryInfoWrapper : DirectoryInfoBase
{
private readonly DirectoryInfo _directoryInfo;
private readonly bool _isParentPath;
public DirectoryInfoWrapper(DirectoryInfo directoryInfo, bool isParentPath = false)
{
_directoryInfo = directoryInfo;
_isParentPath = isParentPath;
}
public override IEnumerable<FileSystemInfoBase> EnumerateFileSystemInfos()
{
if (_directoryInfo.Exists)
{
foreach (var fileSystemInfo in _directoryInfo.EnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly))
{
var directoryInfo = fileSystemInfo as DirectoryInfo;
if (directoryInfo != null)
{
yield return new DirectoryInfoWrapper(directoryInfo);
}
else
{
yield return new FileInfoWrapper((FileInfo)fileSystemInfo);
}
}
}
}
public override DirectoryInfoBase GetDirectory(string name)
{
var isParentPath = string.Equals(name, "..", StringComparison.Ordinal);
if (isParentPath)
{
return new DirectoryInfoWrapper(new DirectoryInfo(Path.Combine(_directoryInfo.FullName, name)), isParentPath);
}
else
{
var dirs = _directoryInfo.GetDirectories(name);
if (dirs.Length == 1)
{
return new DirectoryInfoWrapper(dirs[0], isParentPath);
}
else if (dirs.Length == 0)
{
return null;
}
else
{
// This shouldn't happen. The parameter name isn't supposed to contain wild card.
throw new InvalidOperationException(
string.Format("More than one sub directories are found under {0} with name {1}.", _directoryInfo.FullName, name));
}
}
}
public override FileInfoBase GetFile(string name)
{
return new FileInfoWrapper(new FileInfo(Path.Combine(_directoryInfo.FullName, name)));
}
public override string Name
{
get { return _isParentPath ? ".." : _directoryInfo.Name; }
}
public override string FullName
{
get { return _directoryInfo.FullName; }
}
public override DirectoryInfoBase ParentDirectory
{
get { return new DirectoryInfoWrapper(_directoryInfo.Parent); }
}
}
}

View file

@ -0,0 +1,9 @@
// 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.
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions
{
public abstract class FileInfoBase : FileSystemInfoBase
{
}
}

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.IO;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions
{
public class FileInfoWrapper : FileInfoBase
{
private FileInfo _fileInfo;
public FileInfoWrapper(FileInfo fileInfo)
{
_fileInfo = fileInfo;
}
public override string Name
{
get { return _fileInfo.Name; }
}
public override string FullName
{
get { return _fileInfo.FullName; }
}
public override DirectoryInfoBase ParentDirectory
{
get { return new DirectoryInfoWrapper(_fileInfo.Directory); }
}
}
}

View file

@ -0,0 +1,14 @@
// 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.
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions
{
public abstract class FileSystemInfoBase
{
public abstract string Name { get; }
public abstract string FullName { get; }
public abstract DirectoryInfoBase ParentDirectory { get; }
}
}

View file

@ -0,0 +1,40 @@
// 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.InternalAbstractions;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing
{
public struct FilePatternMatch : IEquatable<FilePatternMatch>
{
public string Path { get; }
public string Stem { get; }
public FilePatternMatch(string path, string stem)
{
Path = path;
Stem = stem;
}
public bool Equals(FilePatternMatch other)
{
return string.Equals(other.Path, Path, StringComparison.OrdinalIgnoreCase) &&
string.Equals(other.Stem, Stem, StringComparison.OrdinalIgnoreCase);
}
public override bool Equals(object obj)
{
return Equals((FilePatternMatch)obj);
}
public override int GetHashCode()
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(Path, StringComparer.OrdinalIgnoreCase);
hashCodeCombiner.Add(Stem, StringComparer.OrdinalIgnoreCase);
return hashCodeCombiner.CombinedHash;
}
}
}

View file

@ -0,0 +1,12 @@
// 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;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal
{
public interface ILinearPattern : IPattern
{
IList<IPathSegment> Segments { get; }
}
}

View file

@ -0,0 +1,12 @@
// 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.
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal
{
public interface IPathSegment
{
bool CanProduceStem { get; }
bool Match(string value);
}
}

View file

@ -0,0 +1,12 @@
// 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.
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal
{
public interface IPattern
{
IPatternContext CreatePatternContextForInclude();
IPatternContext CreatePatternContextForExclude();
}
}

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 System;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal
{
public interface IPatternContext
{
void Declare(Action<IPathSegment, bool> onDeclare);
bool Test(DirectoryInfoBase directory);
PatternTestResult Test(FileInfoBase file);
void PushDirectory(DirectoryInfoBase directory);
void PopDirectory();
}
}

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.Collections.Generic;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal
{
public interface IRaggedPattern : IPattern
{
IList<IPathSegment> Segments { get; }
IList<IPathSegment> StartsWith { get; }
IList<IList<IPathSegment>> Contains { get; }
IList<IPathSegment> EndsWith { get; }
}
}

View file

@ -0,0 +1,250 @@
// 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.Abstractions;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Util;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal
{
public class MatcherContext
{
private readonly DirectoryInfoBase _root;
private readonly IList<IPatternContext> _includePatternContexts;
private readonly IList<IPatternContext> _excludePatternContexts;
private readonly IList<FilePatternMatch> _files;
private readonly HashSet<string> _declaredLiteralFolderSegmentInString;
private readonly HashSet<LiteralPathSegment> _declaredLiteralFolderSegments = new HashSet<LiteralPathSegment>();
private readonly HashSet<LiteralPathSegment> _declaredLiteralFileSegments = new HashSet<LiteralPathSegment>();
private bool _declaredParentPathSegment;
private bool _declaredWildcardPathSegment;
private readonly StringComparison _comparisonType;
public MatcherContext(IEnumerable<IPattern> includePatterns,
IEnumerable<IPattern> excludePatterns,
DirectoryInfoBase directoryInfo,
StringComparison comparison)
{
_root = directoryInfo;
_files = new List<FilePatternMatch>();
_comparisonType = comparison;
_includePatternContexts = includePatterns.Select(pattern => pattern.CreatePatternContextForInclude()).ToList();
_excludePatternContexts = excludePatterns.Select(pattern => pattern.CreatePatternContextForExclude()).ToList();
_declaredLiteralFolderSegmentInString = new HashSet<string>(StringComparisonHelper.GetStringComparer(comparison));
}
public PatternMatchingResult Execute()
{
_files.Clear();
Match(_root, parentRelativePath: null);
return new PatternMatchingResult(_files);
}
private void Match(DirectoryInfoBase directory, string parentRelativePath)
{
// Request all the including and excluding patterns to push current directory onto their status stack.
PushDirectory(directory);
Declare();
var entities = new List<FileSystemInfoBase>();
if (_declaredWildcardPathSegment || _declaredLiteralFileSegments.Any())
{
entities.AddRange(directory.EnumerateFileSystemInfos());
}
else
{
var candidates = directory.EnumerateFileSystemInfos().OfType<DirectoryInfoBase>();
foreach (var candidate in candidates)
{
if (_declaredLiteralFolderSegmentInString.Contains(candidate.Name))
{
entities.Add(candidate);
}
}
}
if (_declaredParentPathSegment)
{
entities.Add(directory.GetDirectory(".."));
}
// collect files and sub directories
var subDirectories = new List<DirectoryInfoBase>();
foreach (var entity in entities)
{
var fileInfo = entity as FileInfoBase;
if (fileInfo != null)
{
var result = MatchPatternContexts(fileInfo, (pattern, file) => pattern.Test(file));
if (result.IsSuccessful)
{
_files.Add(new FilePatternMatch(
path: CombinePath(parentRelativePath, fileInfo.Name),
stem: result.Stem));
}
continue;
}
var directoryInfo = entity as DirectoryInfoBase;
if (directoryInfo != null)
{
if (MatchPatternContexts(directoryInfo, (pattern, dir) => pattern.Test(dir)))
{
subDirectories.Add(directoryInfo);
}
continue;
}
}
// Matches the sub directories recursively
foreach (var subDir in subDirectories)
{
var relativePath = CombinePath(parentRelativePath, subDir.Name);
Match(subDir, relativePath);
}
// Request all the including and excluding patterns to pop their status stack.
PopDirectory();
}
private void Declare()
{
_declaredLiteralFileSegments.Clear();
_declaredLiteralFolderSegments.Clear();
_declaredParentPathSegment = false;
_declaredWildcardPathSegment = false;
foreach (var include in _includePatternContexts)
{
include.Declare(DeclareInclude);
}
}
private void DeclareInclude(IPathSegment patternSegment, bool isLastSegment)
{
var literalSegment = patternSegment as LiteralPathSegment;
if (literalSegment != null)
{
if (isLastSegment)
{
_declaredLiteralFileSegments.Add(literalSegment);
}
else
{
_declaredLiteralFolderSegments.Add(literalSegment);
_declaredLiteralFolderSegmentInString.Add(literalSegment.Value);
}
}
else if (patternSegment is ParentPathSegment)
{
_declaredParentPathSegment = true;
}
else if (patternSegment is WildcardPathSegment)
{
_declaredWildcardPathSegment = true;
}
}
internal static string CombinePath(string left, string right)
{
if (string.IsNullOrEmpty(left))
{
return right;
}
else
{
return string.Format("{0}/{1}", left, right);
}
}
// Used to adapt Test(DirectoryInfoBase) for the below overload
private bool MatchPatternContexts<TFileInfoBase>(TFileInfoBase fileinfo, Func<IPatternContext, TFileInfoBase, bool> test)
{
return MatchPatternContexts(
fileinfo,
(ctx, file) =>
{
if (test(ctx, file))
{
return PatternTestResult.Success(stem: string.Empty);
}
else
{
return PatternTestResult.Failed;
}
}).IsSuccessful;
}
private PatternTestResult MatchPatternContexts<TFileInfoBase>(TFileInfoBase fileinfo, Func<IPatternContext, TFileInfoBase, PatternTestResult> test)
{
var result = PatternTestResult.Failed;
// If the given file/directory matches any including pattern, continues to next step.
foreach (var context in _includePatternContexts)
{
var localResult = test(context, fileinfo);
if (localResult.IsSuccessful)
{
result = localResult;
break;
}
}
// If the given file/directory doesn't match any of the including pattern, returns false.
if (!result.IsSuccessful)
{
return PatternTestResult.Failed;
}
// If the given file/directory matches any excluding pattern, returns false.
foreach (var context in _excludePatternContexts)
{
if (test(context, fileinfo).IsSuccessful)
{
return PatternTestResult.Failed;
}
}
return result;
}
private void PopDirectory()
{
foreach (var context in _excludePatternContexts)
{
context.PopDirectory();
}
foreach (var context in _includePatternContexts)
{
context.PopDirectory();
}
}
private void PushDirectory(DirectoryInfoBase directory)
{
foreach (var context in _includePatternContexts)
{
context.PushDirectory(directory);
}
foreach (var context in _excludePatternContexts)
{
context.PushDirectory(directory);
}
}
}
}

View file

@ -0,0 +1,17 @@
// 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;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments
{
public class CurrentPathSegment : IPathSegment
{
public bool CanProduceStem { get { return false; } }
public bool Match(string value)
{
return false;
}
}
}

View file

@ -0,0 +1,48 @@
// 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.Util;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments
{
public class LiteralPathSegment : IPathSegment
{
private readonly StringComparison _comparisonType;
public bool CanProduceStem { get { return false; } }
public LiteralPathSegment(string value, StringComparison comparisonType)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
Value = value;
_comparisonType = comparisonType;
}
public string Value { get; }
public bool Match(string value)
{
return string.Equals(Value, value, _comparisonType);
}
public override bool Equals(object obj)
{
var other = obj as LiteralPathSegment;
return other != null &&
_comparisonType == other._comparisonType &&
string.Equals(other.Value, Value, _comparisonType);
}
public override int GetHashCode()
{
return StringComparisonHelper.GetStringComparer(_comparisonType).GetHashCode(Value);
}
}
}

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 System;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments
{
public class ParentPathSegment : IPathSegment
{
private static readonly string LiteralParent = "..";
public bool CanProduceStem { get { return false; } }
public bool Match(string value)
{
return string.Equals(LiteralParent, value, StringComparison.Ordinal);
}
}
}

View file

@ -0,0 +1,17 @@
// 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;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments
{
public class RecursiveWildcardSegment : IPathSegment
{
public bool CanProduceStem { get { return true; } }
public bool Match(string value)
{
return false;
}
}
}

View file

@ -0,0 +1,74 @@
// 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;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments
{
public class WildcardPathSegment : IPathSegment
{
// It doesn't matter which StringComparison type is used in this MatchAll segment because
// all comparing are skipped since there is no content in the segment.
public static readonly WildcardPathSegment MatchAll = new WildcardPathSegment(
string.Empty, new List<string>(), string.Empty, StringComparison.OrdinalIgnoreCase);
private readonly StringComparison _comparisonType;
public WildcardPathSegment(string beginsWith, List<string> contains, string endsWith, StringComparison comparisonType)
{
BeginsWith = beginsWith;
Contains = contains;
EndsWith = endsWith;
_comparisonType = comparisonType;
}
public bool CanProduceStem { get { return true; } }
public string BeginsWith { get; }
public List<string> Contains { get; }
public string EndsWith { get; }
public bool Match(string value)
{
var wildcard = this;
if (value.Length < wildcard.BeginsWith.Length + wildcard.EndsWith.Length)
{
return false;
}
if (!value.StartsWith(wildcard.BeginsWith, _comparisonType))
{
return false;
}
if (!value.EndsWith(wildcard.EndsWith, _comparisonType))
{
return false;
}
var beginRemaining = wildcard.BeginsWith.Length;
var endRemaining = value.Length - wildcard.EndsWith.Length;
for (var containsIndex = 0; containsIndex != wildcard.Contains.Count; ++containsIndex)
{
var containsValue = wildcard.Contains[containsIndex];
var indexOf = value.IndexOf(
value: containsValue,
startIndex: beginRemaining,
count: endRemaining - beginRemaining,
comparisonType: _comparisonType);
if (indexOf == -1)
{
return false;
}
beginRemaining = indexOf + containsValue.Length;
}
return true;
}
}
}

View file

@ -0,0 +1,39 @@
// 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 Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PatternContexts
{
public abstract class PatternContext<TFrame> : IPatternContext
{
private Stack<TFrame> _stack = new Stack<TFrame>();
protected TFrame Frame;
public virtual void Declare(Action<IPathSegment, bool> declare) { }
public abstract PatternTestResult Test(FileInfoBase file);
public abstract bool Test(DirectoryInfoBase directory);
public abstract void PushDirectory(DirectoryInfoBase directory);
public virtual void PopDirectory()
{
Frame = _stack.Pop();
}
protected void PushDataFrame(TFrame frame)
{
_stack.Push(Frame);
Frame = frame;
}
protected bool IsStackEmpty()
{
return _stack.Count == 0;
}
}
}

View file

@ -0,0 +1,105 @@
// 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 Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PatternContexts
{
public abstract class PatternContextLinear
: PatternContext<PatternContextLinear.FrameData>
{
public PatternContextLinear(ILinearPattern pattern)
{
Pattern = pattern;
}
public override PatternTestResult Test(FileInfoBase file)
{
if (IsStackEmpty())
{
throw new InvalidOperationException("Can't test file before entering a directory.");
}
if(!Frame.IsNotApplicable && IsLastSegment() && TestMatchingSegment(file.Name))
{
return PatternTestResult.Success(CalculateStem(file));
}
return PatternTestResult.Failed;
}
public override void PushDirectory(DirectoryInfoBase directory)
{
// copy the current frame
var frame = Frame;
if (IsStackEmpty() || Frame.IsNotApplicable)
{
// when the stack is being initialized
// or no change is required.
}
else if (!TestMatchingSegment(directory.Name))
{
// nothing down this path is affected by this pattern
frame.IsNotApplicable = true;
}
else
{
// Determine this frame's contribution to the stem (if any)
var segment = Pattern.Segments[Frame.SegmentIndex];
if (frame.InStem || segment.CanProduceStem)
{
frame.InStem = true;
frame.StemItems.Add(directory.Name);
}
// directory matches segment, advance position in pattern
frame.SegmentIndex = frame.SegmentIndex + 1;
}
PushDataFrame(frame);
}
public struct FrameData
{
public bool IsNotApplicable;
public int SegmentIndex;
public bool InStem;
private IList<string> _stemItems;
public IList<string> StemItems
{
get { return _stemItems ?? (_stemItems = new List<string>()); }
}
public string Stem
{
get { return _stemItems == null ? null : string.Join("/", _stemItems); }
}
}
protected ILinearPattern Pattern { get; }
protected bool IsLastSegment()
{
return Frame.SegmentIndex == Pattern.Segments.Count - 1;
}
protected bool TestMatchingSegment(string value)
{
if (Frame.SegmentIndex >= Pattern.Segments.Count)
{
return false;
}
return Pattern.Segments[Frame.SegmentIndex].Match(value);
}
protected string CalculateStem(FileInfoBase matchedFile)
{
return MatcherContext.CombinePath(Frame.Stem, matchedFile.Name);
}
}
}

View file

@ -0,0 +1,31 @@
// 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.Abstractions;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PatternContexts
{
public class PatternContextLinearExclude : PatternContextLinear
{
public PatternContextLinearExclude(ILinearPattern pattern)
: base(pattern)
{
}
public override bool Test(DirectoryInfoBase directory)
{
if (IsStackEmpty())
{
throw new InvalidOperationException("Can't test directory before entering a directory.");
}
if (Frame.IsNotApplicable)
{
return false;
}
return IsLastSegment() && TestMatchingSegment(directory.Name);
}
}
}

View file

@ -0,0 +1,49 @@
// 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.Abstractions;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PatternContexts
{
public class PatternContextLinearInclude : PatternContextLinear
{
public PatternContextLinearInclude(ILinearPattern pattern)
: base(pattern)
{
}
public override void Declare(Action<IPathSegment, bool> onDeclare)
{
if (IsStackEmpty())
{
throw new InvalidOperationException("Can't declare path segment before entering a directory.");
}
if (Frame.IsNotApplicable)
{
return;
}
if (Frame.SegmentIndex < Pattern.Segments.Count)
{
onDeclare(Pattern.Segments[Frame.SegmentIndex], IsLastSegment());
}
}
public override bool Test(DirectoryInfoBase directory)
{
if (IsStackEmpty())
{
throw new InvalidOperationException("Can't test directory before entering a directory.");
}
if (Frame.IsNotApplicable)
{
return false;
}
return !IsLastSegment() && TestMatchingSegment(directory.Name);
}
}
}

View file

@ -0,0 +1,186 @@
// 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 Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PatternContexts
{
public abstract class PatternContextRagged : PatternContext<PatternContextRagged.FrameData>
{
public PatternContextRagged(IRaggedPattern pattern)
{
Pattern = pattern;
}
public override PatternTestResult Test(FileInfoBase file)
{
if (IsStackEmpty())
{
throw new InvalidOperationException("Can't test file before entering a directory.");
}
if(!Frame.IsNotApplicable && IsEndingGroup() && TestMatchingGroup(file))
{
return PatternTestResult.Success(CalculateStem(file));
}
return PatternTestResult.Failed;
}
public sealed override void PushDirectory(DirectoryInfoBase directory)
{
// copy the current frame
var frame = Frame;
if (IsStackEmpty())
{
// initializing
frame.SegmentGroupIndex = -1;
frame.SegmentGroup = Pattern.StartsWith;
}
else if (Frame.IsNotApplicable)
{
// no change
}
else if (IsStartingGroup())
{
if (!TestMatchingSegment(directory.Name))
{
// nothing down this path is affected by this pattern
frame.IsNotApplicable = true;
}
else
{
// starting path incrementally satisfied
frame.SegmentIndex += 1;
}
}
else if (!IsStartingGroup() && directory.Name == "..")
{
// any parent path segment is not applicable in **
frame.IsNotApplicable = true;
}
else if (!IsStartingGroup() && !IsEndingGroup() && TestMatchingGroup(directory))
{
frame.SegmentIndex = Frame.SegmentGroup.Count;
frame.BacktrackAvailable = 0;
}
else
{
// increase directory backtrack length
frame.BacktrackAvailable += 1;
}
if (frame.InStem)
{
frame.StemItems.Add(directory.Name);
}
while (
frame.SegmentIndex == frame.SegmentGroup.Count &&
frame.SegmentGroupIndex != Pattern.Contains.Count)
{
frame.SegmentGroupIndex += 1;
frame.SegmentIndex = 0;
if (frame.SegmentGroupIndex < Pattern.Contains.Count)
{
frame.SegmentGroup = Pattern.Contains[frame.SegmentGroupIndex];
}
else
{
frame.SegmentGroup = Pattern.EndsWith;
}
// We now care about the stem
frame.InStem = true;
}
PushDataFrame(frame);
}
public override void PopDirectory()
{
base.PopDirectory();
if (Frame.StemItems.Count > 0)
{
Frame.StemItems.RemoveAt(Frame.StemItems.Count - 1);
}
}
public struct FrameData
{
public bool IsNotApplicable;
public int SegmentGroupIndex;
public IList<IPathSegment> SegmentGroup;
public int BacktrackAvailable;
public int SegmentIndex;
public bool InStem;
private IList<string> _stemItems;
public IList<string> StemItems
{
get { return _stemItems ?? (_stemItems = new List<string>()); }
}
public string Stem
{
get { return _stemItems == null ? null : string.Join("/", _stemItems); }
}
}
protected IRaggedPattern Pattern { get; }
protected bool IsStartingGroup()
{
return Frame.SegmentGroupIndex == -1;
}
protected bool IsEndingGroup()
{
return Frame.SegmentGroupIndex == Pattern.Contains.Count;
}
protected bool TestMatchingSegment(string value)
{
if (Frame.SegmentIndex >= Frame.SegmentGroup.Count)
{
return false;
}
return Frame.SegmentGroup[Frame.SegmentIndex].Match(value);
}
protected bool TestMatchingGroup(FileSystemInfoBase value)
{
var groupLength = Frame.SegmentGroup.Count;
var backtrackLength = Frame.BacktrackAvailable + 1;
if (backtrackLength < groupLength)
{
return false;
}
var scan = value;
for (int index = 0; index != groupLength; ++index)
{
var segment = Frame.SegmentGroup[groupLength - index - 1];
if (!segment.Match(scan.Name))
{
return false;
}
scan = scan.ParentDirectory;
}
return true;
}
protected string CalculateStem(FileInfoBase matchedFile)
{
return MatcherContext.CombinePath(Frame.Stem, matchedFile.Name);
}
}
}

View file

@ -0,0 +1,45 @@
// 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.Abstractions;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PatternContexts
{
public class PatternContextRaggedExclude : PatternContextRagged
{
public PatternContextRaggedExclude(IRaggedPattern pattern)
: base(pattern)
{
}
public override bool Test(DirectoryInfoBase directory)
{
if (IsStackEmpty())
{
throw new InvalidOperationException("Can't test directory before entering a directory.");
}
if (Frame.IsNotApplicable)
{
return false;
}
if (IsEndingGroup() && TestMatchingGroup(directory))
{
// directory excluded with file-like pattern
return true;
}
if (Pattern.EndsWith.Count == 0 &&
Frame.SegmentGroupIndex == Pattern.Contains.Count - 1 &&
TestMatchingGroup(directory))
{
// directory excluded by matching up to final '/**'
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,60 @@
// 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.Abstractions;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PatternContexts
{
public class PatternContextRaggedInclude : PatternContextRagged
{
public PatternContextRaggedInclude(IRaggedPattern pattern)
: base(pattern)
{
}
public override void Declare(Action<IPathSegment, bool> onDeclare)
{
if (IsStackEmpty())
{
throw new InvalidOperationException("Can't declare path segment before entering a directory.");
}
if (Frame.IsNotApplicable)
{
return;
}
if (IsStartingGroup() && Frame.SegmentIndex < Frame.SegmentGroup.Count)
{
onDeclare(Frame.SegmentGroup[Frame.SegmentIndex], false);
}
else
{
onDeclare(WildcardPathSegment.MatchAll, false);
}
}
public override bool Test(DirectoryInfoBase directory)
{
if (IsStackEmpty())
{
throw new InvalidOperationException("Can't test directory before entering a directory.");
}
if (Frame.IsNotApplicable)
{
return false;
}
if (IsStartingGroup() && !TestMatchingSegment(directory.Name))
{
// deterministic not-included
return false;
}
return true;
}
}
}

View file

@ -0,0 +1,24 @@
// 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.
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal
{
public struct PatternTestResult
{
public static readonly PatternTestResult Failed = new PatternTestResult(isSuccessful: false, stem: null);
public bool IsSuccessful { get; }
public string Stem { get; }
private PatternTestResult(bool isSuccessful, string stem)
{
IsSuccessful = isSuccessful;
Stem = stem;
}
public static PatternTestResult Success(string stem)
{
return new PatternTestResult(isSuccessful: true, stem: stem);
}
}
}

View file

@ -0,0 +1,272 @@
// 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 Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PathSegments;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.PatternContexts;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.Patterns
{
public class PatternBuilder
{
private static readonly char[] _slashes = new[] { '/', '\\' };
private static readonly char[] _star = new[] { '*' };
public PatternBuilder()
{
ComparisonType = StringComparison.OrdinalIgnoreCase;
}
public PatternBuilder(StringComparison comparisonType)
{
ComparisonType = comparisonType;
}
public StringComparison ComparisonType { get; }
public IPattern Build(string pattern)
{
if (pattern == null)
{
throw new ArgumentNullException("pattern");
}
pattern = pattern.TrimStart(_slashes);
if (pattern.TrimEnd(_slashes).Length < pattern.Length)
{
// If the pattern end with a slash, it is considered as
// a directory.
pattern = pattern.TrimEnd(_slashes) + "/**";
}
var allSegments = new List<IPathSegment>();
var isParentSegmentLegal = true;
IList<IPathSegment> segmentsPatternStartsWith = null;
IList<IList<IPathSegment>> segmentsPatternContains = null;
IList<IPathSegment> segmentsPatternEndsWith = null;
var endPattern = pattern.Length;
for (int scanPattern = 0; scanPattern < endPattern;)
{
var beginSegment = scanPattern;
var endSegment = NextIndex(pattern, _slashes, scanPattern, endPattern);
IPathSegment segment = null;
if (segment == null && endSegment - beginSegment == 3)
{
if (pattern[beginSegment] == '*' &&
pattern[beginSegment + 1] == '.' &&
pattern[beginSegment + 2] == '*')
{
// turn *.* into *
beginSegment += 2;
}
}
if (segment == null && endSegment - beginSegment == 2)
{
if (pattern[beginSegment] == '*' &&
pattern[beginSegment + 1] == '*')
{
// recognized **
segment = new RecursiveWildcardSegment();
}
else if (pattern[beginSegment] == '.' &&
pattern[beginSegment + 1] == '.')
{
// recognized ..
if (!isParentSegmentLegal)
{
throw new ArgumentException("\"..\" can be only added at the beginning of the pattern.");
}
segment = new ParentPathSegment();
}
}
if (segment == null && endSegment - beginSegment == 1)
{
if (pattern[beginSegment] == '.')
{
// recognized .
segment = new CurrentPathSegment();
}
}
if (segment == null && endSegment - beginSegment > 2)
{
if (pattern[beginSegment] == '*' &&
pattern[beginSegment + 1] == '*' &&
pattern[beginSegment + 2] == '.')
{
// recognize **.
// swallow the first *, add the recursive path segment and
// the remaining part will be treat as wild card in next loop.
segment = new RecursiveWildcardSegment();
endSegment = beginSegment;
}
}
if (segment == null)
{
var beginsWith = string.Empty;
var contains = new List<string>();
var endsWith = string.Empty;
for (int scanSegment = beginSegment; scanSegment < endSegment;)
{
var beginLiteral = scanSegment;
var endLiteral = NextIndex(pattern, _star, scanSegment, endSegment);
if (beginLiteral == beginSegment)
{
if (endLiteral == endSegment)
{
// and the only bit
segment = new LiteralPathSegment(Portion(pattern, beginLiteral, endLiteral), ComparisonType);
}
else
{
// this is the first bit
beginsWith = Portion(pattern, beginLiteral, endLiteral);
}
}
else if (endLiteral == endSegment)
{
// this is the last bit
endsWith = Portion(pattern, beginLiteral, endLiteral);
}
else
{
if (beginLiteral != endLiteral)
{
// this is a middle bit
contains.Add(Portion(pattern, beginLiteral, endLiteral));
}
else
{
// note: NOOP here, adjacent *'s are collapsed when they
// are mixed with literal text in a path segment
}
}
scanSegment = endLiteral + 1;
}
if (segment == null)
{
segment = new WildcardPathSegment(beginsWith, contains, endsWith, ComparisonType);
}
}
if (!(segment is ParentPathSegment))
{
isParentSegmentLegal = false;
}
if (segment is CurrentPathSegment)
{
// ignore ".\"
}
else
{
if (segment is RecursiveWildcardSegment)
{
if (segmentsPatternStartsWith == null)
{
segmentsPatternStartsWith = new List<IPathSegment>(allSegments);
segmentsPatternEndsWith = new List<IPathSegment>();
segmentsPatternContains = new List<IList<IPathSegment>>();
}
else if (segmentsPatternEndsWith.Count != 0)
{
segmentsPatternContains.Add(segmentsPatternEndsWith);
segmentsPatternEndsWith = new List<IPathSegment>();
}
}
else if (segmentsPatternEndsWith != null)
{
segmentsPatternEndsWith.Add(segment);
}
allSegments.Add(segment);
}
scanPattern = endSegment + 1;
}
if (segmentsPatternStartsWith == null)
{
return new LinearPattern(allSegments);
}
else
{
return new RaggedPattern(allSegments, segmentsPatternStartsWith, segmentsPatternEndsWith, segmentsPatternContains);
}
}
private static int NextIndex(string pattern, char[] anyOf, int beginIndex, int endIndex)
{
var index = pattern.IndexOfAny(anyOf, beginIndex, endIndex - beginIndex);
return index == -1 ? endIndex : index;
}
private static string Portion(string pattern, int beginIndex, int endIndex)
{
return pattern.Substring(beginIndex, endIndex - beginIndex);
}
private class LinearPattern : ILinearPattern
{
public LinearPattern(List<IPathSegment> allSegments)
{
Segments = allSegments;
}
public IList<IPathSegment> Segments { get; }
public IPatternContext CreatePatternContextForInclude()
{
return new PatternContextLinearInclude(this);
}
public IPatternContext CreatePatternContextForExclude()
{
return new PatternContextLinearExclude(this);
}
}
private class RaggedPattern : IRaggedPattern
{
public RaggedPattern(List<IPathSegment> allSegments, IList<IPathSegment> segmentsPatternStartsWith, IList<IPathSegment> segmentsPatternEndsWith, IList<IList<IPathSegment>> segmentsPatternContains)
{
Segments = allSegments;
StartsWith = segmentsPatternStartsWith;
Contains = segmentsPatternContains;
EndsWith = segmentsPatternEndsWith;
}
public IList<IList<IPathSegment>> Contains { get; }
public IList<IPathSegment> EndsWith { get; }
public IList<IPathSegment> Segments { get; }
public IList<IPathSegment> StartsWith { get; }
public IPatternContext CreatePatternContextForInclude()
{
return new PatternContextRaggedInclude(this);
}
public IPatternContext CreatePatternContextForExclude()
{
return new PatternContextRaggedExclude(this);
}
}
}
}

View file

@ -0,0 +1,48 @@
// 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 Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Internal.Patterns;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing
{
public class Matcher
{
private IList<IPattern> _includePatterns = new List<IPattern>();
private IList<IPattern> _excludePatterns = new List<IPattern>();
private readonly PatternBuilder _builder;
private readonly StringComparison _comparison;
public Matcher()
: this(StringComparison.OrdinalIgnoreCase)
{
}
public Matcher(StringComparison comparisonType)
{
_comparison = comparisonType;
_builder = new PatternBuilder(comparisonType);
}
public virtual Matcher AddInclude(string pattern)
{
_includePatterns.Add(_builder.Build(pattern));
return this;
}
public virtual Matcher AddExclude(string pattern)
{
_excludePatterns.Add(_builder.Build(pattern));
return this;
}
public virtual PatternMatchingResult Execute(DirectoryInfoBase directoryInfo)
{
var context = new MatcherContext(_includePatterns, _excludePatterns, directoryInfo, _comparison);
return context.Execute();
}
}
}

View file

@ -0,0 +1,44 @@
// 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 System.Linq;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing
{
public static class MatcherExtensions
{
public static void AddExcludePatterns(this Matcher matcher, params IEnumerable<string>[] excludePatternsGroups)
{
foreach (var group in excludePatternsGroups)
{
foreach (var pattern in group)
{
matcher.AddExclude(pattern);
}
}
}
public static void AddIncludePatterns(this Matcher matcher, params IEnumerable<string>[] includePatternsGroups)
{
foreach (var group in includePatternsGroups)
{
foreach (var pattern in group)
{
matcher.AddInclude(pattern);
}
}
}
public static IEnumerable<string> GetResultsInFullPath(this Matcher matcher, string directoryPath)
{
var matches = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(directoryPath))).Files;
var result = matches.Select(match => Path.GetFullPath(Path.Combine(directoryPath, match.Path))).ToArray();
return result;
}
}
}

View file

@ -0,0 +1,17 @@
// 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;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing
{
public class PatternMatchingResult
{
public PatternMatchingResult(IEnumerable<FilePatternMatch> files)
{
Files = files;
}
public IEnumerable<FilePatternMatch> Files { get; set; }
}
}

View file

@ -0,0 +1,33 @@
// 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;
namespace Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Util
{
internal static class StringComparisonHelper
{
public static StringComparer GetStringComparer(StringComparison comparisonType)
{
switch (comparisonType)
{
case StringComparison.CurrentCulture:
return StringComparer.CurrentCulture;
case StringComparison.CurrentCultureIgnoreCase:
return StringComparer.CurrentCultureIgnoreCase;
case StringComparison.Ordinal:
return StringComparer.Ordinal;
case StringComparison.OrdinalIgnoreCase:
return StringComparer.OrdinalIgnoreCase;
#if NET451
case StringComparison.InvariantCulture:
return StringComparer.InvariantCulture;
case StringComparison.InvariantCultureIgnoreCase:
return StringComparer.InvariantCultureIgnoreCase;
#endif
default:
throw new InvalidOperationException($"Unexpected StringComparison type: {comparisonType}");
}
}
}
}

View file

@ -2,7 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.Extensions.Internal;
using Microsoft.DotNet.InternalAbstractions;
namespace Microsoft.DotNet.ProjectModel.Files
{

View file

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.ProjectModel.Utilities;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing;
namespace Microsoft.DotNet.ProjectModel.Files
{

View file

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing;
using Newtonsoft.Json.Linq;
namespace Microsoft.DotNet.ProjectModel.Files

View file

@ -2,8 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.Extensions.Internal;
using NuGet;
using Microsoft.DotNet.InternalAbstractions;
using NuGet.Versioning;
namespace Microsoft.DotNet.ProjectModel.Graph

View file

@ -3,7 +3,7 @@
using System;
using System.Text;
using Microsoft.Extensions.Internal;
using Microsoft.DotNet.InternalAbstractions;
using NuGet.Versioning;
namespace Microsoft.DotNet.ProjectModel.Graph

View file

@ -4,8 +4,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.InternalAbstractions;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.Extensions.Internal;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ProjectModel

View file

@ -9,7 +9,6 @@ using System.Text;
using Microsoft.DotNet.InternalAbstractions;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.ProjectModel.Resolution;
using Microsoft.Extensions.Internal;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ProjectModel

View file

@ -1,7 +1,7 @@
// 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.Extensions.Internal;
using Microsoft.DotNet.InternalAbstractions;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ProjectModel

View file

@ -6,11 +6,6 @@
"description": "Types to model a .NET Project",
"dependencies": {
"Microsoft.Extensions.DependencyModel": "1.0.0-*",
"Microsoft.Extensions.FileSystemGlobbing": "1.0.0-rc2-20581",
"Microsoft.Extensions.HashCodeCombiner.Sources": {
"type": "build",
"version": "1.0.0-rc2-20459"
},
"Newtonsoft.Json": "7.0.1",
"NuGet.Packaging": "3.5.0-beta-1199",
"NuGet.RuntimeModel": "3.5.0-beta-1199",

View file

@ -2,7 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.Extensions.Internal;
using Microsoft.DotNet.InternalAbstractions;
namespace Microsoft.Extensions.DependencyModel
{

View file

@ -14,10 +14,6 @@
"target": "project",
"version": "1.0.0-*"
},
"Microsoft.Extensions.HashCodeCombiner.Sources": {
"type": "build",
"version": "1.0.0-rc2-20459"
},
"Newtonsoft.Json": "7.0.1"
},
"frameworks": {

View file

@ -5,9 +5,9 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
namespace Microsoft.DotNet.Tools.Compiler
{

View file

@ -8,12 +8,12 @@ using System.Linq;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing;
using Microsoft.DotNet.ProjectModel.FileSystemGlobbing.Abstractions;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.ProjectModel.Resources;
using Microsoft.DotNet.ProjectModel.Utilities;
using Microsoft.DotNet.Tools.Pack;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
using NuGet;
using NuGet.Frameworks;
using NuGet.Packaging.Core;
@ -167,7 +167,11 @@ namespace Microsoft.DotNet.Tools.Compiler
}
}
internal static IEnumerable<PhysicalPackageFile> CollectAdditionalFiles(DirectoryInfoBase rootDirectory, IEnumerable<PackIncludeEntry> projectFileGlobs, string projectFilePath, IList<DiagnosticMessage> diagnostics)
internal static IEnumerable<PhysicalPackageFile> CollectAdditionalFiles(
DirectoryInfoBase rootDirectory,
IEnumerable<PackIncludeEntry> projectFileGlobs,
string projectFilePath,
IList<DiagnosticMessage> diagnostics)
{
foreach (var entry in projectFileGlobs)
{

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;