diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileData.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileData.cs index 7fcf94514..a7cfebb65 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileData.cs +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileData.cs @@ -119,6 +119,11 @@ public MockFileData(MockFileData template) /// public byte[] Contents { get; set; } + /// + /// Gets or sets the file version info of the + /// + public IFileVersionInfo FileVersionInfo { get; set; } + /// /// Gets or sets the string contents of the . /// diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileInfo.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileInfo.cs index 5356260f1..f9dd951f5 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileInfo.cs +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileInfo.cs @@ -259,7 +259,7 @@ public override void Encrypt() var mockFileData = GetMockFileDataForWrite(); mockFileData.Attributes |= FileAttributes.Encrypted; } - + /// public override void MoveTo(string destFileName) { @@ -323,7 +323,7 @@ public override IFileInfo Replace(string destinationFileName, string destination mockFile.Replace(path, destinationFileName, destinationBackupFileName, ignoreMetadataErrors); return mockFileSystem.FileInfo.New(destinationFileName); } - + /// public override IDirectoryInfo Directory { diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileSystem.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileSystem.cs index 4199c5622..572461551 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileSystem.cs +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileSystem.cs @@ -65,6 +65,7 @@ public MockFileSystem(IDictionary files, MockFileSystemOpt File = new MockFile(this); Directory = new MockDirectory(this, currentDirectory); FileInfo = new MockFileInfoFactory(this); + FileVersionInfo = new MockFileVersionInfoFactory(this); FileStream = new MockFileStreamFactory(this); DirectoryInfo = new MockDirectoryInfoFactory(this); DriveInfo = new MockDriveInfoFactory(this); @@ -98,6 +99,8 @@ public MockFileSystem(IDictionary files, MockFileSystemOpt /// public override IFileInfoFactory FileInfo { get; } /// + public override IFileVersionInfoFactory FileVersionInfo { get; } + /// public override IFileStreamFactory FileStream { get; } /// public override IPath Path { get; } @@ -252,6 +255,8 @@ public void AddFile(string path, MockFileData mockFile) AddDirectory(directoryPath); } + mockFile.FileVersionInfo ??= new MockFileVersionInfo(fixedPath); + SetEntry(fixedPath, mockFile); } @@ -568,7 +573,7 @@ private bool FileIsReadOnly(string path) } #if FEATURE_SERIALIZABLE - [Serializable] + [Serializable] #endif private class FileSystemEntry { diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileVersionInfo.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileVersionInfo.cs new file mode 100644 index 000000000..1eaac4f59 --- /dev/null +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileVersionInfo.cs @@ -0,0 +1,174 @@ +using System.Diagnostics; +using System.Text; + +namespace System.IO.Abstractions.TestingHelpers +{ + /// +#if FEATURE_SERIALIZABLE + [Serializable] +#endif + public class MockFileVersionInfo : FileVersionInfoBase + { + /// + public MockFileVersionInfo( + string fileName, + string fileVersion = null, + string productVersion = null, + string fileDescription = null, + string productName = null, + string companyName = null, + string comments = null, + string internalName = null, + bool isDebug = false, + bool isPatched = false, + bool isPrivateBuild = false, + bool isPreRelease = false, + bool isSpecialBuild = false, + string language = null, + string legalCopyright = null, + string legalTrademarks = null, + string originalFilename = null, + string privateBuild = null, + string specialBuild = null) + { + FileName = fileName; + FileVersion = fileVersion; + ProductVersion = productVersion; + FileDescription = fileDescription; + ProductName = productName; + CompanyName = companyName; + Comments = comments; + InternalName = internalName; + IsDebug = isDebug; + IsPatched = isPatched; + IsPrivateBuild = isPrivateBuild; + IsPreRelease = isPreRelease; + IsSpecialBuild = isSpecialBuild; + Language = language; + LegalCopyright = legalCopyright; + LegalTrademarks = legalTrademarks; + OriginalFilename = originalFilename; + PrivateBuild = privateBuild; + SpecialBuild = specialBuild; + + if (Version.TryParse(fileVersion, out Version version)) + { + FileMajorPart = version.Major; + FileMinorPart = version.Minor; + FileBuildPart = version.Build; + FilePrivatePart = version.Revision; + } + + var parsedProductVersion = ProductVersionParser.Parse(productVersion); + + ProductMajorPart = parsedProductVersion.Major; + ProductMinorPart = parsedProductVersion.Minor; + ProductBuildPart = parsedProductVersion.Build; + ProductPrivatePart = parsedProductVersion.PrivatePart; + } + + /// + public override string FileName { get; } + + /// + public override string FileVersion { get; } + + /// + public override string ProductVersion { get; } + + /// + public override string FileDescription { get; } + + /// + public override string ProductName { get; } + + /// + public override string CompanyName { get; } + + /// + public override string Comments { get; } + + /// + public override string InternalName { get; } + + /// + public override bool IsDebug { get; } + + /// + public override bool IsPatched { get; } + + /// + public override bool IsPrivateBuild { get; } + + /// + public override bool IsPreRelease { get; } + + /// + public override bool IsSpecialBuild { get; } + + /// + public override string Language { get; } + + /// + public override string LegalCopyright { get; } + + /// + public override string LegalTrademarks { get; } + + /// + public override string OriginalFilename { get; } + + /// + public override string PrivateBuild { get; } + + /// + public override string SpecialBuild { get; } + + /// + public override int FileMajorPart { get; } + + /// + public override int FileMinorPart { get; } + + /// + public override int FileBuildPart { get; } + + /// + public override int FilePrivatePart { get; } + + /// + public override int ProductMajorPart { get; } + + /// + public override int ProductMinorPart { get; } + + /// + public override int ProductBuildPart { get; } + + /// + public override int ProductPrivatePart { get; } + + /// + public override string ToString() + { + // An initial capacity of 512 was chosen because it is large enough to cover + // the size of the static strings with enough capacity left over to cover + // average length property values. + var sb = new StringBuilder(512); + sb.Append("File: ").AppendLine(FileName); + sb.Append("InternalName: ").AppendLine(InternalName); + sb.Append("OriginalFilename: ").AppendLine(OriginalFilename); + sb.Append("FileVersion: ").AppendLine(FileVersion); + sb.Append("FileDescription: ").AppendLine(FileDescription); + sb.Append("Product: ").AppendLine(ProductName); + sb.Append("ProductVersion: ").AppendLine(ProductVersion); + sb.Append("Debug: ").AppendLine(IsDebug.ToString()); + sb.Append("Patched: ").AppendLine(IsPatched.ToString()); + sb.Append("PreRelease: ").AppendLine(IsPreRelease.ToString()); + sb.Append("PrivateBuild: ").AppendLine(IsPrivateBuild.ToString()); + sb.Append("SpecialBuild: ").AppendLine(IsSpecialBuild.ToString()); + sb.Append("Language: ").AppendLine(Language); + return sb.ToString(); + } + } +} diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileVersionInfoFactory.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileVersionInfoFactory.cs new file mode 100644 index 000000000..76278f0dc --- /dev/null +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileVersionInfoFactory.cs @@ -0,0 +1,33 @@ +namespace System.IO.Abstractions.TestingHelpers +{ + /// +#if FEATURE_SERIALIZABLE + [Serializable] +#endif + public class MockFileVersionInfoFactory : IFileVersionInfoFactory + { + private readonly IMockFileDataAccessor mockFileSystem; + + /// + public MockFileVersionInfoFactory(IMockFileDataAccessor mockFileSystem) + { + this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem)); + } + + /// + public IFileSystem FileSystem => mockFileSystem; + + /// + public IFileVersionInfo GetVersionInfo(string fileName) + { + MockFileData mockFileData = mockFileSystem.GetFile(fileName); + + if (mockFileData != null) + { + return mockFileData.FileVersionInfo; + } + + throw CommonExceptions.FileNotFound(fileName); + } + } +} diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/ProductVersionParser.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/ProductVersionParser.cs new file mode 100644 index 000000000..011de195c --- /dev/null +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/ProductVersionParser.cs @@ -0,0 +1,99 @@ +using System.Reflection; +using System.Text.RegularExpressions; + +namespace System.IO.Abstractions.TestingHelpers +{ + /// + /// Provides functionality to parse a product version string into its major, minor, build, and private parts. + /// + internal static class ProductVersionParser + { + /// + /// Parses a product version string and extracts the numeric values for the major, minor, build, and private parts, + /// mimicking the behavior of the attribute. + /// + /// The product version string to parse. + /// + /// A object containing the parsed major, minor, build, and private parts. + /// If the input is invalid, returns a with all parts set to 0. + /// + /// + /// The method splits the input string into segments separated by dots ('.') and attempts to extract + /// the leading numeric value from each segment. A maximum of 4 segments are processed; if more than + /// 4 segments are present, all segments are ignored. Additionally, if a segment does not contain + /// a valid numeric part at its start or it contains more than just a number, the rest of the segments are ignored. + /// + public static ProductVersion Parse(string productVersion) + { + if (string.IsNullOrWhiteSpace(productVersion)) + { + return new(); + } + + var segments = productVersion.Split('.'); + if (segments.Length > 4) + { + // if more than 4 segments are present, all segments are ignored + return new(); + } + + var regex = new Regex(@"^\d+"); + + int[] parts = new int[4]; + + for (int i = 0; i < segments.Length; i++) + { + var match = regex.Match(segments[i]); + if (match.Success && int.TryParse(match.Value, out int number)) + { + parts[i] = number; + + if (match.Value != segments[i]) + { + // when a segment contains more than a number, the rest of the segments are ignored + break; + } + } + else + { + // when a segment is not valid, the rest of the segments are ignored + break; + } + } + + return new() + { + Major = parts[0], + Minor = parts[1], + Build = parts[2], + PrivatePart = parts[3] + }; + } + + /// + /// Represents a product version with numeric parts for major, minor, build, and private versions. + /// + public class ProductVersion + { + /// + /// Gets the major part of the version number + /// + public int Major { get; init; } + + /// + /// Gets the minor part of the version number + /// + public int Minor { get; init; } + + /// + /// Gets the build part of the version number + /// + public int Build { get; init; } + + /// + /// Gets the private part of the version number + /// + public int PrivatePart { get; init; } + } + } +} diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileInfoBase.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileInfoBase.cs index 1c8875fb0..ca4df775b 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/FileInfoBase.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileInfoBase.cs @@ -36,7 +36,7 @@ internal FileInfoBase() { } /// public abstract void Encrypt(); - + /// public abstract void MoveTo(string destFileName); @@ -73,7 +73,7 @@ internal FileInfoBase() { } /// public abstract IFileInfo Replace(string destinationFileName, string destinationBackupFileName, bool ignoreMetadataErrors); - + /// public abstract IDirectoryInfo Directory { get; } diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystem.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystem.cs index 80e7ae69e..1e9cfca2b 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystem.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystem.cs @@ -12,6 +12,7 @@ public FileSystem() DriveInfo = new DriveInfoFactory(this); DirectoryInfo = new DirectoryInfoFactory(this); FileInfo = new FileInfoFactory(this); + FileVersionInfo = new FileVersionInfoFactory(this); Path = new PathWrapper(this); File = new FileWrapper(this); Directory = new DirectoryWrapper(this); @@ -28,6 +29,9 @@ public FileSystem() /// public override IFileInfoFactory FileInfo { get; } + /// + public override IFileVersionInfoFactory FileVersionInfo { get; } + /// public override IFileStreamFactory FileStream { get; } diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemBase.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemBase.cs index 6fb508161..b851d9eaa 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemBase.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemBase.cs @@ -15,6 +15,9 @@ public abstract class FileSystemBase : IFileSystem /// public abstract IFileInfoFactory FileInfo { get; } + /// + public abstract IFileVersionInfoFactory FileVersionInfo { get; } + /// public abstract IFileStreamFactory FileStream { get; } diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoBase.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoBase.cs new file mode 100644 index 000000000..d8dd87683 --- /dev/null +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoBase.cs @@ -0,0 +1,106 @@ +using System.Diagnostics; + +namespace System.IO.Abstractions +{ + /// +#if FEATURE_SERIALIZABLE + [Serializable] +#endif + public abstract class FileVersionInfoBase : IFileVersionInfo + { + /// + public abstract string Comments { get; } + + /// + public abstract string CompanyName { get; } + + /// + public abstract int FileBuildPart { get; } + + /// + public abstract string FileDescription { get; } + + /// + public abstract int FileMajorPart { get; } + + /// + public abstract int FileMinorPart { get; } + + /// + public abstract string FileName { get; } + + /// + public abstract int FilePrivatePart { get; } + + /// + public abstract string FileVersion { get; } + + /// + public abstract string InternalName { get; } + + /// + public abstract bool IsDebug { get; } + + /// + public abstract bool IsPatched { get; } + + /// + public abstract bool IsPrivateBuild { get; } + + /// + public abstract bool IsPreRelease { get; } + + /// + public abstract bool IsSpecialBuild { get; } + + /// + public abstract string Language { get; } + + /// + public abstract string LegalCopyright { get; } + + /// + public abstract string LegalTrademarks { get; } + + /// + public abstract string OriginalFilename { get; } + + /// + public abstract string PrivateBuild { get; } + + /// + public abstract int ProductBuildPart { get; } + + /// + public abstract int ProductMajorPart { get; } + + /// + public abstract int ProductMinorPart { get; } + + /// + public abstract string ProductName { get; } + + /// + public abstract int ProductPrivatePart { get; } + + /// + public abstract string ProductVersion { get; } + + /// + public abstract string SpecialBuild { get; } + + /// + public static implicit operator FileVersionInfoBase(FileVersionInfo fileVersionInfo) + { + if (fileVersionInfo == null) + { + return null; + } + + return new FileVersionInfoWrapper(fileVersionInfo); + } + + /// + public new abstract string ToString(); + } +} diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoFactory.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoFactory.cs new file mode 100644 index 000000000..f3594a28f --- /dev/null +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoFactory.cs @@ -0,0 +1,27 @@ +namespace System.IO.Abstractions +{ +#if FEATURE_SERIALIZABLE + [Serializable] +#endif + internal class FileVersionInfoFactory : IFileVersionInfoFactory + { + private readonly IFileSystem fileSystem; + + /// + public FileVersionInfoFactory(IFileSystem fileSystem) + { + this.fileSystem = fileSystem; + } + + /// + public IFileSystem FileSystem => fileSystem; + + /// + public IFileVersionInfo GetVersionInfo(string fileName) + { + Diagnostics.FileVersionInfo fileVersionInfo = Diagnostics.FileVersionInfo.GetVersionInfo(fileName); + + return new FileVersionInfoWrapper(fileVersionInfo); + } + } +} diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoWrapper.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoWrapper.cs new file mode 100644 index 000000000..999864475 --- /dev/null +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoWrapper.cs @@ -0,0 +1,187 @@ +using System.Diagnostics; + +namespace System.IO.Abstractions +{ + /// +#if FEATURE_SERIALIZABLE + [Serializable] +#endif + public class FileVersionInfoWrapper : FileVersionInfoBase + { + private readonly FileVersionInfo instance; + + /// + public FileVersionInfoWrapper(FileVersionInfo fileVersionInfo) + { + instance = fileVersionInfo; + } + + /// + public override string Comments + { + get { return instance.Comments; } + } + + /// + public override string CompanyName + { + get { return instance.CompanyName; } + } + + /// + public override int FileBuildPart + { + get { return instance.FileBuildPart; } + } + + /// + public override string FileDescription + { + get { return instance.FileDescription; } + } + + /// + public override int FileMajorPart + { + get { return instance.FileMajorPart; } + } + + /// + public override int FileMinorPart + { + get { return instance.FileMinorPart; } + } + + /// + public override string FileName + { + get { return instance.FileName; } + } + + /// + public override int FilePrivatePart + { + get { return instance.FilePrivatePart; } + } + + /// + public override string FileVersion + { + get { return instance.FileVersion; } + } + + /// + public override string InternalName + { + get { return instance.InternalName; } + } + + /// + public override bool IsDebug + { + get { return instance.IsDebug; } + } + + /// + public override bool IsPatched + { + get { return instance.IsPatched; } + } + + /// + public override bool IsPrivateBuild + { + get { return instance.IsPrivateBuild; } + } + + /// + public override bool IsPreRelease + { + get { return instance.IsPreRelease; } + } + + /// + public override bool IsSpecialBuild + { + get { return instance.IsSpecialBuild; } + } + + /// + public override string Language + { + get { return instance.Language; } + } + + /// + public override string LegalCopyright + { + get { return instance.LegalCopyright; } + } + + /// + public override string LegalTrademarks + { + get { return instance.LegalTrademarks; } + } + + /// + public override string OriginalFilename + { + get { return instance.OriginalFilename; } + } + + /// + public override string PrivateBuild + { + get { return instance.PrivateBuild; } + } + + /// + public override int ProductBuildPart + { + get { return instance.ProductBuildPart; } + } + + /// + public override int ProductMajorPart + { + get { return instance.ProductMajorPart; } + } + + /// + public override int ProductMinorPart + { + get { return instance.ProductMinorPart; } + } + + /// + public override string ProductName + { + get { return instance.ProductName; } + } + + /// + public override int ProductPrivatePart + { + get { return instance.ProductPrivatePart; } + } + + /// + public override string ProductVersion + { + get { return instance.ProductVersion; } + } + + /// + public override string SpecialBuild + { + get { return instance.SpecialBuild; } + } + + /// + public override string ToString() + { + return instance.ToString(); + } + } +} diff --git a/src/TestableIO.System.IO.Abstractions/IFileSystem.cs b/src/TestableIO.System.IO.Abstractions/IFileSystem.cs index d6f27d526..bc56d6246 100644 --- a/src/TestableIO.System.IO.Abstractions/IFileSystem.cs +++ b/src/TestableIO.System.IO.Abstractions/IFileSystem.cs @@ -30,6 +30,11 @@ public interface IFileSystem /// IFileInfoFactory FileInfo { get; } + /// + /// A factory for the creation of wrappers for . + /// + IFileVersionInfoFactory FileVersionInfo { get; } + /// /// A factory for the creation of wrappers for . /// diff --git a/src/TestableIO.System.IO.Abstractions/IFileVersionInfo.cs b/src/TestableIO.System.IO.Abstractions/IFileVersionInfo.cs new file mode 100644 index 000000000..d7840f2f4 --- /dev/null +++ b/src/TestableIO.System.IO.Abstractions/IFileVersionInfo.cs @@ -0,0 +1,89 @@ +using System.Diagnostics; + +namespace System.IO.Abstractions +{ + /// + public interface IFileVersionInfo + { + /// + string? Comments { get; } + + /// + string? CompanyName { get; } + + /// + int FileBuildPart { get; } + + /// + string? FileDescription { get; } + + /// + int FileMajorPart { get; } + + /// + int FileMinorPart { get; } + + /// + string FileName { get; } + + /// + int FilePrivatePart { get; } + + /// + string? FileVersion { get; } + + /// + string? InternalName { get; } + + /// + bool IsDebug { get; } + + /// + bool IsPatched { get; } + + /// + bool IsPrivateBuild { get; } + + /// + bool IsPreRelease { get; } + + /// + bool IsSpecialBuild { get; } + + /// + string? Language { get; } + + /// + string? LegalCopyright { get; } + + /// + string? LegalTrademarks { get; } + + /// + string? OriginalFilename { get; } + + /// + string? PrivateBuild { get; } + + /// + int ProductBuildPart { get; } + + /// + int ProductMajorPart { get; } + + /// + int ProductMinorPart { get; } + + /// + string? ProductName { get; } + + /// + int ProductPrivatePart { get; } + + /// + string? ProductVersion { get; } + + /// + string? SpecialBuild { get; } + } +} \ No newline at end of file diff --git a/src/TestableIO.System.IO.Abstractions/IFileVersionInfoFactory.cs b/src/TestableIO.System.IO.Abstractions/IFileVersionInfoFactory.cs new file mode 100644 index 000000000..a1d9208eb --- /dev/null +++ b/src/TestableIO.System.IO.Abstractions/IFileVersionInfoFactory.cs @@ -0,0 +1,13 @@ +using System.Diagnostics; + +namespace System.IO.Abstractions +{ + /// + /// A factory for the creation of wrappers for in a . + /// + public interface IFileVersionInfoFactory : IFileSystemEntity + { + /// + IFileVersionInfo GetVersionInfo(string fileName); + } +} diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs index 4b681421c..1f88311c1 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs @@ -286,6 +286,21 @@ public void MockFileSystem_AddFile_ShouldMatchCapitalization_PartialMatch_Furthe Assert.That(fileSystem.AllDirectories.ToList(), Does.Contain(XFS.Path(@"C:\LOUD\SUBLOUD\new\SUBDirectory"))); } + [Test] + public void MockFileSystem_AddFile_InitializesMockFileDataFileVersionInfoIfNull() + { + // Arrange + var fileSystem = new MockFileSystem(); + + // Act + fileSystem.AddFile(XFS.Path(@"C:\file.txt"), string.Empty); + + // Assert + IFileVersionInfo fileVersionInfo = fileSystem.FileVersionInfo.GetVersionInfo(XFS.Path(@"C:\file.txt")); + Assert.That(fileVersionInfo, Is.Not.Null); + Assert.That(fileVersionInfo.FileName, Is.EqualTo(XFS.Path(@"C:\file.txt"))); + } + [Test] public void MockFileSystem_AddFileFromEmbeddedResource_ShouldAddTheFile() { @@ -435,7 +450,7 @@ public void MockFileSystem_Constructor_ThrowsForNonRootedCurrentDirectory() ); Assert.That(ae.ParamName, Is.EqualTo("currentDirectory")); } - + [Test] [WindowsOnly(WindowsSpecifics.Drives)] public void MockFileSystem_Constructor_ShouldSupportDifferentRootDrives() @@ -450,7 +465,7 @@ public void MockFileSystem_Constructor_ShouldSupportDifferentRootDrives() var cExists = fileSystem.Directory.Exists(@"c:\"); var zExists = fileSystem.Directory.Exists(@"z:\"); var dExists = fileSystem.Directory.Exists(@"d:\"); - + Assert.That(fileSystem, Is.Not.Null); Assert.That(cExists, Is.True); Assert.That(zExists, Is.True); @@ -484,9 +499,9 @@ public void MockFileSystem_Constructor_ShouldNotDuplicateDrives() [@"d:\foo\bar\"] = new MockDirectoryData(), [@"d:\"] = new MockDirectoryData() }); - + var drivesInfo = fileSystem.DriveInfo.GetDrives(); - + Assert.That(drivesInfo.Where(d => string.Equals(d.Name, @"D:\", StringComparison.InvariantCultureIgnoreCase)), Has.Exactly(1).Items); } diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileVersionInfoFactoryTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileVersionInfoFactoryTests.cs new file mode 100644 index 000000000..9b10ac3a8 --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileVersionInfoFactoryTests.cs @@ -0,0 +1,43 @@ +using NUnit.Framework; +using System.Collections.Generic; + +namespace System.IO.Abstractions.TestingHelpers.Tests +{ + [TestFixture] + public class MockFileVersionInfoFactoryTests + { + [Test] + public void MockFileVersionInfoFactory_GetVersionInfo_ShouldReturnTheFileVersionInfoOfTheMockFileData() + { + // Arrange + var fileVersionInfo = new MockFileVersionInfo(@"c:\a.txt"); + var fileSystem = new MockFileSystem(new Dictionary + { + { @"c:\a.txt", new MockFileData("Demo text content") { FileVersionInfo = fileVersionInfo } } + }); + + // Act + var result = fileSystem.FileVersionInfo.GetVersionInfo(@"c:\a.txt"); + + // Assert + Assert.That(result, Is.EqualTo(fileVersionInfo)); + } + + [Test] + public void MockFileVersionInfoFactory_GetVersionInfo_ShouldThrowFileNotFoundExceptionIfFileDoesNotExist() + { + // Arrange + var fileSystem = new MockFileSystem(new Dictionary + { + { @"c:\a.txt", new MockFileData("Demo text content") }, + { @"c:\a\b\c.txt", new MockFileData("Demo text content") }, + }); + + // Act + TestDelegate code = () => fileSystem.FileVersionInfo.GetVersionInfo(@"c:\foo.txt"); + + // Assert + Assert.Throws(code); + } + } +} diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileVersionInfoTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileVersionInfoTests.cs new file mode 100644 index 000000000..307c7c1cc --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileVersionInfoTests.cs @@ -0,0 +1,86 @@ +using NUnit.Framework; + +namespace System.IO.Abstractions.TestingHelpers.Tests +{ + [TestFixture] + public class MockFileVersionInfoTests + { + [Test] + public void MockFileVersionInfo_ToString_ShouldReturnTheDefaultFormat() + { + // Arrange + var mockFileVersionInfo = new MockFileVersionInfo( + fileName: @"c:\b.txt", + fileVersion: "1.0.0.0", + productVersion: "1.0.0.0", + fileDescription: "b", + productName: "b", + companyName: null, + comments: null, + internalName: "b.txt", + isDebug: true, + isPatched: true, + isPrivateBuild: true, + isPreRelease: true, + isSpecialBuild: true, + language: "English", + legalCopyright: null, + legalTrademarks: null, + originalFilename: "b.txt", + privateBuild: null, + specialBuild: null); + + string expected = @"File: c:\b.txt +InternalName: b.txt +OriginalFilename: b.txt +FileVersion: 1.0.0.0 +FileDescription: b +Product: b +ProductVersion: 1.0.0.0 +Debug: True +Patched: True +PreRelease: True +PrivateBuild: True +SpecialBuild: True +Language: English +"; + + // Act & Assert + Assert.That(mockFileVersionInfo.ToString(), Is.EqualTo(expected)); + } + + [Test] + public void MockFileVersionInfo_Constructor_ShouldSetFileAndProductVersionNumbersIfFileAndProductVersionAreNotNull() + { + // Arrange + var mockFileVersionInfo = new MockFileVersionInfo(@"c:\file.txt", fileVersion: "1.2.3.4", productVersion: "5.6.7.8"); + + // Assert + Assert.That(mockFileVersionInfo.FileMajorPart, Is.EqualTo(1)); + Assert.That(mockFileVersionInfo.FileMinorPart, Is.EqualTo(2)); + Assert.That(mockFileVersionInfo.FileBuildPart, Is.EqualTo(3)); + Assert.That(mockFileVersionInfo.FilePrivatePart, Is.EqualTo(4)); + Assert.That(mockFileVersionInfo.ProductMajorPart, Is.EqualTo(5)); + Assert.That(mockFileVersionInfo.ProductMinorPart, Is.EqualTo(6)); + Assert.That(mockFileVersionInfo.ProductBuildPart, Is.EqualTo(7)); + Assert.That(mockFileVersionInfo.ProductPrivatePart, Is.EqualTo(8)); + } + + [Test] + public void MockFileVersionInfo_Constructor_ShouldNotSetFileAndProductVersionNumbersIfFileAndProductVersionAreNull() + { + // Act + var mockFileVersionInfo = new MockFileVersionInfo(@"c:\a.txt"); + + // Assert + Assert.That(mockFileVersionInfo.FileMajorPart, Is.EqualTo(0)); + Assert.That(mockFileVersionInfo.FileMinorPart, Is.EqualTo(0)); + Assert.That(mockFileVersionInfo.FileBuildPart, Is.EqualTo(0)); + Assert.That(mockFileVersionInfo.FilePrivatePart, Is.EqualTo(0)); + Assert.That(mockFileVersionInfo.ProductMajorPart, Is.EqualTo(0)); + Assert.That(mockFileVersionInfo.ProductMinorPart, Is.EqualTo(0)); + Assert.That(mockFileVersionInfo.ProductBuildPart, Is.EqualTo(0)); + Assert.That(mockFileVersionInfo.ProductPrivatePart, Is.EqualTo(0)); + } + } +} diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/ProductVersionParserTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/ProductVersionParserTests.cs new file mode 100644 index 000000000..980c8be5d --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/ProductVersionParserTests.cs @@ -0,0 +1,107 @@ +using NUnit.Framework; + +namespace System.IO.Abstractions.TestingHelpers.Tests +{ + [TestFixture] + public class ProductVersionParserTests + { + [Test] + public void ProductVersionParser_Parse_ShouldIgnoreTheSegmentsWhenThereAreMoreThanFiveOfThem() + { + // Arrange + string productVersion = "1.2.3.4.5"; + + // Act + var parsedProductVersion = ProductVersionParser.Parse(productVersion); + + // Assert + Assert.Multiple(() => + { + Assert.That(parsedProductVersion.Major, Is.Zero); + Assert.That(parsedProductVersion.Minor, Is.Zero); + Assert.That(parsedProductVersion.Build, Is.Zero); + Assert.That(parsedProductVersion.PrivatePart, Is.Zero); + }); + } + + [Test] + [TestCase("test.2.3.4", 0, 0, 0, 0)] + [TestCase("1.test.3.4", 1, 0, 0, 0)] + [TestCase("1.2.test.4", 1, 2, 0, 0)] + [TestCase("1.2.3.test", 1, 2, 3, 0)] + public void ProductVersionParser_Parse_ShouldSkipTheRestOfTheSegmentsWhenOneIsNotValidNumber( + string productVersion, + int expectedMajor, + int expectedMinor, + int expectedBuild, + int expectedRevision) + { + // Act + var parsedProductVersion = ProductVersionParser.Parse(productVersion); + + // Assert + Assert.Multiple(() => + { + Assert.That(parsedProductVersion.Major, Is.EqualTo(expectedMajor)); + Assert.That(parsedProductVersion.Minor, Is.EqualTo(expectedMinor)); + Assert.That(parsedProductVersion.Build, Is.EqualTo(expectedBuild)); + Assert.That(parsedProductVersion.PrivatePart, Is.EqualTo(expectedRevision)); + }); + } + + [Test] + [TestCase("1-test.2.3.4", 1, 0, 0, 0)] + [TestCase("1-test5.2.3.4", 1, 0, 0, 0)] + [TestCase("1.2-test.3.4", 1, 2, 0, 0)] + [TestCase("1.2-test5.3.4", 1, 2, 0, 0)] + [TestCase("1.2.3-test.4", 1, 2, 3, 0)] + [TestCase("1.2.3-test5.4", 1, 2, 3, 0)] + [TestCase("1.2.3.4-test", 1, 2, 3, 4)] + [TestCase("1.2.3.4-test5", 1, 2, 3, 4)] + public void ProductVersionParser_Parse_ShouldSkipTheRestOfTheSegmentsWhenOneContainsMoreThanJustOneNumber( + string productVersion, + int expectedMajor, + int expectedMinor, + int expectedBuild, + int expectedRevision) + { + // Act + var parsedProductVersion = ProductVersionParser.Parse(productVersion); + + // Assert + Assert.Multiple(() => + { + Assert.That(parsedProductVersion.Major, Is.EqualTo(expectedMajor)); + Assert.That(parsedProductVersion.Minor, Is.EqualTo(expectedMinor)); + Assert.That(parsedProductVersion.Build, Is.EqualTo(expectedBuild)); + Assert.That(parsedProductVersion.PrivatePart, Is.EqualTo(expectedRevision)); + }); + } + + [Test] + [TestCase("", 0, 0, 0, 0)] + [TestCase("1", 1, 0, 0, 0)] + [TestCase("1.2", 1, 2, 0, 0)] + [TestCase("1.2.3", 1, 2, 3, 0)] + [TestCase("1.2.3.4", 1, 2, 3, 4)] + public void ProductVersionParser_Parse_ShouldParseEachProvidedSegment( + string productVersion, + int expectedMajor, + int expectedMinor, + int expectedBuild, + int expectedRevision) + { + // Act + var parsedProductVersion = ProductVersionParser.Parse(productVersion); + + // Assert + Assert.Multiple(() => + { + Assert.That(parsedProductVersion.Major, Is.EqualTo(expectedMajor)); + Assert.That(parsedProductVersion.Minor, Is.EqualTo(expectedMinor)); + Assert.That(parsedProductVersion.Build, Is.EqualTo(expectedBuild)); + Assert.That(parsedProductVersion.PrivatePart, Is.EqualTo(expectedRevision)); + }); + } + } +} diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/ApiParityTests.cs b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/ApiParityTests.cs index 706b06a2c..ec46effa8 100644 --- a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/ApiParityTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/ApiParityTests.cs @@ -24,6 +24,13 @@ public void FileInfo() => typeof(System.IO.Abstractions.FileInfoBase) ); + [Test] + public void FileVersionInfo() => + AssertParity( + typeof(System.Diagnostics.FileVersionInfo), + typeof(System.IO.Abstractions.FileVersionInfoBase) + ); + [Test] public void Directory() => AssertParity( @@ -72,7 +79,8 @@ static IEnumerable GetMembers(Type type) => type .Select(x => x.Replace("System.IO.FileInfo", "System.IO.Abstractions.IFileInfo")) .Select(x => x.Replace("System.IO.DirectoryInfo", "System.IO.Abstractions.IDirectoryInfo")) .Select(x => x.Replace("System.IO.DriveInfo", "System.IO.Abstractions.IDriveInfo")) - .Select(x => x.Replace("System.IO.WaitForChangedResult", "System.IO.Abstractions.IWaitForChangedResult")); + .Select(x => x.Replace("System.IO.WaitForChangedResult", "System.IO.Abstractions.IWaitForChangedResult")) + .Where(x => x != "System.Diagnostics.FileVersionInfo GetVersionInfo(System.String)"); var abstractionMembers = GetMembers(abstractionType) .Where(x => !x.Contains("op_Implicit")) .Where(x => x != "System.IO.Abstractions.IFileSystem get_FileSystem()") diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/FileVersionInfoBaseConversionTests.cs b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/FileVersionInfoBaseConversionTests.cs new file mode 100644 index 000000000..b53a64e9e --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/FileVersionInfoBaseConversionTests.cs @@ -0,0 +1,27 @@ +using NUnit.Framework; +using System.Diagnostics; + +namespace System.IO.Abstractions.Tests +{ + /// + /// Unit tests for the conversion operators of the class. + /// + public class FileVersionInfoBaseConversionTests + { + /// + /// Tests that a null is correctly converted to a null without exception. + /// + [Test] + public void FileVersionInfoBase_FromFileVersionInfo_ShouldReturnNullIfFileVersionInfoIsNull() + { + // Arrange + FileVersionInfo fileVersionInfo = null; + + // Act + FileVersionInfoBase actual = fileVersionInfo; + + // Assert + Assert.That(actual, Is.Null); + } + } +} diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET 6.0.snap b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET 6.0.snap new file mode 100644 index 000000000..7cb3b9523 --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET 6.0.snap @@ -0,0 +1,4 @@ +{ + "ExtraMembers": [], + "MissingMembers": [] +} diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET 7.0.snap b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET 7.0.snap new file mode 100644 index 000000000..7cb3b9523 --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET 7.0.snap @@ -0,0 +1,4 @@ +{ + "ExtraMembers": [], + "MissingMembers": [] +} diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET 8.0.snap b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET 8.0.snap new file mode 100644 index 000000000..7cb3b9523 --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET 8.0.snap @@ -0,0 +1,4 @@ +{ + "ExtraMembers": [], + "MissingMembers": [] +} diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET Framework 4.6.2.snap b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET Framework 4.6.2.snap new file mode 100644 index 000000000..7cb3b9523 --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileVersionInfo_.NET Framework 4.6.2.snap @@ -0,0 +1,4 @@ +{ + "ExtraMembers": [], + "MissingMembers": [] +} diff --git a/version.json b/version.json index 081fbf157..8e4c8f245 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "21.1", + "version": "21.2", "assemblyVersion": { "precision": "major" },