diff --git a/src/Mime-Detective/Extensions/ByteArrayExtensions.cs b/src/Mime-Detective/Extensions/ByteArrayExtensions.cs index a76227f..5553404 100644 --- a/src/Mime-Detective/Extensions/ByteArrayExtensions.cs +++ b/src/Mime-Detective/Extensions/ByteArrayExtensions.cs @@ -10,15 +10,15 @@ public static class ByteArrayExtensions /// /// Read header of bytes and depending on the information in the header /// return object FileType. - /// Return null in case when the file type is not identified. + /// Return null in case when the file type is not identified. /// Throws Application exception if the file can not be read or does not exist /// /// /// A temp file is written to get a FileInfo from the given bytes. - /// If this is not intended use - /// - /// GetFileType(() => bytes); - /// + /// If this is not intended use + /// + /// GetFileType(() => bytes); + /// /// /// The FileInfo object. /// FileType or null not identified @@ -26,6 +26,5 @@ public static FileType GetFileType(this byte[] bytes) { return MimeTypes.GetFileType(() => MimeTypes.ReadHeaderFromByteArray(bytes, MimeTypes.MaxHeaderSize), null, bytes); } - } } diff --git a/src/Mime-Detective/Extensions/StreamExtensions.cs b/src/Mime-Detective/Extensions/StreamExtensions.cs index 01e7bda..0438a90 100644 --- a/src/Mime-Detective/Extensions/StreamExtensions.cs +++ b/src/Mime-Detective/Extensions/StreamExtensions.cs @@ -14,7 +14,7 @@ public static class StreamExtensions /// /// Read header of a stream and depending on the information in the header /// return object FileType. - /// Return null in case when the file type is not identified. + /// Return null in case when the file type is not identified. /// Throws Application exception if the file can not be read or does not exist /// /// The FileInfo object. @@ -28,7 +28,7 @@ public static FileType GetFileType(this Stream stream) /// /// Read header of a stream and depending on the information in the header /// return object FileType. - /// Return null in case when the file type is not identified. + /// Return null in case when the file type is not identified. /// Throws Application exception if the file can not be read or does not exist /// /// The FileInfo object. @@ -42,7 +42,7 @@ public static Task GetFileTypeAsync(this Stream stream) /// /// Read header of a stream and depending on the information in the header /// return object FileType. - /// Return null in case when the file type is not identified. + /// Return null in case when the file type is not identified. /// Throws Application exception if the file can not be read or does not exist /// /// The FileInfo object. diff --git a/src/Mime-Detective/FileType.cs b/src/Mime-Detective/FileType.cs index bad19b6..73a3a3a 100644 --- a/src/Mime-Detective/FileType.cs +++ b/src/Mime-Detective/FileType.cs @@ -2,28 +2,12 @@ namespace MimeDetective { - /* - public struct FileDetectionResult - { - public readonly FileType FileType; - - public readonly bool WasFileTypeDetected; - - public FileDetectionResult(bool wasFileTypeDetected, FileType fileType) - { - FileType = fileType; - - WasFileTypeDetected = wasFileTypeDetected; - } - } - */ - /// /// Little data structure to hold information about file types. /// Holds information about binary header at the start of the file /// these are mostly static they can be structs /// - public struct FileType + public class FileType { public byte?[] Header { get; } public ushort HeaderOffset { get; } @@ -42,7 +26,7 @@ public FileType(byte?[] header, string extension, string mime, ushort offset = 0 { //header cannot be null, file type normal operation requires the data if (header == null) - throw new ArgumentNullException(nameof(header), "cannot be null file type needs file header data"); + throw new ArgumentNullException(nameof(header), "cannot be null, FileType needs file header data"); Header = header; HeaderOffset = offset; diff --git a/src/Mime-Detective/MimeDetective.cs b/src/Mime-Detective/MimeDetective.cs index 6586979..f83fccc 100644 --- a/src/Mime-Detective/MimeDetective.cs +++ b/src/Mime-Detective/MimeDetective.cs @@ -43,7 +43,7 @@ public static FileType LearnMimeType(FileInfo file, string mimeType, int headerS return new FileType(data, file.Extension, mimeType, offset); } - public static FileType? LearnMimeType(FileInfo first, FileInfo second, string mimeType, int maxHeaderSize = 12, int minMatches = 2, int maxNonMatch = 3) + public static FileType LearnMimeType(FileInfo first, FileInfo second, string mimeType, int maxHeaderSize = 12, int minMatches = 2, int maxNonMatch = 3) { byte?[] header = null; diff --git a/src/Mime-Detective/MimeTypes.cs b/src/Mime-Detective/MimeTypes.cs index c2a0ca6..7d30e12 100644 --- a/src/Mime-Detective/MimeTypes.cs +++ b/src/Mime-Detective/MimeTypes.cs @@ -209,7 +209,6 @@ EML is also used by Outlook Express and QuickMail. #region Main Methods - /* public static void SaveToXmlFile(string path) { using (FileStream file = File.OpenWrite(path)) @@ -226,15 +225,8 @@ public static FileType[] LoadFromXmlFile(string path) var serializer = new System.Xml.Serialization.XmlSerializer(Types.GetType()); return (FileType[])serializer.Deserialize(file); - - //int typeOrgLenth = Types.Length; - - //Array.Resize(ref Types, Types.Length + tmpTypes.Length); - - //Array.Copy(tmpTypes, 0, Types, typeOrgLenth, tmpTypes.Length); } } - */ /* public static FileType GetFileType(FileInfo file) @@ -247,7 +239,6 @@ public static FileType GetFileType(FileInfo file) /// Read header of a file and depending on the information in the header /// return object FileType. /// Return null in case when the file type is not identified. - /// Throws Application exception if the file can not be read or does not exist /// /// A function which returns the bytes found /// If given and file typ is a zip file, a check for docx and xlsx is done @@ -294,13 +285,13 @@ private static FileType getFileType(IReadOnlyList fileHeader, Stream strea var officeXml = CheckForDocxAndXlsxStream(zipData); if (officeXml != null) - return officeXml.Value; + return officeXml; //check for open office formats var openOffice = CheckForOdtAndOds(zipData); if (openOffice != null) - return openOffice.Value; + return openOffice; } } } @@ -311,7 +302,8 @@ private static FileType getFileType(IReadOnlyList fileHeader, Stream strea } } - throw new Exception("No file type match found"); + //no match return null + return null; } /// @@ -333,7 +325,7 @@ public static List GetFileTypesByExtensions(string CSV) return result; } - private static FileType? CheckForDocxAndXlsxStream(ZipArchive zipData) + private static FileType CheckForDocxAndXlsxStream(ZipArchive zipData) { if (zipData.Entries.Any(e => e.FullName.StartsWith("word/"))) return WORDX; @@ -365,7 +357,7 @@ private static FileType CheckForDocxAndXlsx(FileType type, FileInfo fileInfo) */ //check for open doc formats - private static FileType? CheckForOdtAndOds(ZipArchive zipFile) + private static FileType CheckForOdtAndOds(ZipArchive zipFile) { var ooMimeType = zipFile.Entries.FirstOrDefault(e => e.FullName == "mimetype"); @@ -431,7 +423,7 @@ internal static IReadOnlyList ReadFileHeader(FileInfo file, ushort MaxHead } catch (Exception e) // file could not be found/read { - throw new Exception($"Could not read file: {e.Message}"); + throw new System.IO.IOException($"Could not read {nameof(file)}", e); } return header; @@ -454,7 +446,7 @@ internal static async Task> ReadFileHeaderAsync(FileInfo fil } catch (Exception e) // file could not be found/read { - throw new System.IO.FileLoadException($"Could not read file: {e.Message}", file.FullName, e); + throw new System.IO.IOException($"Could not read {nameof(file)}", e); } return header; @@ -470,8 +462,6 @@ internal static IReadOnlyList ReadHeaderFromStream(Stream stream, ushort M { byte[] header = new byte[MaxHeaderSize]; - try // read stream - { if (!stream.CanRead) throw new System.IO.IOException("Could not read from Stream"); @@ -479,11 +469,6 @@ internal static IReadOnlyList ReadHeaderFromStream(Stream stream, ushort M stream.Seek(0, SeekOrigin.Begin); stream.Read(header, 0, MaxHeaderSize); - } - catch (Exception e) // file could not be found/read - { - throw new Exception("Could not read Stream : " + e.Message); - } return header; } @@ -498,32 +483,29 @@ internal static async Task> ReadHeaderFromStreamAsync(Stream { byte[] header = new byte[MaxHeaderSize]; - try // read stream - { if (!stream.CanRead) - throw new System.IO.IOException("Could not read from Stream"); + throw new IOException($"Could not read from {nameof(stream)}"); if (stream.Position > 0) stream.Seek(0, SeekOrigin.Begin); await stream.ReadAsync(header, 0, MaxHeaderSize); - } - catch (Exception e) // file could not be found/read - { - throw new Exception("Could not read Stream : " + e.Message); - } return header; } - internal static IReadOnlyList ReadHeaderFromByteArray(byte[] byteArray, ushort MaxHeaderSize) + internal static IReadOnlyList ReadHeaderFromByteArray(IReadOnlyList byteArray, ushort MaxHeaderSize) { - if (byteArray.Length < MaxHeaderSize) - throw new ArgumentException($"{nameof(byteArray)}:{byteArray} Is smaller than {nameof(MaxHeaderSize)}:{MaxHeaderSize}", nameof(byteArray)); + if (byteArray.Count < MaxHeaderSize) + throw new ArgumentException($"{nameof(byteArray)}:{byteArray.Count} Is smaller than {nameof(MaxHeaderSize)}:{MaxHeaderSize}", nameof(byteArray)); byte[] header = new byte[MaxHeaderSize]; - Array.Copy(byteArray, header, MaxHeaderSize); + //Array.Copy(byteArray, header, MaxHeaderSize); + for (int i = 0; i < MaxHeaderSize; i++) + { + header[i] = byteArray[i]; + } return header; } diff --git a/test/Mime-Detective.Tests/Data/empty.jpg b/test/Mime-Detective.Tests/Data/empty.jpg new file mode 100644 index 0000000..e69de29 diff --git a/test/Mime-Detective.Tests/Tests/Documents/DocumentsTests.cs b/test/Mime-Detective.Tests/Tests/Documents/MsOfficeFormats.cs similarity index 95% rename from test/Mime-Detective.Tests/Tests/Documents/DocumentsTests.cs rename to test/Mime-Detective.Tests/Tests/Documents/MsOfficeFormats.cs index 8cf6124..a6d2862 100644 --- a/test/Mime-Detective.Tests/Tests/Documents/DocumentsTests.cs +++ b/test/Mime-Detective.Tests/Tests/Documents/MsOfficeFormats.cs @@ -9,7 +9,7 @@ namespace MimeDetective.Tests.Documents { - public class DocumentsTests + public class MsOfficeFormats { public const string DocsPath = "./Data/Documents/test."; @@ -18,7 +18,7 @@ public async Task FileInfoDocx() { var info = new FileInfo(DocsPath + "docx"); - var a = System.IO.Directory.GetCurrentDirectory(); + var a = Directory.GetCurrentDirectory(); var fileInfo = await info.GetFileTypeAsync(); diff --git a/test/Mime-Detective.Tests/Tests/FileType.cs b/test/Mime-Detective.Tests/Tests/FileType.cs index 4b58f90..e95f4e7 100644 --- a/test/Mime-Detective.Tests/Tests/FileType.cs +++ b/test/Mime-Detective.Tests/Tests/FileType.cs @@ -6,14 +6,14 @@ namespace MimeDetective.Tests { - public class FileTypeTests + public class FileType { [Fact] public void Constructors() { - var info = new FileType(new byte?[] { 0x12, 0x14, 0x13, 0x15, 0x16 }, "png", "image/png", 4); + var info = new global::MimeDetective.FileType(new byte?[] { 0x12, 0x14, 0x13, 0x15, 0x16 }, "png", "image/png", 4); - Assert.Throws(typeof(ArgumentNullException), () => { var a = new FileType(null, "png", "image/png", 4); }); + Assert.Throws(typeof(ArgumentNullException), () => { var a = new global::MimeDetective.FileType(null, "png", "image/png", 4); }); } [Fact] diff --git a/test/Mime-Detective.Tests/Tests/Images/IsFile.cs b/test/Mime-Detective.Tests/Tests/Images/CommonFormats.cs similarity index 97% rename from test/Mime-Detective.Tests/Tests/Images/IsFile.cs rename to test/Mime-Detective.Tests/Tests/Images/CommonFormats.cs index 7814371..a8394d1 100644 --- a/test/Mime-Detective.Tests/Tests/Images/IsFile.cs +++ b/test/Mime-Detective.Tests/Tests/Images/CommonFormats.cs @@ -11,9 +11,9 @@ namespace MimeDetective.Tests.Images { // This project can output the Class library as a NuGet Package. // To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build". - public class IsFile + public class CommonFormats { - public IsFile() + public CommonFormats() { } diff --git a/test/Mime-Detective.Tests/Tests/Text/TextTests.cs b/test/Mime-Detective.Tests/Tests/Text/CommonFormats.cs similarity index 100% rename from test/Mime-Detective.Tests/Tests/Text/TextTests.cs rename to test/Mime-Detective.Tests/Tests/Text/CommonFormats.cs diff --git a/test/Mime-Detective.Tests/Tests/TypeExtensions.cs b/test/Mime-Detective.Tests/Tests/TypeExtensions.cs new file mode 100644 index 0000000..4db977c --- /dev/null +++ b/test/Mime-Detective.Tests/Tests/TypeExtensions.cs @@ -0,0 +1,142 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MimeDetective.Extensions; +using MimeDetective; +using Xunit; + +namespace MimeDetective.Tests +{ + public class TypeExtensions + { + const string GoodFile = "./data/images/test.jpg"; + + const string BadFile = "./data/empty.jpg"; + + const string NonexistentFile = "./data/nonexistent.jpg"; + + //load from fileinfo + //attempt to load from real file + //attempt to load from nonexistent file /badfile + [Fact] + public async Task FromFile() + { + var fileInfo = new FileInfo(GoodFile); + + var fileType = await fileInfo.GetFileTypeAsync(); + + Assert.True(fileType == MimeTypes.JPEG); + } + + [Fact] + public void FromFileSync() + { + var fileInfo = new FileInfo(GoodFile); + + Assert.True(fileInfo.IsJpeg()); + } + + //test shouldn't fail, an empty file can be valid input + [Fact] + public async Task FromEmptyFile() + { + var fileInfo = new FileInfo(BadFile); + + var type = await fileInfo.GetFileTypeAsync(); + + //no match so return type is null + Assert.Null(type); + } + + [Fact] + public async Task FromNonExistentFile() + { + var fileInfo = new FileInfo(NonexistentFile); + + await Assert.ThrowsAnyAsync(() => fileInfo.GetFileTypeAsync()); + } + + //load from stream + //attempt to load from good stream + //attempt to load from empty stream + + [Fact] + public async Task FromStream() + { + var fileInfo = new FileInfo(GoodFile); + + using (var fileStream = fileInfo.OpenRead()) + { + var fileType = await fileStream.GetFileTypeAsync(); + + Assert.NotNull(fileType); + + Assert.Equal(MimeTypes.JPEG, fileType); + } + } + + [Fact] + public void FromStreamSync() + { + var fileInfo = new FileInfo(GoodFile); + + using (var fileStream = fileInfo.OpenRead()) + { + var fileType = fileStream.GetFileType(); + + Assert.NotNull(fileType); + + Assert.Equal(MimeTypes.JPEG, fileType); + } + } + + [Fact] + public async Task FromEmptyStream() + { + var emptyStream = System.IO.Stream.Null; + + var nullReturn = await emptyStream.GetFileTypeAsync(); + + Assert.Null(nullReturn); + } + + //load from byte array + //load from good byte array + //attempt to load from empty byte array + [Fact] + public async Task FromByteArray() + { + var fileInfo = new FileInfo(GoodFile); + + //560 is the max file header size + byte[] byteArray = new byte[560]; + + using (var fileStream = fileInfo.OpenRead()) + { + await fileStream.ReadAsync(byteArray, 0, 560); + } + + var mimeType = byteArray.GetFileType(); + + Assert.NotNull(mimeType); + + Assert.Equal(MimeTypes.JPEG, mimeType); + } + + [Fact] + public void FromEmptyByteArray() + { + var zerodByteArray = new byte[560]; + + var zerodResult = zerodByteArray.GetFileType(); + + Assert.Null(zerodResult); + + var emptyBtyeArray = new byte[0]; + + Assert.ThrowsAny(() => emptyBtyeArray.GetFileType()); + } + } +} diff --git a/test/Mime-Detective.Tests/Utilities/TypeComparisions.cs b/test/Mime-Detective.Tests/Utilities/TypeComparisions.cs index 01a09e2..e7e4eff 100644 --- a/test/Mime-Detective.Tests/Utilities/TypeComparisions.cs +++ b/test/Mime-Detective.Tests/Utilities/TypeComparisions.cs @@ -1,4 +1,5 @@ using MimeDetective; +using System; using System.IO; using System.Threading.Tasks; using Xunit; @@ -20,4 +21,15 @@ public static async Task AssertIsType(FileInfo info, FileType type) Assert.False(info.GetFileType() != type); } } + + /* + public class MyClass + { + [Fact] + public void test() + { + Assert.ThrowsAny(() => { TypeComparisions.AssertIsType(null, MimeTypes.AES).Wait(); }); + } + } + */ } \ No newline at end of file