change spacing logic

This commit is contained in:
Michael Freno
2025-10-13 11:07:47 -04:00
parent 4bf1d2cc73
commit ae2e08ca1b
4 changed files with 555 additions and 22 deletions

View File

@@ -778,9 +778,37 @@ function Grid.layoutGridItems(element)
local rows = element.gridRows or 1 local rows = element.gridRows or 1
local columns = element.gridColumns or 1 local columns = element.gridColumns or 1
-- Calculate available space -- Calculate space reserved by absolutely positioned siblings
local availableWidth = element.width - element.padding.left - element.padding.right local reservedLeft = 0
local availableHeight = element.height - element.padding.top - element.padding.bottom local reservedRight = 0
local reservedTop = 0
local reservedBottom = 0
for _, child in ipairs(element.children) do
-- Only consider absolutely positioned children with explicit positioning
if child.positioning == Positioning.ABSOLUTE and child._explicitlyAbsolute then
if child.left then
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
reservedLeft = math.max(reservedLeft, child.left + childTotalWidth)
end
if child.right then
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
reservedRight = math.max(reservedRight, child.right + childTotalWidth)
end
if child.top then
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
reservedTop = math.max(reservedTop, child.top + childTotalHeight)
end
if child.bottom then
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
reservedBottom = math.max(reservedBottom, child.bottom + childTotalHeight)
end
end
end
-- Calculate available space (accounting for padding and reserved space)
local availableWidth = element.width - element.padding.left - element.padding.right - reservedLeft - reservedRight
local availableHeight = element.height - element.padding.top - element.padding.bottom - reservedTop - reservedBottom
-- Get gaps -- Get gaps
local columnGap = element.columnGap or 0 local columnGap = element.columnGap or 0
@@ -812,9 +840,9 @@ function Grid.layoutGridItems(element)
break break
end end
-- Calculate cell position -- Calculate cell position (accounting for reserved space)
local cellX = element.x + element.padding.left + (col * (cellWidth + columnGap)) local cellX = element.x + element.padding.left + reservedLeft + (col * (cellWidth + columnGap))
local cellY = element.y + element.padding.top + (row * (cellHeight + rowGap)) local cellY = element.y + element.padding.top + reservedTop + (row * (cellHeight + rowGap))
-- Apply alignment within grid cell (default to stretch) -- Apply alignment within grid cell (default to stretch)
local effectiveAlignItems = element.alignItems or AlignItems.STRETCH local effectiveAlignItems = element.alignItems or AlignItems.STRETCH
@@ -2016,15 +2044,80 @@ function Element:layoutChildren()
return return
end end
-- Calculate available space (accounting for padding) -- Calculate space reserved by absolutely positioned siblings with explicit positioning
local reservedMainStart = 0 -- Space reserved at the start of main axis (left for horizontal, top for vertical)
local reservedMainEnd = 0 -- Space reserved at the end of main axis (right for horizontal, bottom for vertical)
local reservedCrossStart = 0 -- Space reserved at the start of cross axis (top for horizontal, left for vertical)
local reservedCrossEnd = 0 -- Space reserved at the end of cross axis (bottom for horizontal, right for vertical)
for _, child in ipairs(self.children) do
-- Only consider absolutely positioned children with explicit positioning
if child.positioning == Positioning.ABSOLUTE and child._explicitlyAbsolute then
if self.flexDirection == FlexDirection.HORIZONTAL then
-- Horizontal layout: main axis is X, cross axis is Y
-- Check for left positioning (reserves space at main axis start)
if child.left then
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
local spaceNeeded = child.left + childTotalWidth
reservedMainStart = math.max(reservedMainStart, spaceNeeded)
end
-- Check for right positioning (reserves space at main axis end)
if child.right then
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
local spaceNeeded = child.right + childTotalWidth
reservedMainEnd = math.max(reservedMainEnd, spaceNeeded)
end
-- Check for top positioning (reserves space at cross axis start)
if child.top then
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
local spaceNeeded = child.top + childTotalHeight
reservedCrossStart = math.max(reservedCrossStart, spaceNeeded)
end
-- Check for bottom positioning (reserves space at cross axis end)
if child.bottom then
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
local spaceNeeded = child.bottom + childTotalHeight
reservedCrossEnd = math.max(reservedCrossEnd, spaceNeeded)
end
else
-- Vertical layout: main axis is Y, cross axis is X
-- Check for top positioning (reserves space at main axis start)
if child.top then
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
local spaceNeeded = child.top + childTotalHeight
reservedMainStart = math.max(reservedMainStart, spaceNeeded)
end
-- Check for bottom positioning (reserves space at main axis end)
if child.bottom then
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
local spaceNeeded = child.bottom + childTotalHeight
reservedMainEnd = math.max(reservedMainEnd, spaceNeeded)
end
-- Check for left positioning (reserves space at cross axis start)
if child.left then
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
local spaceNeeded = child.left + childTotalWidth
reservedCrossStart = math.max(reservedCrossStart, spaceNeeded)
end
-- Check for right positioning (reserves space at cross axis end)
if child.right then
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
local spaceNeeded = child.right + childTotalWidth
reservedCrossEnd = math.max(reservedCrossEnd, spaceNeeded)
end
end
end
end
-- Calculate available space (accounting for padding and reserved space)
local availableMainSize = 0 local availableMainSize = 0
local availableCrossSize = 0 local availableCrossSize = 0
if self.flexDirection == FlexDirection.HORIZONTAL then if self.flexDirection == FlexDirection.HORIZONTAL then
availableMainSize = self.width - self.padding.left - self.padding.right availableMainSize = self.width - self.padding.left - self.padding.right - reservedMainStart - reservedMainEnd
availableCrossSize = self.height - self.padding.top - self.padding.bottom availableCrossSize = self.height - self.padding.top - self.padding.bottom - reservedCrossStart - reservedCrossEnd
else else
availableMainSize = self.height - self.padding.top - self.padding.bottom availableMainSize = self.height - self.padding.top - self.padding.bottom - reservedMainStart - reservedMainEnd
availableCrossSize = self.width - self.padding.left - self.padding.right availableCrossSize = self.width - self.padding.left - self.padding.right - reservedCrossStart - reservedCrossEnd
end end
-- Handle flex wrap: create lines of children -- Handle flex wrap: create lines of children
@@ -2208,20 +2301,21 @@ function Element:layoutChildren()
if self.flexDirection == FlexDirection.HORIZONTAL then if self.flexDirection == FlexDirection.HORIZONTAL then
-- Horizontal layout: main axis is X, cross axis is Y -- Horizontal layout: main axis is X, cross axis is Y
-- Position child at border box (x, y represents top-left including padding) -- Position child at border box (x, y represents top-left including padding)
child.x = self.x + self.padding.left + currentMainPos -- Add reservedMainStart to account for absolutely positioned siblings
child.x = self.x + self.padding.left + reservedMainStart + currentMainPos
if effectiveAlign == AlignItems.FLEX_START then if effectiveAlign == AlignItems.FLEX_START then
child.y = self.y + self.padding.top + currentCrossPos child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos
elseif effectiveAlign == AlignItems.CENTER then elseif effectiveAlign == AlignItems.CENTER then
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
child.y = self.y + self.padding.top + currentCrossPos + ((lineHeight - childTotalHeight) / 2) child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos + ((lineHeight - childTotalHeight) / 2)
elseif effectiveAlign == AlignItems.FLEX_END then elseif effectiveAlign == AlignItems.FLEX_END then
local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom local childTotalHeight = (child.height or 0) + child.padding.top + child.padding.bottom
child.y = self.y + self.padding.top + currentCrossPos + lineHeight - childTotalHeight child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos + lineHeight - childTotalHeight
elseif effectiveAlign == AlignItems.STRETCH then elseif effectiveAlign == AlignItems.STRETCH then
-- STRETCH always stretches children in cross-axis direction -- STRETCH always stretches children in cross-axis direction
child.height = lineHeight - child.padding.top - child.padding.bottom child.height = lineHeight - child.padding.top - child.padding.bottom
child.y = self.y + self.padding.top + currentCrossPos child.y = self.y + self.padding.top + reservedCrossStart + currentCrossPos
end end
-- Apply positioning offsets (top, right, bottom, left) -- Apply positioning offsets (top, right, bottom, left)
@@ -2243,20 +2337,21 @@ function Element:layoutChildren()
else else
-- Vertical layout: main axis is Y, cross axis is X -- Vertical layout: main axis is Y, cross axis is X
-- Position child at border box (x, y represents top-left including padding) -- Position child at border box (x, y represents top-left including padding)
child.y = self.y + self.padding.top + currentMainPos -- Add reservedMainStart to account for absolutely positioned siblings
child.y = self.y + self.padding.top + reservedMainStart + currentMainPos
if effectiveAlign == AlignItems.FLEX_START then if effectiveAlign == AlignItems.FLEX_START then
child.x = self.x + self.padding.left + currentCrossPos child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos
elseif effectiveAlign == AlignItems.CENTER then elseif effectiveAlign == AlignItems.CENTER then
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
child.x = self.x + self.padding.left + currentCrossPos + ((lineHeight - childTotalWidth) / 2) child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos + ((lineHeight - childTotalWidth) / 2)
elseif effectiveAlign == AlignItems.FLEX_END then elseif effectiveAlign == AlignItems.FLEX_END then
local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right local childTotalWidth = (child.width or 0) + child.padding.left + child.padding.right
child.x = self.x + self.padding.left + currentCrossPos + lineHeight - childTotalWidth child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos + lineHeight - childTotalWidth
elseif effectiveAlign == AlignItems.STRETCH then elseif effectiveAlign == AlignItems.STRETCH then
-- STRETCH always stretches children in cross-axis direction -- STRETCH always stretches children in cross-axis direction
child.width = lineHeight - child.padding.left - child.padding.right child.width = lineHeight - child.padding.left - child.padding.right
child.x = self.x + self.padding.left + currentCrossPos child.x = self.x + self.padding.left + reservedCrossStart + currentCrossPos
end end
-- Apply positioning offsets (top, right, bottom, left) -- Apply positioning offsets (top, right, bottom, left)

View File

@@ -6,7 +6,7 @@ FlexLöve is a lightweight, flexible GUI library for Löve2D that implements a f
## ⚠️ Development Status ## ⚠️ Development Status
This library is under active development. While many features are functional, some aspects may change or have incomplete implementations. This library is under active development. While many features are functional, some aspects may change or have incomplete/broken implementations.
## Features ## Features

View File

@@ -0,0 +1,437 @@
-- Test: Sibling Space Reservation in Flex and Grid Layouts
-- Purpose: Verify that absolutely positioned siblings with explicit positioning
-- properly reserve space in flex and grid containers
local lu = require("testing.luaunit")
local FlexLove = require("libs.FlexLove")
local Gui = FlexLove.GUI
local Color = FlexLove.Color
-- Mock love.graphics and love.window
_G.love = require("testing.loveStub")
TestSiblingSpaceReservation = {}
function TestSiblingSpaceReservation:setUp()
-- Reset GUI state before each test
Gui.destroy()
-- Set up a standard viewport
love.window.setMode(1920, 1080)
end
function TestSiblingSpaceReservation:tearDown()
Gui.destroy()
end
-- ====================
-- Flex Layout Tests
-- ====================
function TestSiblingSpaceReservation:test_flex_horizontal_left_positioned_sibling_reserves_space()
-- Create a flex container
local container = Gui.new({
x = 0,
y = 0,
width = 1000,
height = 200,
positioning = "flex",
flexDirection = "horizontal",
justifyContent = "flex-start",
padding = { top = 0, right = 0, bottom = 0, left = 0 },
})
-- Add an absolutely positioned sibling with left positioning
local absoluteSibling = Gui.new({
parent = container,
positioning = "absolute",
left = 10, -- 10px from left edge
width = 50,
height = 50,
backgroundColor = Color.new(1, 0, 0, 1),
})
-- Add a flex child that should start after the absolutely positioned sibling
local flexChild = Gui.new({
parent = container,
width = 100,
height = 50,
backgroundColor = Color.new(0, 1, 0, 1),
})
-- Layout children
container:layoutChildren()
-- The absolutely positioned sibling reserves: left (10) + width (50) + padding (0) = 60px
-- The flex child should start at x = container.x + padding.left + reservedLeft
-- = 0 + 0 + 60 = 60
lu.assertEquals(flexChild.x, 60, "Flex child should start after absolutely positioned sibling")
end
function TestSiblingSpaceReservation:test_flex_horizontal_right_positioned_sibling_reserves_space()
-- Create a flex container
local container = Gui.new({
x = 0,
y = 0,
width = 1000,
height = 200,
positioning = "flex",
flexDirection = "horizontal",
justifyContent = "flex-start",
padding = { top = 0, right = 0, bottom = 0, left = 0 },
})
-- Add an absolutely positioned sibling with right positioning
local absoluteSibling = Gui.new({
parent = container,
positioning = "absolute",
right = 10, -- 10px from right edge
width = 50,
height = 50,
backgroundColor = Color.new(1, 0, 0, 1),
})
-- Add a flex child
local flexChild = Gui.new({
parent = container,
width = 100,
height = 50,
backgroundColor = Color.new(0, 1, 0, 1),
})
-- Layout children
container:layoutChildren()
-- The absolutely positioned sibling reserves: right (10) + width (50) + padding (0) = 60px
-- Available space = 1000 - 0 (padding) - 0 (reservedLeft) - 60 (reservedRight) = 940px
-- The flex child (width 100) should fit within this space
-- Child should start at x = 0
lu.assertEquals(flexChild.x, 0, "Flex child should start at container left edge")
-- The absolutely positioned sibling should be at the right edge
-- x = container.x + container.width + padding.left - right - (width + padding)
-- = 0 + 1000 + 0 - 10 - 50 = 940
lu.assertEquals(absoluteSibling.x, 940, "Absolutely positioned sibling should be at right edge")
end
function TestSiblingSpaceReservation:test_flex_vertical_top_positioned_sibling_reserves_space()
-- Create a vertical flex container
local container = Gui.new({
x = 0,
y = 0,
width = 200,
height = 1000,
positioning = "flex",
flexDirection = "vertical",
justifyContent = "flex-start",
padding = { top = 0, right = 0, bottom = 0, left = 0 },
})
-- Add an absolutely positioned sibling with top positioning
local absoluteSibling = Gui.new({
parent = container,
positioning = "absolute",
top = 10, -- 10px from top edge
width = 50,
height = 50,
backgroundColor = Color.new(1, 0, 0, 1),
})
-- Add a flex child that should start after the absolutely positioned sibling
local flexChild = Gui.new({
parent = container,
width = 50,
height = 100,
backgroundColor = Color.new(0, 1, 0, 1),
})
-- Layout children
container:layoutChildren()
-- The absolutely positioned sibling reserves: top (10) + height (50) + padding (0) = 60px
-- The flex child should start at y = container.y + padding.top + reservedTop
-- = 0 + 0 + 60 = 60
lu.assertEquals(flexChild.y, 60, "Flex child should start after absolutely positioned sibling")
end
function TestSiblingSpaceReservation:test_flex_horizontal_multiple_positioned_siblings()
-- Create a flex container
local container = Gui.new({
x = 0,
y = 0,
width = 1000,
height = 200,
positioning = "flex",
flexDirection = "horizontal",
justifyContent = "flex-start",
padding = { top = 0, right = 0, bottom = 0, left = 0 },
})
-- Add two absolutely positioned siblings (left and right)
local leftSibling = Gui.new({
parent = container,
positioning = "absolute",
left = 5,
width = 40,
height = 50,
backgroundColor = Color.new(1, 0, 0, 1),
})
local rightSibling = Gui.new({
parent = container,
positioning = "absolute",
right = 5,
width = 40,
height = 50,
backgroundColor = Color.new(0, 0, 1, 1),
})
-- Add flex children
local flexChild1 = Gui.new({
parent = container,
width = 100,
height = 50,
backgroundColor = Color.new(0, 1, 0, 1),
})
local flexChild2 = Gui.new({
parent = container,
width = 100,
height = 50,
backgroundColor = Color.new(0, 1, 1, 1),
})
-- Layout children
container:layoutChildren()
-- Reserved left: 5 + 40 = 45px
-- Reserved right: 5 + 40 = 45px
-- Available space: 1000 - 45 - 45 = 910px
-- First flex child should start at x = 0 + 0 + 45 = 45
lu.assertEquals(flexChild1.x, 45, "First flex child should start after left sibling")
-- Second flex child should start at x = 45 + 100 + gap = 145 (assuming gap=10)
lu.assertIsTrue(flexChild2.x >= 145, "Second flex child should be positioned after first")
end
-- ====================
-- Grid Layout Tests
-- ====================
function TestSiblingSpaceReservation:test_grid_left_positioned_sibling_reserves_space()
-- Create a grid container
local container = Gui.new({
x = 0,
y = 0,
width = 1000,
height = 500,
positioning = "grid",
gridRows = 2,
gridColumns = 3,
columnGap = 10,
rowGap = 10,
padding = { top = 0, right = 0, bottom = 0, left = 0 },
})
-- Add an absolutely positioned sibling with left positioning
local absoluteSibling = Gui.new({
parent = container,
positioning = "absolute",
left = 10,
width = 50,
height = 50,
backgroundColor = Color.new(1, 0, 0, 1),
})
-- Add grid children
local gridChild1 = Gui.new({
parent = container,
backgroundColor = Color.new(0, 1, 0, 1),
})
-- Layout children
container:layoutChildren()
-- Reserved left: 10 + 50 = 60px
-- Available width: 1000 - 60 = 940px
-- Column gaps: 2 * 10 = 20px
-- Cell width: (940 - 20) / 3 = 306.67px
-- First grid child should start at x = 0 + 0 + 60 = 60
lu.assertEquals(gridChild1.x, 60, "Grid child should start after absolutely positioned sibling")
end
function TestSiblingSpaceReservation:test_grid_top_positioned_sibling_reserves_space()
-- Create a grid container
local container = Gui.new({
x = 0,
y = 0,
width = 1000,
height = 500,
positioning = "grid",
gridRows = 2,
gridColumns = 3,
columnGap = 10,
rowGap = 10,
padding = { top = 0, right = 0, bottom = 0, left = 0 },
})
-- Add an absolutely positioned sibling with top positioning
local absoluteSibling = Gui.new({
parent = container,
positioning = "absolute",
top = 10,
width = 50,
height = 50,
backgroundColor = Color.new(1, 0, 0, 1),
})
-- Add grid children
local gridChild1 = Gui.new({
parent = container,
backgroundColor = Color.new(0, 1, 0, 1),
})
-- Layout children
container:layoutChildren()
-- Reserved top: 10 + 50 = 60px
-- Available height: 500 - 60 = 440px
-- Row gaps: 1 * 10 = 10px
-- Cell height: (440 - 10) / 2 = 215px
-- First grid child should start at y = 0 + 0 + 60 = 60
lu.assertEquals(gridChild1.y, 60, "Grid child should start after absolutely positioned sibling")
end
function TestSiblingSpaceReservation:test_grid_multiple_positioned_siblings()
-- Create a grid container
local container = Gui.new({
x = 0,
y = 0,
width = 1000,
height = 500,
positioning = "grid",
gridRows = 2,
gridColumns = 2,
columnGap = 0,
rowGap = 0,
padding = { top = 0, right = 0, bottom = 0, left = 0 },
})
-- Add absolutely positioned siblings at all corners
local topLeftSibling = Gui.new({
parent = container,
positioning = "absolute",
left = 10,
top = 10,
width = 40,
height = 40,
backgroundColor = Color.new(1, 0, 0, 1),
})
local bottomRightSibling = Gui.new({
parent = container,
positioning = "absolute",
right = 10,
bottom = 10,
width = 40,
height = 40,
backgroundColor = Color.new(0, 0, 1, 1),
})
-- Add grid children
local gridChild1 = Gui.new({
parent = container,
backgroundColor = Color.new(0, 1, 0, 1),
})
-- Layout children
container:layoutChildren()
-- Reserved left: 10 + 40 = 50px
-- Reserved right: 10 + 40 = 50px
-- Reserved top: 10 + 40 = 50px
-- Reserved bottom: 10 + 40 = 50px
-- Available width: 1000 - 50 - 50 = 900px
-- Available height: 500 - 50 - 50 = 400px
-- Cell width: 900 / 2 = 450px
-- Cell height: 400 / 2 = 200px
-- First grid child should start at (50, 50)
lu.assertEquals(gridChild1.x, 50, "Grid child X should account for left sibling")
lu.assertEquals(gridChild1.y, 50, "Grid child Y should account for top sibling")
lu.assertEquals(gridChild1.width, 450, "Grid cell width should account for reserved space")
lu.assertEquals(gridChild1.height, 200, "Grid cell height should account for reserved space")
end
-- ====================
-- Edge Cases
-- ====================
function TestSiblingSpaceReservation:test_non_explicitly_absolute_children_dont_reserve_space()
-- Children that default to absolute positioning (not explicitly set)
-- should NOT reserve space in flex layouts
local container = Gui.new({
x = 0,
y = 0,
width = 1000,
height = 200,
positioning = "flex",
flexDirection = "horizontal",
padding = { top = 0, right = 0, bottom = 0, left = 0 },
})
-- This child has positioning="flex" so it participates in layout
local flexChild = Gui.new({
parent = container,
positioning = "flex",
left = 10, -- This should be ignored since it's a flex child
width = 100,
height = 50,
backgroundColor = Color.new(0, 1, 0, 1),
})
-- Layout children
container:layoutChildren()
-- Flex child should start at x = 0 (no reserved space)
lu.assertEquals(flexChild.x, 0, "Flex children with positioning offsets should not reserve space")
end
function TestSiblingSpaceReservation:test_absolute_without_positioning_offsets_doesnt_reserve_space()
-- Absolutely positioned children without left/right/top/bottom
-- should NOT reserve space
local container = Gui.new({
x = 0,
y = 0,
width = 1000,
height = 200,
positioning = "flex",
flexDirection = "horizontal",
padding = { top = 0, right = 0, bottom = 0, left = 0 },
})
-- Absolutely positioned but no positioning offsets
local absoluteChild = Gui.new({
parent = container,
positioning = "absolute",
x = 50,
y = 50,
width = 50,
height = 50,
backgroundColor = Color.new(1, 0, 0, 1),
})
-- Flex child
local flexChild = Gui.new({
parent = container,
width = 100,
height = 50,
backgroundColor = Color.new(0, 1, 0, 1),
})
-- Layout children
container:layoutChildren()
-- Flex child should start at x = 0 (no reserved space)
lu.assertEquals(flexChild.x, 0, "Absolute children without positioning offsets should not reserve space")
end
return TestSiblingSpaceReservation

View File

@@ -20,6 +20,7 @@ local testFiles = {
"testing/__tests__/14_text_scaling_basic_tests.lua", "testing/__tests__/14_text_scaling_basic_tests.lua",
"testing/__tests__/15_grid_layout_tests.lua", "testing/__tests__/15_grid_layout_tests.lua",
"testing/__tests__/16_event_system_tests.lua", "testing/__tests__/16_event_system_tests.lua",
"testing/__tests__/17_sibling_space_reservation_tests.lua",
} }
-- testingun all tests, but don't exit on error -- testingun all tests, but don't exit on error