using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace AMWD.Common.Test { /// /// Implements a snapshot comparison for content aggregation (e.g. files). /// public sealed class SnapshotAssert { /// /// Tests whether the specified string is equal to the saved snapshot. /// /// The current aggregated content string. /// An error message. /// The absolute file path of the calling file (filled automatically by the compiler service). /// The name of the calling method (filled automatically by the compiler service). public static void AreEqual(string actual, string message = null, [CallerFilePath] string callerFilePath = null, [CallerMemberName] string callerMemberName = null) { string cleanLineEnding = actual .Replace("\r\n", "\n") // Windows .Replace("\r", "\n"); // old MacOS AreEqual(Encoding.UTF8.GetBytes(cleanLineEnding), message, callerFilePath, callerMemberName); } /// /// Tests whether the specified byte array is equal to the saved snapshot. /// /// The current aggregated content bytes. /// An error message. /// The absolute file path of the calling file (filled automatically by the compiler service). /// The name of the calling method (filled automatically by the compiler service). public static void AreEqual(byte[] actual, string message = null, [CallerFilePath] string callerFilePath = null, [CallerMemberName] string callerMemberName = null) => AreEqual(actual, 0, -1, message, callerFilePath, callerMemberName); /// /// Tests whether the specified byte array is equal to the saved snapshot. /// /// /// The past has shown, that e.g. wkhtmltopdf prints the current timestamp at the beginning of the PDF file. /// Therefore only a specific part of that file can be asserted to be equal. /// /// The current aggregated content bytes. /// The first byte to compare. /// The last byte to compare. /// An error message. /// The absolute file path of the calling file (filled automatically by the compiler service). /// The name of the calling method (filled automatically by the compiler service). public static void AreEqual(byte[] actual, int firstByteIndex, int lastByteIndex, string message = null, [CallerFilePath] string callerFilePath = null, [CallerMemberName] string callerMemberName = null) { string callerDir = Path.GetDirectoryName(callerFilePath); string callerFile = Path.GetFileNameWithoutExtension(callerFilePath); string snapshotDir = Path.Combine(callerDir, "Snapshots", callerFile); string snapshotFile = Path.Combine(snapshotDir, $"{callerMemberName}.snap"); if (File.Exists(snapshotFile)) { byte[] expected = File.ReadAllBytes(snapshotFile); var actualBytes = actual.Skip(firstByteIndex); var expectedBytes = expected.Skip(firstByteIndex); if (lastByteIndex > firstByteIndex) { actualBytes = actualBytes.Take(lastByteIndex - firstByteIndex); expectedBytes = expectedBytes.Take(lastByteIndex - firstByteIndex); } if (message == null) CollectionAssert.AreEqual(expectedBytes.ToArray(), actualBytes.ToArray()); else CollectionAssert.AreEqual(expectedBytes.ToArray(), actualBytes.ToArray(), message); } else { if (!Directory.Exists(snapshotDir)) Directory.CreateDirectory(snapshotDir); File.WriteAllBytes(snapshotFile, actual); } } } }