From 0741439dd6face978a46bf3b0e7a3e4f363280b2 Mon Sep 17 00:00:00 2001 From: Bujacikk Date: Mon, 20 Nov 2023 23:35:09 +0100 Subject: [PATCH] Task 20918. Improving Png.Save and tests Comments removing update3 --- OpenRA.Game/FileFormats/Png.cs | 8 +- OpenRA.Test/OpenRA.Game/PngTest.cs | 162 +++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 OpenRA.Test/OpenRA.Game/PngTest.cs diff --git a/OpenRA.Game/FileFormats/Png.cs b/OpenRA.Game/FileFormats/Png.cs index 347326c283..2e1463af7c 100644 --- a/OpenRA.Game/FileFormats/Png.cs +++ b/OpenRA.Game/FileFormats/Png.cs @@ -16,6 +16,7 @@ using System.Linq; using System.Net; using System.Text; using ICSharpCode.SharpZipLib.Checksum; +using ICSharpCode.SharpZipLib.Zip.Compression; using ICSharpCode.SharpZipLib.Zip.Compression.Streams; using OpenRA.Graphics; using OpenRA.Primitives; @@ -371,13 +372,14 @@ namespace OpenRA.FileFormats using (var data = new MemoryStream()) { - using (var compressed = new DeflaterOutputStream(data)) + using (var compressed = new DeflaterOutputStream(data, new Deflater(Deflater.BEST_COMPRESSION))) { var rowStride = Width * PixelStride; for (var y = 0; y < Height; y++) { - // Write uncompressed scanlines for simplicity - compressed.WriteByte(0); + // Assuming no filtering for simplicity + const byte FilterType = 0; + compressed.WriteByte(FilterType); compressed.Write(Data, y * rowStride, rowStride); } diff --git a/OpenRA.Test/OpenRA.Game/PngTest.cs b/OpenRA.Test/OpenRA.Game/PngTest.cs new file mode 100644 index 0000000000..373d6d51f5 --- /dev/null +++ b/OpenRA.Test/OpenRA.Game/PngTest.cs @@ -0,0 +1,162 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System.IO; +using System.Linq; +using NUnit.Framework; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Primitives; + +namespace OpenRA.Test +{ + [TestFixture] + public class PngTests + { + [Test] + public void Save_ShouldProduceValidPngFile() + { + // Arrange + var colors = Enumerable.Range(0, 256).Select(i => Color.FromArgb(i, i, i)).ToArray(); + var png = new Png(new byte[10 * 20], SpriteFrameType.Indexed8, 10, 20, colors); + + // Act + var result = png.Save(); + + // Assert + Assert.IsTrue(Png.Verify(new MemoryStream(result))); + } + + [Test] + public void Save_Method_Should_Write_Indexed8_Palette_If_256_Colors_Or_Less() + { + // Arrange + var colors = Enumerable.Range(0, 256).Select(i => Color.FromArgb(i, i, i)).ToArray(); + var png = new Png(new byte[10 * 20], SpriteFrameType.Indexed8, 10, 20, colors); + + // Act + var result = png.Save(); + + // Assert + // Byte at index 25 contains color type information + // 0x03 represents Indexed8 with a palette + var colorTypeByte = result[25]; + Assert.That(colorTypeByte, Is.EqualTo(0x03)); + } + + [Test] + public void Save_Method_Should_Write_Rgba32_If_Alpha_Channel_Required() + { + // Arrange + var png = new Png(new byte[10 * 20 * 4], SpriteFrameType.Rgba32, 10, 20); + + // Act + var result = png.Save(); + + // Assert + // Byte at index 25 contains color type information + // 0x06 represents RGBA32 with alpha + var colorTypeByte = result[25]; + Assert.That(colorTypeByte, Is.EqualTo(0x06)); + } + + [Test] + public void Save_ShouldThrowException_WhenDataLenghtNotEqualExpectedLenght() + { + // Arrange + var colors = Enumerable.Range(0, 256).Select(i => Color.FromArgb(i, i, i)).ToArray(); + + // Act + void TestDelegate() => new Png(new byte[10 * 20], SpriteFrameType.Indexed8, 100, 20, colors); + + // Assert + var ex = Assert.Throws(TestDelegate); + Assert.That(ex.Message, Is.EqualTo("Input data does not match expected length")); + } + + [Test] + public void PngConstructor_InvalidSignature_ThrowsInvalidDataException() + { + // Arrange + byte[] invalidSignature = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; + + // Act & Assert + var exception = Assert.Throws(() => new Png(new MemoryStream(invalidSignature))); + StringAssert.Contains("PNG Signature is bogus", exception.Message); + } + + [Test] + public void PngConstructor_HeaderNotFirst_ThrowsInvalidDataException() + { + // Arrange + var invalidPngData = new byte[] + { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, + 0x00, 0x00, 0x00, 0x00, + }; + + // Act & Assert + var exception = Assert.Throws(() => new Png(new MemoryStream(invalidPngData))); + StringAssert.Contains("Invalid PNG file - header does not appear first.", exception.Message); + } + + [Test] + public void PngConstructor_DuplicateIhdrHeader_ThrowsInvalidDataException() + { + // Arrange + var invalidPngData = new byte[] + { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, + 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, + }; + + using (var stream = new MemoryStream(invalidPngData)) + { + // Act & Assert + var exception = Assert.Throws(() => new Png(stream)); + } + } + + public void PngConstructor_CompressionMethodNotSupported_ThrowsInvalidDataException() + { + // Arrange + var invalidPngData = new byte[] + { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, + 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, + }; + + using (var stream = new MemoryStream(invalidPngData)) + { + // Act & Assert + var exception = Assert.Throws(() => new Png(stream)); + Assert.AreEqual("Compression method not supported", exception.Message); + } + } + + [Test] + public void Constructor_ThrowsExceptionForNullPaletteIfTypeIsIndexed8() + { + // Arrange + const int Width = 100; + const int Height = 100; + const SpriteFrameType Type = SpriteFrameType.Indexed8; + + // Act and Assert + Assert.Throws(() => new Png(new byte[Width * Height], Type, Width, Height, null)); + } + } +}