image work, fix text wrapping
This commit is contained in:
48
FlexLove.lua
48
FlexLove.lua
@@ -4918,6 +4918,25 @@ function Element:draw(backdropCanvas)
|
|||||||
local contentX = self.x + textPaddingLeft
|
local contentX = self.x + textPaddingLeft
|
||||||
local contentY = self.y + textPaddingTop
|
local contentY = self.y + textPaddingTop
|
||||||
|
|
||||||
|
-- Check if text wrapping is enabled
|
||||||
|
if self.textWrap and (self.textWrap == "word" or self.textWrap == "char" or self.textWrap == true) then
|
||||||
|
-- Use printf for wrapped text
|
||||||
|
local align = "left"
|
||||||
|
if self.textAlign == TextAlign.CENTER then
|
||||||
|
align = "center"
|
||||||
|
elseif self.textAlign == TextAlign.END then
|
||||||
|
align = "right"
|
||||||
|
elseif self.textAlign == TextAlign.JUSTIFY then
|
||||||
|
align = "justify"
|
||||||
|
end
|
||||||
|
|
||||||
|
tx = contentX
|
||||||
|
ty = contentY
|
||||||
|
|
||||||
|
-- Use printf with the available width for wrapping
|
||||||
|
love.graphics.printf(self.text, tx, ty, textAreaWidth, align)
|
||||||
|
else
|
||||||
|
-- Use regular print for non-wrapped text
|
||||||
if self.textAlign == TextAlign.START then
|
if self.textAlign == TextAlign.START then
|
||||||
tx = contentX
|
tx = contentX
|
||||||
ty = contentY
|
ty = contentY
|
||||||
@@ -4933,6 +4952,7 @@ function Element:draw(backdropCanvas)
|
|||||||
ty = contentY
|
ty = contentY
|
||||||
end
|
end
|
||||||
love.graphics.print(self.text, tx, ty)
|
love.graphics.print(self.text, tx, ty)
|
||||||
|
end
|
||||||
if self.textSize then
|
if self.textSize then
|
||||||
love.graphics.setFont(origFont)
|
love.graphics.setFont(origFont)
|
||||||
end
|
end
|
||||||
@@ -5575,6 +5595,8 @@ function Element:calculateTextHeight()
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Get the font
|
||||||
|
local font
|
||||||
if self.textSize then
|
if self.textSize then
|
||||||
-- Resolve font path from font family (same logic as in draw)
|
-- Resolve font path from font family (same logic as in draw)
|
||||||
local fontPath = nil
|
local fontPath = nil
|
||||||
@@ -5591,22 +5613,30 @@ function Element:calculateTextHeight()
|
|||||||
fontPath = themeToUse.fonts.default
|
fontPath = themeToUse.fonts.default
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
font = FONT_CACHE.get(self.textSize, fontPath)
|
||||||
local tempFont = FONT_CACHE.get(self.textSize, fontPath)
|
else
|
||||||
local height = tempFont:getHeight()
|
font = love.graphics.getFont()
|
||||||
-- Apply contentAutoSizingMultiplier if set
|
|
||||||
if self.contentAutoSizingMultiplier and self.contentAutoSizingMultiplier.height then
|
|
||||||
height = height * self.contentAutoSizingMultiplier.height
|
|
||||||
end
|
|
||||||
return height
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local font = love.graphics.getFont()
|
|
||||||
local height = font:getHeight()
|
local height = font:getHeight()
|
||||||
|
|
||||||
|
-- If text wrapping is enabled, calculate height based on wrapped lines
|
||||||
|
if self.textWrap and (self.textWrap == "word" or self.textWrap == "char" or self.textWrap == true) then
|
||||||
|
-- Calculate available width for wrapping
|
||||||
|
local availableWidth = self.width
|
||||||
|
if availableWidth and availableWidth > 0 then
|
||||||
|
-- Get the wrapped text lines using getWrap (returns width and table of lines)
|
||||||
|
local wrappedWidth, wrappedLines = font:getWrap(self.text, availableWidth)
|
||||||
|
-- Height is line height * number of lines
|
||||||
|
height = height * #wrappedLines
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Apply contentAutoSizingMultiplier if set
|
-- Apply contentAutoSizingMultiplier if set
|
||||||
if self.contentAutoSizingMultiplier and self.contentAutoSizingMultiplier.height then
|
if self.contentAutoSizingMultiplier and self.contentAutoSizingMultiplier.height then
|
||||||
height = height * self.contentAutoSizingMultiplier.height
|
height = height * self.contentAutoSizingMultiplier.height
|
||||||
end
|
end
|
||||||
|
|
||||||
return height
|
return height
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
226
testing/__tests__/25_image_cache_tests.lua
Normal file
226
testing/__tests__/25_image_cache_tests.lua
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
local lu = require("testing.luaunit")
|
||||||
|
local FlexLove = require("FlexLove")
|
||||||
|
local ImageCache = FlexLove.ImageCache
|
||||||
|
|
||||||
|
TestImageCache = {}
|
||||||
|
|
||||||
|
function TestImageCache:setUp()
|
||||||
|
-- Clear cache before each test
|
||||||
|
ImageCache.clear()
|
||||||
|
|
||||||
|
-- Create a test image programmatically
|
||||||
|
self.testImageData = love.image.newImageData(64, 64)
|
||||||
|
-- Fill with a simple pattern
|
||||||
|
for y = 0, 63 do
|
||||||
|
for x = 0, 63 do
|
||||||
|
local r = x / 63
|
||||||
|
local g = y / 63
|
||||||
|
local b = 0.5
|
||||||
|
self.testImageData:setPixel(x, y, r, g, b, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Save to a temporary file (register in mock filesystem)
|
||||||
|
self.testImagePath = "testing/temp_test_image.png"
|
||||||
|
self.testImageData:encode("png", self.testImagePath)
|
||||||
|
-- Register file in mock filesystem so love.graphics.newImage can find it
|
||||||
|
love.filesystem.addMockFile(self.testImagePath, "mock_png_data")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestImageCache:tearDown()
|
||||||
|
-- Clear cache after each test
|
||||||
|
ImageCache.clear()
|
||||||
|
|
||||||
|
-- Clean up temporary test file
|
||||||
|
if love.filesystem.getInfo(self.testImagePath) then
|
||||||
|
love.filesystem.remove(self.testImagePath)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Basic Loading Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestImageCache:testLoadValidImage()
|
||||||
|
local image, err = ImageCache.load(self.testImagePath)
|
||||||
|
|
||||||
|
lu.assertNotNil(image)
|
||||||
|
lu.assertNil(err)
|
||||||
|
lu.assertEquals(type(image), "userdata") -- love.Image is userdata
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestImageCache:testLoadInvalidPath()
|
||||||
|
local image, err = ImageCache.load("nonexistent/path/to/image.png")
|
||||||
|
|
||||||
|
lu.assertNil(image)
|
||||||
|
lu.assertNotNil(err)
|
||||||
|
lu.assertStrContains(err, "Failed to load image")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestImageCache:testLoadEmptyPath()
|
||||||
|
local image, err = ImageCache.load("")
|
||||||
|
|
||||||
|
lu.assertNil(image)
|
||||||
|
lu.assertNotNil(err)
|
||||||
|
lu.assertStrContains(err, "Invalid image path")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestImageCache:testLoadNilPath()
|
||||||
|
local image, err = ImageCache.load(nil)
|
||||||
|
|
||||||
|
lu.assertNil(image)
|
||||||
|
lu.assertNotNil(err)
|
||||||
|
lu.assertStrContains(err, "Invalid image path")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Caching Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestImageCache:testCachingSameImageReturnsSameReference()
|
||||||
|
local image1, err1 = ImageCache.load(self.testImagePath)
|
||||||
|
local image2, err2 = ImageCache.load(self.testImagePath)
|
||||||
|
|
||||||
|
lu.assertNotNil(image1)
|
||||||
|
lu.assertNotNil(image2)
|
||||||
|
lu.assertEquals(image1, image2) -- Same reference
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestImageCache:testCachingDifferentImages()
|
||||||
|
-- Create a second test image
|
||||||
|
local testImageData2 = love.image.newImageData(32, 32)
|
||||||
|
for y = 0, 31 do
|
||||||
|
for x = 0, 31 do
|
||||||
|
testImageData2:setPixel(x, y, 1, 0, 0, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local testImagePath2 = "testing/temp_test_image2.png"
|
||||||
|
testImageData2:encode("png", testImagePath2)
|
||||||
|
|
||||||
|
local image1 = ImageCache.load(self.testImagePath)
|
||||||
|
local image2 = ImageCache.load(testImagePath2)
|
||||||
|
|
||||||
|
lu.assertNotNil(image1)
|
||||||
|
lu.assertNotNil(image2)
|
||||||
|
lu.assertNotEquals(image1, image2) -- Different images
|
||||||
|
|
||||||
|
-- Cleanup
|
||||||
|
love.filesystem.remove(testImagePath2)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestImageCache:testGetCachedImage()
|
||||||
|
-- Load image first
|
||||||
|
local loadedImage = ImageCache.load(self.testImagePath)
|
||||||
|
|
||||||
|
-- Get from cache
|
||||||
|
local cachedImage = ImageCache.get(self.testImagePath)
|
||||||
|
|
||||||
|
lu.assertNotNil(cachedImage)
|
||||||
|
lu.assertEquals(loadedImage, cachedImage)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestImageCache:testGetNonCachedImage()
|
||||||
|
local image = ImageCache.get("nonexistent.png")
|
||||||
|
|
||||||
|
lu.assertNil(image)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- ImageData Loading Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestImageCache:testLoadWithImageData()
|
||||||
|
local image, err = ImageCache.load(self.testImagePath, true)
|
||||||
|
|
||||||
|
lu.assertNotNil(image)
|
||||||
|
lu.assertNil(err)
|
||||||
|
|
||||||
|
local imageData = ImageCache.getImageData(self.testImagePath)
|
||||||
|
lu.assertNotNil(imageData)
|
||||||
|
lu.assertEquals(type(imageData), "userdata") -- love.ImageData is userdata
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestImageCache:testLoadWithoutImageData()
|
||||||
|
local image, err = ImageCache.load(self.testImagePath, false)
|
||||||
|
|
||||||
|
lu.assertNotNil(image)
|
||||||
|
lu.assertNil(err)
|
||||||
|
|
||||||
|
local imageData = ImageCache.getImageData(self.testImagePath)
|
||||||
|
lu.assertNil(imageData) -- Should not be loaded
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Cache Management Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestImageCache:testRemoveImage()
|
||||||
|
ImageCache.load(self.testImagePath)
|
||||||
|
|
||||||
|
local removed = ImageCache.remove(self.testImagePath)
|
||||||
|
|
||||||
|
lu.assertTrue(removed)
|
||||||
|
|
||||||
|
-- Verify it's no longer in cache
|
||||||
|
local cachedImage = ImageCache.get(self.testImagePath)
|
||||||
|
lu.assertNil(cachedImage)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestImageCache:testRemoveNonExistentImage()
|
||||||
|
local removed = ImageCache.remove("nonexistent.png")
|
||||||
|
|
||||||
|
lu.assertFalse(removed)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestImageCache:testClearCache()
|
||||||
|
-- Load multiple images
|
||||||
|
ImageCache.load(self.testImagePath)
|
||||||
|
|
||||||
|
local stats1 = ImageCache.getStats()
|
||||||
|
lu.assertEquals(stats1.count, 1)
|
||||||
|
|
||||||
|
ImageCache.clear()
|
||||||
|
|
||||||
|
local stats2 = ImageCache.getStats()
|
||||||
|
lu.assertEquals(stats2.count, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Statistics Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestImageCache:testCacheStats()
|
||||||
|
local stats1 = ImageCache.getStats()
|
||||||
|
lu.assertEquals(stats1.count, 0)
|
||||||
|
lu.assertEquals(stats1.memoryEstimate, 0)
|
||||||
|
|
||||||
|
ImageCache.load(self.testImagePath)
|
||||||
|
|
||||||
|
local stats2 = ImageCache.getStats()
|
||||||
|
lu.assertEquals(stats2.count, 1)
|
||||||
|
lu.assertTrue(stats2.memoryEstimate > 0)
|
||||||
|
|
||||||
|
-- Memory estimate should be approximately 64*64*4 bytes
|
||||||
|
local expectedMemory = 64 * 64 * 4
|
||||||
|
lu.assertEquals(stats2.memoryEstimate, expectedMemory)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Path Normalization Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestImageCache:testPathNormalization()
|
||||||
|
-- Load with different path formats
|
||||||
|
local image1 = ImageCache.load(self.testImagePath)
|
||||||
|
local image2 = ImageCache.load(" " .. self.testImagePath .. " ") -- With whitespace
|
||||||
|
local image3 = ImageCache.load(self.testImagePath:gsub("/", "\\")) -- With backslashes
|
||||||
|
|
||||||
|
lu.assertEquals(image1, image2)
|
||||||
|
lu.assertEquals(image1, image3)
|
||||||
|
|
||||||
|
-- Should only have one cache entry
|
||||||
|
local stats = ImageCache.getStats()
|
||||||
|
lu.assertEquals(stats.count, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
lu.LuaUnit.run()
|
||||||
244
testing/__tests__/26_object_fit_modes_tests.lua
Normal file
244
testing/__tests__/26_object_fit_modes_tests.lua
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
local lu = require("testing.luaunit")
|
||||||
|
local FlexLove = require("FlexLove")
|
||||||
|
local ImageRenderer = FlexLove.ImageRenderer
|
||||||
|
|
||||||
|
TestObjectFitModes = {}
|
||||||
|
|
||||||
|
function TestObjectFitModes:setUp()
|
||||||
|
-- Test dimensions
|
||||||
|
self.imageWidth = 400
|
||||||
|
self.imageHeight = 300
|
||||||
|
self.boundsWidth = 200
|
||||||
|
self.boundsHeight = 200
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Fill Mode Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectFitModes:testFillModeStretchesToExactBounds()
|
||||||
|
local params = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "fill")
|
||||||
|
|
||||||
|
lu.assertEquals(params.dw, self.boundsWidth)
|
||||||
|
lu.assertEquals(params.dh, self.boundsHeight)
|
||||||
|
lu.assertEquals(params.dx, 0)
|
||||||
|
lu.assertEquals(params.dy, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testFillModeUsesFullSourceImage()
|
||||||
|
local params = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "fill")
|
||||||
|
|
||||||
|
lu.assertEquals(params.sx, 0)
|
||||||
|
lu.assertEquals(params.sy, 0)
|
||||||
|
lu.assertEquals(params.sw, self.imageWidth)
|
||||||
|
lu.assertEquals(params.sh, self.imageHeight)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Contain Mode Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectFitModes:testContainModePreservesAspectRatio()
|
||||||
|
local params = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "contain")
|
||||||
|
|
||||||
|
-- Image is 4:3, bounds are 1:1
|
||||||
|
-- Should scale to fit width (200), height becomes 150
|
||||||
|
local expectedScale = self.boundsWidth / self.imageWidth
|
||||||
|
local expectedHeight = self.imageHeight * expectedScale
|
||||||
|
|
||||||
|
lu.assertAlmostEquals(params.dw, self.boundsWidth, 0.01)
|
||||||
|
lu.assertAlmostEquals(params.dh, expectedHeight, 0.01)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testContainModeFitsWithinBounds()
|
||||||
|
local params = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "contain")
|
||||||
|
|
||||||
|
lu.assertTrue(params.dw <= self.boundsWidth)
|
||||||
|
lu.assertTrue(params.dh <= self.boundsHeight)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testContainModeCentersImage()
|
||||||
|
local params = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "contain")
|
||||||
|
|
||||||
|
-- Image should be centered in letterbox
|
||||||
|
-- With default "center center" position
|
||||||
|
lu.assertTrue(params.dx >= 0)
|
||||||
|
lu.assertTrue(params.dy >= 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Cover Mode Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectFitModes:testCoverModePreservesAspectRatio()
|
||||||
|
local params = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "cover")
|
||||||
|
|
||||||
|
-- Check that aspect ratio is preserved in source crop
|
||||||
|
local sourceAspect = params.sw / params.sh
|
||||||
|
local boundsAspect = self.boundsWidth / self.boundsHeight
|
||||||
|
|
||||||
|
lu.assertAlmostEquals(sourceAspect, boundsAspect, 0.01)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testCoverModeCoversEntireBounds()
|
||||||
|
local params = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "cover")
|
||||||
|
|
||||||
|
lu.assertEquals(params.dw, self.boundsWidth)
|
||||||
|
lu.assertEquals(params.dh, self.boundsHeight)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testCoverModeCropsImage()
|
||||||
|
local params = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "cover")
|
||||||
|
|
||||||
|
-- Source should be cropped (not full image)
|
||||||
|
lu.assertTrue(params.sw < self.imageWidth or params.sh < self.imageHeight)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- None Mode Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectFitModes:testNoneModeUsesNaturalSize()
|
||||||
|
local params = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "none")
|
||||||
|
|
||||||
|
lu.assertEquals(params.dw, self.imageWidth)
|
||||||
|
lu.assertEquals(params.dh, self.imageHeight)
|
||||||
|
lu.assertEquals(params.scaleX, 1)
|
||||||
|
lu.assertEquals(params.scaleY, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testNoneModeUsesFullSourceImage()
|
||||||
|
local params = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "none")
|
||||||
|
|
||||||
|
lu.assertEquals(params.sx, 0)
|
||||||
|
lu.assertEquals(params.sy, 0)
|
||||||
|
lu.assertEquals(params.sw, self.imageWidth)
|
||||||
|
lu.assertEquals(params.sh, self.imageHeight)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Scale-Down Mode Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectFitModes:testScaleDownUsesNoneWhenImageFits()
|
||||||
|
-- Image smaller than bounds
|
||||||
|
local smallWidth = 100
|
||||||
|
local smallHeight = 75
|
||||||
|
|
||||||
|
local params = ImageRenderer.calculateFit(smallWidth, smallHeight, self.boundsWidth, self.boundsHeight, "scale-down")
|
||||||
|
|
||||||
|
-- Should use natural size (none mode)
|
||||||
|
lu.assertEquals(params.dw, smallWidth)
|
||||||
|
lu.assertEquals(params.dh, smallHeight)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testScaleDownUsesContainWhenImageTooBig()
|
||||||
|
local params = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "scale-down")
|
||||||
|
|
||||||
|
-- Should use contain mode
|
||||||
|
lu.assertTrue(params.dw <= self.boundsWidth)
|
||||||
|
lu.assertTrue(params.dh <= self.boundsHeight)
|
||||||
|
|
||||||
|
-- Should preserve aspect ratio
|
||||||
|
local scale = params.dw / self.imageWidth
|
||||||
|
lu.assertAlmostEquals(params.dh, self.imageHeight * scale, 0.01)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Edge Cases
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectFitModes:testLandscapeImageInPortraitBounds()
|
||||||
|
local params = ImageRenderer.calculateFit(
|
||||||
|
400,
|
||||||
|
200, -- Landscape image (2:1)
|
||||||
|
200,
|
||||||
|
400, -- Portrait bounds (1:2)
|
||||||
|
"contain"
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Should fit width
|
||||||
|
lu.assertEquals(params.dw, 200)
|
||||||
|
lu.assertTrue(params.dh < 400)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testPortraitImageInLandscapeBounds()
|
||||||
|
local params = ImageRenderer.calculateFit(
|
||||||
|
200,
|
||||||
|
400, -- Portrait image (1:2)
|
||||||
|
400,
|
||||||
|
200, -- Landscape bounds (2:1)
|
||||||
|
"contain"
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Should fit height
|
||||||
|
lu.assertEquals(params.dh, 200)
|
||||||
|
lu.assertTrue(params.dw < 400)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testSquareImageInNonSquareBounds()
|
||||||
|
local params = ImageRenderer.calculateFit(
|
||||||
|
300,
|
||||||
|
300, -- Square image
|
||||||
|
200,
|
||||||
|
400, -- Non-square bounds
|
||||||
|
"contain"
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Should fit to smaller dimension (width)
|
||||||
|
lu.assertEquals(params.dw, 200)
|
||||||
|
lu.assertEquals(params.dh, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testImageSmallerThanBounds()
|
||||||
|
local params = ImageRenderer.calculateFit(100, 100, 200, 200, "contain")
|
||||||
|
|
||||||
|
-- Should scale up to fit
|
||||||
|
lu.assertEquals(params.dw, 200)
|
||||||
|
lu.assertEquals(params.dh, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testImageLargerThanBounds()
|
||||||
|
local params = ImageRenderer.calculateFit(800, 600, 200, 200, "contain")
|
||||||
|
|
||||||
|
-- Should scale down to fit
|
||||||
|
lu.assertTrue(params.dw <= 200)
|
||||||
|
lu.assertTrue(params.dh <= 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Invalid Input Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectFitModes:testInvalidFitModeThrowsError()
|
||||||
|
lu.assertErrorMsgContains("Invalid fit mode", ImageRenderer.calculateFit, 100, 100, 200, 200, "invalid-mode")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testZeroDimensionsThrowsError()
|
||||||
|
lu.assertErrorMsgContains("Dimensions must be positive", ImageRenderer.calculateFit, 0, 100, 200, 200, "fill")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectFitModes:testNegativeDimensionsThrowsError()
|
||||||
|
lu.assertErrorMsgContains("Dimensions must be positive", ImageRenderer.calculateFit, 100, -100, 200, 200, "fill")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Default Mode Test
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectFitModes:testDefaultModeIsFill()
|
||||||
|
local params1 = ImageRenderer.calculateFit(
|
||||||
|
self.imageWidth,
|
||||||
|
self.imageHeight,
|
||||||
|
self.boundsWidth,
|
||||||
|
self.boundsHeight,
|
||||||
|
nil -- No mode specified
|
||||||
|
)
|
||||||
|
|
||||||
|
local params2 = ImageRenderer.calculateFit(self.imageWidth, self.imageHeight, self.boundsWidth, self.boundsHeight, "fill")
|
||||||
|
|
||||||
|
lu.assertEquals(params1.dw, params2.dw)
|
||||||
|
lu.assertEquals(params1.dh, params2.dh)
|
||||||
|
end
|
||||||
|
|
||||||
|
lu.LuaUnit.run()
|
||||||
184
testing/__tests__/27_object_position_tests.lua
Normal file
184
testing/__tests__/27_object_position_tests.lua
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
local lu = require("testing.luaunit")
|
||||||
|
local FlexLove = require("FlexLove")
|
||||||
|
local ImageRenderer = FlexLove.ImageRenderer
|
||||||
|
|
||||||
|
TestObjectPosition = {}
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Position Parsing Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectPosition:testCenterCenterDefault()
|
||||||
|
local x, y = ImageRenderer._parsePosition("center center")
|
||||||
|
lu.assertEquals(x, 0.5)
|
||||||
|
lu.assertEquals(y, 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testTopLeft()
|
||||||
|
local x, y = ImageRenderer._parsePosition("top left")
|
||||||
|
lu.assertEquals(x, 0)
|
||||||
|
lu.assertEquals(y, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testBottomRight()
|
||||||
|
local x, y = ImageRenderer._parsePosition("bottom right")
|
||||||
|
lu.assertEquals(x, 1)
|
||||||
|
lu.assertEquals(y, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testPercentage50()
|
||||||
|
local x, y = ImageRenderer._parsePosition("50% 50%")
|
||||||
|
lu.assertEquals(x, 0.5)
|
||||||
|
lu.assertEquals(y, 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testPercentage0()
|
||||||
|
local x, y = ImageRenderer._parsePosition("0% 0%")
|
||||||
|
lu.assertEquals(x, 0)
|
||||||
|
lu.assertEquals(y, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testPercentage100()
|
||||||
|
local x, y = ImageRenderer._parsePosition("100% 100%")
|
||||||
|
lu.assertEquals(x, 1)
|
||||||
|
lu.assertEquals(y, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testMixedKeywordPercentage()
|
||||||
|
local x, y = ImageRenderer._parsePosition("center 25%")
|
||||||
|
lu.assertEquals(x, 0.5)
|
||||||
|
lu.assertEquals(y, 0.25)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testSingleValueLeft()
|
||||||
|
local x, y = ImageRenderer._parsePosition("left")
|
||||||
|
lu.assertEquals(x, 0)
|
||||||
|
lu.assertEquals(y, 0.5) -- Should center on Y axis
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testSingleValueTop()
|
||||||
|
local x, y = ImageRenderer._parsePosition("top")
|
||||||
|
lu.assertEquals(x, 0.5) -- Should center on X axis
|
||||||
|
lu.assertEquals(y, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testInvalidPositionDefaultsToCenter()
|
||||||
|
local x, y = ImageRenderer._parsePosition("invalid position")
|
||||||
|
lu.assertEquals(x, 0.5)
|
||||||
|
lu.assertEquals(y, 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testNilPositionDefaultsToCenter()
|
||||||
|
local x, y = ImageRenderer._parsePosition(nil)
|
||||||
|
lu.assertEquals(x, 0.5)
|
||||||
|
lu.assertEquals(y, 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testEmptyStringDefaultsToCenter()
|
||||||
|
local x, y = ImageRenderer._parsePosition("")
|
||||||
|
lu.assertEquals(x, 0.5)
|
||||||
|
lu.assertEquals(y, 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Position with Contain Mode Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectPosition:testContainWithTopLeft()
|
||||||
|
local params = ImageRenderer.calculateFit(
|
||||||
|
400,
|
||||||
|
300, -- Image (landscape)
|
||||||
|
200,
|
||||||
|
200, -- Bounds (square)
|
||||||
|
"contain",
|
||||||
|
"top left"
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Image should be in top-left of letterbox
|
||||||
|
lu.assertEquals(params.dx, 0)
|
||||||
|
lu.assertEquals(params.dy, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testContainWithBottomRight()
|
||||||
|
local params = ImageRenderer.calculateFit(
|
||||||
|
400,
|
||||||
|
300, -- Image (landscape)
|
||||||
|
200,
|
||||||
|
200, -- Bounds (square)
|
||||||
|
"contain",
|
||||||
|
"bottom right"
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Image should be in bottom-right of letterbox
|
||||||
|
lu.assertTrue(params.dx + params.dw <= 200)
|
||||||
|
lu.assertTrue(params.dy + params.dh <= 200)
|
||||||
|
-- Should be at the bottom right
|
||||||
|
lu.assertAlmostEquals(params.dx + params.dw, 200, 0.01)
|
||||||
|
lu.assertAlmostEquals(params.dy + params.dh, 200, 0.01)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testContainWithCenter()
|
||||||
|
local params = ImageRenderer.calculateFit(400, 300, 200, 200, "contain", "center center")
|
||||||
|
|
||||||
|
-- Image (400x300) will be scaled to fit width (200x150)
|
||||||
|
-- Should be centered horizontally (dx=0) and vertically (dy=25)
|
||||||
|
lu.assertEquals(params.dx, 0)
|
||||||
|
lu.assertTrue(params.dy > 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Position with Cover Mode Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectPosition:testCoverWithTopLeft()
|
||||||
|
local params = ImageRenderer.calculateFit(400, 300, 200, 200, "cover", "top left")
|
||||||
|
|
||||||
|
-- Crop should start from top-left
|
||||||
|
lu.assertEquals(params.sx, 0)
|
||||||
|
lu.assertEquals(params.sy, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testCoverWithBottomRight()
|
||||||
|
local params = ImageRenderer.calculateFit(400, 300, 200, 200, "cover", "bottom right")
|
||||||
|
|
||||||
|
-- Crop should be from bottom-right
|
||||||
|
lu.assertTrue(params.sx > 0)
|
||||||
|
lu.assertTrue(params.sy >= 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testCoverWithCenter()
|
||||||
|
local params = ImageRenderer.calculateFit(400, 300, 200, 200, "cover", "center center")
|
||||||
|
|
||||||
|
-- Crop should be centered
|
||||||
|
lu.assertTrue(params.sx > 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Position with None Mode Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestObjectPosition:testNoneWithTopLeft()
|
||||||
|
local params = ImageRenderer.calculateFit(100, 100, 200, 200, "none", "top left")
|
||||||
|
|
||||||
|
-- Image should be at top-left
|
||||||
|
lu.assertEquals(params.dx, 0)
|
||||||
|
lu.assertEquals(params.dy, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testNoneWithBottomRight()
|
||||||
|
local params = ImageRenderer.calculateFit(100, 100, 200, 200, "none", "bottom right")
|
||||||
|
|
||||||
|
-- Image should be at bottom-right
|
||||||
|
lu.assertEquals(params.dx, 100) -- 200 - 100
|
||||||
|
lu.assertEquals(params.dy, 100) -- 200 - 100
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestObjectPosition:testNoneWithCenter()
|
||||||
|
local params = ImageRenderer.calculateFit(100, 100, 200, 200, "none", "center center")
|
||||||
|
|
||||||
|
-- Image should be centered
|
||||||
|
lu.assertEquals(params.dx, 50) -- (200 - 100) / 2
|
||||||
|
lu.assertEquals(params.dy, 50) -- (200 - 100) / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
lu.LuaUnit.run()
|
||||||
391
testing/__tests__/28_element_image_integration_tests.lua
Normal file
391
testing/__tests__/28_element_image_integration_tests.lua
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
local lu = require("testing.luaunit")
|
||||||
|
local FlexLove = require("FlexLove")
|
||||||
|
local Gui = FlexLove.Gui
|
||||||
|
local Element = FlexLove.Element
|
||||||
|
local ImageCache = FlexLove.ImageCache
|
||||||
|
|
||||||
|
TestElementImageIntegration = {}
|
||||||
|
|
||||||
|
function TestElementImageIntegration:setUp()
|
||||||
|
Gui.init({ baseScale = { width = 1920, height = 1080 } })
|
||||||
|
|
||||||
|
-- Create a test image programmatically
|
||||||
|
self.testImageData = love.image.newImageData(400, 300)
|
||||||
|
-- Fill with a gradient pattern
|
||||||
|
for y = 0, 299 do
|
||||||
|
for x = 0, 399 do
|
||||||
|
local r = x / 399
|
||||||
|
local g = y / 299
|
||||||
|
local b = 0.5
|
||||||
|
self.testImageData:setPixel(x, y, r, g, b, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Save to a temporary file (mock filesystem)
|
||||||
|
self.testImagePath = "testing/temp_element_test_image.png"
|
||||||
|
self.testImageData:encode("png", self.testImagePath)
|
||||||
|
love.filesystem.addMockFile(self.testImagePath, "mock_image_data")
|
||||||
|
|
||||||
|
-- Create test image object
|
||||||
|
self.testImage = love.graphics.newImage(self.testImageData)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:tearDown()
|
||||||
|
Gui.destroy()
|
||||||
|
ImageCache.clear()
|
||||||
|
|
||||||
|
-- Clean up temporary test file
|
||||||
|
if love.filesystem.getInfo(self.testImagePath) then
|
||||||
|
love.filesystem.remove(self.testImagePath)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Element Creation Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testElementWithImagePath()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertNotNil(element._loadedImage)
|
||||||
|
lu.assertEquals(element.imagePath, self.testImagePath)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testElementWithImageObject()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
image = self.testImage,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertNotNil(element._loadedImage)
|
||||||
|
lu.assertEquals(element._loadedImage, self.testImage)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testElementWithInvalidImagePath()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = "nonexistent/image.png",
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Should not crash, just not have a loaded image
|
||||||
|
lu.assertNil(element._loadedImage)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testElementWithoutImage()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertNil(element._loadedImage)
|
||||||
|
lu.assertNil(element.imagePath)
|
||||||
|
lu.assertNil(element.image)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Property Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testObjectFitProperty()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
objectFit = "contain",
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertEquals(element.objectFit, "contain")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testObjectFitDefaultValue()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertEquals(element.objectFit, "fill")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testObjectPositionProperty()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
objectPosition = "top left",
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertEquals(element.objectPosition, "top left")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testObjectPositionDefaultValue()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertEquals(element.objectPosition, "center center")
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testImageOpacityProperty()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
imageOpacity = 0.5,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertEquals(element.imageOpacity, 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testImageOpacityDefaultValue()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertEquals(element.imageOpacity, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Image Caching Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testMultipleElementsShareCachedImage()
|
||||||
|
local element1 = Element.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 100,
|
||||||
|
height = 100,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
local element2 = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Both should have the same cached image reference
|
||||||
|
lu.assertEquals(element1._loadedImage, element2._loadedImage)
|
||||||
|
|
||||||
|
-- Cache should only have one entry
|
||||||
|
local stats = ImageCache.getStats()
|
||||||
|
lu.assertEquals(stats.count, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Rendering Tests (Basic Validation)
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testDrawDoesNotCrashWithImage()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Should not crash when drawing
|
||||||
|
lu.assertNotNil(function()
|
||||||
|
element:draw()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testDrawDoesNotCrashWithoutImage()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Should not crash when drawing without image
|
||||||
|
lu.assertNotNil(function()
|
||||||
|
element:draw()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testDrawWithZeroOpacity()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
opacity = 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Should not crash (early exit in draw)
|
||||||
|
lu.assertNotNil(function()
|
||||||
|
element:draw()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Combined Properties Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testImageWithPadding()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
padding = { top = 10, right = 10, bottom = 10, left = 10 },
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertNotNil(element._loadedImage)
|
||||||
|
lu.assertEquals(element.padding.top, 10)
|
||||||
|
lu.assertEquals(element.padding.left, 10)
|
||||||
|
-- Image should render in content area (200x200)
|
||||||
|
lu.assertEquals(element.width, 200)
|
||||||
|
lu.assertEquals(element.height, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testImageWithCornerRadius()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
cornerRadius = 20,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertNotNil(element._loadedImage)
|
||||||
|
lu.assertEquals(element.cornerRadius.topLeft, 20)
|
||||||
|
lu.assertEquals(element.cornerRadius.topRight, 20)
|
||||||
|
lu.assertEquals(element.cornerRadius.bottomLeft, 20)
|
||||||
|
lu.assertEquals(element.cornerRadius.bottomRight, 20)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testImageWithBackgroundColor()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
backgroundColor = FlexLove.Color.new(1, 0, 0, 1),
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertNotNil(element._loadedImage)
|
||||||
|
lu.assertEquals(element.backgroundColor.r, 1)
|
||||||
|
lu.assertEquals(element.backgroundColor.g, 0)
|
||||||
|
lu.assertEquals(element.backgroundColor.b, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testImageWithAllObjectFitModes()
|
||||||
|
local modes = { "fill", "contain", "cover", "scale-down", "none" }
|
||||||
|
|
||||||
|
for _, mode in ipairs(modes) do
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
objectFit = mode,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertEquals(element.objectFit, mode)
|
||||||
|
lu.assertNotNil(element._loadedImage)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testImageWithCombinedOpacity()
|
||||||
|
local element = Element.new({
|
||||||
|
x = 100,
|
||||||
|
y = 100,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
opacity = 0.5,
|
||||||
|
imageOpacity = 0.8,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertEquals(element.opacity, 0.5)
|
||||||
|
lu.assertEquals(element.imageOpacity, 0.8)
|
||||||
|
-- Combined opacity should be 0.5 * 0.8 = 0.4 (tested in rendering)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- Layout Integration Tests
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testImageWithFlexLayout()
|
||||||
|
local container = Element.new({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 600,
|
||||||
|
height = 200,
|
||||||
|
flexDirection = FlexLove.enums.FlexDirection.HORIZONTAL,
|
||||||
|
})
|
||||||
|
|
||||||
|
local imageElement = Element.new({
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
parent = container,
|
||||||
|
})
|
||||||
|
|
||||||
|
table.insert(container.children, imageElement)
|
||||||
|
|
||||||
|
lu.assertNotNil(imageElement._loadedImage)
|
||||||
|
lu.assertEquals(imageElement.width, 200)
|
||||||
|
lu.assertEquals(imageElement.height, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestElementImageIntegration:testImageWithAbsolutePositioning()
|
||||||
|
local element = Element.new({
|
||||||
|
positioning = FlexLove.enums.Positioning.ABSOLUTE,
|
||||||
|
top = 50,
|
||||||
|
left = 50,
|
||||||
|
width = 200,
|
||||||
|
height = 200,
|
||||||
|
imagePath = self.testImagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
lu.assertNotNil(element._loadedImage)
|
||||||
|
lu.assertEquals(element.positioning, FlexLove.enums.Positioning.ABSOLUTE)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Run tests if executed directly
|
||||||
|
if arg and arg[0]:match("28_element_image_integration_tests.lua$") then
|
||||||
|
os.exit(lu.LuaUnit.run())
|
||||||
|
end
|
||||||
|
|
||||||
|
return TestElementImageIntegration
|
||||||
Reference in New Issue
Block a user