Reorganize source code in preparation to move into aspnet/Extensions
Prior to reorganization, this source code was found in https://github.com/aspnet/FileSystem/tree/dotnet/extensions@baebb8b0c672ab37bac72d7196da1b919d362cc5
\n\nCommit migrated from c087cadf1d
This commit is contained in:
commit
574a034ddd
|
|
@ -0,0 +1,8 @@
|
|||
<Project>
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.props))\Directory.Build.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateDocumentationFile Condition=" '$(IsTestProject)' != 'true' ">true</GenerateDocumentationFile>
|
||||
<PackageTags>files;filesystem</PackageTags>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.FileProviders.Embedded;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Looks up files using embedded resources in the specified assembly.
|
||||
/// This file provider is case sensitive.
|
||||
/// </summary>
|
||||
public class EmbeddedFileProvider : IFileProvider
|
||||
{
|
||||
private static readonly char[] _invalidFileNameChars = Path.GetInvalidFileNameChars()
|
||||
.Where(c => c != '/' && c != '\\').ToArray();
|
||||
|
||||
private readonly Assembly _assembly;
|
||||
private readonly string _baseNamespace;
|
||||
private readonly DateTimeOffset _lastModified;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EmbeddedFileProvider" /> class using the specified
|
||||
/// assembly with the base namespace defaulting to the assembly name.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly that contains the embedded resources.</param>
|
||||
public EmbeddedFileProvider(Assembly assembly)
|
||||
: this(assembly, assembly?.GetName()?.Name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EmbeddedFileProvider" /> class using the specified
|
||||
/// assembly and base namespace.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly that contains the embedded resources.</param>
|
||||
/// <param name="baseNamespace">The base namespace that contains the embedded resources.</param>
|
||||
public EmbeddedFileProvider(Assembly assembly, string baseNamespace)
|
||||
{
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new ArgumentNullException("assembly");
|
||||
}
|
||||
|
||||
_baseNamespace = string.IsNullOrEmpty(baseNamespace) ? string.Empty : baseNamespace + ".";
|
||||
_assembly = assembly;
|
||||
|
||||
_lastModified = DateTimeOffset.UtcNow;
|
||||
|
||||
if (!string.IsNullOrEmpty(_assembly.Location))
|
||||
{
|
||||
try
|
||||
{
|
||||
_lastModified = File.GetLastWriteTimeUtc(_assembly.Location);
|
||||
}
|
||||
catch (PathTooLongException)
|
||||
{
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locates a file at the given path.
|
||||
/// </summary>
|
||||
/// <param name="subpath">The path that identifies the file. </param>
|
||||
/// <returns>
|
||||
/// The file information. Caller must check Exists property. A <see cref="NotFoundFileInfo" /> if the file could
|
||||
/// not be found.
|
||||
/// </returns>
|
||||
public IFileInfo GetFileInfo(string subpath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(subpath))
|
||||
{
|
||||
return new NotFoundFileInfo(subpath);
|
||||
}
|
||||
|
||||
var builder = new StringBuilder(_baseNamespace.Length + subpath.Length);
|
||||
builder.Append(_baseNamespace);
|
||||
|
||||
// Relative paths starting with a leading slash okay
|
||||
if (subpath.StartsWith("/", StringComparison.Ordinal))
|
||||
{
|
||||
builder.Append(subpath, 1, subpath.Length - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(subpath);
|
||||
}
|
||||
|
||||
for (var i = _baseNamespace.Length; i < builder.Length; i++)
|
||||
{
|
||||
if (builder[i] == '/' || builder[i] == '\\')
|
||||
{
|
||||
builder[i] = '.';
|
||||
}
|
||||
}
|
||||
|
||||
var resourcePath = builder.ToString();
|
||||
if (HasInvalidPathChars(resourcePath))
|
||||
{
|
||||
return new NotFoundFileInfo(resourcePath);
|
||||
}
|
||||
|
||||
var name = Path.GetFileName(subpath);
|
||||
if (_assembly.GetManifestResourceInfo(resourcePath) == null)
|
||||
{
|
||||
return new NotFoundFileInfo(name);
|
||||
}
|
||||
|
||||
return new EmbeddedResourceFileInfo(_assembly, resourcePath, name, _lastModified);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerate a directory at the given path, if any.
|
||||
/// This file provider uses a flat directory structure. Everything under the base namespace is considered to be one
|
||||
/// directory.
|
||||
/// </summary>
|
||||
/// <param name="subpath">The path that identifies the directory</param>
|
||||
/// <returns>
|
||||
/// Contents of the directory. Caller must check Exists property. A <see cref="NotFoundDirectoryContents" /> if no
|
||||
/// resources were found that match <paramref name="subpath" />
|
||||
/// </returns>
|
||||
public IDirectoryContents GetDirectoryContents(string subpath)
|
||||
{
|
||||
// The file name is assumed to be the remainder of the resource name.
|
||||
if (subpath == null)
|
||||
{
|
||||
return NotFoundDirectoryContents.Singleton;
|
||||
}
|
||||
|
||||
// EmbeddedFileProvider only supports a flat file structure at the base namespace.
|
||||
if (subpath.Length != 0 && !string.Equals(subpath, "/", StringComparison.Ordinal))
|
||||
{
|
||||
return NotFoundDirectoryContents.Singleton;
|
||||
}
|
||||
|
||||
var entries = new List<IFileInfo>();
|
||||
|
||||
// TODO: The list of resources in an assembly isn't going to change. Consider caching.
|
||||
var resources = _assembly.GetManifestResourceNames();
|
||||
for (var i = 0; i < resources.Length; i++)
|
||||
{
|
||||
var resourceName = resources[i];
|
||||
if (resourceName.StartsWith(_baseNamespace, StringComparison.Ordinal))
|
||||
{
|
||||
entries.Add(new EmbeddedResourceFileInfo(
|
||||
_assembly,
|
||||
resourceName,
|
||||
resourceName.Substring(_baseNamespace.Length),
|
||||
_lastModified));
|
||||
}
|
||||
}
|
||||
|
||||
return new EnumerableDirectoryContents(entries);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Embedded files do not change.
|
||||
/// </summary>
|
||||
/// <param name="pattern">This parameter is ignored</param>
|
||||
/// <returns>A <see cref="NullChangeToken" /></returns>
|
||||
public IChangeToken Watch(string pattern)
|
||||
{
|
||||
return NullChangeToken.Singleton;
|
||||
}
|
||||
|
||||
private static bool HasInvalidPathChars(string path)
|
||||
{
|
||||
return path.IndexOfAny(_invalidFileNameChars) != -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a file embedded in an assembly.
|
||||
/// </summary>
|
||||
public class EmbeddedResourceFileInfo : IFileInfo
|
||||
{
|
||||
private readonly Assembly _assembly;
|
||||
private readonly string _resourcePath;
|
||||
|
||||
private long? _length;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="EmbeddedFileProvider"/> for an assembly using <paramref name="resourcePath"/> as the base
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly that contains the embedded resource</param>
|
||||
/// <param name="resourcePath">The path to the embedded resource</param>
|
||||
/// <param name="name">An arbitrary name for this instance</param>
|
||||
/// <param name="lastModified">The <see cref="DateTimeOffset" /> to use for <see cref="LastModified" /></param>
|
||||
public EmbeddedResourceFileInfo(
|
||||
Assembly assembly,
|
||||
string resourcePath,
|
||||
string name,
|
||||
DateTimeOffset lastModified)
|
||||
{
|
||||
_assembly = assembly;
|
||||
_resourcePath = resourcePath;
|
||||
Name = name;
|
||||
LastModified = lastModified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always true.
|
||||
/// </summary>
|
||||
public bool Exists => true;
|
||||
|
||||
/// <summary>
|
||||
/// The length, in bytes, of the embedded resource
|
||||
/// </summary>
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_length.HasValue)
|
||||
{
|
||||
using (var stream = _assembly.GetManifestResourceStream(_resourcePath))
|
||||
{
|
||||
_length = stream.Length;
|
||||
}
|
||||
}
|
||||
return _length.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always null.
|
||||
/// </summary>
|
||||
public string PhysicalPath => null;
|
||||
|
||||
/// <summary>
|
||||
/// The name of embedded file
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The time, in UTC, when the <see cref="EmbeddedFileProvider"/> was created
|
||||
/// </summary>
|
||||
public DateTimeOffset LastModified { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Always false.
|
||||
/// </summary>
|
||||
public bool IsDirectory => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Stream CreateReadStream()
|
||||
{
|
||||
var stream = _assembly.GetManifestResourceStream(_resourcePath);
|
||||
if (!_length.HasValue)
|
||||
{
|
||||
_length = stream.Length;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded
|
||||
{
|
||||
internal class EnumerableDirectoryContents : IDirectoryContents
|
||||
{
|
||||
private readonly IEnumerable<IFileInfo> _entries;
|
||||
|
||||
public EnumerableDirectoryContents(IEnumerable<IFileInfo> entries)
|
||||
{
|
||||
if (entries == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entries));
|
||||
}
|
||||
|
||||
_entries = entries;
|
||||
}
|
||||
|
||||
public bool Exists
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public IEnumerator<IFileInfo> GetEnumerator()
|
||||
{
|
||||
return _entries.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _entries.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
internal class EmbeddedFilesManifest
|
||||
{
|
||||
private static readonly char[] _invalidFileNameChars = Path.GetInvalidFileNameChars()
|
||||
.Where(c => c != Path.DirectorySeparatorChar && c != Path.AltDirectorySeparatorChar).ToArray();
|
||||
|
||||
private static readonly char[] _separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
|
||||
|
||||
private readonly ManifestDirectory _rootDirectory;
|
||||
|
||||
internal EmbeddedFilesManifest(ManifestDirectory rootDirectory)
|
||||
{
|
||||
if (rootDirectory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(rootDirectory));
|
||||
}
|
||||
|
||||
_rootDirectory = rootDirectory;
|
||||
}
|
||||
|
||||
internal ManifestEntry ResolveEntry(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path) || HasInvalidPathChars(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// trimmed is a string without leading nor trailing path separators
|
||||
// so if we find an empty string while iterating over the segments
|
||||
// we know for sure the path is invalid and we treat it as the above
|
||||
// case by returning null.
|
||||
// Examples of invalid paths are: //wwwroot /\wwwroot //wwwroot//jquery.js
|
||||
var trimmed = RemoveLeadingAndTrailingDirectorySeparators(path);
|
||||
// Paths consisting only of a single path separator like / or \ are ok.
|
||||
if (trimmed.Length == 0)
|
||||
{
|
||||
return _rootDirectory;
|
||||
}
|
||||
|
||||
var tokenizer = new StringTokenizer(trimmed, _separators);
|
||||
ManifestEntry currentEntry = _rootDirectory;
|
||||
foreach (var segment in tokenizer)
|
||||
{
|
||||
if (segment.Equals(""))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
currentEntry = currentEntry.Traverse(segment);
|
||||
}
|
||||
|
||||
return currentEntry;
|
||||
}
|
||||
|
||||
private static StringSegment RemoveLeadingAndTrailingDirectorySeparators(string path)
|
||||
{
|
||||
Debug.Assert(path.Length > 0);
|
||||
var start = Array.IndexOf(_separators, path[0]) == -1 ? 0 : 1;
|
||||
if (start == path.Length)
|
||||
{
|
||||
return StringSegment.Empty;
|
||||
}
|
||||
|
||||
var end = Array.IndexOf(_separators, path[path.Length - 1]) == -1 ? path.Length : path.Length - 1;
|
||||
var trimmed = new StringSegment(path, start, end - start);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
internal EmbeddedFilesManifest Scope(string path)
|
||||
{
|
||||
if (ResolveEntry(path) is ManifestDirectory directory && directory != ManifestEntry.UnknownPath)
|
||||
{
|
||||
return new EmbeddedFilesManifest(directory.ToRootDirectory());
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Invalid path: '{path}'");
|
||||
}
|
||||
|
||||
private static bool HasInvalidPathChars(string path) => path.IndexOfAny(_invalidFileNameChars) != -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
internal class ManifestDirectory : ManifestEntry
|
||||
{
|
||||
protected ManifestDirectory(string name, ManifestEntry[] children)
|
||||
: base(name)
|
||||
{
|
||||
if (children == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(children));
|
||||
}
|
||||
|
||||
Children = children;
|
||||
}
|
||||
|
||||
public IReadOnlyList<ManifestEntry> Children { get; protected set; }
|
||||
|
||||
public override ManifestEntry Traverse(StringSegment segment)
|
||||
{
|
||||
if (segment.Equals(".", StringComparison.Ordinal))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
if (segment.Equals("..", StringComparison.Ordinal))
|
||||
{
|
||||
return Parent;
|
||||
}
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (segment.Equals(child.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return UnknownPath;
|
||||
}
|
||||
|
||||
public virtual ManifestDirectory ToRootDirectory() => CreateRootDirectory(CopyChildren());
|
||||
|
||||
public static ManifestDirectory CreateDirectory(string name, ManifestEntry[] children)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentException($"'{nameof(name)}' must not be null, empty or whitespace.", nameof(name));
|
||||
}
|
||||
|
||||
if (children == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(children));
|
||||
}
|
||||
|
||||
var result = new ManifestDirectory(name, children);
|
||||
ValidateChildrenAndSetParent(children, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ManifestRootDirectory CreateRootDirectory(ManifestEntry[] children)
|
||||
{
|
||||
if (children == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(children));
|
||||
}
|
||||
|
||||
var result = new ManifestRootDirectory(children);
|
||||
ValidateChildrenAndSetParent(children, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static void ValidateChildrenAndSetParent(ManifestEntry[] children, ManifestDirectory parent)
|
||||
{
|
||||
foreach (var child in children)
|
||||
{
|
||||
if (child == UnknownPath)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid entry type '{nameof(ManifestSinkDirectory)}'");
|
||||
}
|
||||
|
||||
if (child is ManifestRootDirectory)
|
||||
{
|
||||
throw new InvalidOperationException($"Can't add a root folder as a child");
|
||||
}
|
||||
|
||||
child.SetParent(parent);
|
||||
}
|
||||
}
|
||||
|
||||
private ManifestEntry[] CopyChildren()
|
||||
{
|
||||
var list = new List<ManifestEntry>();
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
var child = Children[i];
|
||||
switch (child)
|
||||
{
|
||||
case ManifestSinkDirectory s:
|
||||
case ManifestRootDirectory r:
|
||||
throw new InvalidOperationException("Unexpected manifest node.");
|
||||
case ManifestDirectory d:
|
||||
var grandChildren = d.CopyChildren();
|
||||
var newDirectory = CreateDirectory(d.Name, grandChildren);
|
||||
list.Add(newDirectory);
|
||||
break;
|
||||
case ManifestFile f:
|
||||
var file = new ManifestFile(f.Name, f.ResourcePath);
|
||||
list.Add(file);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("Unexpected manifest node.");
|
||||
}
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
internal class ManifestDirectoryContents : IDirectoryContents
|
||||
{
|
||||
private readonly DateTimeOffset _lastModified;
|
||||
private IFileInfo[] _entries;
|
||||
|
||||
public ManifestDirectoryContents(Assembly assembly, ManifestDirectory directory, DateTimeOffset lastModified)
|
||||
{
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
}
|
||||
|
||||
if (directory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(directory));
|
||||
}
|
||||
|
||||
Assembly = assembly;
|
||||
Directory = directory;
|
||||
_lastModified = lastModified;
|
||||
}
|
||||
|
||||
public bool Exists => true;
|
||||
|
||||
public Assembly Assembly { get; }
|
||||
|
||||
public ManifestDirectory Directory { get; }
|
||||
|
||||
public IEnumerator<IFileInfo> GetEnumerator()
|
||||
{
|
||||
return EnsureEntries().GetEnumerator();
|
||||
|
||||
IReadOnlyList<IFileInfo> EnsureEntries() => _entries = _entries ?? ResolveEntries().ToArray();
|
||||
|
||||
IEnumerable<IFileInfo> ResolveEntries()
|
||||
{
|
||||
if (Directory == ManifestEntry.UnknownPath)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (var entry in Directory.Children)
|
||||
{
|
||||
switch (entry)
|
||||
{
|
||||
case ManifestFile f:
|
||||
yield return new ManifestFileInfo(Assembly, f, _lastModified);
|
||||
break;
|
||||
case ManifestDirectory d:
|
||||
yield return new ManifestDirectoryInfo(d, _lastModified);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("Unknown entry type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
internal class ManifestDirectoryInfo : IFileInfo
|
||||
{
|
||||
public ManifestDirectoryInfo(ManifestDirectory directory, DateTimeOffset lastModified)
|
||||
{
|
||||
if (directory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(directory));
|
||||
}
|
||||
|
||||
Directory = directory;
|
||||
LastModified = lastModified;
|
||||
}
|
||||
|
||||
public bool Exists => true;
|
||||
|
||||
public long Length => -1;
|
||||
|
||||
public string PhysicalPath => null;
|
||||
|
||||
public string Name => Directory.Name;
|
||||
|
||||
public DateTimeOffset LastModified { get; }
|
||||
|
||||
public bool IsDirectory => true;
|
||||
|
||||
public ManifestDirectory Directory { get; }
|
||||
|
||||
public Stream CreateReadStream() =>
|
||||
throw new InvalidOperationException("Cannot create a stream for a directory.");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
internal abstract class ManifestEntry
|
||||
{
|
||||
public ManifestEntry(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public ManifestEntry Parent { get; private set; }
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public static ManifestEntry UnknownPath { get; } = ManifestSinkDirectory.Instance;
|
||||
|
||||
protected internal virtual void SetParent(ManifestDirectory directory)
|
||||
{
|
||||
if (Parent != null)
|
||||
{
|
||||
throw new InvalidOperationException("Directory already has a parent.");
|
||||
}
|
||||
|
||||
Parent = directory;
|
||||
}
|
||||
|
||||
public abstract ManifestEntry Traverse(StringSegment segment);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
internal class ManifestFile : ManifestEntry
|
||||
{
|
||||
public ManifestFile(string name, string resourcePath)
|
||||
: base(name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentException($"'{nameof(name)}' must not be null, empty or whitespace.", nameof(name));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(resourcePath))
|
||||
{
|
||||
throw new ArgumentException($"'{nameof(resourcePath)}' must not be null, empty or whitespace.", nameof(resourcePath));
|
||||
}
|
||||
|
||||
ResourcePath = resourcePath;
|
||||
}
|
||||
|
||||
public string ResourcePath { get; }
|
||||
|
||||
public override ManifestEntry Traverse(StringSegment segment) => UnknownPath;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
internal class ManifestFileInfo : IFileInfo
|
||||
{
|
||||
private long? _length;
|
||||
|
||||
public ManifestFileInfo(Assembly assembly, ManifestFile file, DateTimeOffset lastModified)
|
||||
{
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
}
|
||||
|
||||
if (file == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(file));
|
||||
}
|
||||
|
||||
Assembly = assembly;
|
||||
ManifestFile = file;
|
||||
LastModified = lastModified;
|
||||
}
|
||||
|
||||
public Assembly Assembly { get; }
|
||||
|
||||
public ManifestFile ManifestFile { get; }
|
||||
|
||||
public bool Exists => true;
|
||||
|
||||
public long Length => EnsureLength();
|
||||
|
||||
public string PhysicalPath => null;
|
||||
|
||||
public string Name => ManifestFile.Name;
|
||||
|
||||
public DateTimeOffset LastModified { get; }
|
||||
|
||||
public bool IsDirectory => false;
|
||||
|
||||
private long EnsureLength()
|
||||
{
|
||||
if (_length == null)
|
||||
{
|
||||
using (var stream = Assembly.GetManifestResourceStream(ManifestFile.ResourcePath))
|
||||
{
|
||||
_length = stream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
return _length.Value;
|
||||
}
|
||||
|
||||
public Stream CreateReadStream()
|
||||
{
|
||||
var stream = Assembly.GetManifestResourceStream(ManifestFile.ResourcePath);
|
||||
if (!_length.HasValue)
|
||||
{
|
||||
_length = stream.Length;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
internal static class ManifestParser
|
||||
{
|
||||
private static readonly string DefaultManifestName = "Microsoft.Extensions.FileProviders.Embedded.Manifest.xml";
|
||||
|
||||
public static EmbeddedFilesManifest Parse(Assembly assembly)
|
||||
{
|
||||
return Parse(assembly, DefaultManifestName);
|
||||
}
|
||||
|
||||
public static EmbeddedFilesManifest Parse(Assembly assembly, string name)
|
||||
{
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
}
|
||||
|
||||
if (name == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
var stream = assembly.GetManifestResourceStream(name);
|
||||
if (stream == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not load the embedded file manifest " +
|
||||
$"'{name}' for assembly '{assembly.GetName().Name}'.");
|
||||
}
|
||||
|
||||
var document = XDocument.Load(stream);
|
||||
|
||||
var manifest = EnsureElement(document, "Manifest");
|
||||
var manifestVersion = EnsureElement(manifest, "ManifestVersion");
|
||||
var version = EnsureText(manifestVersion);
|
||||
if (!string.Equals("1.0", version, StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException($"The embedded file manifest '{name}' for " +
|
||||
$"assembly '{assembly.GetName().Name}' specifies an unsupported file format" +
|
||||
$" version: '{version}'.");
|
||||
}
|
||||
var fileSystem = EnsureElement(manifest, "FileSystem");
|
||||
|
||||
var entries = fileSystem.Elements();
|
||||
var entriesList = new List<ManifestEntry>();
|
||||
foreach (var element in entries)
|
||||
{
|
||||
var entry = BuildEntry(element);
|
||||
entriesList.Add(entry);
|
||||
}
|
||||
|
||||
ValidateEntries(entriesList);
|
||||
|
||||
var rootDirectory = ManifestDirectory.CreateRootDirectory(entriesList.ToArray());
|
||||
|
||||
return new EmbeddedFilesManifest(rootDirectory);
|
||||
|
||||
}
|
||||
|
||||
private static void ValidateEntries(List<ManifestEntry> entriesList)
|
||||
{
|
||||
for (int i = 0; i < entriesList.Count - 1; i++)
|
||||
{
|
||||
for (int j = i + 1; j < entriesList.Count; j++)
|
||||
{
|
||||
if (string.Equals(entriesList[i].Name, entriesList[j].Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Found two entries with the same name but different casing:" +
|
||||
$" '{entriesList[i].Name}' and '{entriesList[j]}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ManifestEntry BuildEntry(XElement element)
|
||||
{
|
||||
RuntimeHelpers.EnsureSufficientExecutionStack();
|
||||
if (element.NodeType != XmlNodeType.Element)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid manifest format. Expected a 'File' or a 'Directory' node:" +
|
||||
$" '{element.ToString()}'");
|
||||
}
|
||||
|
||||
if (string.Equals(element.Name.LocalName, "File", StringComparison.Ordinal))
|
||||
{
|
||||
var entryName = EnsureName(element);
|
||||
var path = EnsureElement(element, "ResourcePath");
|
||||
var pathValue = EnsureText(path);
|
||||
return new ManifestFile(entryName, pathValue);
|
||||
}
|
||||
|
||||
if (string.Equals(element.Name.LocalName, "Directory", StringComparison.Ordinal))
|
||||
{
|
||||
var directoryName = EnsureName(element);
|
||||
var children = new List<ManifestEntry>();
|
||||
foreach (var child in element.Elements())
|
||||
{
|
||||
children.Add(BuildEntry(child));
|
||||
}
|
||||
|
||||
ValidateEntries(children);
|
||||
|
||||
return ManifestDirectory.CreateDirectory(directoryName, children.ToArray());
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Invalid manifest format.Expected a 'File' or a 'Directory' node. " +
|
||||
$"Got '{element.Name.LocalName}' instead.");
|
||||
}
|
||||
|
||||
private static XElement EnsureElement(XContainer container, string elementName)
|
||||
{
|
||||
var element = container.Element(elementName);
|
||||
if (element == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid manifest format. Missing '{elementName}' element name");
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private static string EnsureName(XElement element)
|
||||
{
|
||||
var value = element.Attribute("Name")?.Value;
|
||||
if (value == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid manifest format. '{element.Name}' must contain a 'Name' attribute.");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static string EnsureText(XElement element)
|
||||
{
|
||||
if (element.Elements().Count() == 0 &&
|
||||
!element.IsEmpty &&
|
||||
element.Nodes().Count() == 1 &&
|
||||
element.FirstNode.NodeType == XmlNodeType.Text)
|
||||
{
|
||||
return element.Value;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Invalid manifest format. '{element.Name.LocalName}' must contain " +
|
||||
$"a text value. '{element.Value}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
internal class ManifestRootDirectory : ManifestDirectory
|
||||
{
|
||||
public ManifestRootDirectory(ManifestEntry[] children)
|
||||
: base(name: null, children: children)
|
||||
{
|
||||
SetParent(ManifestSinkDirectory.Instance);
|
||||
}
|
||||
|
||||
public override ManifestDirectory ToRootDirectory() => this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
internal class ManifestSinkDirectory : ManifestDirectory
|
||||
{
|
||||
private ManifestSinkDirectory()
|
||||
: base(name: null, children: Array.Empty<ManifestEntry>())
|
||||
{
|
||||
SetParent(this);
|
||||
Children = new[] { this };
|
||||
}
|
||||
|
||||
public static ManifestDirectory Instance { get; } = new ManifestSinkDirectory();
|
||||
|
||||
public override ManifestEntry Traverse(StringSegment segment) => this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.FileProviders.Embedded.Manifest;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// An embedded file provider that uses a manifest compiled in the assembly to
|
||||
/// reconstruct the original paths of the embedded files when they were embedded
|
||||
/// into the assembly.
|
||||
/// </summary>
|
||||
public class ManifestEmbeddedFileProvider : IFileProvider
|
||||
{
|
||||
private readonly DateTimeOffset _lastModified;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly containing the embedded files.</param>
|
||||
public ManifestEmbeddedFileProvider(Assembly assembly)
|
||||
: this(assembly, ManifestParser.Parse(assembly), ResolveLastModified(assembly)) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly containing the embedded files.</param>
|
||||
/// <param name="root">The relative path from the root of the manifest to use as root for the provider.</param>
|
||||
public ManifestEmbeddedFileProvider(Assembly assembly, string root)
|
||||
: this(assembly, root, ResolveLastModified(assembly))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly containing the embedded files.</param>
|
||||
/// <param name="root">The relative path from the root of the manifest to use as root for the provider.</param>
|
||||
/// <param name="lastModified">The LastModified date to use on the <see cref="IFileInfo"/> instances
|
||||
/// returned by this <see cref="IFileProvider"/>.</param>
|
||||
public ManifestEmbeddedFileProvider(Assembly assembly, string root, DateTimeOffset lastModified)
|
||||
: this(assembly, ManifestParser.Parse(assembly).Scope(root), lastModified)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly containing the embedded files.</param>
|
||||
/// <param name="root">The relative path from the root of the manifest to use as root for the provider.</param>
|
||||
/// <param name="manifestName">The name of the embedded resource containing the manifest.</param>
|
||||
/// <param name="lastModified">The LastModified date to use on the <see cref="IFileInfo"/> instances
|
||||
/// returned by this <see cref="IFileProvider"/>.</param>
|
||||
public ManifestEmbeddedFileProvider(Assembly assembly, string root, string manifestName, DateTimeOffset lastModified)
|
||||
: this(assembly, ManifestParser.Parse(assembly, manifestName).Scope(root), lastModified)
|
||||
{
|
||||
}
|
||||
|
||||
internal ManifestEmbeddedFileProvider(Assembly assembly, EmbeddedFilesManifest manifest, DateTimeOffset lastModified)
|
||||
{
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
}
|
||||
|
||||
if (manifest == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(manifest));
|
||||
}
|
||||
|
||||
Assembly = assembly;
|
||||
Manifest = manifest;
|
||||
_lastModified = lastModified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Assembly"/> for this provider.
|
||||
/// </summary>
|
||||
public Assembly Assembly { get; }
|
||||
|
||||
internal EmbeddedFilesManifest Manifest { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDirectoryContents GetDirectoryContents(string subpath)
|
||||
{
|
||||
var entry = Manifest.ResolveEntry(subpath);
|
||||
if (entry == null || entry == ManifestEntry.UnknownPath)
|
||||
{
|
||||
return NotFoundDirectoryContents.Singleton;
|
||||
}
|
||||
|
||||
if (!(entry is ManifestDirectory directory))
|
||||
{
|
||||
return NotFoundDirectoryContents.Singleton;
|
||||
}
|
||||
|
||||
return new ManifestDirectoryContents(Assembly, directory, _lastModified);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IFileInfo GetFileInfo(string subpath)
|
||||
{
|
||||
var entry = Manifest.ResolveEntry(subpath);
|
||||
switch (entry)
|
||||
{
|
||||
case null:
|
||||
return new NotFoundFileInfo(subpath);
|
||||
case ManifestFile f:
|
||||
return new ManifestFileInfo(Assembly, f, _lastModified);
|
||||
case ManifestDirectory d when d != ManifestEntry.UnknownPath:
|
||||
return new NotFoundFileInfo(d.Name);
|
||||
}
|
||||
|
||||
return new NotFoundFileInfo(subpath);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IChangeToken Watch(string filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filter));
|
||||
}
|
||||
|
||||
return NullChangeToken.Singleton;
|
||||
}
|
||||
|
||||
private static DateTimeOffset ResolveLastModified(Assembly assembly)
|
||||
{
|
||||
var result = DateTimeOffset.UtcNow;
|
||||
|
||||
if (!string.IsNullOrEmpty(assembly.Location))
|
||||
{
|
||||
try
|
||||
{
|
||||
result = File.GetLastWriteTimeUtc(assembly.Location);
|
||||
}
|
||||
catch (PathTooLongException)
|
||||
{
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.Extensions.FileProviders</RootNamespace>
|
||||
<Description>File provider for files in embedded resources for Microsoft.Extensions.FileProviders.</Description>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
|
||||
<ProjectReference Include="..\..\Manifest.MSBuildTask\src\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj" PrivateAssets="All" ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PopulateNuspec" BeforeTargets="GenerateNuspec" DependsOnTargets="BuiltProjectOutputGroup;DocumentationProjectOutputGroup;DebugSymbolsProjectOutputGroup;">
|
||||
|
||||
<PropertyGroup>
|
||||
<NuspecProperties>
|
||||
id=$(PackageId);
|
||||
version=$(PackageVersion);
|
||||
authors=$(Authors);
|
||||
description=$(Description);
|
||||
tags=$(PackageTags.Replace(';', ' '));
|
||||
licenseUrl=$(PackageLicenseUrl);
|
||||
projectUrl=$(PackageProjectUrl);
|
||||
iconUrl=$(PackageIconUrl);
|
||||
repositoryUrl=$(RepositoryUrl);
|
||||
repositoryCommit=$(RepositoryCommit);
|
||||
copyright=$(Copyright);
|
||||
targetframework=$(TargetFramework);
|
||||
AssemblyName=$(AssemblyName);
|
||||
|
||||
OutputBinary=@(BuiltProjectOutputGroupOutput);
|
||||
OutputSymbol=@(DebugSymbolsProjectOutputGroupOutput);
|
||||
OutputDocumentation=@(DocumentationProjectOutputGroupOutput);
|
||||
|
||||
<!-- Include the assembly and symbols from the tasks project -->
|
||||
TaskAssemblyNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard1.5\$(AssemblyName).Manifest.Task.dll;
|
||||
TaskSymbolNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard1.5\$(AssemblyName).Manifest.Task.pdb;
|
||||
TaskAssemblyNet461=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.dll;
|
||||
TaskSymbolNet461=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.pdb;
|
||||
</NuspecProperties>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>$id$</id>
|
||||
<version>$version$</version>
|
||||
<authors>$authors$</authors>
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<licenseUrl>$licenseUrl$</licenseUrl>
|
||||
<projectUrl>$projectUrl$</projectUrl>
|
||||
<iconUrl>$iconUrl$</iconUrl>
|
||||
<description>$description$</description>
|
||||
<copyright>$copyright$</copyright>
|
||||
<tags>$tags$</tags>
|
||||
<repository type="git" url="$repositoryUrl$" commit="$repositoryCommit$" />
|
||||
<dependencies>
|
||||
<group targetFramework=".NETStandard2.0">
|
||||
<dependency id="Microsoft.Extensions.FileProviders.Abstractions" version="$version$" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<file src="$OutputBinary$" target="lib\$targetframework$\" />
|
||||
<file src="$OutputSymbol$" target="lib\$targetframework$\" />
|
||||
<file src="$OutputDocumentation$" target="lib\$targetframework$\" />
|
||||
<file src="build\**\*" target="build\" />
|
||||
<file src="buildMultiTargeting\**\*" target="buildMultiTargeting\" />
|
||||
<file src="$TaskAssemblyNetStandard$" target="tasks\netstandard1.5\$AssemblyName$.Manifest.Task.dll" />
|
||||
<file src="$TaskSymbolNetStandard$" target="tasks\netstandard1.5\$AssemblyName$.Manifest.Task.pdb" />
|
||||
<file src="$TaskAssemblyNet461$" target="tasks\net461\$AssemblyName$.Manifest.Task.dll" />
|
||||
<file src="$TaskSymbolNet461$" target="tasks\net461\$AssemblyName$.Manifest.Task.pdb" />
|
||||
</files>
|
||||
</package>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.Extensions.FileProviders.Embedded.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
@ -0,0 +1,343 @@
|
|||
{
|
||||
"AssemblyIdentity": "Microsoft.Extensions.FileProviders.Embedded, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
|
||||
"Types": [
|
||||
{
|
||||
"Name": "Microsoft.Extensions.FileProviders.EmbeddedFileProvider",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [
|
||||
"Microsoft.Extensions.FileProviders.IFileProvider"
|
||||
],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "GetFileInfo",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "subpath",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.Extensions.FileProviders.IFileInfo",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "GetDirectoryContents",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "subpath",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.Extensions.FileProviders.IDirectoryContents",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "Watch",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "pattern",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.Extensions.Primitives.IChangeToken",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "assembly",
|
||||
"Type": "System.Reflection.Assembly"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "assembly",
|
||||
"Type": "System.Reflection.Assembly"
|
||||
},
|
||||
{
|
||||
"Name": "baseNamespace",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.Extensions.FileProviders.ManifestEmbeddedFileProvider",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [
|
||||
"Microsoft.Extensions.FileProviders.IFileProvider"
|
||||
],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_Assembly",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Reflection.Assembly",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "GetDirectoryContents",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "subpath",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.Extensions.FileProviders.IDirectoryContents",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "GetFileInfo",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "subpath",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.Extensions.FileProviders.IFileInfo",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "Watch",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "filter",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.Extensions.Primitives.IChangeToken",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "assembly",
|
||||
"Type": "System.Reflection.Assembly"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "assembly",
|
||||
"Type": "System.Reflection.Assembly"
|
||||
},
|
||||
{
|
||||
"Name": "root",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "assembly",
|
||||
"Type": "System.Reflection.Assembly"
|
||||
},
|
||||
{
|
||||
"Name": "root",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "lastModified",
|
||||
"Type": "System.DateTimeOffset"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "assembly",
|
||||
"Type": "System.Reflection.Assembly"
|
||||
},
|
||||
{
|
||||
"Name": "root",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "manifestName",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "lastModified",
|
||||
"Type": "System.DateTimeOffset"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.Extensions.FileProviders.Embedded.EmbeddedResourceFileInfo",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [
|
||||
"Microsoft.Extensions.FileProviders.IFileInfo"
|
||||
],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_Exists",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Boolean",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_Length",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Int64",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_PhysicalPath",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.String",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_Name",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.String",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_LastModified",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.DateTimeOffset",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_IsDirectory",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Boolean",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "CreateReadStream",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.IO.Stream",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "assembly",
|
||||
"Type": "System.Reflection.Assembly"
|
||||
},
|
||||
{
|
||||
"Name": "resourcePath",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "name",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "lastModified",
|
||||
"Type": "System.DateTimeOffset"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<Project TreatAsLocalProperty="_FileProviderTaskFolder;_FileProviderTaskAssembly">
|
||||
<PropertyGroup>
|
||||
<GenerateEmbeddedFilesManifest Condition="'$(GenerateEmbeddedFilesManifest)' == ''">false</GenerateEmbeddedFilesManifest>
|
||||
<EmbeddedFilesManifestFileName Condition="'$(EmbeddedFilesManifestFileName)' == ''">Microsoft.Extensions.FileProviders.Embedded.Manifest.xml</EmbeddedFilesManifestFileName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard1.5</_FileProviderTaskFolder>
|
||||
<_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' != 'Core'">net461</_FileProviderTaskFolder>
|
||||
<_FileProviderTaskAssembly>$(MSBuildThisFileDirectory)..\..\tasks\$(_FileProviderTaskFolder)\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll</_FileProviderTaskAssembly>
|
||||
</PropertyGroup>
|
||||
|
||||
<UsingTask
|
||||
AssemblyFile="$(_FileProviderTaskAssembly)"
|
||||
TaskName="Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.GenerateEmbeddedResourcesManifest" />
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<PrepareResourceNamesDependsOn>_CalculateEmbeddedFilesManifestInputs;$(PrepareResourceNamesDependsOn)</PrepareResourceNamesDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target
|
||||
Name="_CalculateEmbeddedFilesManifestInputs"
|
||||
Condition="'$(GenerateEmbeddedFilesManifest)' == 'true'">
|
||||
|
||||
<PropertyGroup>
|
||||
<_GeneratedManifestFile>$(IntermediateOutputPath)$(EmbeddedFilesManifestFileName)</_GeneratedManifestFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_FilesForManifest Include="@(EmbeddedResource)" />
|
||||
<_FilesForManifest Remove="@(EmbeddedResource->WithMetadataValue('ExcludeFromManifest','true'))" />
|
||||
</ItemGroup>
|
||||
|
||||
<Warning
|
||||
Text="GenerateEmbeddedFilesManifest was set, but no EmbeddedResource items were found that could be added to the manifest."
|
||||
Condition="@(_FilesForManifest->Count()) == 0" />
|
||||
|
||||
<ItemGroup Condition="@(_FilesForManifest->Count()) != 0">
|
||||
<EmbeddedResource
|
||||
Include="$(_GeneratedManifestFile)"
|
||||
LogicalName="$(EmbeddedFilesManifestFileName)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_CreateGeneratedManifestInfoInputsCacheFile" DependsOnTargets="_CalculateEmbeddedFilesManifestInputs">
|
||||
<PropertyGroup>
|
||||
<_GeneratedManifestInfoInputsCacheFile>$(IntermediateOutputPath)$(MSBuildProjectName).EmbeddedFilesManifest.cache</_GeneratedManifestInfoInputsCacheFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<Hash ItemsToHash="@(_FilesForManifest)">
|
||||
<Output TaskParameter="HashResult" PropertyName="_EmbeddedGeneratedManifestHash" />
|
||||
</Hash>
|
||||
|
||||
<WriteLinesToFile
|
||||
Lines="$(_EmbeddedGeneratedManifestHash)"
|
||||
File="$(_GeneratedManifestInfoInputsCacheFile)"
|
||||
Overwrite="True"
|
||||
WriteOnlyWhenDifferent="True" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(_GeneratedManifestInfoInputsCacheFile)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_GenerateEmbeddedFilesManifest"
|
||||
DependsOnTargets="_CreateGeneratedManifestInfoInputsCacheFile"
|
||||
AfterTargets="PrepareResourceNames"
|
||||
Condition="'$(GenerateEmbeddedFilesManifest)' == 'true' AND @(_FilesForManifest->Count()) != 0"
|
||||
Inputs="$(_GeneratedManifestInfoInputsCacheFile)"
|
||||
Outputs="$(_GeneratedManifestFile)">
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Rebuild _FilesForManifest since PrepareResourceNames would have updated EmbeddedResource. -->
|
||||
<_FilesForManifest Remove="@(_FilesForManifest)" />
|
||||
<_FilesForManifest Include="@(EmbeddedResource)" />
|
||||
<_FilesForManifest Remove="@(EmbeddedResource->WithMetadataValue('ExcludeFromManifest','true'))" />
|
||||
</ItemGroup>
|
||||
|
||||
<GenerateEmbeddedResourcesManifest
|
||||
EmbeddedFiles="@(_FilesForManifest)"
|
||||
ManifestFile="$(_GeneratedManifestFile)" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<Project>
|
||||
<Import Project="..\build\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.props" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<Project>
|
||||
<Import Project="..\build\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.targets" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,231 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Tests
|
||||
{
|
||||
public class EmbeddedFileProviderTests
|
||||
{
|
||||
private static readonly string Namespace = typeof(EmbeddedFileProviderTests).Namespace;
|
||||
|
||||
[Fact]
|
||||
public void ConstructorWithNullAssemblyThrowsArgumentException()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => new EmbeddedFileProvider(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFileInfo_ReturnsNotFoundFileInfo_IfFileDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
|
||||
|
||||
// Act
|
||||
var fileInfo = provider.GetFileInfo("DoesNotExist.Txt");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(fileInfo);
|
||||
Assert.False(fileInfo.Exists);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("File.txt")]
|
||||
[InlineData("/File.txt")]
|
||||
public void GetFileInfo_ReturnsFilesAtRoot(string filePath)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
|
||||
var expectedFileLength = 8;
|
||||
|
||||
// Act
|
||||
var fileInfo = provider.GetFileInfo(filePath);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(fileInfo);
|
||||
Assert.True(fileInfo.Exists);
|
||||
Assert.NotEqual(default(DateTimeOffset), fileInfo.LastModified);
|
||||
Assert.Equal(expectedFileLength, fileInfo.Length);
|
||||
Assert.False(fileInfo.IsDirectory);
|
||||
Assert.Null(fileInfo.PhysicalPath);
|
||||
Assert.Equal("File.txt", fileInfo.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFileInfo_ReturnsNotFoundFileInfo_IfFileDoesNotExistUnderSpecifiedNamespace()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, Namespace + ".SubNamespace");
|
||||
|
||||
// Act
|
||||
var fileInfo = provider.GetFileInfo("File.txt");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(fileInfo);
|
||||
Assert.False(fileInfo.Exists);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFileInfo_ReturnsNotFoundIfPathStartsWithBackSlash()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
|
||||
|
||||
// Act
|
||||
var fileInfo = provider.GetFileInfo("\\File.txt");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(fileInfo);
|
||||
Assert.False(fileInfo.Exists);
|
||||
}
|
||||
|
||||
public static TheoryData GetFileInfo_LocatesFilesUnderSpecifiedNamespaceData
|
||||
{
|
||||
get
|
||||
{
|
||||
var theoryData = new TheoryData<string>
|
||||
{
|
||||
"ResourcesInSubdirectory/File3.txt"
|
||||
};
|
||||
|
||||
if (TestPlatformHelper.IsWindows)
|
||||
{
|
||||
theoryData.Add("ResourcesInSubdirectory\\File3.txt");
|
||||
}
|
||||
|
||||
return theoryData;
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetFileInfo_LocatesFilesUnderSpecifiedNamespaceData))]
|
||||
public void GetFileInfo_LocatesFilesUnderSpecifiedNamespace(string path)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, Namespace + ".Resources");
|
||||
|
||||
// Act
|
||||
var fileInfo = provider.GetFileInfo(path);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(fileInfo);
|
||||
Assert.True(fileInfo.Exists);
|
||||
Assert.NotEqual(default(DateTimeOffset), fileInfo.LastModified);
|
||||
Assert.True(fileInfo.Length > 0);
|
||||
Assert.False(fileInfo.IsDirectory);
|
||||
Assert.Null(fileInfo.PhysicalPath);
|
||||
Assert.Equal("File3.txt", fileInfo.Name);
|
||||
}
|
||||
|
||||
public static TheoryData GetFileInfo_LocatesFilesUnderSubDirectoriesData
|
||||
{
|
||||
get
|
||||
{
|
||||
var theoryData = new TheoryData<string>
|
||||
{
|
||||
"Resources/File.txt"
|
||||
};
|
||||
|
||||
if (TestPlatformHelper.IsWindows)
|
||||
{
|
||||
theoryData.Add("Resources\\File.txt");
|
||||
}
|
||||
|
||||
return theoryData;
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetFileInfo_LocatesFilesUnderSubDirectoriesData))]
|
||||
public void GetFileInfo_LocatesFilesUnderSubDirectories(string path)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
|
||||
|
||||
// Act
|
||||
var fileInfo = provider.GetFileInfo(path);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(fileInfo);
|
||||
Assert.True(fileInfo.Exists);
|
||||
Assert.NotEqual(default(DateTimeOffset), fileInfo.LastModified);
|
||||
Assert.True(fileInfo.Length > 0);
|
||||
Assert.False(fileInfo.IsDirectory);
|
||||
Assert.Null(fileInfo.PhysicalPath);
|
||||
Assert.Equal("File.txt", fileInfo.Name);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData("/")]
|
||||
public void GetDirectoryContents_ReturnsAllFilesInFileSystem(string path)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, Namespace + ".Resources");
|
||||
|
||||
// Act
|
||||
var files = provider.GetDirectoryContents(path);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(files.OrderBy(f => f.Name, StringComparer.Ordinal),
|
||||
file => Assert.Equal("File.txt", file.Name),
|
||||
file => Assert.Equal("ResourcesInSubdirectory.File3.txt", file.Name));
|
||||
|
||||
Assert.False(provider.GetDirectoryContents("file").Exists);
|
||||
Assert.False(provider.GetDirectoryContents("file/").Exists);
|
||||
Assert.False(provider.GetDirectoryContents("file.txt").Exists);
|
||||
Assert.False(provider.GetDirectoryContents("file/txt").Exists);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDirectoryContents_ReturnsEmptySequence_IfResourcesDoNotExistUnderNamespace()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, "Unknown.Namespace");
|
||||
|
||||
// Act
|
||||
var files = provider.GetDirectoryContents(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(files);
|
||||
Assert.True(files.Exists);
|
||||
Assert.Empty(files);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Resources")]
|
||||
[InlineData("/Resources")]
|
||||
public void GetDirectoryContents_ReturnsNotFoundDirectoryContents_IfHierarchicalPathIsSpecified(string path)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
|
||||
|
||||
// Act
|
||||
var files = provider.GetDirectoryContents(path);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(files);
|
||||
Assert.False(files.Exists);
|
||||
Assert.Empty(files);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Watch_ReturnsNoOpTrigger()
|
||||
{
|
||||
// Arange
|
||||
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
|
||||
|
||||
// Act
|
||||
var token = provider.Watch("Resources/File.txt");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(token);
|
||||
Assert.False(token.ActiveChangeCallbacks);
|
||||
Assert.False(token.HasChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Hello
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders
|
||||
{
|
||||
internal class FileInfoComparer : IEqualityComparer<IFileInfo>
|
||||
{
|
||||
public static FileInfoComparer Instance { get; set; } = new FileInfoComparer();
|
||||
|
||||
public bool Equals(IFileInfo x, IFileInfo y)
|
||||
{
|
||||
if (x == null && y == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((x == null && y != null) || (x != null && y == null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return x.Exists == y.Exists &&
|
||||
x.IsDirectory == y.IsDirectory &&
|
||||
x.Length == y.Length &&
|
||||
string.Equals(x.Name, y.Name, StringComparison.Ordinal) &&
|
||||
string.Equals(x.PhysicalPath, y.PhysicalPath, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public int GetHashCode(IFileInfo obj) => 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// 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.
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
public class EmbeddedFilesManifestTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("/wwwroot//jquery.validate.js")]
|
||||
[InlineData("//wwwroot/jquery.validate.js")]
|
||||
public void ResolveEntry_IgnoresInvalidPaths(string path)
|
||||
{
|
||||
// Arrange
|
||||
var manifest = new EmbeddedFilesManifest(
|
||||
ManifestDirectory.CreateRootDirectory(
|
||||
new[]
|
||||
{
|
||||
ManifestDirectory.CreateDirectory("wwwroot",
|
||||
new[]
|
||||
{
|
||||
new ManifestFile("jquery.validate.js","wwwroot.jquery.validate.js")
|
||||
})
|
||||
}));
|
||||
// Act
|
||||
var entry = manifest.ResolveEntry(path);
|
||||
|
||||
// Assert
|
||||
Assert.Null(entry);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/")]
|
||||
[InlineData("./")]
|
||||
[InlineData("/wwwroot/jquery.validate.js")]
|
||||
[InlineData("/wwwroot/")]
|
||||
public void ResolveEntry_AllowsSingleDirectorySeparator(string path)
|
||||
{
|
||||
// Arrange
|
||||
var manifest = new EmbeddedFilesManifest(
|
||||
ManifestDirectory.CreateRootDirectory(
|
||||
new[]
|
||||
{
|
||||
ManifestDirectory.CreateDirectory("wwwroot",
|
||||
new[]
|
||||
{
|
||||
new ManifestFile("jquery.validate.js","wwwroot.jquery.validate.js")
|
||||
})
|
||||
}));
|
||||
// Act
|
||||
var entry = manifest.ResolveEntry(path);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
public class ManifestEntryTests
|
||||
{
|
||||
[Fact]
|
||||
public void TraversingAFile_ReturnsUnknownPath()
|
||||
{
|
||||
// Arrange
|
||||
var file = new ManifestFile("a", "a.b.c");
|
||||
|
||||
// Act
|
||||
var result = file.Traverse(".");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ManifestEntry.UnknownPath, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TraversingANonExistingFile_ReturnsUnknownPath()
|
||||
{
|
||||
// Arrange
|
||||
var directory = ManifestDirectory.CreateDirectory("a", Array.Empty<ManifestEntry>());
|
||||
|
||||
// Act
|
||||
var result = directory.Traverse("missing.txt");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ManifestEntry.UnknownPath, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TraversingWithDot_ReturnsSelf()
|
||||
{
|
||||
// Arrange
|
||||
var directory = ManifestDirectory.CreateDirectory("a", Array.Empty<ManifestEntry>());
|
||||
|
||||
// Act
|
||||
var result = directory.Traverse(".");
|
||||
|
||||
// Assert
|
||||
Assert.Same(directory, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TraversingWithDotDot_ReturnsParent()
|
||||
{
|
||||
// Arrange
|
||||
var childDirectory = ManifestDirectory.CreateDirectory("b", Array.Empty<ManifestEntry>());
|
||||
var directory = ManifestDirectory.CreateDirectory("a", new[] { childDirectory });
|
||||
|
||||
// Act
|
||||
var result = childDirectory.Traverse("..");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(directory, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TraversingRootDirectoryWithDotDot_ReturnsSinkDirectory()
|
||||
{
|
||||
// Arrange
|
||||
var directory = ManifestDirectory.CreateRootDirectory(Array.Empty<ManifestEntry>());
|
||||
|
||||
// Act
|
||||
var result = directory.Traverse("..");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ManifestEntry.UnknownPath, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScopingAFolderAndTryingToGetAScopedFile_ReturnsSinkDirectory()
|
||||
{
|
||||
// Arrange
|
||||
var directory = ManifestDirectory.CreateRootDirectory(new[] {
|
||||
ManifestDirectory.CreateDirectory("a",
|
||||
new[] { new ManifestFile("test1.txt", "text.txt") }),
|
||||
ManifestDirectory.CreateDirectory("b",
|
||||
new[] { new ManifestFile("test2.txt", "test2.txt") }) });
|
||||
|
||||
var newRoot = ((ManifestDirectory)directory.Traverse("a")).ToRootDirectory();
|
||||
|
||||
// Act
|
||||
var result = newRoot.Traverse("../b/test.txt");
|
||||
|
||||
// Assert
|
||||
Assert.Same(ManifestEntry.UnknownPath, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("..")]
|
||||
[InlineData(".")]
|
||||
[InlineData("file.txt")]
|
||||
[InlineData("folder")]
|
||||
public void TraversingUnknownPath_ReturnsSinkDirectory(string path)
|
||||
{
|
||||
// Arrange
|
||||
var directory = ManifestEntry.UnknownPath;
|
||||
|
||||
// Act
|
||||
var result = directory.Traverse(path);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ManifestEntry.UnknownPath, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
public class ManifestParserTests
|
||||
{
|
||||
[Fact]
|
||||
public void Parse_UsesDefaultManifestNameForManifest()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.File("sample.txt")));
|
||||
|
||||
// Act
|
||||
var manifest = ManifestParser.Parse(assembly);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(manifest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_FindsManifestWithCustomName()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.File("sample.txt")),
|
||||
manifestName: "Manifest.xml");
|
||||
|
||||
// Act
|
||||
var manifest = ManifestParser.Parse(assembly, "Manifest.xml");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(manifest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_ThrowsForEntriesWithDifferentCasing()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.File("sample.txt"),
|
||||
TestEntry.File("SAMPLE.TXT")));
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<InvalidOperationException>(() => ManifestParser.Parse(assembly));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(MalformedManifests))]
|
||||
public void Parse_ThrowsForInvalidManifests(string invalidManifest)
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(invalidManifest);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<InvalidOperationException>(() => ManifestParser.Parse(assembly));
|
||||
}
|
||||
|
||||
public static TheoryData<string> MalformedManifests =>
|
||||
new TheoryData<string>
|
||||
{
|
||||
"<Manifest></Manifest>",
|
||||
"<Manifest><ManifestVersion></ManifestVersion></Manifest>",
|
||||
"<Manifest><ManifestVersion /></Manifest>",
|
||||
"<Manifest><ManifestVersion><Version>2.0</Version></ManifestVersion></Manifest>",
|
||||
"<Manifest><ManifestVersion>2.0</ManifestVersion></Manifest>",
|
||||
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
|
||||
<FileSystem><File><ResourcePath>path</ResourcePath></File></FileSystem></Manifest>",
|
||||
|
||||
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
|
||||
<FileSystem><File Name=""sample.txt""><ResourcePath></ResourcePath></File></FileSystem></Manifest>",
|
||||
|
||||
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
|
||||
<FileSystem><File Name=""sample.txt"">sample.txt</File></FileSystem></Manifest>",
|
||||
|
||||
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
|
||||
<FileSystem><Directory></Directory></FileSystem></Manifest>",
|
||||
|
||||
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
|
||||
<FileSystem><Directory Name=""wwwroot""><Unknown /></Directory></FileSystem></Manifest>"
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ManifestsWithAdditionalData))]
|
||||
public void Parse_IgnoresAdditionalDataOnFileAndDirectoryNodes(string manifest)
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(manifest);
|
||||
|
||||
// Act
|
||||
var result = ManifestParser.Parse(assembly);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
}
|
||||
|
||||
public static TheoryData<string> ManifestsWithAdditionalData =>
|
||||
new TheoryData<string>
|
||||
{
|
||||
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
|
||||
<FileSystem><Directory Name=""wwwroot"" AdditionalAttribute=""value""></Directory></FileSystem></Manifest>",
|
||||
|
||||
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
|
||||
<FileSystem><Directory Name=""wwwroot"" AdditionalAttribute=""value"">
|
||||
<File Name=""sample.txt"" AdditionalValue=""value""><ResourcePath something=""abc"">path</ResourcePath><hash>1234</hash></File>
|
||||
</Directory></FileSystem></Manifest>"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// 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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
|
||||
{
|
||||
class TestEntry
|
||||
{
|
||||
public bool IsFile => ResourcePath != null;
|
||||
public string Name { get; set; }
|
||||
public TestEntry[] Children { get; set; }
|
||||
public string ResourcePath { get; set; }
|
||||
|
||||
public static TestEntry Directory(string name, params TestEntry[] entries) =>
|
||||
new TestEntry() { Name = name, Children = entries };
|
||||
|
||||
public static TestEntry File(string name, string path = null) =>
|
||||
new TestEntry() { Name = name, ResourcePath = path ?? name };
|
||||
|
||||
public XElement ToXElement() => IsFile ?
|
||||
new XElement("File", new XAttribute("Name", Name), new XElement("ResourcePath", ResourcePath)) :
|
||||
new XElement("Directory", new XAttribute("Name", Name), Children.Select(c => c.ToXElement()));
|
||||
|
||||
public IEnumerable<TestEntry> GetFiles()
|
||||
{
|
||||
if (IsFile)
|
||||
{
|
||||
return Enumerable.Empty<TestEntry>();
|
||||
}
|
||||
|
||||
var files = Children.Where(c => c.IsFile).ToArray();
|
||||
var otherFiles = Children.Where(c => !c.IsFile).SelectMany(d => d.GetFiles()).ToArray();
|
||||
|
||||
return files.Concat(otherFiles).ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,428 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.FileProviders.Embedded.Manifest;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders
|
||||
{
|
||||
public class ManifestEmbeddedFileProviderTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetFileInfo_CanResolveSimpleFiles()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css")));
|
||||
|
||||
// Act
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Assert
|
||||
var jqueryValidate = provider.GetFileInfo("jquery.validate.js");
|
||||
Assert.True(jqueryValidate.Exists);
|
||||
Assert.False(jqueryValidate.IsDirectory);
|
||||
Assert.Equal("jquery.validate.js", jqueryValidate.Name);
|
||||
Assert.Null(jqueryValidate.PhysicalPath);
|
||||
Assert.Equal(0, jqueryValidate.Length);
|
||||
|
||||
var jqueryMin = provider.GetFileInfo("jquery.min.js");
|
||||
Assert.True(jqueryMin.Exists);
|
||||
Assert.False(jqueryMin.IsDirectory);
|
||||
Assert.Equal("jquery.min.js", jqueryMin.Name);
|
||||
Assert.Null(jqueryMin.PhysicalPath);
|
||||
Assert.Equal(0, jqueryMin.Length);
|
||||
|
||||
var siteCss = provider.GetFileInfo("site.css");
|
||||
Assert.True(siteCss.Exists);
|
||||
Assert.False(siteCss.IsDirectory);
|
||||
Assert.Equal("site.css", siteCss.Name);
|
||||
Assert.Null(siteCss.PhysicalPath);
|
||||
Assert.Equal(0, siteCss.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFileInfo_CanResolveFilesInsideAFolder()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css"))));
|
||||
|
||||
// Act
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Assert
|
||||
var jqueryValidate = provider.GetFileInfo(Path.Combine("wwwroot", "jquery.validate.js"));
|
||||
Assert.True(jqueryValidate.Exists);
|
||||
Assert.False(jqueryValidate.IsDirectory);
|
||||
Assert.Equal("jquery.validate.js", jqueryValidate.Name);
|
||||
Assert.Null(jqueryValidate.PhysicalPath);
|
||||
Assert.Equal(0, jqueryValidate.Length);
|
||||
|
||||
var jqueryMin = provider.GetFileInfo(Path.Combine("wwwroot", "jquery.min.js"));
|
||||
Assert.True(jqueryMin.Exists);
|
||||
Assert.False(jqueryMin.IsDirectory);
|
||||
Assert.Equal("jquery.min.js", jqueryMin.Name);
|
||||
Assert.Null(jqueryMin.PhysicalPath);
|
||||
Assert.Equal(0, jqueryMin.Length);
|
||||
|
||||
var siteCss = provider.GetFileInfo(Path.Combine("wwwroot", "site.css"));
|
||||
Assert.True(siteCss.Exists);
|
||||
Assert.False(siteCss.IsDirectory);
|
||||
Assert.Equal("site.css", siteCss.Name);
|
||||
Assert.Null(siteCss.PhysicalPath);
|
||||
Assert.Equal(0, siteCss.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFileInfo_ResolveNonExistingFile_ReturnsNotFoundFileInfo()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css"))));
|
||||
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Act
|
||||
var file = provider.GetFileInfo("some/non/existing/file.txt");
|
||||
|
||||
// Assert
|
||||
Assert.IsType<NotFoundFileInfo>(file);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFileInfo_ResolveNonExistingDirectory_ReturnsNotFoundFileInfo()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css"))));
|
||||
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Act
|
||||
var file = provider.GetFileInfo("some");
|
||||
|
||||
// Assert
|
||||
Assert.IsType<NotFoundFileInfo>(file);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFileInfo_ResolveExistingDirectory_ReturnsNotFoundFileInfo()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css"))));
|
||||
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Act
|
||||
var file = provider.GetFileInfo("wwwroot");
|
||||
|
||||
// Assert
|
||||
Assert.IsType<NotFoundFileInfo>(file);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("WWWROOT", "JQUERY.VALIDATE.JS")]
|
||||
[InlineData("WwWRoOT", "JQuERY.VALiDATE.js")]
|
||||
public void GetFileInfo_ResolvesFiles_WithDifferentCasing(string folder, string file)
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css"))));
|
||||
|
||||
// Act
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Assert
|
||||
var jqueryValidate = provider.GetFileInfo(Path.Combine(folder, file));
|
||||
Assert.True(jqueryValidate.Exists);
|
||||
Assert.False(jqueryValidate.IsDirectory);
|
||||
Assert.Equal("jquery.validate.js", jqueryValidate.Name);
|
||||
Assert.Null(jqueryValidate.PhysicalPath);
|
||||
Assert.Equal(0, jqueryValidate.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFileInfo_AllowsLeadingDots_OnThePath()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css"))));
|
||||
|
||||
// Act
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Assert
|
||||
var jqueryValidate = provider.GetFileInfo(Path.Combine(".", "wwwroot", "jquery.validate.js"));
|
||||
Assert.True(jqueryValidate.Exists);
|
||||
Assert.False(jqueryValidate.IsDirectory);
|
||||
Assert.Equal("jquery.validate.js", jqueryValidate.Name);
|
||||
Assert.Null(jqueryValidate.PhysicalPath);
|
||||
Assert.Equal(0, jqueryValidate.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFileInfo_EscapingFromTheRootFolder_ReturnsNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css"))));
|
||||
|
||||
// Act
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Assert
|
||||
var jqueryValidate = provider.GetFileInfo(Path.Combine("..", "wwwroot", "jquery.validate.js"));
|
||||
Assert.IsType<NotFoundFileInfo>(jqueryValidate);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("wwwroot/jquery?validate.js")]
|
||||
[InlineData("wwwroot/jquery*validate.js")]
|
||||
[InlineData("wwwroot/jquery:validate.js")]
|
||||
[InlineData("wwwroot/jquery<validate.js")]
|
||||
[InlineData("wwwroot/jquery>validate.js")]
|
||||
[InlineData("wwwroot/jquery\0validate.js")]
|
||||
public void GetFileInfo_ReturnsNotFoundfileInfo_ForPathsWithInvalidCharacters(string path)
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css"))));
|
||||
|
||||
// Act
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Assert
|
||||
var file = provider.GetFileInfo(path);
|
||||
Assert.IsType<NotFoundFileInfo>(file);
|
||||
Assert.Equal(path, file.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDirectoryContents_CanEnumerateExistingFolders()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css"))));
|
||||
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
var expectedContents = new[]
|
||||
{
|
||||
CreateTestFileInfo("jquery.validate.js"),
|
||||
CreateTestFileInfo("jquery.min.js"),
|
||||
CreateTestFileInfo("site.css")
|
||||
};
|
||||
|
||||
// Act
|
||||
var contents = provider.GetDirectoryContents("wwwroot").ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContents, contents, FileInfoComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDirectoryContents_EnumeratesOnlyAGivenLevel()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css"))));
|
||||
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
var expectedContents = new[]
|
||||
{
|
||||
CreateTestFileInfo("wwwroot", isDirectory: true)
|
||||
};
|
||||
|
||||
// Act
|
||||
var contents = provider.GetDirectoryContents(".").ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContents, contents, FileInfoComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDirectoryContents_EnumeratesFilesAndDirectoriesOnAGivenPath()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot"),
|
||||
TestEntry.File("site.css")));
|
||||
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
var expectedContents = new[]
|
||||
{
|
||||
CreateTestFileInfo("wwwroot", isDirectory: true),
|
||||
CreateTestFileInfo("site.css")
|
||||
};
|
||||
|
||||
// Act
|
||||
var contents = provider.GetDirectoryContents(".").ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContents, contents, FileInfoComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDirectoryContents_ReturnsNoEntries_ForNonExistingDirectories()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot"),
|
||||
TestEntry.File("site.css")));
|
||||
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Act
|
||||
var contents = provider.GetDirectoryContents("non-existing");
|
||||
|
||||
// Assert
|
||||
Assert.IsType<NotFoundDirectoryContents>(contents);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDirectoryContents_ReturnsNoEntries_ForFilePaths()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot"),
|
||||
TestEntry.File("site.css")));
|
||||
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Act
|
||||
var contents = provider.GetDirectoryContents("site.css");
|
||||
|
||||
// Assert
|
||||
Assert.IsType<NotFoundDirectoryContents>(contents);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("wwwro*t")]
|
||||
[InlineData("wwwro?t")]
|
||||
[InlineData("wwwro:t")]
|
||||
[InlineData("wwwro<t")]
|
||||
[InlineData("wwwro>t")]
|
||||
[InlineData("wwwro\0t")]
|
||||
public void GetDirectoryContents_ReturnsNotFoundDirectoryContents_ForPathsWithInvalidCharacters(string path)
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js"),
|
||||
TestEntry.File("jquery.min.js"),
|
||||
TestEntry.File("site.css"))));
|
||||
|
||||
// Act
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
|
||||
// Assert
|
||||
var directory = provider.GetDirectoryContents(path);
|
||||
Assert.IsType<NotFoundDirectoryContents>(directory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Contructor_CanScopeManifestToAFolder()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js")),
|
||||
TestEntry.File("site.css")));
|
||||
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
var scopedProvider = new ManifestEmbeddedFileProvider(assembly, provider.Manifest.Scope("wwwroot"), DateTimeOffset.UtcNow);
|
||||
|
||||
// Act
|
||||
var jqueryValidate = scopedProvider.GetFileInfo("jquery.validate.js");
|
||||
|
||||
// Assert
|
||||
Assert.True(jqueryValidate.Exists);
|
||||
Assert.False(jqueryValidate.IsDirectory);
|
||||
Assert.Equal("jquery.validate.js", jqueryValidate.Name);
|
||||
Assert.Null(jqueryValidate.PhysicalPath);
|
||||
Assert.Equal(0, jqueryValidate.Length);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("wwwroot/jquery.validate.js")]
|
||||
[InlineData("../wwwroot/jquery.validate.js")]
|
||||
[InlineData("site.css")]
|
||||
[InlineData("../site.css")]
|
||||
public void ScopedFileProvider_DoesNotReturnFilesOutOfScope(string path)
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new TestAssembly(
|
||||
TestEntry.Directory("unused",
|
||||
TestEntry.Directory("wwwroot",
|
||||
TestEntry.File("jquery.validate.js")),
|
||||
TestEntry.File("site.css")));
|
||||
|
||||
var provider = new ManifestEmbeddedFileProvider(assembly);
|
||||
var scopedProvider = new ManifestEmbeddedFileProvider(assembly, provider.Manifest.Scope("wwwroot"), DateTimeOffset.UtcNow);
|
||||
|
||||
// Act
|
||||
var jqueryValidate = scopedProvider.GetFileInfo(path);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<NotFoundFileInfo>(jqueryValidate);
|
||||
}
|
||||
|
||||
private IFileInfo CreateTestFileInfo(string name, bool isDirectory = false) =>
|
||||
new TestFileInfo(name, isDirectory);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="File.txt;sub\**\*;Resources\**\*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Extensions.FileProviders.Embedded" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1 @@
|
|||
Resources-Hello
|
||||
|
|
@ -0,0 +1 @@
|
|||
Hello3
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// 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.
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Extensions.FileProviders.Embedded.Manifest;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders
|
||||
{
|
||||
internal class TestAssembly : Assembly
|
||||
{
|
||||
public TestAssembly(string manifest, string manifestName = "Microsoft.Extensions.FileProviders.Embedded.Manifest.xml")
|
||||
{
|
||||
ManifestStream = new MemoryStream();
|
||||
using (var writer = new StreamWriter(ManifestStream, Encoding.UTF8, 1024, leaveOpen: true))
|
||||
{
|
||||
writer.Write(manifest);
|
||||
}
|
||||
|
||||
ManifestStream.Seek(0, SeekOrigin.Begin);
|
||||
ManifestName = manifestName;
|
||||
}
|
||||
|
||||
public TestAssembly(TestEntry entry, string manifestName = "Microsoft.Extensions.FileProviders.Embedded.Manifest.xml")
|
||||
{
|
||||
ManifestName = manifestName;
|
||||
|
||||
var manifest = new XDocument(
|
||||
new XDeclaration("1.0", "utf-8", "yes"),
|
||||
new XElement("Manifest",
|
||||
new XElement("ManifestVersion", "1.0"),
|
||||
new XElement("FileSystem", entry.Children.Select(c => c.ToXElement()))));
|
||||
|
||||
ManifestStream = new MemoryStream();
|
||||
using (var writer = XmlWriter.Create(ManifestStream, new XmlWriterSettings { CloseOutput = false }))
|
||||
{
|
||||
manifest.WriteTo(writer);
|
||||
}
|
||||
|
||||
ManifestStream.Seek(0, SeekOrigin.Begin);
|
||||
Files = entry.GetFiles().Select(f => f.ResourcePath).ToArray();
|
||||
}
|
||||
|
||||
public string ManifestName { get; }
|
||||
public MemoryStream ManifestStream { get; private set; }
|
||||
public string[] Files { get; private set; }
|
||||
|
||||
public override Stream GetManifestResourceStream(string name)
|
||||
{
|
||||
if (string.Equals(ManifestName, name))
|
||||
{
|
||||
return ManifestStream;
|
||||
}
|
||||
|
||||
return Files.Contains(name) ? Stream.Null : null;
|
||||
}
|
||||
|
||||
public override string Location => null;
|
||||
|
||||
public override AssemblyName GetName()
|
||||
{
|
||||
return new AssemblyName("TestAssembly");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders
|
||||
{
|
||||
internal class TestFileInfo : IFileInfo
|
||||
{
|
||||
private readonly string _name;
|
||||
private readonly bool _isDirectory;
|
||||
|
||||
public TestFileInfo(string name, bool isDirectory)
|
||||
{
|
||||
_name = name;
|
||||
_isDirectory = isDirectory;
|
||||
}
|
||||
|
||||
public bool Exists => true;
|
||||
|
||||
public long Length => _isDirectory ? -1 : 0;
|
||||
|
||||
public string PhysicalPath => null;
|
||||
|
||||
public string Name => _name;
|
||||
|
||||
public DateTimeOffset LastModified => throw new NotImplementedException();
|
||||
|
||||
public bool IsDirectory => _isDirectory;
|
||||
|
||||
public Stream CreateReadStream() => Stream.Null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Hello2
|
||||
Binary file not shown.
|
|
@ -0,0 +1,21 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
|
||||
{
|
||||
public class EmbeddedItem : IEquatable<EmbeddedItem>
|
||||
{
|
||||
public string ManifestFilePath { get; set; }
|
||||
|
||||
public string AssemblyResourceName { get; set; }
|
||||
|
||||
public bool Equals(EmbeddedItem other) =>
|
||||
string.Equals(ManifestFilePath, other?.ManifestFilePath, StringComparison.Ordinal) &&
|
||||
string.Equals(AssemblyResourceName, other?.AssemblyResourceName, StringComparison.Ordinal);
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as EmbeddedItem);
|
||||
public override int GetHashCode() => ManifestFilePath.GetHashCode() ^ AssemblyResourceName.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This type is for internal uses only and is not meant to be consumed by any other library.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Name,nq}")]
|
||||
public class Entry : IEquatable<Entry>
|
||||
{
|
||||
public bool IsFile { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string AssemblyResourceName { get; private set; }
|
||||
|
||||
public ISet<Entry> Children { get; } = new SortedSet<Entry>(NameComparer.Instance);
|
||||
|
||||
public static Entry Directory(string name) =>
|
||||
new Entry { Name = name };
|
||||
|
||||
public static Entry File(string name, string assemblyResourceName) =>
|
||||
new Entry { Name = name, AssemblyResourceName = assemblyResourceName, IsFile = true };
|
||||
|
||||
internal void AddChild(Entry child)
|
||||
{
|
||||
if (IsFile)
|
||||
{
|
||||
throw new InvalidOperationException("Tried to add children to a file.");
|
||||
}
|
||||
|
||||
if (Children.Contains(child))
|
||||
{
|
||||
throw new InvalidOperationException($"An item with the name '{child.Name}' already exists.");
|
||||
}
|
||||
|
||||
Children.Add(child);
|
||||
}
|
||||
|
||||
internal Entry GetDirectory(string currentSegment)
|
||||
{
|
||||
if (IsFile)
|
||||
{
|
||||
throw new InvalidOperationException("Tried to get a directory from a file.");
|
||||
}
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child.HasName(currentSegment))
|
||||
{
|
||||
if (child.IsFile)
|
||||
{
|
||||
throw new InvalidOperationException("Tried to find a directory but found a file instead");
|
||||
}
|
||||
else
|
||||
{
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool Equals(Entry other)
|
||||
{
|
||||
if (other == null || !other.HasName(Name) || other.IsFile != IsFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsFile)
|
||||
{
|
||||
return string.Equals(other.AssemblyResourceName, AssemblyResourceName, StringComparison.Ordinal);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SameChildren(Children, other.Children);
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasName(string currentSegment)
|
||||
{
|
||||
return string.Equals(Name, currentSegment, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private bool SameChildren(ISet<Entry> left, ISet<Entry> right)
|
||||
{
|
||||
if (left.Count != right.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var le = left.GetEnumerator();
|
||||
var re = right.GetEnumerator();
|
||||
while (le.MoveNext() && re.MoveNext())
|
||||
{
|
||||
if (!le.Current.Equals(re.Current))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private class NameComparer : IComparer<Entry>
|
||||
{
|
||||
public static NameComparer Instance { get; } = new NameComparer();
|
||||
|
||||
public int Compare(Entry x, Entry y) =>
|
||||
string.Compare(x?.Name, y?.Name, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Microsoft.Build.Framework;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
|
||||
{
|
||||
/// <summary>
|
||||
/// Task for generating a manifest file out of the embedded resources in an
|
||||
/// assembly.
|
||||
/// </summary>
|
||||
public class GenerateEmbeddedResourcesManifest : Microsoft.Build.Utilities.Task
|
||||
{
|
||||
private const string LogicalName = "LogicalName";
|
||||
private const string ManifestResourceName = "ManifestResourceName";
|
||||
private const string TargetPath = "TargetPath";
|
||||
|
||||
[Required]
|
||||
public ITaskItem[] EmbeddedFiles { get; set; }
|
||||
|
||||
[Required]
|
||||
public string ManifestFile { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Execute()
|
||||
{
|
||||
var processedItems = CreateEmbeddedItems(EmbeddedFiles);
|
||||
|
||||
var manifest = BuildManifest(processedItems);
|
||||
|
||||
var document = manifest.ToXmlDocument();
|
||||
|
||||
var settings = new XmlWriterSettings()
|
||||
{
|
||||
Encoding = Encoding.UTF8,
|
||||
CloseOutput = true
|
||||
};
|
||||
|
||||
using (var xmlWriter = GetXmlWriter(settings))
|
||||
{
|
||||
document.WriteTo(xmlWriter);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual XmlWriter GetXmlWriter(XmlWriterSettings settings)
|
||||
{
|
||||
if (settings == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
}
|
||||
|
||||
var fileStream = new FileStream(ManifestFile, FileMode.Create);
|
||||
return XmlWriter.Create(fileStream, settings);
|
||||
}
|
||||
|
||||
public EmbeddedItem[] CreateEmbeddedItems(params ITaskItem[] items)
|
||||
{
|
||||
if (items == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(items));
|
||||
}
|
||||
|
||||
return items.Select(er => new EmbeddedItem
|
||||
{
|
||||
ManifestFilePath = GetManifestPath(er),
|
||||
AssemblyResourceName = GetAssemblyResourceName(er)
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
public Manifest BuildManifest(EmbeddedItem[] processedItems)
|
||||
{
|
||||
if (processedItems == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(processedItems));
|
||||
}
|
||||
|
||||
var manifest = new Manifest();
|
||||
foreach (var item in processedItems)
|
||||
{
|
||||
manifest.AddElement(item.ManifestFilePath, item.AssemblyResourceName);
|
||||
}
|
||||
|
||||
return manifest;
|
||||
}
|
||||
|
||||
private string GetManifestPath(ITaskItem taskItem) => string.Equals(taskItem.GetMetadata(LogicalName), taskItem.GetMetadata(ManifestResourceName)) ?
|
||||
taskItem.GetMetadata(TargetPath) :
|
||||
NormalizePath(taskItem.GetMetadata(LogicalName));
|
||||
|
||||
private string GetAssemblyResourceName(ITaskItem taskItem) => string.Equals(taskItem.GetMetadata(LogicalName), taskItem.GetMetadata(ManifestResourceName)) ?
|
||||
taskItem.GetMetadata(ManifestResourceName) :
|
||||
taskItem.GetMetadata(LogicalName);
|
||||
|
||||
private string NormalizePath(string path) => Path.DirectorySeparatorChar == '\\' ?
|
||||
path.Replace("/", "\\") : path.Replace("\\", "/");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// 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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
|
||||
{
|
||||
public class Manifest
|
||||
{
|
||||
public Entry Root { get; set; } = Entry.Directory("");
|
||||
|
||||
public void AddElement(string originalPath, string assemblyResourceName)
|
||||
{
|
||||
if (originalPath == null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(originalPath));
|
||||
}
|
||||
|
||||
if (assemblyResourceName == null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(assemblyResourceName));
|
||||
}
|
||||
|
||||
var paths = originalPath.Split(Path.DirectorySeparatorChar);
|
||||
var current = Root;
|
||||
for (int i = 0; i < paths.Length - 1; i++)
|
||||
{
|
||||
var currentSegment = paths[i];
|
||||
var next = current.GetDirectory(currentSegment);
|
||||
if (next == null)
|
||||
{
|
||||
next = Entry.Directory(currentSegment);
|
||||
current.AddChild(next);
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
|
||||
current.AddChild(Entry.File(paths[paths.Length - 1], assemblyResourceName));
|
||||
}
|
||||
|
||||
public XDocument ToXmlDocument()
|
||||
{
|
||||
var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
|
||||
var root = new XElement(ElementNames.Root,
|
||||
new XElement(ElementNames.ManifestVersion, "1.0"),
|
||||
new XElement(ElementNames.FileSystem,
|
||||
Root.Children.Select(e => BuildNode(e))));
|
||||
|
||||
document.Add(root);
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
private XElement BuildNode(Entry entry)
|
||||
{
|
||||
if (entry.IsFile)
|
||||
{
|
||||
return new XElement(ElementNames.File,
|
||||
new XAttribute(ElementNames.Name, entry.Name),
|
||||
new XElement(ElementNames.ResourcePath, entry.AssemblyResourceName));
|
||||
}
|
||||
else
|
||||
{
|
||||
var directory = new XElement(ElementNames.Directory, new XAttribute(ElementNames.Name, entry.Name));
|
||||
directory.Add(entry.Children.Select(c => BuildNode(c)));
|
||||
return directory;
|
||||
}
|
||||
}
|
||||
|
||||
private class ElementNames
|
||||
{
|
||||
public static readonly string Directory = "Directory";
|
||||
public static readonly string Name = "Name";
|
||||
public static readonly string FileSystem = "FileSystem";
|
||||
public static readonly string Root = "Manifest";
|
||||
public static readonly string File = "File";
|
||||
public static readonly string ResourcePath = "ResourcePath";
|
||||
public static readonly string ManifestVersion = "ManifestVersion";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>MSBuild task to generate a manifest that can be used by Microsoft.Extensions.FileProviders.Embedded to preserve
|
||||
metadata of the files embedded in the assembly at compilation time.</Description>
|
||||
<TargetFrameworks>netstandard1.5;net461</TargetFrameworks>
|
||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsImplementationProject>false</IsImplementationProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.5'">
|
||||
<Reference Include="Microsoft.Build" />
|
||||
<Reference Include="Microsoft.Build.Framework" />
|
||||
<Reference Include="Microsoft.Build.Utilities.Core" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
|
||||
<Reference Include="Microsoft.Build" />
|
||||
<Reference Include="Microsoft.Build.Framework" />
|
||||
<Reference Include="Microsoft.Build.Utilities.v4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,388 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
using Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
|
||||
{
|
||||
public class GenerateEmbeddedResourcesManifestTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateEmbeddedItems_MapsMetadataFromEmbeddedResources_UsesTheTargetPath()
|
||||
{
|
||||
// Arrange
|
||||
var task = new TestGenerateEmbeddedResourcesManifest();
|
||||
var embeddedFiles = CreateEmbeddedResource(
|
||||
CreateMetadata(@"lib\js\jquery.validate.js"));
|
||||
|
||||
var expectedItems = new[]
|
||||
{
|
||||
CreateEmbeddedItem(@"lib\js\jquery.validate.js","lib.js.jquery.validate.js")
|
||||
};
|
||||
|
||||
// Act
|
||||
var embeddedItems = task.CreateEmbeddedItems(embeddedFiles);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedItems, embeddedItems);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateEmbeddedItems_MapsMetadataFromEmbeddedResources_WithLogicalName()
|
||||
{
|
||||
// Arrange
|
||||
var task = new TestGenerateEmbeddedResourcesManifest();
|
||||
var DirectorySeparator = (Path.DirectorySeparatorChar == '\\' ? '/' : '\\');
|
||||
var embeddedFiles = CreateEmbeddedResource(
|
||||
CreateMetadata("site.css", null, "site.css"),
|
||||
CreateMetadata("lib/jquery.validate.js", null, $"dist{DirectorySeparator}jquery.validate.js"));
|
||||
|
||||
var expectedItems = new[]
|
||||
{
|
||||
CreateEmbeddedItem("site.css","site.css"),
|
||||
CreateEmbeddedItem(Path.Combine("dist","jquery.validate.js"),$"dist{DirectorySeparator}jquery.validate.js")
|
||||
};
|
||||
|
||||
// Act
|
||||
var embeddedItems = task.CreateEmbeddedItems(embeddedFiles);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedItems, embeddedItems);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildManifest_CanCreatesManifest_ForTopLevelFiles()
|
||||
{
|
||||
// Arrange
|
||||
var task = new TestGenerateEmbeddedResourcesManifest();
|
||||
var embeddedFiles = CreateEmbeddedResource(
|
||||
CreateMetadata("jquery.validate.js"),
|
||||
CreateMetadata("jquery.min.js"),
|
||||
CreateMetadata("Site.css"));
|
||||
|
||||
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
|
||||
|
||||
var expectedManifest = new Manifest()
|
||||
{
|
||||
Root = Entry.Directory("").AddRange(
|
||||
Entry.File("jquery.validate.js", "jquery.validate.js"),
|
||||
Entry.File("jquery.min.js", "jquery.min.js"),
|
||||
Entry.File("Site.css", "Site.css"))
|
||||
};
|
||||
|
||||
// Act
|
||||
var manifest = task.BuildManifest(manifestFiles);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildManifest_CanCreatesManifest_ForFilesWithinAFolder()
|
||||
{
|
||||
// Arrange
|
||||
var task = new TestGenerateEmbeddedResourcesManifest();
|
||||
var embeddedFiles = CreateEmbeddedResource(
|
||||
CreateMetadata(Path.Combine("wwwroot", "js", "jquery.validate.js")),
|
||||
CreateMetadata(Path.Combine("wwwroot", "js", "jquery.min.js")),
|
||||
CreateMetadata(Path.Combine("wwwroot", "css", "Site.css")),
|
||||
CreateMetadata(Path.Combine("Areas", "Identity", "Views", "Account", "Index.cshtml")));
|
||||
|
||||
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
|
||||
|
||||
var expectedManifest = new Manifest()
|
||||
{
|
||||
Root = Entry.Directory("").AddRange(
|
||||
Entry.Directory("wwwroot").AddRange(
|
||||
Entry.Directory("js").AddRange(
|
||||
Entry.File("jquery.validate.js", "wwwroot.js.jquery.validate.js"),
|
||||
Entry.File("jquery.min.js", "wwwroot.js.jquery.min.js")),
|
||||
Entry.Directory("css").AddRange(
|
||||
Entry.File("Site.css", "wwwroot.css.Site.css"))),
|
||||
Entry.Directory("Areas").AddRange(
|
||||
Entry.Directory("Identity").AddRange(
|
||||
Entry.Directory("Views").AddRange(
|
||||
Entry.Directory("Account").AddRange(
|
||||
Entry.File("Index.cshtml", "Areas.Identity.Views.Account.Index.cshtml"))))))
|
||||
};
|
||||
|
||||
// Act
|
||||
var manifest = task.BuildManifest(manifestFiles);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildManifest_RespectsEntriesWithLogicalName()
|
||||
{
|
||||
// Arrange
|
||||
var task = new TestGenerateEmbeddedResourcesManifest();
|
||||
var embeddedFiles = CreateEmbeddedResource(
|
||||
CreateMetadata("jquery.validate.js", null, @"wwwroot\lib\js\jquery.validate.js"),
|
||||
CreateMetadata("jquery.min.js", null, @"wwwroot\lib/js\jquery.min.js"),
|
||||
CreateMetadata("Site.css", null, "wwwroot/lib/css/site.css"));
|
||||
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
|
||||
|
||||
var expectedManifest = new Manifest()
|
||||
{
|
||||
Root = Entry.Directory("").AddRange(
|
||||
Entry.Directory("wwwroot").AddRange(
|
||||
Entry.Directory("lib").AddRange(
|
||||
Entry.Directory("js").AddRange(
|
||||
Entry.File("jquery.validate.js", @"wwwroot\lib\js\jquery.validate.js"),
|
||||
Entry.File("jquery.min.js", @"wwwroot\lib/js\jquery.min.js")),
|
||||
Entry.Directory("css").AddRange(
|
||||
Entry.File("site.css", "wwwroot/lib/css/site.css")))))
|
||||
};
|
||||
|
||||
// Act
|
||||
var manifest = task.BuildManifest(manifestFiles);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildManifest_SupportsFilesAndFoldersWithDifferentCasing()
|
||||
{
|
||||
// Arrange
|
||||
var task = new TestGenerateEmbeddedResourcesManifest();
|
||||
var embeddedFiles = CreateEmbeddedResource(
|
||||
CreateMetadata(Path.Combine("A", "b", "c.txt")),
|
||||
CreateMetadata(Path.Combine("A", "B", "c.txt")),
|
||||
CreateMetadata(Path.Combine("A", "B", "C.txt")),
|
||||
CreateMetadata(Path.Combine("A", "b", "C.txt")),
|
||||
CreateMetadata(Path.Combine("A", "d")),
|
||||
CreateMetadata(Path.Combine("A", "D", "e.txt")));
|
||||
|
||||
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
|
||||
|
||||
var expectedManifest = new Manifest()
|
||||
{
|
||||
Root = Entry.Directory("").AddRange(
|
||||
Entry.Directory("A").AddRange(
|
||||
Entry.Directory("b").AddRange(
|
||||
Entry.File("c.txt", @"A.b.c.txt"),
|
||||
Entry.File("C.txt", @"A.b.C.txt")),
|
||||
Entry.Directory("B").AddRange(
|
||||
Entry.File("c.txt", @"A.B.c.txt"),
|
||||
Entry.File("C.txt", @"A.B.C.txt")),
|
||||
Entry.Directory("D").AddRange(
|
||||
Entry.File("e.txt", "A.D.e.txt")),
|
||||
Entry.File("d", "A.d")))
|
||||
};
|
||||
|
||||
// Act
|
||||
var manifest = task.BuildManifest(manifestFiles);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildManifest_ThrowsInvalidOperationException_WhenTryingToAddAFileWithTheSameNameAsAFolder()
|
||||
{
|
||||
// Arrange
|
||||
var task = new TestGenerateEmbeddedResourcesManifest();
|
||||
var embeddedFiles = CreateEmbeddedResource(
|
||||
CreateMetadata(Path.Combine("A", "b", "c.txt")),
|
||||
CreateMetadata(Path.Combine("A", "b")));
|
||||
|
||||
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<InvalidOperationException>(() => task.BuildManifest(manifestFiles));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildManifest_ThrowsInvalidOperationException_WhenTryingToAddAFolderWithTheSameNameAsAFile()
|
||||
{
|
||||
// Arrange
|
||||
var task = new TestGenerateEmbeddedResourcesManifest();
|
||||
var embeddedFiles = CreateEmbeddedResource(
|
||||
CreateMetadata(Path.Combine("A", "b")),
|
||||
CreateMetadata(Path.Combine("A", "b", "c.txt")));
|
||||
|
||||
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<InvalidOperationException>(() => task.BuildManifest(manifestFiles));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToXmlDocument_GeneratesTheCorrectXmlDocument()
|
||||
{
|
||||
// Arrange
|
||||
var manifest = new Manifest()
|
||||
{
|
||||
Root = Entry.Directory("").AddRange(
|
||||
Entry.Directory("A").AddRange(
|
||||
Entry.Directory("b").AddRange(
|
||||
Entry.File("c.txt", @"A.b.c.txt"),
|
||||
Entry.File("C.txt", @"A.b.C.txt")),
|
||||
Entry.Directory("B").AddRange(
|
||||
Entry.File("c.txt", @"A.B.c.txt"),
|
||||
Entry.File("C.txt", @"A.B.C.txt")),
|
||||
Entry.Directory("D").AddRange(
|
||||
Entry.File("e.txt", "A.D.e.txt")),
|
||||
Entry.File("d", "A.d")))
|
||||
};
|
||||
|
||||
var expectedDocument = new XDocument(
|
||||
new XDeclaration("1.0", "utf-8", "yes"),
|
||||
new XElement("Manifest",
|
||||
new XElement("ManifestVersion", "1.0"),
|
||||
new XElement("FileSystem",
|
||||
new XElement("Directory", new XAttribute("Name", "A"),
|
||||
new XElement("Directory", new XAttribute("Name", "B"),
|
||||
new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.B.C.txt")),
|
||||
new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.B.c.txt"))),
|
||||
new XElement("Directory", new XAttribute("Name", "D"),
|
||||
new XElement("File", new XAttribute("Name", "e.txt"), new XElement("ResourcePath", "A.D.e.txt"))),
|
||||
new XElement("Directory", new XAttribute("Name", "b"),
|
||||
new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.b.C.txt")),
|
||||
new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.b.c.txt"))),
|
||||
new XElement("File", new XAttribute("Name", "d"), new XElement("ResourcePath", "A.d"))))));
|
||||
|
||||
// Act
|
||||
var document = manifest.ToXmlDocument();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDocument.ToString(), document.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_WritesManifest_ToOutputFile()
|
||||
{
|
||||
// Arrange
|
||||
var task = new TestGenerateEmbeddedResourcesManifest();
|
||||
var embeddedFiles = CreateEmbeddedResource(
|
||||
CreateMetadata(Path.Combine("A", "b", "c.txt")),
|
||||
CreateMetadata(Path.Combine("A", "B", "c.txt")),
|
||||
CreateMetadata(Path.Combine("A", "B", "C.txt")),
|
||||
CreateMetadata(Path.Combine("A", "b", "C.txt")),
|
||||
CreateMetadata(Path.Combine("A", "d")),
|
||||
CreateMetadata(Path.Combine("A", "D", "e.txt")));
|
||||
|
||||
task.EmbeddedFiles = embeddedFiles;
|
||||
task.ManifestFile = Path.Combine("obj", "debug", "netstandard2.0");
|
||||
|
||||
var expectedDocument = new XDocument(
|
||||
new XDeclaration("1.0", "utf-8", "yes"),
|
||||
new XElement("Manifest",
|
||||
new XElement("ManifestVersion", "1.0"),
|
||||
new XElement("FileSystem",
|
||||
new XElement("Directory", new XAttribute("Name", "A"),
|
||||
new XElement("Directory", new XAttribute("Name", "B"),
|
||||
new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.B.C.txt")),
|
||||
new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.B.c.txt"))),
|
||||
new XElement("Directory", new XAttribute("Name", "D"),
|
||||
new XElement("File", new XAttribute("Name", "e.txt"), new XElement("ResourcePath", "A.D.e.txt"))),
|
||||
new XElement("Directory", new XAttribute("Name", "b"),
|
||||
new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.b.C.txt")),
|
||||
new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.b.c.txt"))),
|
||||
new XElement("File", new XAttribute("Name", "d"), new XElement("ResourcePath", "A.d"))))));
|
||||
|
||||
var expectedOutput = new MemoryStream();
|
||||
var writer = XmlWriter.Create(expectedOutput, new XmlWriterSettings { Encoding = Encoding.UTF8 });
|
||||
expectedDocument.WriteTo(writer);
|
||||
writer.Flush();
|
||||
expectedOutput.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Act
|
||||
task.Execute();
|
||||
|
||||
// Assert
|
||||
task.Output.Seek(0, SeekOrigin.Begin);
|
||||
using (var expectedReader = new StreamReader(expectedOutput))
|
||||
{
|
||||
using (var reader = new StreamReader(task.Output))
|
||||
{
|
||||
Assert.Equal(expectedReader.ReadToEnd(), reader.ReadToEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EmbeddedItem CreateEmbeddedItem(string manifestPath, string assemblyName) =>
|
||||
new EmbeddedItem
|
||||
{
|
||||
ManifestFilePath = manifestPath,
|
||||
AssemblyResourceName = assemblyName
|
||||
};
|
||||
|
||||
|
||||
public class TestGenerateEmbeddedResourcesManifest
|
||||
: GenerateEmbeddedResourcesManifest
|
||||
{
|
||||
public TestGenerateEmbeddedResourcesManifest()
|
||||
: this(new MemoryStream())
|
||||
{
|
||||
}
|
||||
|
||||
public TestGenerateEmbeddedResourcesManifest(Stream output)
|
||||
{
|
||||
Output = output;
|
||||
}
|
||||
|
||||
public Stream Output { get; }
|
||||
|
||||
protected override XmlWriter GetXmlWriter(XmlWriterSettings settings)
|
||||
{
|
||||
settings.CloseOutput = false;
|
||||
return XmlWriter.Create(Output, settings);
|
||||
}
|
||||
}
|
||||
|
||||
private ITaskItem[] CreateEmbeddedResource(params IDictionary<string, string>[] files) =>
|
||||
files.Select(f => CreateTaskItem(f)).ToArray();
|
||||
|
||||
private ITaskItem CreateTaskItem(IDictionary<string, string> metadata)
|
||||
{
|
||||
var result = new TaskItem();
|
||||
foreach (var kvp in metadata)
|
||||
{
|
||||
result.SetMetadata(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IDictionary<string, string>
|
||||
CreateMetadata(
|
||||
string targetPath,
|
||||
string manifestResourceName = null,
|
||||
string logicalName = null) =>
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["TargetPath"] = targetPath,
|
||||
["ManifestResourceName"] = manifestResourceName ?? targetPath.Replace("/", ".").Replace("\\", "."),
|
||||
["LogicalName"] = logicalName ?? targetPath.Replace("/", ".").Replace("\\", "."),
|
||||
};
|
||||
|
||||
private class ManifestComparer : IEqualityComparer<Manifest>
|
||||
{
|
||||
public static IEqualityComparer<Manifest> Instance { get; } = new ManifestComparer();
|
||||
|
||||
public bool Equals(Manifest x, Manifest y)
|
||||
{
|
||||
return x.Root.Equals(y.Root);
|
||||
}
|
||||
|
||||
public int GetHashCode(Manifest obj)
|
||||
{
|
||||
return obj.Root.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\src\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// 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.
|
||||
|
||||
using Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal;
|
||||
|
||||
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
|
||||
{
|
||||
internal static class SetExtensions
|
||||
{
|
||||
public static Entry AddRange(this Entry source, params Entry[] elements)
|
||||
{
|
||||
foreach (var element in elements)
|
||||
{
|
||||
source.Children.Add(element);
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue