From 9e7cd79b37e93de4f3f2bf36a7424afcc420fe13 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 27 Apr 2020 11:56:15 +0200 Subject: [PATCH] Add tests to ensure chunk order --- .../Formats/Png/PngEncoderTests.cs | 142 ++++++++++++++++-- tests/Images/Input/Png/PngWithMetaData.png | 4 +- 2 files changed, 132 insertions(+), 14 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 5a31d2d93..0407ef295 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -2,6 +2,9 @@ // Licensed under the Apache License, Version 2.0. // ReSharper disable InconsistentNaming + +using System; +using System.Buffers.Binary; using System.IO; using System.Linq; @@ -18,6 +21,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png { public class PngEncoderTests { + private static PngEncoder PngEncoder => new PngEncoder(); + public static readonly TheoryData PngBitDepthFiles = new TheoryData { @@ -234,8 +239,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png { using (Stream stream = new MemoryStream()) { - var encoder = new PngEncoder(); - encoder.Encode(provider.GetImage(), stream); + PngEncoder.Encode(provider.GetImage(), stream); stream.Seek(0, SeekOrigin.Begin); @@ -281,7 +285,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png using (Image image = provider.GetImage()) using (var ms = new MemoryStream()) { - image.Save(ms, new PngEncoder()); + image.Save(ms, PngEncoder); byte[] data = ms.ToArray().Take(8).ToArray(); byte[] expected = @@ -304,14 +308,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png [MemberData(nameof(RatioFiles))] public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { - var options = new PngEncoder(); - var testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { - input.Save(memStream, options); + input.Save(memStream, PngEncoder); memStream.Position = 0; using (var output = Image.Load(memStream)) @@ -329,14 +331,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png [MemberData(nameof(PngBitDepthFiles))] public void Encode_PreserveBits(string imagePath, PngBitDepth pngBitDepth) { - var options = new PngEncoder(); - var testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { - input.Save(memStream, options); + input.Save(memStream, PngEncoder); memStream.Position = 0; using (var output = Image.Load(memStream)) @@ -353,8 +353,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png [MemberData(nameof(PngTrnsFiles))] public void Encode_PreserveTrns(string imagePath, PngBitDepth pngBitDepth, PngColorType pngColorType) { - var options = new PngEncoder(); - var testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateRgba32Image()) { @@ -363,7 +361,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png using (var memStream = new MemoryStream()) { - input.Save(memStream, options); + input.Save(memStream, PngEncoder); memStream.Position = 0; using (var output = Image.Load(memStream)) { @@ -404,6 +402,126 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } } + [Fact] + public void HeaderChunk_ComesFirst() + { + var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); + using Image input = testFile.CreateRgba32Image(); + using var memStream = new MemoryStream(); + input.Save(memStream, PngEncoder); + memStream.Position = 0; + + // Skip header. + Span bytesSpan = memStream.ToArray().AsSpan(8); + BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(0, 4)); + var type = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); + Assert.Equal(PngChunkType.Header, type); + } + + [Fact] + public void EndChunk_IsLast() + { + var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); + using Image input = testFile.CreateRgba32Image(); + using var memStream = new MemoryStream(); + input.Save(memStream, PngEncoder); + memStream.Position = 0; + + // Skip header. + Span bytesSpan = memStream.ToArray().AsSpan(8); + + bool endChunkFound = false; + while (bytesSpan.Length > 0) + { + int length = BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(0, 4)); + var type = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); + Assert.False(endChunkFound); + if (type == PngChunkType.End) + { + endChunkFound = true; + } + + bytesSpan = bytesSpan.Slice(4 + 4 + length + 4); + } + } + + [Theory] + [InlineData(PngChunkType.Gamma)] + [InlineData(PngChunkType.Chroma)] + [InlineData(PngChunkType.EmbeddedColorProfile)] + [InlineData(PngChunkType.SignificantBits)] + [InlineData(PngChunkType.StandardRgbColourSpace)] + public void Chunk_ComesBeforePlteAndIDat(object chunkTypeObj) + { + var chunkType = (PngChunkType)chunkTypeObj; + var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); + using Image input = testFile.CreateRgba32Image(); + using var memStream = new MemoryStream(); + input.Save(memStream, PngEncoder); + memStream.Position = 0; + + // Skip header. + Span bytesSpan = memStream.ToArray().AsSpan(8); + + bool palFound = false; + bool dataFound = false; + while (bytesSpan.Length > 0) + { + int length = BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(0, 4)); + var type = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); + if (chunkType == type) + { + Assert.False(palFound || dataFound, $"{chunkType} chunk should come before data and palette chunk"); + } + + switch (type) + { + case PngChunkType.Data: + dataFound = true; + break; + case PngChunkType.Palette: + palFound = true; + break; + } + + bytesSpan = bytesSpan.Slice(4 + 4 + length + 4); + } + } + + [Theory] + [InlineData(PngChunkType.Physical)] + [InlineData(PngChunkType.SuggestedPalette)] + public void Chunk_ComesBeforeIDat(object chunkTypeObj) + { + var chunkType = (PngChunkType)chunkTypeObj; + var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); + using Image input = testFile.CreateRgba32Image(); + using var memStream = new MemoryStream(); + input.Save(memStream, PngEncoder); + memStream.Position = 0; + + // Skip header. + Span bytesSpan = memStream.ToArray().AsSpan(8); + + bool dataFound = false; + while (bytesSpan.Length > 0) + { + int length = BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(0, 4)); + var type = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); + if (chunkType == type) + { + Assert.False(dataFound, $"{chunkType} chunk should come before data chunk"); + } + + if (type == PngChunkType.Data) + { + dataFound = true; + } + + bytesSpan = bytesSpan.Slice(4 + 4 + length + 4); + } + } + [Theory] [WithTestPatternImages(587, 821, PixelTypes.Rgba32)] [WithTestPatternImages(677, 683, PixelTypes.Rgba32)] diff --git a/tests/Images/Input/Png/PngWithMetaData.png b/tests/Images/Input/Png/PngWithMetaData.png index 16be2b0e8..8db95fa63 100644 --- a/tests/Images/Input/Png/PngWithMetaData.png +++ b/tests/Images/Input/Png/PngWithMetaData.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c92a4ea43f50a7b5f5a492991c0a619ab639c4e802bf4ae0c2433a066e8c05a7 -size 777 +oid sha256:a37d2d31c2148b94bfd732c8964808dcc2dcdb6d2c187bb5d0403dc09af9ab46 +size 60544