local luaunit = require("testing.luaunit") require("testing.loveStub") local ImageRenderer = require("modules.ImageRenderer") TestImageRenderer = {} function TestImageRenderer:setUp() -- Create a mock image for testing self.mockImage = { getDimensions = function() return 100, 100 end, } end -- Unhappy path tests for calculateFit function TestImageRenderer:testCalculateFitWithZeroImageWidth() luaunit.assertError(function() ImageRenderer.calculateFit(0, 100, 200, 200, "fill") end) end function TestImageRenderer:testCalculateFitWithZeroImageHeight() luaunit.assertError(function() ImageRenderer.calculateFit(100, 0, 200, 200, "fill") end) end function TestImageRenderer:testCalculateFitWithNegativeImageWidth() luaunit.assertError(function() ImageRenderer.calculateFit(-100, 100, 200, 200, "fill") end) end function TestImageRenderer:testCalculateFitWithNegativeImageHeight() luaunit.assertError(function() ImageRenderer.calculateFit(100, -100, 200, 200, "fill") end) end function TestImageRenderer:testCalculateFitWithZeroBoundsWidth() luaunit.assertError(function() ImageRenderer.calculateFit(100, 100, 0, 200, "fill") end) end function TestImageRenderer:testCalculateFitWithZeroBoundsHeight() luaunit.assertError(function() ImageRenderer.calculateFit(100, 100, 200, 0, "fill") end) end function TestImageRenderer:testCalculateFitWithNegativeBoundsWidth() luaunit.assertError(function() ImageRenderer.calculateFit(100, 100, -200, 200, "fill") end) end function TestImageRenderer:testCalculateFitWithNegativeBoundsHeight() luaunit.assertError(function() ImageRenderer.calculateFit(100, 100, 200, -200, "fill") end) end function TestImageRenderer:testCalculateFitWithInvalidFitMode() -- Now uses 'fill' fallback with warning instead of error local result = ImageRenderer.calculateFit(100, 100, 200, 200, "invalid-mode") luaunit.assertNotNil(result) -- Should fall back to 'fill' mode behavior (scales to fill bounds) luaunit.assertEquals(result.scaleX, 2) luaunit.assertEquals(result.scaleY, 2) end function TestImageRenderer:testCalculateFitWithNilFitMode() -- Should default to "fill" local result = ImageRenderer.calculateFit(100, 100, 200, 200, nil) luaunit.assertNotNil(result) luaunit.assertEquals(result.dw, 200) luaunit.assertEquals(result.dh, 200) end function TestImageRenderer:testCalculateFitFillMode() local result = ImageRenderer.calculateFit(100, 100, 200, 200, "fill") luaunit.assertEquals(result.scaleX, 2) luaunit.assertEquals(result.scaleY, 2) end function TestImageRenderer:testCalculateFitContainMode() local result = ImageRenderer.calculateFit(100, 100, 200, 200, "contain") luaunit.assertEquals(result.scaleX, 2) luaunit.assertEquals(result.scaleY, 2) end function TestImageRenderer:testCalculateFitCoverMode() local result = ImageRenderer.calculateFit(100, 100, 200, 200, "cover") luaunit.assertEquals(result.scaleX, 2) luaunit.assertEquals(result.scaleY, 2) end function TestImageRenderer:testCalculateFitNoneMode() local result = ImageRenderer.calculateFit(100, 100, 200, 200, "none") luaunit.assertEquals(result.scaleX, 1) luaunit.assertEquals(result.scaleY, 1) end function TestImageRenderer:testCalculateFitScaleDownModeWithLargeImage() local result = ImageRenderer.calculateFit(300, 300, 200, 200, "scale-down") -- Should behave like contain for larger images luaunit.assertNotNil(result) end function TestImageRenderer:testCalculateFitScaleDownModeWithSmallImage() local result = ImageRenderer.calculateFit(50, 50, 200, 200, "scale-down") -- Should behave like none for smaller images luaunit.assertEquals(result.scaleX, 1) luaunit.assertEquals(result.scaleY, 1) end -- Unhappy path tests for _parsePosition function TestImageRenderer:testParsePositionWithNil() local x, y = ImageRenderer._parsePosition(nil) luaunit.assertEquals(x, 0.5) luaunit.assertEquals(y, 0.5) end function TestImageRenderer:testParsePositionWithEmptyString() local x, y = ImageRenderer._parsePosition("") luaunit.assertEquals(x, 0.5) luaunit.assertEquals(y, 0.5) end function TestImageRenderer:testParsePositionWithInvalidType() local x, y = ImageRenderer._parsePosition(123) luaunit.assertEquals(x, 0.5) luaunit.assertEquals(y, 0.5) end function TestImageRenderer:testParsePositionWithInvalidKeyword() local x, y = ImageRenderer._parsePosition("invalid keyword") -- Should default to center luaunit.assertEquals(x, 0.5) luaunit.assertEquals(y, 0.5) end function TestImageRenderer:testParsePositionWithMixedValid() local x, y = ImageRenderer._parsePosition("left top") luaunit.assertEquals(x, 0) luaunit.assertEquals(y, 0) end function TestImageRenderer:testParsePositionWithPercentage() local x, y = ImageRenderer._parsePosition("75% 25%") luaunit.assertAlmostEquals(x, 0.75, 0.01) luaunit.assertAlmostEquals(y, 0.25, 0.01) end function TestImageRenderer:testParsePositionWithOutOfRangePercentage() local x, y = ImageRenderer._parsePosition("150% -50%") -- 150% clamps to 1, but -50% doesn't match pattern so defaults to 0.5 luaunit.assertEquals(x, 1) luaunit.assertEquals(y, 0.5) end function TestImageRenderer:testParsePositionWithSingleValue() local x, y = ImageRenderer._parsePosition("left") luaunit.assertEquals(x, 0) luaunit.assertEquals(y, 0.5) -- Should use center for Y end function TestImageRenderer:testParsePositionWithSinglePercentage() local x, y = ImageRenderer._parsePosition("25%") luaunit.assertAlmostEquals(x, 0.25, 0.01) luaunit.assertAlmostEquals(y, 0.25, 0.01) end -- Unhappy path tests for draw function TestImageRenderer:testDrawWithNilImage() -- Should not crash, just return early ImageRenderer.draw(nil, 0, 0, 100, 100, "fill") -- If we get here without error, test passes luaunit.assertTrue(true) end function TestImageRenderer:testDrawWithZeroWidth() -- Should error in calculateFit luaunit.assertError(function() ImageRenderer.draw(self.mockImage, 0, 0, 0, 100, "fill") end) end function TestImageRenderer:testDrawWithZeroHeight() luaunit.assertError(function() ImageRenderer.draw(self.mockImage, 0, 0, 100, 0, "fill") end) end function TestImageRenderer:testDrawWithNegativeOpacity() -- Should work but render with negative opacity ImageRenderer.draw(self.mockImage, 0, 0, 100, 100, "fill", "center center", -0.5) luaunit.assertTrue(true) end function TestImageRenderer:testDrawWithOpacityGreaterThanOne() -- Should work but render with >1 opacity ImageRenderer.draw(self.mockImage, 0, 0, 100, 100, "fill", "center center", 2.0) luaunit.assertTrue(true) end function TestImageRenderer:testDrawWithInvalidFitMode() -- Now uses 'fill' fallback with warning instead of error -- Should not throw an error, just use fill mode ImageRenderer.draw(self.mockImage, 0, 0, 100, 100, "invalid") luaunit.assertTrue(true) -- If we reach here, no error was thrown end function TestImageRenderer:testCalculateFitWithVerySmallBounds() local result = ImageRenderer.calculateFit(1000, 1000, 1, 1, "contain") luaunit.assertNotNil(result) -- Scale should be very small luaunit.assertTrue(result.scaleX < 0.01) end function TestImageRenderer:testCalculateFitWithVeryLargeBounds() local result = ImageRenderer.calculateFit(10, 10, 10000, 10000, "contain") luaunit.assertNotNil(result) -- Scale should be very large luaunit.assertTrue(result.scaleX > 100) end function TestImageRenderer:testCalculateFitWithAspectRatioMismatch() -- Wide image, tall bounds local result = ImageRenderer.calculateFit(200, 100, 100, 200, "contain") luaunit.assertNotNil(result) -- Should maintain aspect ratio luaunit.assertEquals(result.scaleX, result.scaleY) end function TestImageRenderer:testCalculateFitCoverWithAspectRatioMismatch() -- Wide image, tall bounds local result = ImageRenderer.calculateFit(200, 100, 100, 200, "cover") luaunit.assertNotNil(result) luaunit.assertEquals(result.scaleX, result.scaleY) end if not _G.RUNNING_ALL_TESTS then os.exit(luaunit.LuaUnit.run()) end