diff --git a/tests/Chip8.Tests/PaletteTests.cs b/tests/Chip8.Tests/PaletteTests.cs new file mode 100644 index 0000000..23e47f0 --- /dev/null +++ b/tests/Chip8.Tests/PaletteTests.cs @@ -0,0 +1,91 @@ +using System; +using Chip8.Core; +using Xunit; + +namespace Chip8.Tests; + +public class PaletteTests +{ + [Fact] + public void hexParsesWithAndWithoutHash() + { + var withHash = Rgb.fromHex("#1A2B3C"); + var without = Rgb.fromHex("1A2B3C"); + Assert.Equal(withHash, without); + Assert.Equal(0x1A, withHash.r); + Assert.Equal(0x2B, withHash.g); + Assert.Equal(0x3C, withHash.b); + } + + [Fact] + public void hexRoundTripsThroughToHex() + { + var color = new Rgb(0xFF, 0x80, 0x00); + Assert.Equal("#FF8000", color.toHex()); + Assert.Equal(color, Rgb.fromHex(color.toHex())); + } + + [Theory] + [InlineData("12345")] + [InlineData("#12")] + [InlineData("XYZXYZ")] + public void malformedHexThrows(string value) + { + Assert.Throws(() => Rgb.fromHex(value)); + } + + [Fact] + public void monochromePaletteHasTwoColors() + { + var palette = Palette.monochrome(); + Assert.Equal(2, palette.colorCount); + Assert.Equal(new Rgb(0, 0, 0), palette[0]); + Assert.Equal(new Rgb(0xFF, 0xFF, 0xFF), palette[1]); + } + + [Fact] + public void octoPaletteHasFourColors() + { + var palette = Palette.octo(); + Assert.Equal(4, palette.colorCount); + Assert.Equal(Rgb.fromHex("#996600"), palette[0]); + Assert.Equal(Rgb.fromHex("#662200"), palette[3]); + } + + [Fact] + public void extendedPaletteHasSixteenColors() + { + Assert.Equal(16, Palette.extended16().colorCount); + } + + [Fact] + public void resolveWrapsIndexIntoRange() + { + var palette = Palette.grayscale(); + Assert.Equal(palette[0], palette.resolve(4)); + Assert.Equal(palette[1], palette.resolve(5)); + Assert.Equal(palette[0], palette.resolve(-1)); + } + + [Theory] + [InlineData("mono")] + [InlineData("grayscale")] + [InlineData("octo")] + [InlineData("extended16")] + public void byNameResolvesKnownPalettes(string name) + { + Assert.True(Palette.byName(name).colorCount >= 2); + } + + [Fact] + public void byNameRejectsUnknownPalette() + { + Assert.Throws(() => Palette.byName("neon")); + } + + [Fact] + public void paletteRejectsSingleColor() + { + Assert.Throws(() => new Palette(new Rgb(0, 0, 0))); + } +} diff --git a/tests/Chip8.Tests/VideoTests.cs b/tests/Chip8.Tests/VideoTests.cs new file mode 100644 index 0000000..3a019b3 --- /dev/null +++ b/tests/Chip8.Tests/VideoTests.cs @@ -0,0 +1,107 @@ +using Chip8.Core; +using Xunit; + +namespace Chip8.Tests; + +public class VideoTests +{ + static Video withPixel(int x, int y, int planeMask = 1) + { + var video = new Video(); + var sprite = new byte[] { 0x80 }; + video.drawSprite(sprite, 0, x, y, 1, planeMask, false); + return video; + } + + [Fact] + public void scrollDownMovesRowsDown() + { + var video = withPixel(5, 2); + video.scrollDown(3, 1); + Assert.False(video.pixel(0, 5, 2)); + Assert.True(video.pixel(0, 5, 5)); + } + + [Fact] + public void scrollUpMovesRowsUp() + { + var video = withPixel(5, 10); + video.scrollUp(3, 1); + Assert.False(video.pixel(0, 5, 10)); + Assert.True(video.pixel(0, 5, 7)); + } + + [Fact] + public void scrollRightMovesColumnsRight() + { + var video = withPixel(5, 2); + video.scrollRight(4, 1); + Assert.False(video.pixel(0, 5, 2)); + Assert.True(video.pixel(0, 9, 2)); + } + + [Fact] + public void scrollLeftMovesColumnsLeft() + { + var video = withPixel(9, 2); + video.scrollLeft(4, 1); + Assert.False(video.pixel(0, 9, 2)); + Assert.True(video.pixel(0, 5, 2)); + } + + [Fact] + public void scrollRespectsPlaneMask() + { + var video = new Video(); + var sprite = new byte[] { 0x80 }; + video.drawSprite(sprite, 0, 5, 2, 1, 0x3, false); + video.scrollDown(3, 0x1); + Assert.True(video.pixel(0, 5, 5)); + Assert.True(video.pixel(1, 5, 2)); + Assert.False(video.pixel(1, 5, 5)); + } + + [Fact] + public void setResolutionHiresResizesAndClears() + { + var video = withPixel(5, 2); + video.setResolution(true); + Assert.Equal(Video.hiWidth, video.width); + Assert.Equal(Video.hiHeight, video.height); + Assert.False(video.pixel(0, 5, 2)); + } + + [Fact] + public void drawSpriteWrapsAroundEdgesWhenNotClipping() + { + var video = new Video(); + var sprite = new byte[] { 0xC0 }; + video.drawSprite(sprite, 0, 63, 0, 1, 1, true); + Assert.True(video.pixel(0, 63, 0)); + Assert.True(video.pixel(0, 0, 0)); + } + + [Fact] + public void drawSpriteClipsAtEdgeWhenClipping() + { + var video = new Video(); + var sprite = new byte[] { 0xC0 }; + video.drawSprite(sprite, 0, 63, 0, 1, 1, false); + Assert.True(video.pixel(0, 63, 0)); + Assert.False(video.pixel(0, 0, 0)); + } + + [Fact] + public void sixteenBySixteenSpriteFillsBlock() + { + var video = new Video(); + var sprite = new byte[32]; + for (int index = 0; index < sprite.Length; index++) + { + sprite[index] = 0xFF; + } + bool collision = video.drawSprite(sprite, 0, 0, 0, 0, 1, false); + Assert.False(collision); + Assert.Equal(256, AsciiScreen.litPixelCount(video)); + } +}