From 9a9b7d287b7bf63e07b9a6a4209272af6889c313 Mon Sep 17 00:00:00 2001 From: Aaron Pearson Date: Sat, 13 Jan 2024 23:17:40 +0000 Subject: [PATCH] Commenting * Mostly commented what I feel like is needed. * Made MSDelta not read in the whole file but only the first 70 bytes upon checking status as that is required to allow it to do it's job. * BSDiffDelta using some more modern logic * More TODO's in places which could be expanded upon --- .../TinyUpdate.Delta.BSDiff/BSDiffDelta.cs | 49 +++++------- .../TinyUpdate.Delta.MSDelta/MSDelta.cs | 52 +++++++------ src/Packages/TinyUpdate.TUUP/Consts.cs | 19 +++++ src/Packages/TinyUpdate.TUUP/FileEntryExt.cs | 7 +- src/Packages/TinyUpdate.TUUP/StreamExt.cs | 21 ++--- .../TinyUpdate.TUUP/TuupUpdatePackage.cs | 32 ++++---- .../TuupUpdatePackageCreator.cs | 77 +++++++++++-------- .../TuupUpdatePackageCreatorOptions.cs | 4 + src/TinyUpdate.Core/Abstract/IDeltaApplier.cs | 10 +-- .../Abstract/IDeltaCreation.cs | 13 ++-- src/TinyUpdate.Core/Abstract/IDeltaManager.cs | 33 ++++---- src/TinyUpdate.Core/Abstract/IExtension.cs | 3 + src/TinyUpdate.Core/Abstract/IHasher.cs | 24 +++--- .../Abstract/IUpdatePackage.cs | 13 +++- .../Abstract/IUpdatePackageCreator.cs | 34 +++++++- src/TinyUpdate.Core/DeltaManager.cs | 3 +- .../Model/DeltaCreationResult.cs | 31 ++++++++ src/TinyUpdate.Core/{ => Model}/FileEntry.cs | 5 +- .../Abstract/DeltaCan.cs | 44 ++++++----- .../Abstract/DeltaManagerCan.cs | 32 ++++---- tests/TinyUpdate.Delta.Tests/DeltaTests.cs | 14 ++-- .../Abstract/UpdatePackageCan.cs | 35 +++++---- .../UpdatePackageTests.cs | 18 ++--- 23 files changed, 346 insertions(+), 227 deletions(-) create mode 100644 src/TinyUpdate.Core/Model/DeltaCreationResult.cs rename src/TinyUpdate.Core/{ => Model}/FileEntry.cs (95%) diff --git a/src/Deltas/TinyUpdate.Delta.BSDiff/BSDiffDelta.cs b/src/Deltas/TinyUpdate.Delta.BSDiff/BSDiffDelta.cs index 77647ba..473058d 100644 --- a/src/Deltas/TinyUpdate.Delta.BSDiff/BSDiffDelta.cs +++ b/src/Deltas/TinyUpdate.Delta.BSDiff/BSDiffDelta.cs @@ -1,8 +1,10 @@ -using Microsoft.Extensions.Logging; +using System.Buffers.Binary; +using Microsoft.Extensions.Logging; using SharpCompress.Compressors; using SharpCompress.Compressors.BZip2; using TinyUpdate.Core.Abstract; +//TODO: Bring in changes from bsdiff.net into here // Squirrel.Bsdiff: Adapted from https://github.com/LogosBible/bsdiff.net/blob/master/src/bsdiff/BinaryPatchUtility.cs // TinyUpdate: Adapted from https://github.com/Squirrel/Squirrel.Windows/blob/develop/src/Squirrel/BinaryPatchUtility.cs namespace TinyUpdate.Delta.BSDiff; @@ -72,7 +74,8 @@ public long TargetStreamSize(Stream deltaStream) return newSize; } - public async Task ApplyDeltaFile(Stream originalStream, Stream deltaStream, Stream targetStream, IProgress? progress = null) + //TODO: Add Source + Target size check + public async Task ApplyDeltaFile(Stream sourceStream, Stream deltaStream, Stream targetStream, IProgress? progress = null) { // check patch stream capabilities if (!deltaStream.CanRead) @@ -107,6 +110,7 @@ with control block a set of triples (x,y,z) meaning "add x bytes from oldfile to x bytes from the diff block; copy y bytes from the extra block; seek forwards in oldfile by z bytes". */ + // read header long controlLength, diffLength, newSize; await using (var patchStream = CreatePatchStream()) @@ -177,7 +181,7 @@ with control block a set of triples (x,y,z) meaning "add x bytes } // seek old file to the position that the new data is diffed against - originalStream.Position = oldPosition; + sourceStream.Position = oldPosition; var bytesToCopy = (int)control[0]; while (bytesToCopy > 0) @@ -188,8 +192,8 @@ with control block a set of triples (x,y,z) meaning "add x bytes await diffStream.ReadExactlyAsync(newData.AsMemory(0, actualBytesToCopy)); // add old data to diff string - var availableOriginalStreamBytes = Math.Min(actualBytesToCopy, (int)(originalStream.Length - originalStream.Position)); - await originalStream.ReadExactlyAsync(oldData.AsMemory(0, availableOriginalStreamBytes)); + var availableOriginalStreamBytes = Math.Min(actualBytesToCopy, (int)(sourceStream.Length - sourceStream.Position)); + await sourceStream.ReadExactlyAsync(oldData.AsMemory(0, availableOriginalStreamBytes)); for (var index = 0; index < availableOriginalStreamBytes; index++) newData[index] += oldData[index]; @@ -241,7 +245,8 @@ Stream CreatePatchStream() } } - public async Task CreateDeltaFile(Stream originalStream, Stream targetStream, Stream deltaStream, IProgress? progress = null) + //TODO: Add Source + Target size check + public async Task CreateDeltaFile(Stream sourceStream, Stream targetStream, Stream deltaStream, IProgress? progress = null) { // check arguments if (!deltaStream.CanSeek) @@ -257,8 +262,8 @@ public async Task CreateDeltaFile(Stream originalStream, Stream targetStre } // get data - var oldData = new byte[originalStream.Length]; - await originalStream.ReadExactlyAsync(oldData.AsMemory(0, oldData.Length)); + var oldData = new byte[sourceStream.Length]; + await sourceStream.ReadExactlyAsync(oldData.AsMemory(0, oldData.Length)); var newData = new byte[targetStream.Length]; await targetStream.ReadExactlyAsync(newData.AsMemory(0, newData.Length)); @@ -670,32 +675,14 @@ private static int[] SuffixSort(byte[] oldData) private static long ReadInt64(byte[] buf, int offset) { - long value = buf[offset + 7] & 0x7F; - - for (var index = 6; index >= 0; index--) - { - value *= 256; - value += buf[offset + index]; - } - - if ((buf[offset + 7] & 0x80) != 0) - value = -value; - - return value; + var value = BinaryPrimitives.ReadInt64LittleEndian(buf); + var mask = value >> 63; + return (~mask & value) | (((value & unchecked((long) 0x8000_0000_0000_0000)) - value) & mask); } private static void WriteInt64(long value, byte[] buf, int offset) { - var valueToWrite = value < 0 ? -value : value; - - for (var byteIndex = 0; byteIndex < 8; byteIndex++) - { - buf[offset + byteIndex] = (byte)(valueToWrite % 256); - valueToWrite -= buf[offset + byteIndex]; - valueToWrite /= 256; - } - - if (value < 0) - buf[offset + 7] |= 0x80; + var mask = value >> 63; + BinaryPrimitives.WriteInt64LittleEndian(buf, ((value + mask) ^ mask) | (value & unchecked((long) 0x8000_0000_0000_0000))); } } \ No newline at end of file diff --git a/src/Deltas/TinyUpdate.Delta.MSDelta/MSDelta.cs b/src/Deltas/TinyUpdate.Delta.MSDelta/MSDelta.cs index 83e4edf..7787e6d 100644 --- a/src/Deltas/TinyUpdate.Delta.MSDelta/MSDelta.cs +++ b/src/Deltas/TinyUpdate.Delta.MSDelta/MSDelta.cs @@ -1,30 +1,36 @@ -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; +using System.Text; using TinyUpdate.Core.Abstract; using TinyUpdate.Delta.MSDelta.Enum; using TinyUpdate.Delta.MSDelta.Struct; namespace TinyUpdate.Delta.MSDelta; +/*TODO: Possibly imp https://github.com/smilingthax/msdelta-pa30-format so we can at least detect valid files in a cross-platform matter?*/ + +/// +/// Provides creating and applying MSDelta... deltas +/// public partial class MSDelta : IDeltaApplier, IDeltaCreation { public string Extension => ".diff"; public unsafe bool SupportedStream(Stream deltaStream) { - var deltaBytes = new byte[deltaStream.Length]; + var deltaBytes = new byte[70]; //This is enough space to hold a MSDelta header deltaStream.ReadExactly(deltaBytes, 0, deltaBytes.Length); fixed (byte* deltaBuf = deltaBytes) { var deltaDeltaInput = new DeltaInput(deltaBuf, deltaBytes.Length, true); - var supported = GetDeltaInfoB(deltaDeltaInput, out var info); + var supported = GetDeltaInfoB(deltaDeltaInput, out _); return supported; } } public unsafe long TargetStreamSize(Stream deltaStream) { - var deltaBytes = new byte[deltaStream.Length]; + var deltaBytes = new byte[70]; //This is enough space to hold a MSDelta header deltaStream.ReadExactly(deltaBytes, 0, deltaBytes.Length); fixed (byte* deltaBuf = deltaBytes) @@ -39,15 +45,15 @@ public unsafe long TargetStreamSize(Stream deltaStream) return -1; } - public unsafe Task ApplyDeltaFile(Stream sourceFileStream, Stream deltaFileStream, - Stream targetFileStream, + public unsafe Task ApplyDeltaFile(Stream sourceStream, Stream deltaStream, + Stream targetStream, IProgress? progress = null) { - var sourceBytes = new byte[sourceFileStream.Length]; - sourceFileStream.ReadExactly(sourceBytes, 0, sourceBytes.Length); + var sourceBytes = new byte[sourceStream.Length]; + sourceStream.ReadExactly(sourceBytes, 0, sourceBytes.Length); - var deltaBytes = new byte[deltaFileStream.Length]; - deltaFileStream.ReadExactly(deltaBytes, 0, deltaBytes.Length); + var deltaBytes = new byte[deltaStream.Length]; + deltaStream.ReadExactly(deltaBytes, 0, deltaBytes.Length); fixed (byte* sourceBuf = sourceBytes) fixed (byte* deltaBuf = deltaBytes) @@ -59,9 +65,9 @@ public unsafe Task ApplyDeltaFile(Stream sourceFileStream, Stream deltaFil var success = ApplyDeltaB(ApplyFlag.None, sourceDeltaInput, deltaDeltaInput, out var output); if (success) { - var deltaStream = new UnmanagedMemoryStream((byte*)output.BufferPtr, output.Size); - deltaStream.CopyTo(targetFileStream); - deltaStream.Dispose(); + var deltaProcessedStream = new UnmanagedMemoryStream((byte*)output.BufferPtr, output.Size); + deltaProcessedStream.CopyTo(targetStream); + deltaProcessedStream.Dispose(); cleared = DeltaFree(output.BufferPtr); } @@ -69,16 +75,16 @@ public unsafe Task ApplyDeltaFile(Stream sourceFileStream, Stream deltaFil } } - public unsafe Task CreateDeltaFile(Stream sourceFileStream, Stream targetFileStream, - Stream deltaFileStream, + //TODO: Add Source + Target size check + public unsafe Task CreateDeltaFile(Stream sourceStream, Stream targetStream, + Stream deltaStream, IProgress? progress = null) { - //TODO: Add stream length check - var sourceBytes = new byte[sourceFileStream.Length]; - sourceFileStream.ReadExactly(sourceBytes, 0, sourceBytes.Length); + var sourceBytes = new byte[sourceStream.Length]; + sourceStream.ReadExactly(sourceBytes, 0, sourceBytes.Length); - var targetBytes = new byte[targetFileStream.Length]; - targetFileStream.ReadExactly(targetBytes, 0, targetBytes.Length); + var targetBytes = new byte[targetStream.Length]; + targetStream.ReadExactly(targetBytes, 0, targetBytes.Length); fixed (byte* sourceBuf = sourceBytes) fixed (byte* targetBuf = targetBytes) @@ -93,8 +99,8 @@ public unsafe Task CreateDeltaFile(Stream sourceFileStream, Stream targetF if (success) { - using var deltaStream = new UnmanagedMemoryStream((byte*)deltaBuffer.BufferPtr, deltaBuffer.Size); - deltaStream.CopyTo(deltaFileStream); + using var deltaProcessedStream = new UnmanagedMemoryStream((byte*)deltaBuffer.BufferPtr, deltaBuffer.Size); + deltaProcessedStream.CopyTo(deltaStream); cleared = DeltaFree(deltaBuffer.BufferPtr); } @@ -105,7 +111,7 @@ public unsafe Task CreateDeltaFile(Stream sourceFileStream, Stream targetF } } -//Ms Imp +//MSDelta Hooks public partial class MSDelta { /// diff --git a/src/Packages/TinyUpdate.TUUP/Consts.cs b/src/Packages/TinyUpdate.TUUP/Consts.cs index 5d7d3ad..5968ffd 100644 --- a/src/Packages/TinyUpdate.TUUP/Consts.cs +++ b/src/Packages/TinyUpdate.TUUP/Consts.cs @@ -2,9 +2,28 @@ public static class Consts { + /// + /// The Tuup file extension! + /// public const string TuupExtension = ".tuup"; + + /// + /// Extension to indicate the file is a brand new file + /// public const string NewFileExtension = ".new"; + + /// + /// Extension to indicate the file has moved + /// public const string MovedFileExtension = ".moved"; + + /// + /// Extension to indicate this file contains hash details about another file + /// public const string ShasumFileExtension = ".shasum"; + + /// + /// Extension to indicate the file has unchanged + /// public const string UnchangedFileExtension = ".diff"; } \ No newline at end of file diff --git a/src/Packages/TinyUpdate.TUUP/FileEntryExt.cs b/src/Packages/TinyUpdate.TUUP/FileEntryExt.cs index 18b645c..fb24ece 100644 --- a/src/Packages/TinyUpdate.TUUP/FileEntryExt.cs +++ b/src/Packages/TinyUpdate.TUUP/FileEntryExt.cs @@ -1,4 +1,5 @@ using TinyUpdate.Core; +using TinyUpdate.Core.Model; namespace TinyUpdate.TUUP; @@ -8,19 +9,19 @@ namespace TinyUpdate.TUUP; internal static class FileEntryExt { /// - /// Checks that this file is a delta file + /// Checks if the file is a delta file /// /// Details about the file public static bool IsDeltaFile(this FileEntry fileEntry) => fileEntry.Filesize != 0 && fileEntry.Extension != Consts.NewFileExtension && fileEntry.Extension != Consts.MovedFileExtension; /// - /// Checks that this file is a new file + /// Checks if the file is new /// /// Details about the file public static bool IsNewFile(this FileEntry fileEntry) => fileEntry.Filesize != 0 && fileEntry.Extension == Consts.NewFileExtension; /// - /// Checks that this file has moved + /// Checks if the file has moved /// /// Details about the file public static bool HasFileMoved(this FileEntry fileEntry) => fileEntry.Filesize != 0 && fileEntry.Extension == Consts.MovedFileExtension; diff --git a/src/Packages/TinyUpdate.TUUP/StreamExt.cs b/src/Packages/TinyUpdate.TUUP/StreamExt.cs index ae2f0b0..2a63eb9 100644 --- a/src/Packages/TinyUpdate.TUUP/StreamExt.cs +++ b/src/Packages/TinyUpdate.TUUP/StreamExt.cs @@ -10,28 +10,31 @@ public static class StreamExt /// /// Gets the hash and filesize from a file that contains data about a file we need to use for updating /// - /// Stream of that file + /// Stream of that file /// Hasher to use for grabbing checksums /// Hash and filesize that is expected - public static async Task<(string? hash, long filesize)> GetShasumDetails(this Stream fileStream, IHasher hasher) + public static async Task<(string? hash, long filesize)> GetShasumDetails(this Stream stream, IHasher hasher) { - //Grab the text from the file - using var textStream = new StreamReader(fileStream); + //We expect the information to be contained in the following way: + // + + using var textStream = new StreamReader(stream); var text = await textStream.ReadToEndAsync(); - //Return nothing if we don't have anything if (string.IsNullOrWhiteSpace(text)) { return (null, -1); } + //TODO: Make a Span splitter //Grab what we need, checking that it's what we expect - var textS = text.Split(' '); - var hash = textS[0]; - if (textS.Length != 2 || + var textSplit = text.Split(' '); + var hash = textSplit[0]; + + if (textSplit.Length != 2 || string.IsNullOrWhiteSpace(hash) || !hasher.IsValidHash(hash) || - !long.TryParse(textS[1], out var filesize)) + !long.TryParse(textSplit[1], out var filesize)) { return (null, -1); } diff --git a/src/Packages/TinyUpdate.TUUP/TuupUpdatePackage.cs b/src/Packages/TinyUpdate.TUUP/TuupUpdatePackage.cs index b09b178..5333e5f 100644 --- a/src/Packages/TinyUpdate.TUUP/TuupUpdatePackage.cs +++ b/src/Packages/TinyUpdate.TUUP/TuupUpdatePackage.cs @@ -1,14 +1,16 @@ using System.IO.Compression; using TinyUpdate.Core; using TinyUpdate.Core.Abstract; +using TinyUpdate.Core.Model; namespace TinyUpdate.TUUP; /// -/// Update Package based on TinyUpdate V1, Makes use of zip format +/// Update Package based on TinyUpdate V1 (With some extra functionally) /// public class TuupUpdatePackage(IDeltaManager deltaManager, IHasher hasher) : IUpdatePackage, IDisposable { + //What we expect to be contained for every file within the update package private static readonly string[] ExpectedData = ["Filename", "Path", "Hash", "Filesize", "Extension"]; private bool _loaded; @@ -69,17 +71,15 @@ private async IAsyncEnumerable GetFilesFromPackage(ZipArchive zip) foreach (var zipEntry in zip.Entries) { //Check if the name contains a extension - var entryEtx = Path.GetExtension(zipEntry.Name); - if (string.IsNullOrEmpty(entryEtx)) + var extension = Path.GetExtension(zipEntry.Name); + if (string.IsNullOrEmpty(extension)) { continue; } - //We append the an extension to tell the system how to handle the file, remove it so we get the actual filename - var filename = - zipEntry.Name[..zipEntry.Name.LastIndexOf(entryEtx, StringComparison.Ordinal)]; + //We append the extension so the system knows how to handle the file, we want to remove it so we get the actual filename + var filename = zipEntry.Name[..zipEntry.Name.LastIndexOf(extension, StringComparison.Ordinal)]; var filepath = Path.GetDirectoryName(zipEntry.FullName) ?? ""; - var key = Path.Combine(filepath, filename); //So we can store data in the same place if (!fileEntriesData.TryGetValue(key, out var fileEntryData)) @@ -90,7 +90,7 @@ private async IAsyncEnumerable GetFilesFromPackage(ZipArchive zip) fileEntryData["Path"] = filepath; } - switch (entryEtx) + switch (extension) { case Consts.MovedFileExtension: { @@ -110,19 +110,20 @@ private async IAsyncEnumerable GetFilesFromPackage(ZipArchive zip) } //This means that this entry contains data we want to work with - if (entryEtx == Consts.NewFileExtension - || deltaManager.Appliers.Any(x => x.Extension == entryEtx)) + if (extension == Consts.NewFileExtension + || deltaManager.Appliers.Any(x => x.Extension == extension)) { fileEntryData["Stream"] = zipEntry.Open(); - fileEntryData["Extension"] = entryEtx; + fileEntryData["Extension"] = extension; } - if (entryEtx is Consts.UnchangedFileExtension or Consts.MovedFileExtension) + if (extension is Consts.UnchangedFileExtension or Consts.MovedFileExtension) { - fileEntryData["Extension"] = entryEtx; + fileEntryData["Extension"] = extension; } } + //TODO: See if they is a way that we could make this without having to cast everything, this works for now //Now that we got all the information needed, lets throw it back! foreach (var (_, fileEntryData) in fileEntriesData) { @@ -148,6 +149,8 @@ private static bool HasAllFileEntryData(Dictionary fileEntryDat return false; } + //TODO: Maybe relax this restriction? + //Check if we've got a file without any data when data is expected! var filesize = (long?)fileEntryData["Filesize"]; if (filesize != 0 && fileEntryData["Extension"] is not Consts.UnchangedFileExtension and not Consts.MovedFileExtension @@ -156,13 +159,14 @@ private static bool HasAllFileEntryData(Dictionary fileEntryDat return false; } - //Clear out stream if it'll be nothing, no need to keep it + //Clear out the stream if it'll be nothing, no need to keep it if (filesize == 0 && fileEntryData.TryGetValue("Stream", out var streamObj)) { ((Stream?)streamObj)?.Dispose(); fileEntryData["Stream"] = null; } + //Possible that these will be nothing, still need something for them fileEntryData.TryAdd("Stream", null); fileEntryData.TryAdd("PreviousLocation", null); return true; diff --git a/src/Packages/TinyUpdate.TUUP/TuupUpdatePackageCreator.cs b/src/Packages/TinyUpdate.TUUP/TuupUpdatePackageCreator.cs index c8608a6..ef3378a 100644 --- a/src/Packages/TinyUpdate.TUUP/TuupUpdatePackageCreator.cs +++ b/src/Packages/TinyUpdate.TUUP/TuupUpdatePackageCreator.cs @@ -6,6 +6,9 @@ namespace TinyUpdate.TUUP; +/// +/// Update package creator for +/// public class TuupUpdatePackageCreator : IUpdatePackageCreator { private readonly AsyncLock _zipLock; @@ -52,16 +55,17 @@ public async Task CreateFullPackage(string applicationLocation, SemanticVe return true; } - public async Task CreateDeltaPackage(string oldApplicationLocation, SemanticVersion oldApplicationVersion, + public async Task CreateDeltaPackage(string previousApplicationLocation, SemanticVersion previousApplicationVersion, string newApplicationLocation, SemanticVersion newApplicationVersion, string updatePackageLocation, string applicationName, IProgress? progress = null) { - var oldFiles = Directory.GetFiles(oldApplicationLocation, "*", SearchOption.AllDirectories); + var previousFiles = Directory.GetFiles(previousApplicationLocation, "*", SearchOption.AllDirectories); var newFiles = Directory.GetFiles(newApplicationLocation, "*", SearchOption.AllDirectories); - Dictionary> oldFilesHashes = new(); + Dictionary> previousFilesHashes = new(); Dictionary> newFilesHashes = new(); - await GetHashes(oldFilesHashes, oldFiles); + //We make use of the hashes so we can find files that have moved! + await GetHashes(previousFilesHashes, previousFiles); await GetHashes(newFilesHashes, newFiles); using var zipArchive = @@ -69,34 +73,38 @@ public async Task CreateDeltaPackage(string oldApplicationLocation, Semant for (int i = 0; i < newFiles.Length; ReportAndBump(ref i)) { - var newFile = newFiles[i]; - var newHash = newFilesHashes.First(x => x.Value.Any(y => y == newFile)).Key; - var relativeNewFile = Path.GetRelativePath(newApplicationLocation, newFile); + var newFilePath = newFiles[i]; + var newHash = newFilesHashes.First(x => x.Value.Any(y => y == newFilePath)).Key; + var newFileRelativePath = Path.GetRelativePath(newApplicationLocation, newFilePath); - var oldFile = oldFiles.FirstOrDefault(x => Path.GetRelativePath(oldApplicationLocation, x) == relativeNewFile); - await using var newFileContentStream = File.OpenRead(newFile); + //First see if the file is in the same place but changed + var previousFilePath = previousFiles.FirstOrDefault(x => Path.GetRelativePath(previousApplicationLocation, x) == newFileRelativePath); + await using var newFileContentStream = File.OpenRead(newFilePath); - if (string.IsNullOrWhiteSpace(oldFile)) + //If we can't find the path in it's original location, maybe it's moved + if (string.IsNullOrWhiteSpace(previousFilePath)) { - if (!await FindAndAddMovedFile(newHash, relativeNewFile, newFileContentStream.Length)) + if (!await FindAndAddMovedFile(newHash, newFileRelativePath, newFileContentStream.Length)) { - await AddNewFile(zipArchive, newFileContentStream, relativeNewFile); + // (It hasn't moved) + await AddNewFile(zipArchive, newFileContentStream, newFileRelativePath); } continue; } //See if the file is the same by the outputted hash - var oldHash = oldFilesHashes.First(x => x.Value.Any(y => y == oldFile)).Key; - if (newHash == oldHash) + var previousHash = previousFilesHashes.First(x => x.Value.Any(y => y == previousFilePath)).Key; + if (newHash == previousHash) { - await AddSameFile(zipArchive, relativeNewFile, newHash); + await AddSameFile(zipArchive, newFileRelativePath, newHash); continue; } - await using var oldFileContentStream = File.OpenRead(oldFile); + //Now the fun stuff, the contents has changed so we want to get a delta file :D + await using var previousFileContentStream = File.OpenRead(previousFilePath); newFileContentStream.Seek(0, SeekOrigin.Begin); - var deltaResult = await _deltaManager.CreateDeltaFile(oldFileContentStream, newFileContentStream); + var deltaResult = await _deltaManager.CreateDeltaUpdate(previousFileContentStream, newFileContentStream); if (deltaResult.Successful) { deltaResult.DeltaStream.Seek(0, SeekOrigin.Begin); @@ -104,7 +112,7 @@ public async Task CreateDeltaPackage(string oldApplicationLocation, Semant await AddFile( zipArchive, deltaResult.DeltaStream, - relativeNewFile + deltaResult.Creator.Extension); + newFileRelativePath + deltaResult.Creator.Extension); } } @@ -113,37 +121,40 @@ await AddFile( async Task FindAndAddMovedFile(string newHash, string relativeNewFile, long filesize) { //V1 Tuup format didn't have the concept of moved files, if we want to make v1 packages then don't handle this - if (!_options.V1Compatible && oldFilesHashes.TryGetValue(newHash, out var oldFilesList) && oldFilesList.Count > 0) + if (!_options.V1Compatible && previousFilesHashes.TryGetValue(newHash, out var previousFilesList) && previousFilesList.Count > 0) { - var oldFile = oldFilesList[0]; - var filepath = relativeNewFile + Consts.MovedFileExtension; + var previousFileLocation = previousFilesList[0]; + var newFilePath = relativeNewFile + Consts.MovedFileExtension; using (await _zipLock.LockAsync()) { - CheckFilePath(ref filepath); - - await using var zipShasumEntryStream = zipArchive.CreateEntry(filepath, CompressionLevel.SmallestSize).Open(); - await using var shasumStreamWriter = new StreamWriter(zipShasumEntryStream); - var path = Path.GetRelativePath(oldApplicationLocation, oldFile); - - CheckFilePath(ref path); - await shasumStreamWriter.WriteAsync(path); + CheckFilePath(ref newFilePath); + await AddMovedPathFile(newFilePath, previousFileLocation); } - await AddHashAndSizeData(zipArchive, filepath, newHash, filesize); - + await AddHashAndSizeData(zipArchive, newFilePath, newHash, filesize); return true; } return false; } + + async Task AddMovedPathFile(string newFilePath, string previousFileLocation) + { + await using var movedPathStream = zipArchive.CreateEntry(newFilePath, CompressionLevel.SmallestSize).Open(); + await using var movedPathStreamWriter = new StreamWriter(movedPathStream); + var previousRelativePath = Path.GetRelativePath(previousApplicationLocation, previousFileLocation); + + CheckFilePath(ref previousRelativePath); + await movedPathStreamWriter.WriteAsync(previousRelativePath); + } async Task GetHashes(IDictionary> hashes, IEnumerable files) { //We want to get a hash of all the old files, this allows us to detect files which have moved foreach (var oldFile in files) { - await using var oldFileContentStream = File.OpenRead(oldFile); - var hash = _hasher.HashData(oldFileContentStream); + await using var previousFileContentStream = File.OpenRead(oldFile); + var hash = _hasher.HashData(previousFileContentStream); if (!hashes.TryGetValue(hash, out var filesList)) { filesList = new List(); diff --git a/src/Packages/TinyUpdate.TUUP/TuupUpdatePackageCreatorOptions.cs b/src/Packages/TinyUpdate.TUUP/TuupUpdatePackageCreatorOptions.cs index b277467..4bb0806 100644 --- a/src/Packages/TinyUpdate.TUUP/TuupUpdatePackageCreatorOptions.cs +++ b/src/Packages/TinyUpdate.TUUP/TuupUpdatePackageCreatorOptions.cs @@ -1,5 +1,9 @@ namespace TinyUpdate.TUUP; +//TODO: Add Loader option with V1 setting? +/// +/// Options to make update package creation behave differently +/// public class TuupUpdatePackageCreatorOptions { /// diff --git a/src/TinyUpdate.Core/Abstract/IDeltaApplier.cs b/src/TinyUpdate.Core/Abstract/IDeltaApplier.cs index 5866d9c..0fbf2eb 100644 --- a/src/TinyUpdate.Core/Abstract/IDeltaApplier.cs +++ b/src/TinyUpdate.Core/Abstract/IDeltaApplier.cs @@ -1,7 +1,7 @@ namespace TinyUpdate.Core.Abstract; /// -/// Provides base functions for applying an delta update +/// Provides base functionality for applying delta updates /// public interface IDeltaApplier : IExtension { @@ -18,10 +18,10 @@ public interface IDeltaApplier : IExtension /// /// Applies the delta update into the target stream /// - /// Source stream - /// Delta stream - /// Target stream (OUTPUT) + /// Source stream (Previous data) + /// Delta stream (Patch data) + /// Target stream (New data - OUTPUT) /// Progress on applying delta update /// If applying the update was successful - Task ApplyDeltaFile(Stream sourceFileStream, Stream deltaFileStream, Stream targetFileStream, IProgress? progress = null); + Task ApplyDeltaFile(Stream sourceStream, Stream deltaStream, Stream targetStream, IProgress? progress = null); } \ No newline at end of file diff --git a/src/TinyUpdate.Core/Abstract/IDeltaCreation.cs b/src/TinyUpdate.Core/Abstract/IDeltaCreation.cs index eed4a8a..da13999 100644 --- a/src/TinyUpdate.Core/Abstract/IDeltaCreation.cs +++ b/src/TinyUpdate.Core/Abstract/IDeltaCreation.cs @@ -1,14 +1,17 @@ namespace TinyUpdate.Core.Abstract; +/// +/// Provides base functionality for creating delta updates +/// public interface IDeltaCreation : IExtension { /// - /// Creates the delta file + /// Creates an delta update /// - /// Source stream - /// Target stream - /// Delta stream (OUTPUT) + /// Source stream (Previous data) + /// Target stream (New data) + /// Delta stream (Patch data - OUTPUT) /// Progress on creating the delta /// If creating the delta was successful - Task CreateDeltaFile(Stream sourceFileStream, Stream targetFileStream, Stream deltaFileStream, IProgress? progress = null); + Task CreateDeltaFile(Stream sourceStream, Stream targetStream, Stream deltaStream, IProgress? progress = null); } \ No newline at end of file diff --git a/src/TinyUpdate.Core/Abstract/IDeltaManager.cs b/src/TinyUpdate.Core/Abstract/IDeltaManager.cs index e76adc1..f9a0ad9 100644 --- a/src/TinyUpdate.Core/Abstract/IDeltaManager.cs +++ b/src/TinyUpdate.Core/Abstract/IDeltaManager.cs @@ -1,24 +1,27 @@ -using System.Diagnostics.CodeAnalysis; +using TinyUpdate.Core.Model; namespace TinyUpdate.Core.Abstract; +/// +/// Manages delta processing for external packages +/// public interface IDeltaManager { + /// + /// The s which will be used for applying delta updates + /// public IReadOnlyCollection Appliers { get; } - - public IReadOnlyCollection Creators { get; } - - public Task CreateDeltaFile(Stream sourceStream, Stream targetStream); -} - -public record DeltaCreationResult(IDeltaCreation? creator, Stream? deltaStream, bool successful) -{ - public static readonly DeltaCreationResult Failed = new DeltaCreationResult(null, null, false); - - public IDeltaCreation? Creator { get; } = creator; - public Stream? DeltaStream { get; } = deltaStream; + /// + /// The s which will be used for creating delta updates + /// + public IReadOnlyCollection Creators { get; } - [MemberNotNullWhen(true, nameof(Creator), nameof(DeltaStream))] - public bool Successful { get; } = successful; + /// + /// Creates a delta update, returning the best delta created + /// + /// Source Stream (Previous data) + /// Target Stream (New data) + /// The results of delta creation + public Task CreateDeltaUpdate(Stream sourceStream, Stream targetStream); } \ No newline at end of file diff --git a/src/TinyUpdate.Core/Abstract/IExtension.cs b/src/TinyUpdate.Core/Abstract/IExtension.cs index fa998f3..b872780 100644 --- a/src/TinyUpdate.Core/Abstract/IExtension.cs +++ b/src/TinyUpdate.Core/Abstract/IExtension.cs @@ -1,5 +1,8 @@ namespace TinyUpdate.Core.Abstract; +/// +/// Declares that an extension is used +/// public interface IExtension { /// diff --git a/src/TinyUpdate.Core/Abstract/IHasher.cs b/src/TinyUpdate.Core/Abstract/IHasher.cs index 2230325..2cf30bf 100644 --- a/src/TinyUpdate.Core/Abstract/IHasher.cs +++ b/src/TinyUpdate.Core/Abstract/IHasher.cs @@ -1,38 +1,40 @@ namespace TinyUpdate.Core.Abstract; +/// +/// Provides base functionality to create & check hashes from a or [] +/// public interface IHasher { /// - /// Checks the output of a to a hash that is expected + /// Compares the with an expected hash /// - /// to check against + /// to check /// Hash that we are expecting - /// If the outputs the same hash as we are expecting + /// If the outputs the same hash as the expected hash bool CompareHash(Stream stream, string expectedHash); /// - /// Checks a [] to a hash that is expected + /// Compares the [] with an expected hash /// - /// [] to check against + /// [] to check /// Hash that we are expecting - /// If the [] outputs the same hash as we are expecting + /// If the [] outputs the same hash as the expected hash bool CompareHash(byte[] byteArray, string expectedHash); /// /// Creates a hash from a /// - /// to create hash for + /// to create the hash from string HashData(Stream stream); /// /// Creates a hash from a [] /// - /// [] to use for creating hash - string HashData(byte[] bytes); + /// [] to create the hash from + string HashData(byte[] byteArray); /// - /// Gets if this string is a valid hash + /// Ensures that the is a valid hash /// - /// string to check bool IsValidHash(string hash); } \ No newline at end of file diff --git a/src/TinyUpdate.Core/Abstract/IUpdatePackage.cs b/src/TinyUpdate.Core/Abstract/IUpdatePackage.cs index fe65c07..fe265ff 100644 --- a/src/TinyUpdate.Core/Abstract/IUpdatePackage.cs +++ b/src/TinyUpdate.Core/Abstract/IUpdatePackage.cs @@ -1,11 +1,20 @@ -namespace TinyUpdate.Core.Abstract; +using TinyUpdate.Core.Model; +namespace TinyUpdate.Core.Abstract; + +/// +/// Provides base functionality for handling an update package +/// public interface IUpdatePackage : IExtension { + /// + /// Loads the update package data in + /// + /// Data to load in Task Load(Stream updatePackageStream); /// - /// Files that need to be processed as a delta file + /// Files that have been processed into a delta file /// ICollection DeltaFiles { get; } diff --git a/src/TinyUpdate.Core/Abstract/IUpdatePackageCreator.cs b/src/TinyUpdate.Core/Abstract/IUpdatePackageCreator.cs index faf7942..b449ebd 100644 --- a/src/TinyUpdate.Core/Abstract/IUpdatePackageCreator.cs +++ b/src/TinyUpdate.Core/Abstract/IUpdatePackageCreator.cs @@ -2,12 +2,42 @@ namespace TinyUpdate.Core.Abstract; +/// +/// Provides base functionality for creating an update package +/// public interface IUpdatePackageCreator : IExtension { + /// + /// Template for a full package filename + /// public string FullPackageFilenameTemplate { get; } + + /// + /// Template for a delta package filename + /// public string DeltaPackageFilenameTemplate { get; } + /// + /// Creates a full update package + /// + /// Where the application files are located + /// What the application version is + /// Where we should store the created update package + /// The applications name + /// Process about creating the update package + /// If we successfully created the update package Task CreateFullPackage(string applicationLocation, SemanticVersion applicationVersion, string updatePackageLocation, string applicationName, IProgress? progress = null); - - Task CreateDeltaPackage(string oldApplicationLocation, SemanticVersion oldApplicationVersion, string newApplicationLocation, SemanticVersion newApplicationVersion, string updatePackageLocation, string applicationName, IProgress? progress = null); + + /// + /// Creates a delta update package + /// + /// Where the previous version of the application is located + /// What the previous version of the application is + /// Where the new version of the application is located + /// What the new version of the application is + /// Where we should store the created update package + /// The applications name + /// Process about creating the update package + /// If we successfully created the update package + Task CreateDeltaPackage(string previousApplicationLocation, SemanticVersion previousApplicationVersion, string newApplicationLocation, SemanticVersion newApplicationVersion, string updatePackageLocation, string applicationName, IProgress? progress = null); } \ No newline at end of file diff --git a/src/TinyUpdate.Core/DeltaManager.cs b/src/TinyUpdate.Core/DeltaManager.cs index 2561a5c..ec7b61b 100644 --- a/src/TinyUpdate.Core/DeltaManager.cs +++ b/src/TinyUpdate.Core/DeltaManager.cs @@ -2,6 +2,7 @@ using System.Collections.Immutable; using NeoSmart.AsyncLock; using TinyUpdate.Core.Abstract; +using TinyUpdate.Core.Model; namespace TinyUpdate.Core; @@ -13,7 +14,7 @@ public class DeltaManager(IEnumerable appliers, IEnumerable Appliers { get; } = appliers.ToImmutableArray(); public IReadOnlyCollection Creators { get; } = creators.ToImmutableArray(); - public async Task CreateDeltaFile(Stream sourceStream, Stream targetStream) + public async Task CreateDeltaUpdate(Stream sourceStream, Stream targetStream) { var resultBag = new ConcurrentBag(); var sourceStreamMasterCopy = new MemoryStream(); diff --git a/src/TinyUpdate.Core/Model/DeltaCreationResult.cs b/src/TinyUpdate.Core/Model/DeltaCreationResult.cs new file mode 100644 index 0000000..48a65af --- /dev/null +++ b/src/TinyUpdate.Core/Model/DeltaCreationResult.cs @@ -0,0 +1,31 @@ +using System.Diagnostics.CodeAnalysis; +using TinyUpdate.Core.Abstract; + +namespace TinyUpdate.Core.Model; + +/// +/// Details of creating a new delta update +/// +public record DeltaCreationResult(IDeltaCreation? Creator, Stream? DeltaStream, bool Successful) +{ + /// + /// Failed to create a delta update + /// + public static readonly DeltaCreationResult Failed = new DeltaCreationResult(null, null, false); + + /// + /// The that created the update + /// + public IDeltaCreation? Creator { get; } = Creator; + + /// + /// The contents of the delta update + /// + public Stream? DeltaStream { get; } = DeltaStream; + + /// + /// If we was successful in creating a delta update + /// + [MemberNotNullWhen(true, nameof(Creator), nameof(DeltaStream))] + public bool Successful { get; } = Successful; +} \ No newline at end of file diff --git a/src/TinyUpdate.Core/FileEntry.cs b/src/TinyUpdate.Core/Model/FileEntry.cs similarity index 95% rename from src/TinyUpdate.Core/FileEntry.cs rename to src/TinyUpdate.Core/Model/FileEntry.cs index c690a7e..b29fbe2 100644 --- a/src/TinyUpdate.Core/FileEntry.cs +++ b/src/TinyUpdate.Core/Model/FileEntry.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace TinyUpdate.Core; +namespace TinyUpdate.Core.Model; /// /// File entry with all the information to do a update @@ -64,8 +64,5 @@ public FileEntry(string filename, string path) /// /// The extension that exposes how to process this file /// - /// - /// .new -> "New File" - /// public required string Extension { get; init; } } \ No newline at end of file diff --git a/tests/TinyUpdate.Delta.Tests/Abstract/DeltaCan.cs b/tests/TinyUpdate.Delta.Tests/Abstract/DeltaCan.cs index 1eb8fbc..74ec5a4 100644 --- a/tests/TinyUpdate.Delta.Tests/Abstract/DeltaCan.cs +++ b/tests/TinyUpdate.Delta.Tests/Abstract/DeltaCan.cs @@ -29,13 +29,15 @@ public void BaseSetup() FileSystem = Functions.SetupMockFileSystem(); } + //TODO: Add test for handling streams over the array size + [Test] public void CanGetCorrectTargetStreamSize() { SkipIfNoApplier(); - using var deltaFileStream = FileSystem.File.OpenRead(Path.Combine("Assets", ApplierName, "expected_pass" + Applier.Extension)); - var targetSize = Applier.TargetStreamSize(deltaFileStream); + using var deltaStream = FileSystem.File.OpenRead(Path.Combine("Assets", ApplierName, "expected_pass" + Applier.Extension)); + var targetSize = Applier.TargetStreamSize(deltaStream); Assert.That(targetSize, Is.EqualTo(233237)); } @@ -48,8 +50,8 @@ public void GetCorrectSupportStatus(string targetFilename, bool expectedResult) { SkipIfNoApplier(); - using var deltaFileStream = FileSystem.File.OpenRead(Path.Combine("Assets", ApplierName, targetFilename + Applier.Extension)); - var returnedStatus = Applier.SupportedStream(deltaFileStream); + using var deltaStream = FileSystem.File.OpenRead(Path.Combine("Assets", ApplierName, targetFilename + Applier.Extension)); + var returnedStatus = Applier.SupportedStream(deltaStream); var error = GetWin32Error(); Assert.That(returnedStatus, Is.EqualTo(expectedResult), () => CreateErrorMessage(error)); @@ -61,21 +63,21 @@ public async Task CorrectlyApplyDeltaFile() { SkipIfNoApplier(); - await using var sourceFileStream = FileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); - await using var deltaFileStream = FileSystem.File.OpenRead(Path.Combine("Assets", ApplierName, "expected_pass" + Applier.Extension)); - await using var targetFileStream = FileSystem.File.Create(Path.Combine("Assets", ApplierName, "new (diff).jpg")); - await using var expectedTargetFileStream = FileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); + await using var sourceStream = FileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); + await using var deltaStream = FileSystem.File.OpenRead(Path.Combine("Assets", ApplierName, "expected_pass" + Applier.Extension)); + await using var targetStream = FileSystem.File.Create(Path.Combine("Assets", ApplierName, "new (diff).jpg")); + await using var expectedTargetStream = FileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); - var applyResult = await Applier.ApplyDeltaFile(sourceFileStream, deltaFileStream, targetFileStream); + var applyResult = await Applier.ApplyDeltaFile(sourceStream, deltaStream, targetStream); var error = GetWin32Error(); - targetFileStream.Seek(0, SeekOrigin.Begin); + targetStream.Seek(0, SeekOrigin.Begin); - var expectedTargetFileStreamHash = Hasher.HashData(expectedTargetFileStream); - var targetFileStreamHash = Hasher.HashData(targetFileStream); + var expectedTargetStreamHash = Hasher.HashData(expectedTargetStream); + var targetStreamHash = Hasher.HashData(targetStream); Assert.Multiple(() => { Assert.That(applyResult, Is.True, () => CreateErrorMessage(error)); - Assert.That(expectedTargetFileStreamHash, Is.EqualTo(targetFileStreamHash), () => $"{ApplierName} didn't create an exact replica of the target stream. Expected Hash: {expectedTargetFileStreamHash}, Target Hash: {targetFileStreamHash}"); + Assert.That(expectedTargetStreamHash, Is.EqualTo(targetStreamHash), () => $"{ApplierName} didn't create an exact replica of the target stream. Expected Hash: {expectedTargetStreamHash}, Target Hash: {targetStreamHash}"); }); } @@ -85,22 +87,22 @@ public async Task CreateDeltaFile() { SkipIfNoCreator(); - await using var sourceFileStream = FileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); - await using var targetFileStream = FileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); - await using var deltaFileStream = FileSystem.File.Create(Path.Combine("Assets", CreatorName, "result_delta" + Creator.Extension)); + await using var sourceStream = FileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); + await using var targetStream = FileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); + await using var deltaStream = FileSystem.File.Create(Path.Combine("Assets", CreatorName, "result_delta" + Creator.Extension)); - var createResult = await Creator.CreateDeltaFile(sourceFileStream, targetFileStream, deltaFileStream); + var createResult = await Creator.CreateDeltaFile(sourceStream, targetStream, deltaStream); var error = GetWin32Error(); Assert.That(createResult, Is.True, () => CreateErrorMessage(error)); - deltaFileStream.Seek(0, SeekOrigin.Begin); - await using var expectedDeltaFileStream = FileSystem.File.OpenRead(Path.Combine("Assets", CreatorName, "expectedDelta" + Creator.Extension)); + deltaStream.Seek(0, SeekOrigin.Begin); + await using var expectedDeltaStream = FileSystem.File.OpenRead(Path.Combine("Assets", CreatorName, "expectedDelta" + Creator.Extension)); - CheckDeltaFile(deltaFileStream, expectedDeltaFileStream); + CheckDeltaFile(deltaStream, expectedDeltaStream); } - protected abstract void CheckDeltaFile(Stream targetFileStreamHash, Stream expectedTargetFileStreamHash); + protected abstract void CheckDeltaFile(Stream targetHashStream, Stream expectedTargetHashStream); [MemberNotNull(nameof(Applier))] private void SkipIfNoApplier() diff --git a/tests/TinyUpdate.Delta.Tests/Abstract/DeltaManagerCan.cs b/tests/TinyUpdate.Delta.Tests/Abstract/DeltaManagerCan.cs index 8638ace..d8a66ce 100644 --- a/tests/TinyUpdate.Delta.Tests/Abstract/DeltaManagerCan.cs +++ b/tests/TinyUpdate.Delta.Tests/Abstract/DeltaManagerCan.cs @@ -1,6 +1,8 @@ using System.IO.Abstractions; using Moq; +using TinyUpdate.Core; using TinyUpdate.Core.Abstract; +using TinyUpdate.Core.Model; using TinyUpdate.Core.Tests; using TinyUpdate.Core.Tests.Attributes; @@ -28,10 +30,10 @@ public async Task PickSmallestDeltaFile() } var deltaManager = CreateDeltaManager(Array.Empty(), creatorList.Select(x => x.Object)); - await using var sourceFileStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); - await using var targetFileStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); + await using var sourceStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); + await using var targetStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); - var result = await deltaManager.CreateDeltaFile(sourceFileStream, targetFileStream); + var result = await deltaManager.CreateDeltaUpdate(sourceStream, targetStream); Assert.Multiple(() => { Assert.That(result.Successful, Is.True, () => "Failed to create a dummy delta file"); @@ -50,10 +52,10 @@ public async Task GracefullyHandleFailedDeltaCreation() creatorList[1] = CreateMockDeltaCreation(GetRandomExtension(), false, 0.2); var deltaManager = CreateDeltaManager(Array.Empty(), creatorList.Select(x => x.Object)); - await using var sourceFileStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); - await using var targetFileStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); + await using var sourceStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); + await using var targetStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); - var result = await deltaManager.CreateDeltaFile(sourceFileStream, targetFileStream); + var result = await deltaManager.CreateDeltaUpdate(sourceStream, targetStream); Assert.Multiple(() => { Assert.That(result.Successful, Is.True, () => "Failed to create a dummy delta file"); @@ -70,10 +72,10 @@ public async Task ProvideFailedDeltaCreationResultOnAllFailed() creatorList[1] = CreateMockDeltaCreation(GetRandomExtension(), false, 0.2); var deltaManager = CreateDeltaManager(Array.Empty(), creatorList.Select(x => x.Object)); - await using var sourceFileStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); - await using var targetFileStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); + await using var sourceStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); + await using var targetStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); - var result = await deltaManager.CreateDeltaFile(sourceFileStream, targetFileStream); + var result = await deltaManager.CreateDeltaUpdate(sourceStream, targetStream); Assert.That(result, Is.EqualTo(DeltaCreationResult.Failed), () => $"Didn't pass failed {nameof(DeltaCreationResult)}"); } @@ -83,10 +85,10 @@ public async Task RunAllDeltaCreatorsOnlyOnce() { var creatorList = CreateMockDeltaCreators(); var deltaManager = CreateDeltaManager(Array.Empty(), creatorList.Select(x => x.Object)); - await using var sourceFileStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); - await using var targetFileStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); + await using var sourceStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "original.jpg")); + await using var targetStream = _fileSystem.File.OpenRead(Path.Combine("Assets", "new.jpg")); - var result = await deltaManager.CreateDeltaFile(sourceFileStream, targetFileStream); + var result = await deltaManager.CreateDeltaUpdate(sourceStream, targetStream); Assert.Multiple(() => { //One will come back as successful, we want to ensure that's the case here @@ -124,13 +126,13 @@ private static Mock CreateMockDeltaCreation(string extension, bo mockCreation.Setup(x => x.Extension).Returns(extension); mockCreation.Setup(x => x.CreateDeltaFile(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((Stream sourceFileStream, Stream targetFileStream, Stream deltaFileStream, + .Callback((Stream sourceStream, Stream targetStream, Stream deltaStream, IProgress? progress) => { targetFilesizePercent ??= (long)Random.Shared.NextDouble(); - var filesize = (long)(targetFileStream.Length * targetFilesizePercent.Value); - Functions.FillStreamWithRandomData(deltaFileStream, filesize); + var filesize = (long)(targetStream.Length * targetFilesizePercent.Value); + Functions.FillStreamWithRandomData(deltaStream, filesize); }).ReturnsAsync(pass); return mockCreation; diff --git a/tests/TinyUpdate.Delta.Tests/DeltaTests.cs b/tests/TinyUpdate.Delta.Tests/DeltaTests.cs index 2e7f03f..abd41fe 100644 --- a/tests/TinyUpdate.Delta.Tests/DeltaTests.cs +++ b/tests/TinyUpdate.Delta.Tests/DeltaTests.cs @@ -16,11 +16,11 @@ public void OneTimeSetup() Applier = delta; } - protected override void CheckDeltaFile(Stream targetFileStream, Stream expectedTargetFileStream) + protected override void CheckDeltaFile(Stream targetHashStream, Stream expectedTargetHashStream) { - var expectedTargetFileStreamHash = Hasher.HashData(expectedTargetFileStream); - var targetFileStreamHash = Hasher.HashData(targetFileStream); - Assert.That(expectedTargetFileStreamHash, Is.EqualTo(targetFileStreamHash), () => $"{ApplierName} delta file is not as expected"); + var expectedHash = Hasher.HashData(expectedTargetHashStream); + var hash = Hasher.HashData(targetHashStream); + Assert.That(hash, Is.EqualTo(expectedHash), () => $"{ApplierName} delta file is not as expected"); } } @@ -39,10 +39,10 @@ public void OneTimeSetup() } } - protected override void CheckDeltaFile(Stream targetFileStream, Stream expectedTargetFileStream) + protected override void CheckDeltaFile(Stream targetHashStream, Stream expectedTargetHashStream) { - var targetHash = GetDeltaHash(targetFileStream); - var expectedTargetHash = GetDeltaHash(expectedTargetFileStream); + var targetHash = GetDeltaHash(targetHashStream); + var expectedTargetHash = GetDeltaHash(expectedTargetHashStream); Assert.That(targetHash, Is.EqualTo(expectedTargetHash)); } diff --git a/tests/TinyUpdate.Packages.Tests/Abstract/UpdatePackageCan.cs b/tests/TinyUpdate.Packages.Tests/Abstract/UpdatePackageCan.cs index 2d4000a..4ea6b77 100644 --- a/tests/TinyUpdate.Packages.Tests/Abstract/UpdatePackageCan.cs +++ b/tests/TinyUpdate.Packages.Tests/Abstract/UpdatePackageCan.cs @@ -5,6 +5,7 @@ using SemVersion; using TinyUpdate.Core; using TinyUpdate.Core.Abstract; +using TinyUpdate.Core.Model; using TinyUpdate.Core.Tests; using TinyUpdate.Core.Tests.Attributes; using TinyUpdate.Packages.Tests.Model; @@ -27,8 +28,8 @@ public void BaseSetup() public async Task ProcessFileData() { var baseFilePath = Path.Combine("Assets", UpdatePackage.GetType().Name); - await using var fileStream = FileSystem.File.OpenRead(Path.Combine(baseFilePath, "exampleUpdatePackage" + UpdatePackage.Extension)); - await UpdatePackage.Load(fileStream); + await using var updatePackageStream = FileSystem.File.OpenRead(Path.Combine(baseFilePath, "exampleUpdatePackage" + UpdatePackage.Extension)); + await UpdatePackage.Load(updatePackageStream); IReadOnlyCollection? expectedDeltaFiles = null, expectedMovedFiles = null, expectedNewFiles = null, expectedUnchangedFiles = null; await Assert.MultipleAsync(async () => @@ -109,12 +110,12 @@ public async Task TestFullPackageCreation(FullUpdatePackageTestData testData) return; } - await using var targetFileStream = GetFullTargetFileStream(packageLocation, testData.ApplicationName, testData.Version); - await using var expectedTargetFileStream = GetExpectedTargetFileStream(expectedFileLocation); + await using var targetStream = GetFullTargetStream(packageLocation, testData.ApplicationName, testData.Version); + await using var expectedTargetStream = GetExpectedTargetStream(expectedFileLocation); try { - CheckUpdatePackageWithExpected(targetFileStream, expectedTargetFileStream); + CheckUpdatePackageWithExpected(targetStream, expectedTargetStream); } catch (NotImplementedException) { @@ -145,12 +146,12 @@ public async Task TestDeltaPackageCreation(DeltaUpdatePackageTestData testData) return; } - await using var targetFileStream = GetDeltaTargetFileStream(packageLocation, testData.ApplicationName, testData.NewVersion); - await using var expectedTargetFileStream = GetExpectedTargetFileStream(expectedFileLocation); + await using var targetStream = GetDeltaTargetStream(packageLocation, testData.ApplicationName, testData.NewVersion); + await using var expectedTargetStream = GetExpectedTargetStream(expectedFileLocation); try { - CheckUpdatePackageWithExpected(targetFileStream, expectedTargetFileStream); + CheckUpdatePackageWithExpected(targetStream, expectedTargetStream); } catch (NotImplementedException) { @@ -201,7 +202,7 @@ public async Task MakeDeltaUpdatePackage() //Content checking is done by other tests, we just want to check if we can create a more complex update package } - protected abstract void CheckUpdatePackageWithExpected(Stream targetFileStream, Stream expectedTargetFileStream); + protected abstract void CheckUpdatePackageWithExpected(Stream targetStream, Stream expectedTargetStream); protected Mock CreateMockDeltaApplier(string extension) { @@ -211,8 +212,8 @@ protected Mock CreateMockDeltaApplier(string extension) mockApplier.Setup(x => x.SupportedStream(It.IsAny())).Returns(true); mockApplier.Setup(x => x.ApplyDeltaFile(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((Stream sourceFileStream, Stream deltaFileStream, Stream targetFileStream, - IProgress? progress) => Functions.FillStreamWithRandomData(deltaFileStream)) + .Callback((Stream sourceStream, Stream deltaStream, Stream targetStream, + IProgress? progress) => Functions.FillStreamWithRandomData(deltaStream)) .ReturnsAsync(true); return mockApplier; @@ -226,13 +227,13 @@ protected Mock CreateMockDeltaCreation(string extension, double? mockCreation.Setup(x => x.Extension).Returns(extension); mockCreation.Setup(x => x.CreateDeltaFile(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((Stream sourceFileStream, Stream targetFileStream, Stream deltaFileStream, + .Callback((Stream sourceStream, Stream targetStream, Stream deltaStream, IProgress? progress) => { filesizePercent ??= Random.Shared.NextDouble(); - var filesize = (long)(targetFileStream.Length * filesizePercent); + var filesize = (long)(targetStream.Length * filesizePercent); - Functions.FillStreamWithRandomData(deltaFileStream, filesize); + Functions.FillStreamWithRandomData(deltaStream, filesize); }).ReturnsAsync(true); return mockCreation; @@ -317,19 +318,19 @@ private async Task MakeRandomFile(string file) private string UpdatePackageCreatorName => UpdatePackageCreator.GetType().Name; - private Stream GetDeltaTargetFileStream(string packageLocation, string applicationName, SemanticVersion newVersion) + private Stream GetDeltaTargetStream(string packageLocation, string applicationName, SemanticVersion newVersion) { return FileSystem.File.OpenRead(Path.Combine(packageLocation, string.Format(UpdatePackageCreator.DeltaPackageFilenameTemplate, applicationName, newVersion))); } - private Stream GetFullTargetFileStream(string packageLocation, string applicationName, SemanticVersion newVersion) + private Stream GetFullTargetStream(string packageLocation, string applicationName, SemanticVersion newVersion) { return FileSystem.File.OpenRead(Path.Combine(packageLocation, string.Format(UpdatePackageCreator.FullPackageFilenameTemplate, applicationName, newVersion))); } - private Stream GetExpectedTargetFileStream(string fileLocation) => FileSystem.File.OpenRead(fileLocation); + private Stream GetExpectedTargetStream(string fileLocation) => FileSystem.File.OpenRead(fileLocation); private string ExpectedTargetFileLocation(string filename) => Path.Combine("Assets", UpdatePackageCreatorName, filename + UpdatePackageCreator.Extension); diff --git a/tests/TinyUpdate.Packages.Tests/UpdatePackageTests.cs b/tests/TinyUpdate.Packages.Tests/UpdatePackageTests.cs index 5445a61..484476f 100644 --- a/tests/TinyUpdate.Packages.Tests/UpdatePackageTests.cs +++ b/tests/TinyUpdate.Packages.Tests/UpdatePackageTests.cs @@ -40,24 +40,24 @@ private static bool NeedsFixedCreatorSize } } - protected override void CheckUpdatePackageWithExpected(Stream targetFileStream, Stream expectedTargetFileStream) + protected override void CheckUpdatePackageWithExpected(Stream targetStream, Stream expectedTargetStream) { - var targetFileStreamZip = new ZipArchive(targetFileStream, ZipArchiveMode.Read); - var expectedTargetFileStreamZip = new ZipArchive(expectedTargetFileStream, ZipArchiveMode.Read); + var targetStreamZip = new ZipArchive(targetStream, ZipArchiveMode.Read); + var expectedTargetStreamZip = new ZipArchive(expectedTargetStream, ZipArchiveMode.Read); Assert.Multiple(() => { - Assert.That(expectedTargetFileStreamZip.Entries, - Has.Count.EqualTo(targetFileStreamZip.Entries.Count), + Assert.That(expectedTargetStreamZip.Entries, + Has.Count.EqualTo(targetStreamZip.Entries.Count), () => "They isn't the correct amount of files"); - Assert.That(expectedTargetFileStreamZip.Entries.Select(x => x.FullName).OrderDescending(), - Is.EquivalentTo(targetFileStreamZip.Entries.Select(x => x.FullName).OrderDescending()), + Assert.That(expectedTargetStreamZip.Entries.Select(x => x.FullName).OrderDescending(), + Is.EquivalentTo(targetStreamZip.Entries.Select(x => x.FullName).OrderDescending()), () => "File structure is not the same in both files"); - foreach (var expectedEntry in expectedTargetFileStreamZip.Entries) + foreach (var expectedEntry in expectedTargetStreamZip.Entries) { - var targetEntry = targetFileStreamZip.GetEntry(expectedEntry.FullName); + var targetEntry = targetStreamZip.GetEntry(expectedEntry.FullName); if (targetEntry == null) { Assert.Fail($"{expectedEntry.FullName} doesn't exist within the target file");