initial commit
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
local test_helper = require('test.test_helper')
|
||||
local mocks = test_helper.mocks
|
||||
local utils = test_helper.utils
|
||||
|
||||
describe('Edge Cases', function()
|
||||
local core, cat, house, ui, voxel, json
|
||||
|
||||
setup(function()
|
||||
mocks.setupFilesystem()
|
||||
core = require('src.core')
|
||||
cat = require('src.cat.init')
|
||||
house = require('src.house.init')
|
||||
ui = require('src.ui.init')
|
||||
voxel = require('src.voxel.init')
|
||||
json = require('src.json')
|
||||
end)
|
||||
|
||||
it('should handle zero cats', function()
|
||||
core.state.cats = {}
|
||||
core.calculateHouseHappiness()
|
||||
assert.are.equal(0, core.state.house.totalHappiness)
|
||||
end)
|
||||
|
||||
it('should handle zero distance between cats', function()
|
||||
local c1 = utils.createMockCat({ position = { x = 0, y = 0, z = 0 } })
|
||||
local c2 = utils.createMockCat({ position = { x = 0, y = 0, z = 0 } })
|
||||
|
||||
c1:updateNeeds()
|
||||
c2:updateNeeds()
|
||||
end)
|
||||
|
||||
it('should handle nil personality', function()
|
||||
local c = cat.createCat(1, 'Test', nil)
|
||||
assert.are.equal('social', c.personality.id)
|
||||
end)
|
||||
|
||||
it('should handle invalid JSON', function()
|
||||
local decoded, err = json.decode('{ invalid json }')
|
||||
assert.are_equal(nil, decoded)
|
||||
assert.are_not_equal(nil, err)
|
||||
end)
|
||||
|
||||
it('should handle max capacity', function()
|
||||
core.state.cats = {}
|
||||
for i = 1, 8 do
|
||||
core.spawnCat()
|
||||
end
|
||||
assert.are.equal(8, #core.state.cats)
|
||||
|
||||
-- Should not exceed max
|
||||
local initialCount = #core.state.cats
|
||||
core.spawnCat()
|
||||
assert.are.equal(initialCount, #core.state.cats)
|
||||
end)
|
||||
|
||||
it('should handle furniture at grid bounds', function()
|
||||
local canPlace, reason = house.canPlaceFurniture({ x = 2.25, y = 0, z = 2.25 }, { 0.5, 1.5, 0.5 })
|
||||
assert.is_true(canPlace)
|
||||
end)
|
||||
|
||||
it('should handle low happiness departure', function()
|
||||
local catObj = utils.createMockCat({
|
||||
needs = { comfort = 0.05, hunger = 0.5 }
|
||||
})
|
||||
table.insert(core.state.cats, catObj)
|
||||
|
||||
local initialCount = #core.state.cats
|
||||
core.checkCatDepartures()
|
||||
|
||||
assert.is_true(#core.state.cats < initialCount)
|
||||
end)
|
||||
|
||||
it('should handle furniture upgrades', function()
|
||||
local furn = utils.createMockFurniture()
|
||||
furn:upgrade()
|
||||
assert.are.equal(2, furn.upgradeLevel)
|
||||
|
||||
furn:upgrade()
|
||||
assert.are.equal(3, furn.upgradeLevel)
|
||||
|
||||
local result = furn:upgrade()
|
||||
assert.is_false(result)
|
||||
end)
|
||||
|
||||
it('should handle personality compatibility', function()
|
||||
local c1 = utils.createMockCat({
|
||||
personality = { id = 'lone_wolf', compatibility = { ['social'] = -0.5 } }
|
||||
})
|
||||
local c2 = utils.createMockCat({
|
||||
personality = { id = 'social' },
|
||||
position = { x = 0.5, y = 0, z = 0 }
|
||||
})
|
||||
|
||||
local oldSocial = c1.needs.social
|
||||
c1:updateNeeds()
|
||||
assert.is_true(c1.needs.social < oldSocial)
|
||||
end)
|
||||
|
||||
it('should handle audio generation', function()
|
||||
local purr = audio.createPurrSound()
|
||||
assert.are_not_equal(nil, purr)
|
||||
|
||||
local meow = audio.createMeowSound()
|
||||
assert.are_not_equal(nil, meow)
|
||||
|
||||
local click = audio.createClickSound()
|
||||
assert.are_not_equal(nil, click)
|
||||
|
||||
local ambient = audio.createAmbientSound()
|
||||
assert.are_not_equal(nil, ambient)
|
||||
end)
|
||||
|
||||
it('should handle serialization edge cases', function()
|
||||
local c = utils.createMockCat()
|
||||
c.personality = nil
|
||||
local serialized = c:serialize()
|
||||
assert.are_equal('social', serialized.personalityId)
|
||||
end)
|
||||
|
||||
it('should handle rapid updates', function()
|
||||
core.state.cats = {}
|
||||
for i = 1, 8 do
|
||||
table.insert(core.state.cats, utils.createMockCat())
|
||||
end
|
||||
|
||||
for i = 1, 100 do
|
||||
core.update(0.016)
|
||||
end
|
||||
|
||||
assert.is_true(#core.state.cats > 0)
|
||||
end)
|
||||
|
||||
it('should handle 25+ furniture items', function()
|
||||
core.state.furniture = {}
|
||||
for i = 1, 25 do
|
||||
local furn = utils.createMockFurniture({
|
||||
position = { x = i * 0.5, y = 0, z = 0 }
|
||||
})
|
||||
table.insert(core.state.furniture, furn)
|
||||
end
|
||||
|
||||
assert.are.equal(25, #core.state.furniture)
|
||||
end)
|
||||
|
||||
it('should handle multiple save/load cycles', function()
|
||||
for i = 1, 5 do
|
||||
core.state.cats = {}
|
||||
core.state.furniture = {}
|
||||
|
||||
core.spawnCat()
|
||||
core.addFurniture('bed', { x = 0, y = 0, z = 0 })
|
||||
|
||||
core.saveGame()
|
||||
core.loadGame()
|
||||
end
|
||||
end)
|
||||
|
||||
it('should handle nil core in all modules', function()
|
||||
_G.core = nil
|
||||
|
||||
cat.updateCats(0.1)
|
||||
house.checkCollision({ 0, 0, 0 }, { 1, 1, 1 })
|
||||
house.canPlaceFurniture({ 0, 0, 0 }, { 1, 1, 1 })
|
||||
house.placeFurniture('bed', { 0, 0, 0 })
|
||||
house.interactWithFurniture({ 0, 0, 0 })
|
||||
|
||||
ui.renderHUD(test_helper.utils.createMockPass())
|
||||
ui.renderInventory(test_helper.utils.createMockPass(), 1280, 720, 0.72)
|
||||
ui.renderCatLog(test_helper.utils.createMockPass(), 1280, 0.72)
|
||||
ui.handleMousePress(100, 100, 1)
|
||||
|
||||
voxel.renderWorld(test_helper.utils.createMockPass())
|
||||
voxel.renderFurniture(test_helper.utils.createMockPass())
|
||||
voxel.renderCats(test_helper.utils.createMockPass())
|
||||
voxel.raycast({ 0, 0, 0 }, { 0, 0, -1 }, 10)
|
||||
|
||||
audio.playPurr({ 0, 0, 0 })
|
||||
audio.playMeow({ 0, 0, 0 })
|
||||
audio.playClick()
|
||||
end)
|
||||
|
||||
it('should handle nil house module', function()
|
||||
_G.house = nil
|
||||
core.addFurniture('bed', { 0, 0, 0 })
|
||||
end)
|
||||
|
||||
it('should handle nil ui module', function()
|
||||
_G.ui = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil voxel module', function()
|
||||
_G.voxel = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil audio module', function()
|
||||
_G.audio = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil cat module', function()
|
||||
_G.cat = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle zero distance in movement', function()
|
||||
local c = utils.createMockCat({
|
||||
position = { x = 0, y = 0, z = 0 },
|
||||
targetPosition = { x = 0, y = 0, z = 0 }
|
||||
})
|
||||
c.state = 'moving'
|
||||
c:update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil furniture in raycast', function()
|
||||
core.state.furniture = nil
|
||||
local results = voxel.raycast({ 0, 0, 0 }, { 0, 0, -1 }, 10)
|
||||
assert.are.equal('table', type(results))
|
||||
end)
|
||||
|
||||
it('should handle nil cats in raycast', function()
|
||||
core.state.cats = nil
|
||||
local results = voxel.raycast({ 0, 0, 0 }, { 0, 0, -1 }, 10)
|
||||
assert.are.equal('table', type(results))
|
||||
end)
|
||||
|
||||
it('should handle nil core in JSON encoding', function()
|
||||
local encoded = json.encode({ core = core })
|
||||
assert.are_not_equal(nil, encoded:find('version'))
|
||||
end)
|
||||
|
||||
it('should handle nil core in JSON decoding', function()
|
||||
local decoded = json.decode(encoded)
|
||||
assert.are_not_equal(nil, decoded)
|
||||
end)
|
||||
|
||||
it('should handle empty core state', function()
|
||||
core.state = {}
|
||||
core.calculateHouseHappiness()
|
||||
assert.are.equal(0, core.state.house.totalHappiness)
|
||||
end)
|
||||
|
||||
it('should handle nil core.state', function()
|
||||
core.state = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil core.CONFIG', function()
|
||||
core.CONFIG = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil core.personalities', function()
|
||||
core.personalities = nil
|
||||
local c = cat.createCat(1, 'Test', nil)
|
||||
assert.are_equal('social', c.personality.id)
|
||||
end)
|
||||
|
||||
it('should handle nil core.furnitureTypes', function()
|
||||
core.furnitureTypes = nil
|
||||
local furn = house.placeFurniture('bed', { 0, 0, 0 })
|
||||
assert.are_equal(nil, furn)
|
||||
end)
|
||||
end)
|
||||
@@ -0,0 +1,189 @@
|
||||
local test_helper = require('test.test_helper')
|
||||
local mocks = test_helper.mocks
|
||||
|
||||
describe('Integration: Game Flow', function()
|
||||
local core, cat, house, ui, voxel, audio, json
|
||||
|
||||
setup(function()
|
||||
mocks.setupFilesystem()
|
||||
core = require('src.core')
|
||||
cat = require('src.cat.init')
|
||||
house = require('src.house.init')
|
||||
ui = require('src.ui.init')
|
||||
voxel = require('src.voxel.init')
|
||||
audio = require('src.audio.init')
|
||||
json = require('src.json')
|
||||
end)
|
||||
|
||||
it('should initialize full game', function()
|
||||
core.init()
|
||||
assert.are.equal(2, #core.state.cats)
|
||||
assert.are.equal(0, #core.state.furniture)
|
||||
end)
|
||||
|
||||
it('should add furniture and render', function()
|
||||
core.init()
|
||||
local furn = core.addFurniture('bed', { x = 0, y = 0, z = 0 })
|
||||
assert.are_not.equal(nil, furn)
|
||||
|
||||
local pass = test_helper.utils.createMockPass()
|
||||
voxel.renderFurniture(pass)
|
||||
end)
|
||||
|
||||
it('should update cats and render', function()
|
||||
core.init()
|
||||
cat.updateCats(0.1)
|
||||
|
||||
local pass = test_helper.utils.createMockPass()
|
||||
voxel.renderCats(pass)
|
||||
end)
|
||||
|
||||
it('should render complete UI', function()
|
||||
core.init()
|
||||
local pass = test_helper.utils.createMockPass()
|
||||
ui.renderHUD(pass)
|
||||
end)
|
||||
|
||||
it('should save and load game state', function()
|
||||
core.init()
|
||||
|
||||
-- Add some furniture
|
||||
core.addFurniture('bed', { x = 0, y = 0, z = 0 })
|
||||
core.addFurniture('cat_tree', { x = 1, y = 0, z = 1 })
|
||||
|
||||
-- Spawn more cats
|
||||
core.spawnCat()
|
||||
core.spawnCat()
|
||||
|
||||
-- Save
|
||||
local saveResult = core.saveGame()
|
||||
assert.is_true(saveResult)
|
||||
|
||||
-- Load
|
||||
local loadResult = core.loadGame()
|
||||
assert.is_true(loadResult)
|
||||
|
||||
-- Verify state
|
||||
assert.are.equal(4, #core.state.cats)
|
||||
assert.are.equal(2, #core.state.furniture)
|
||||
end)
|
||||
|
||||
it('should handle cat interaction with furniture', function()
|
||||
core.init()
|
||||
|
||||
local furn = core.addFurniture('bed', { x = 0, y = 0, z = 0 })
|
||||
local catObj = core.state.cats[1]
|
||||
|
||||
-- Move cat near furniture
|
||||
catObj.position = { x = 0.1, y = 0, z = 0.1 }
|
||||
|
||||
-- AI should seek furniture
|
||||
catObj:aiBehavior()
|
||||
|
||||
-- Should have target position
|
||||
assert.are_not.equal(nil, catObj.targetPosition)
|
||||
end)
|
||||
|
||||
it('should handle cat personality compatibility', function()
|
||||
core.init()
|
||||
|
||||
local c1 = core.state.cats[1]
|
||||
local c2 = utils.createMockCat({
|
||||
personality = { id = 'social', compatibility = { ['social'] = 0.5 } }
|
||||
})
|
||||
c2.position = { x = 0.5, y = 0, z = 0 }
|
||||
|
||||
table.insert(core.state.cats, c2)
|
||||
|
||||
c1:updateNeeds()
|
||||
|
||||
assert.is_true(c1.needs.social >= 0.7)
|
||||
end)
|
||||
|
||||
it('should handle furniture upgrades', function()
|
||||
core.init()
|
||||
|
||||
local furn = core.addFurniture('bed', { x = 0, y = 0, z = 0 })
|
||||
local originalContribution = furn.happinessContribution
|
||||
|
||||
furn:upgrade()
|
||||
assert.are.equal(2, furn.upgradeLevel)
|
||||
assert.is_true(furn.happinessContribution > originalContribution)
|
||||
|
||||
furn:upgrade()
|
||||
assert.are.equal(3, furn.upgradeLevel)
|
||||
|
||||
local result = furn:upgrade()
|
||||
assert.is_false(result)
|
||||
end)
|
||||
|
||||
it('should render audio on pet', function()
|
||||
local catObj = utils.createMockCat()
|
||||
local result = catObj:pet()
|
||||
assert.is_true(result)
|
||||
end)
|
||||
|
||||
it('should handle UI interaction', function()
|
||||
core.init()
|
||||
ui.showInventory = true
|
||||
|
||||
-- Simulate clicking inventory item
|
||||
local scale = 0.72
|
||||
local startX = 640 - 300 * scale + 20 * scale
|
||||
local startY = 360 - 200 * scale + 80 * scale
|
||||
local itemY = startY + (1 - 1) * 50 * scale
|
||||
|
||||
ui.handleMousePress(startX + 10, itemY + 10, 1)
|
||||
|
||||
assert.are_not.equal(nil, ui.selectedFurniture)
|
||||
end)
|
||||
|
||||
it('should handle empty cat list', function()
|
||||
core.state.cats = {}
|
||||
core.calculateHouseHappiness()
|
||||
assert.are.equal(0, core.state.house.totalHappiness)
|
||||
end)
|
||||
|
||||
it('should handle empty furniture list', function()
|
||||
core.state.furniture = {}
|
||||
local pass = test_helper.utils.createMockPass()
|
||||
voxel.renderFurniture(pass)
|
||||
end)
|
||||
|
||||
it('should handle nil core in modules', function()
|
||||
_G.core = nil
|
||||
cat.updateCats(0.1)
|
||||
house.checkCollision({ 0, 0, 0 }, { 1, 1, 1 })
|
||||
ui.renderHUD(test_helper.utils.createMockPass())
|
||||
end)
|
||||
|
||||
it('should handle nil core in house functions', function()
|
||||
_G.core = nil
|
||||
local furn = house.createFurniture(nil, { x = 0, y = 0, z = 0 })
|
||||
assert.are.equal(nil, furn)
|
||||
|
||||
local canPlace, reason = house.canPlaceFurniture({ 0, 0, 0 }, { 1, 1, 1 })
|
||||
assert.is_false(canPlace)
|
||||
end)
|
||||
|
||||
it('should handle nil core in ui functions', function()
|
||||
_G.core = nil
|
||||
ui.renderHUD(test_helper.utils.createMockPass())
|
||||
ui.renderInventory(test_helper.utils.createMockPass(), 1280, 720, 0.72)
|
||||
ui.renderCatLog(test_helper.utils.createMockPass(), 1280, 0.72)
|
||||
end)
|
||||
|
||||
it('should handle nil core in voxel functions', function()
|
||||
_G.core = nil
|
||||
voxel.renderWorld(test_helper.utils.createMockPass())
|
||||
voxel.renderFurniture(test_helper.utils.createMockPass())
|
||||
voxel.renderCats(test_helper.utils.createMockPass())
|
||||
end)
|
||||
|
||||
it('should handle nil core in audio functions', function()
|
||||
_G.core = nil
|
||||
audio.playPurr({ 0, 0, 0 })
|
||||
audio.playMeow({ 0, 0, 0 })
|
||||
audio.playClick()
|
||||
end)
|
||||
end)
|
||||
@@ -0,0 +1,103 @@
|
||||
-- Cat Haven - Test Runner
|
||||
-- Runs all tests and generates reports
|
||||
|
||||
local busted = require('busted')
|
||||
local lfs = require('lfs')
|
||||
|
||||
local testDirs = {
|
||||
'test/spec',
|
||||
'test/integration',
|
||||
'test/e2e'
|
||||
}
|
||||
|
||||
local testFiles = {}
|
||||
|
||||
-- Find all test files
|
||||
for _, dir in ipairs(testDirs) do
|
||||
if lfs.attributes(dir, 'mode') == 'directory' then
|
||||
for file in lfs.dir(dir) do
|
||||
if file:match('.*_spec%.lua$') then
|
||||
table.insert(testFiles, dir .. '/' .. file)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Run tests
|
||||
local success, results = busted.run(testFiles, {
|
||||
reporter = 'tap',
|
||||
color = false
|
||||
})
|
||||
|
||||
-- Generate JUnit XML report
|
||||
local function generateJUnitXML(results)
|
||||
local xml = '<?xml version="1.0" encoding="UTF-8"?>\n'
|
||||
xml = xml .. '<testsuites name="Cat Haven Tests">\n'
|
||||
|
||||
local totalTests = 0
|
||||
local totalFailures = 0
|
||||
local totalErrors = 0
|
||||
|
||||
for _, suite in ipairs(results.suites) do
|
||||
totalTests = totalTests + suite.tests
|
||||
totalFailures = totalFailures + suite.failures
|
||||
totalErrors = totalErrors + suite.errors
|
||||
end
|
||||
|
||||
xml = xml .. ' <testsuite name="Cat Haven" tests="' .. totalTests .. '" failures="' .. totalFailures .. '" errors="' .. totalErrors .. '">\n'
|
||||
|
||||
for _, suite in ipairs(results.suites) do
|
||||
xml = xml .. ' <testcase name="' .. suite.name .. '" classname="' .. suite.file .. '">\n'
|
||||
|
||||
if suite.errors > 0 then
|
||||
xml = xml .. ' <error message="Test suite error" type="error">\n'
|
||||
xml = xml .. ' ' .. suite.error .. '\n'
|
||||
xml = xml .. ' </error>\n'
|
||||
end
|
||||
|
||||
if suite.failures > 0 then
|
||||
for _, test in ipairs(suite.tests) do
|
||||
if test.status == 'failed' then
|
||||
xml = xml .. ' <failure message="' .. test.message .. '" type="failure">\n'
|
||||
xml = xml .. ' ' .. test.trace .. '\n'
|
||||
xml = xml .. ' </failure>\n'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xml = xml .. ' </testcase>\n'
|
||||
end
|
||||
|
||||
xml = xml .. ' </testsuite>\n'
|
||||
xml = xml .. '</testsuites>\n'
|
||||
|
||||
return xml
|
||||
end
|
||||
|
||||
-- Write XML report
|
||||
local xmlReport = generateJUnitXML(results)
|
||||
local file = io.open('test-report.xml', 'w')
|
||||
if file then
|
||||
file:write(xmlReport)
|
||||
file:close()
|
||||
print('\nXML report written to test-report.xml')
|
||||
end
|
||||
|
||||
-- Output summary
|
||||
print('\n========================================')
|
||||
print('Test Summary')
|
||||
print('========================================')
|
||||
print('Suites:', results.suites)
|
||||
print('Tests:', results.tests)
|
||||
print('Passed:', results.passed)
|
||||
print('Failed:', results.failures)
|
||||
print('Errors:', results.errors)
|
||||
print('Skipped:', results.skipped)
|
||||
|
||||
if results.failures > 0 or results.errors > 0 then
|
||||
print('\n❌ Tests failed')
|
||||
return 1
|
||||
else
|
||||
print('\n✅ All tests passed')
|
||||
return 0
|
||||
end
|
||||
@@ -0,0 +1,80 @@
|
||||
local test_helper = require('test.test_helper')
|
||||
local mocks = test_helper.mocks
|
||||
local utils = test_helper.utils
|
||||
|
||||
describe('Audio Module', function()
|
||||
local audio
|
||||
|
||||
before_each(function()
|
||||
audio = require('src.audio.init')
|
||||
end)
|
||||
|
||||
it('should create purr sound', function()
|
||||
local sound = audio.createPurrSound()
|
||||
assert.are_equal('source', sound.type)
|
||||
assert.is_true(sound.spatial)
|
||||
assert.are_equal(5.0, sound.maxDistance)
|
||||
end)
|
||||
|
||||
it('should create meow sound', function()
|
||||
local sound = audio.createMeowSound()
|
||||
assert.are_equal('source', sound.type)
|
||||
assert.is_true(sound.spatial)
|
||||
assert.are_equal(3.0, sound.maxDistance)
|
||||
end)
|
||||
|
||||
it('should create click sound', function()
|
||||
local sound = audio.createClickSound()
|
||||
assert.are_equal('source', sound.type)
|
||||
assert.is_true(sound.spatial)
|
||||
assert.are_equal(2.0, sound.maxDistance)
|
||||
end)
|
||||
|
||||
it('should create ambient sound', function()
|
||||
local sound = audio.createAmbientSound()
|
||||
assert.are_equal('source', sound.type)
|
||||
assert.is_false(sound.spatial)
|
||||
assert.is_true(sound.looping)
|
||||
end)
|
||||
|
||||
it('should play purr at position', function()
|
||||
audio.playPurr({ 1, 2, 3 })
|
||||
end)
|
||||
|
||||
it('should play meow at position', function()
|
||||
audio.playMeow({ 1, 2, 3 })
|
||||
end)
|
||||
|
||||
it('should play click sound', function()
|
||||
audio.playClick()
|
||||
end)
|
||||
|
||||
it('should handle nil position in playPurr', function()
|
||||
audio.playPurr(nil)
|
||||
end)
|
||||
|
||||
it('should handle nil position in playMeow', function()
|
||||
audio.playMeow(nil)
|
||||
end)
|
||||
|
||||
it('should handle nil sounds', function()
|
||||
audio.sounds = {}
|
||||
audio.playPurr({ 1, 2, 3 })
|
||||
audio.playMeow({ 1, 2, 3 })
|
||||
end)
|
||||
|
||||
it('should handle nil sounds.purr', function()
|
||||
audio.sounds.purr = nil
|
||||
audio.playPurr({ 1, 2, 3 })
|
||||
end)
|
||||
|
||||
it('should handle nil sounds.meow', function()
|
||||
audio.sounds.meow = nil
|
||||
audio.playMeow({ 1, 2, 3 })
|
||||
end)
|
||||
|
||||
it('should handle nil sounds.click', function()
|
||||
audio.sounds.click = nil
|
||||
audio.playClick()
|
||||
end)
|
||||
end)
|
||||
@@ -0,0 +1,155 @@
|
||||
local test_helper = require('test.test_helper')
|
||||
local mocks = test_helper.mocks
|
||||
local utils = test_helper.utils
|
||||
|
||||
describe('Cat Module', function()
|
||||
local cat
|
||||
|
||||
setup(function()
|
||||
cat = require('src.cat.init')
|
||||
end)
|
||||
|
||||
it('should create cat with default personality', function()
|
||||
local c = cat.createCat(1, 'Test Cat', nil)
|
||||
assert.are.equal(1, c.id)
|
||||
assert.are.equal('Test Cat', c.name)
|
||||
assert.are.equal('social', c.personality.id)
|
||||
end)
|
||||
|
||||
it('should create cat with custom personality', function()
|
||||
local personality = { id = 'lone_wolf', color = { 0.5, 0.5, 0.6 } }
|
||||
local c = cat.createCat(2, 'Lone Cat', personality)
|
||||
assert.are.equal('lone_wolf', c.personality.id)
|
||||
assert.are.equal(0.5, c.personality.color[1])
|
||||
end)
|
||||
|
||||
it('should update cat needs over time', function()
|
||||
local c = cat.createCat(1, 'Test Cat', nil)
|
||||
local oldComfort = c.needs.comfort
|
||||
c:update(1.0)
|
||||
assert.is_true(c.needs.comfort < oldComfort)
|
||||
end)
|
||||
|
||||
it('should transition to sleep state', function()
|
||||
local c = cat.createCat(1, 'Test Cat', nil)
|
||||
c.needs.comfort = 0.1
|
||||
c:update(1.0)
|
||||
assert.is_true(c.isSleeping)
|
||||
assert.are.equal('sleep', c.state)
|
||||
end)
|
||||
|
||||
it('should wake up after sleep timer', function()
|
||||
local c = cat.createCat(1, 'Test Cat', nil)
|
||||
c.needs.comfort = 0.1
|
||||
c.isSleeping = true
|
||||
c.sleepTimer = 0.5
|
||||
c:update(1.0)
|
||||
assert.is_false(c.isSleeping)
|
||||
assert.are.equal('idle', c.state)
|
||||
end)
|
||||
|
||||
it('should move toward target position', function()
|
||||
local c = cat.createCat(1, 'Test Cat', nil)
|
||||
c.targetPosition = { x = 1, y = 0, z = 1 }
|
||||
local oldX = c.position.x
|
||||
local oldZ = c.position.z
|
||||
c.state = 'moving'
|
||||
c:update(0.1)
|
||||
assert.is_true(c.position.x ~= oldX or c.position.z ~= oldZ)
|
||||
end)
|
||||
|
||||
it('should handle zero distance movement', function()
|
||||
local c = cat.createCat(1, 'Test Cat', nil)
|
||||
c.targetPosition = { x = 0, y = 0, z = 0 }
|
||||
c.position = { x = 0, y = 0, z = 0 }
|
||||
c.state = 'moving'
|
||||
c:update(0.1)
|
||||
assert.is_true(true) -- Should not crash
|
||||
end)
|
||||
|
||||
it('should seek furniture based on needs', function()
|
||||
mocks.setupFilesystem()
|
||||
require('src.core')
|
||||
local c = cat.createCat(1, 'Test Cat', nil)
|
||||
c.needs.comfort = 0.1
|
||||
c:aiBehavior()
|
||||
-- Should set targetPosition if furniture found
|
||||
end)
|
||||
|
||||
it('should check personality compatibility', function()
|
||||
mocks.setupFilesystem()
|
||||
require('src.core')
|
||||
local c1 = cat.createCat(1, 'Test Cat 1', { id = 'social', compatibility = { ['social'] = 0.5 } })
|
||||
local c2 = cat.createCat(2, 'Test Cat 2', { id = 'social', compatibility = { ['social'] = 0.5 } })
|
||||
c1.position = { x = 0, y = 0, z = 0 }
|
||||
c2.position = { x = 0.5, y = 0, z = 0 }
|
||||
c1:updateNeeds()
|
||||
assert.is_true(c1.needs.social >= 0.7)
|
||||
end)
|
||||
|
||||
it('should handle incompatible cats', function()
|
||||
mocks.setupFilesystem()
|
||||
require('src.core')
|
||||
local c1 = cat.createCat(1, 'Test Cat 1', { id = 'lone_wolf', compatibility = { ['social'] = -0.5 } })
|
||||
local c2 = cat.createCat(2, 'Test Cat 2', { id = 'social' })
|
||||
c1.position = { x = 0, y = 0, z = 0 }
|
||||
c2.position = { x = 0.5, y = 0, z = 0 }
|
||||
local oldSocial = c1.needs.social
|
||||
c1:updateNeeds()
|
||||
assert.is_true(c1.needs.social < oldSocial)
|
||||
end)
|
||||
|
||||
it('should pet cat and increase mood', function()
|
||||
local c = cat.createCat(1, 'Test Cat', nil)
|
||||
local oldMood = c.mood
|
||||
c:pet()
|
||||
assert.is_true(c.mood > oldMood)
|
||||
end)
|
||||
|
||||
it('should serialize cat', function()
|
||||
local c = cat.createCat(1, 'Test Cat', nil)
|
||||
local serialized = c:serialize()
|
||||
assert.are.equal(1, serialized.id)
|
||||
assert.are.equal('Test Cat', serialized.name)
|
||||
assert.are.equal('social', serialized.personalityId)
|
||||
end)
|
||||
|
||||
it('should deserialize cat', function()
|
||||
mocks.setupFilesystem()
|
||||
require('src.core')
|
||||
local original = cat.createCat(1, 'Test Cat', nil)
|
||||
local serialized = original:serialize()
|
||||
local deserialized = cat.deserialize(serialized)
|
||||
assert.are.equal(original.id, deserialized.id)
|
||||
assert.are.equal(original.name, deserialized.name)
|
||||
end)
|
||||
|
||||
it('should handle nil personality in deserialize', function()
|
||||
local data = {
|
||||
id = 1,
|
||||
name = 'Test',
|
||||
personalityId = 'invalid',
|
||||
position = { x = 0, y = 0, z = 0 }
|
||||
}
|
||||
local c = cat.deserialize(data)
|
||||
assert.are_not.equal(nil, c)
|
||||
assert.are.equal('social', c.personality.id)
|
||||
end)
|
||||
|
||||
it('should get cat animation', function()
|
||||
local c = cat.createCat(1, 'Test Cat', nil)
|
||||
local anim = cat.getCatAnimation(c, 0)
|
||||
assert.are.equal('table', type(anim))
|
||||
assert.are.equal('number', type(anim.bounce))
|
||||
end)
|
||||
|
||||
it('should handle nil cat in animation', function()
|
||||
local anim = cat.getCatAnimation(nil, 0)
|
||||
assert.are.equal(0, anim.bounce)
|
||||
end)
|
||||
|
||||
it('should handle nil input in updateCats', function()
|
||||
cat.updateCats(0.1)
|
||||
assert.is_true(true) -- Should not crash
|
||||
end)
|
||||
end)
|
||||
@@ -0,0 +1,160 @@
|
||||
local test_helper = require('test.test_helper')
|
||||
local mocks = test_helper.mocks
|
||||
local utils = test_helper.utils
|
||||
|
||||
describe('Core Module', function()
|
||||
local core
|
||||
|
||||
setup(function()
|
||||
mocks.setupFilesystem()
|
||||
core = require('src.core')
|
||||
end)
|
||||
|
||||
it('should initialize game state', function()
|
||||
core.init()
|
||||
assert.are.equal(0, core.state.gameTime)
|
||||
assert.are.equal(2, #core.state.cats)
|
||||
assert.are.equal(0, #core.state.furniture)
|
||||
end)
|
||||
|
||||
it('should spawn cats', function()
|
||||
local initialCount = #core.state.cats
|
||||
core.spawnCat()
|
||||
assert.are.equal(initialCount + 1, #core.state.cats)
|
||||
end)
|
||||
|
||||
it('should generate cat names', function()
|
||||
local name = core.generateCatName()
|
||||
assert.are_not.equal(nil, name:find(' '))
|
||||
end)
|
||||
|
||||
it('should remove cats', function()
|
||||
local catId = core.state.cats[1].id
|
||||
local result = core.removeCat(catId)
|
||||
assert.is_true(result)
|
||||
assert.are.equal(1, #core.state.cats)
|
||||
end)
|
||||
|
||||
it('should add furniture', function()
|
||||
local furn = core.addFurniture('bed', { x = 0, y = 0, z = 0 })
|
||||
assert.are_not.equal(nil, furn)
|
||||
end)
|
||||
|
||||
it('should reject invalid furniture type', function()
|
||||
local furn = core.addFurniture('invalid_type', { x = 0, y = 0, z = 0 })
|
||||
assert.is_false(furn)
|
||||
end)
|
||||
|
||||
it('should save game', function()
|
||||
local result = core.saveGame()
|
||||
assert.is_true(result)
|
||||
end)
|
||||
|
||||
it('should load game', function()
|
||||
core.saveGame()
|
||||
local result = core.loadGame()
|
||||
assert.is_true(result)
|
||||
end)
|
||||
|
||||
it('should handle missing save file', function()
|
||||
local result = core.loadGame()
|
||||
assert.is_false(result)
|
||||
end)
|
||||
|
||||
it('should toggle pause', function()
|
||||
core.togglePause()
|
||||
assert.is_true(core.state.isPaused)
|
||||
core.togglePause()
|
||||
assert.is_false(core.state.isPaused)
|
||||
end)
|
||||
|
||||
it('should get happiness percentage', function()
|
||||
local happiness = core.getHappinessPercentage()
|
||||
assert.is_true(happiness >= 0)
|
||||
assert.is_true(happiness <= 100)
|
||||
end)
|
||||
|
||||
it('should calculate happiness with zero cats', function()
|
||||
core.state.cats = {}
|
||||
core.calculateHouseHappiness()
|
||||
assert.are.equal(0, core.state.house.totalHappiness)
|
||||
end)
|
||||
|
||||
it('should check cat departures', function()
|
||||
local cat = utils.createMockCat({ needs = { comfort = 0.05, hunger = 0.5 } })
|
||||
table.insert(core.state.cats, cat)
|
||||
local initialCount = #core.state.cats
|
||||
core.checkCatDepartures()
|
||||
assert.is_true(#core.state.cats < initialCount)
|
||||
end)
|
||||
|
||||
it('should handle nil cat in update', function()
|
||||
core.state.cats = { nil }
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil cat in happiness calculation', function()
|
||||
core.state.cats = { nil }
|
||||
core.calculateHouseHappiness()
|
||||
end)
|
||||
|
||||
it('should handle nil cat in removeCat', function()
|
||||
local result = core.removeCat(999)
|
||||
assert.is_false(result)
|
||||
end)
|
||||
|
||||
it('should handle nil core in update', function()
|
||||
_G.core = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil cat module', function()
|
||||
_G.cat = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil ui module', function()
|
||||
_G.ui = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil voxel module', function()
|
||||
_G.voxel = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil audio module', function()
|
||||
_G.audio = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle nil house module', function()
|
||||
_G.house = nil
|
||||
core.update(0.1)
|
||||
end)
|
||||
|
||||
it('should handle empty furniture list', function()
|
||||
core.state.furniture = {}
|
||||
core.calculateHouseHappiness()
|
||||
end)
|
||||
|
||||
it('should handle nil needs in cat', function()
|
||||
local cat = utils.createMockCat()
|
||||
cat.needs = nil
|
||||
table.insert(core.state.cats, cat)
|
||||
core.calculateHouseHappiness()
|
||||
end)
|
||||
|
||||
it('should handle nil personality in cat', function()
|
||||
local cat = utils.createMockCat()
|
||||
cat.personality = nil
|
||||
table.insert(core.state.cats, cat)
|
||||
cat:updateNeeds()
|
||||
end)
|
||||
|
||||
it('should handle nil furniture in cat ai behavior', function()
|
||||
core.state.furniture = nil
|
||||
local cat = utils.createMockCat()
|
||||
cat:aiBehavior()
|
||||
end)
|
||||
end)
|
||||
@@ -0,0 +1,124 @@
|
||||
local test_helper = require('test.test_helper')
|
||||
local mocks = test_helper.mocks
|
||||
local utils = test_helper.utils
|
||||
|
||||
describe('House Module', function()
|
||||
local house
|
||||
|
||||
before_each(function()
|
||||
house = require('src.house.init')
|
||||
end)
|
||||
|
||||
it('should create furniture', function()
|
||||
local type = { id = 'bed', name = 'Bed', baseHappiness = 0.7, size = { 1, 0.3, 1 } }
|
||||
local furn = house.createFurniture(type, { x = 0, y = 0, z = 0 })
|
||||
assert.are.equal('bed', furn.type.id)
|
||||
assert.are.equal(1, furn.upgradeLevel)
|
||||
end)
|
||||
|
||||
it('should upgrade furniture', function()
|
||||
local type = { id = 'bed', name = 'Bed', baseHappiness = 0.7, size = { 1, 0.3, 1 } }
|
||||
local furn = house.createFurniture(type, { x = 0, y = 0, z = 0 })
|
||||
local oldContribution = furn.happinessContribution
|
||||
furn:upgrade()
|
||||
assert.are.equal(2, furn.upgradeLevel)
|
||||
assert.is_true(furn.happinessContribution > oldContribution)
|
||||
end)
|
||||
|
||||
it('should limit furniture upgrades to 3', function()
|
||||
local type = { id = 'bed', name = 'Bed', baseHappiness = 0.7, size = { 1, 0.3, 1 } }
|
||||
local furn = house.createFurniture(type, { x = 0, y = 0, z = 0 })
|
||||
furn:upgrade()
|
||||
furn:upgrade()
|
||||
local result = furn:upgrade()
|
||||
assert.is_false(result)
|
||||
assert.are.equal(3, furn.upgradeLevel)
|
||||
end)
|
||||
|
||||
it('should serialize furniture', function()
|
||||
local type = { id = 'bed', name = 'Bed', baseHappiness = 0.7, size = { 1, 0.3, 1 } }
|
||||
local furn = house.createFurniture(type, { x = 0, y = 0, z = 0 })
|
||||
local serialized = furn:serialize()
|
||||
assert.are.equal('bed', serialized.typeId)
|
||||
assert.are.equal(1, serialized.upgradeLevel)
|
||||
end)
|
||||
|
||||
it('should deserialize furniture', function()
|
||||
mocks.setupFilesystem()
|
||||
require('src.core')
|
||||
local type = { id = 'bed', name = 'Bed', baseHappiness = 0.7, size = { 1, 0.3, 1 } }
|
||||
local furn = house.createFurniture(type, { x = 0, y = 0, z = 0 })
|
||||
local serialized = furn:serialize()
|
||||
local deserialized = house.deserializeFurniture(serialized)
|
||||
assert.are.equal('bed', deserialized.type.id)
|
||||
assert.are.equal(1, deserialized.upgradeLevel)
|
||||
end)
|
||||
|
||||
it('should check furniture placement bounds', function()
|
||||
local canPlace, reason = house.canPlaceFurniture({ x = 0, y = 0, z = 0 }, { 1, 1, 1 })
|
||||
assert.is_true(canPlace)
|
||||
end)
|
||||
|
||||
it('should reject furniture out of bounds', function()
|
||||
local canPlace, reason = house.canPlaceFurniture({ x = 100, y = 0, z = 100 }, { 1, 1, 1 })
|
||||
assert.is_false(canPlace)
|
||||
assert.are.equal('out of bounds', reason)
|
||||
end)
|
||||
|
||||
it('should detect furniture collision', function()
|
||||
mocks.setupFilesystem()
|
||||
require('src.core')
|
||||
local type = { id = 'bed', name = 'Bed', baseHappiness = 0.7, size = { 1, 0.3, 1 } }
|
||||
house.createFurniture(type, { x = 0, y = 0, z = 0 })
|
||||
local canPlace, reason = house.canPlaceFurniture({ x = 0, y = 0, z = 0 }, { 1, 1, 1 })
|
||||
assert.is_false(canPlace)
|
||||
assert.are.equal('collision', reason)
|
||||
end)
|
||||
|
||||
it('should place furniture', function()
|
||||
mocks.setupFilesystem()
|
||||
require('src.core')
|
||||
local furn = house.placeFurniture('bed', { x = 0, y = 0, z = 0 })
|
||||
assert.are_not.equal(nil, furn)
|
||||
end)
|
||||
|
||||
it('should reject invalid furniture type', function()
|
||||
local furn = house.placeFurniture('invalid_type', { x = 0, y = 0, z = 0 })
|
||||
assert.are.equal(nil, furn)
|
||||
end)
|
||||
|
||||
it('should detect collision with furniture', function()
|
||||
local result = house.checkCollision({ x = 0, y = 0, z = 0 }, { 1, 1, 1 })
|
||||
assert.is_false(result)
|
||||
end)
|
||||
|
||||
it('should interact with furniture', function()
|
||||
local furn = house.interactWithFurniture({ x = 0, y = 0, z = 0 })
|
||||
assert.are.equal(nil, furn)
|
||||
end)
|
||||
|
||||
it('should handle nil furniture type', function()
|
||||
local furn = house.createFurniture(nil, { x = 0, y = 0, z = 0 })
|
||||
assert.are.equal(nil, furn)
|
||||
end)
|
||||
|
||||
it('should handle nil data in deserialize', function()
|
||||
local furn = house.deserializeFurniture(nil)
|
||||
assert.are.equal(nil, furn)
|
||||
end)
|
||||
|
||||
it('should handle nil core in canPlaceFurniture', function()
|
||||
local canPlace, reason = house.canPlaceFurniture({ x = 0, y = 0, z = 0 }, { 1, 1, 1 })
|
||||
assert.is_false(canPlace)
|
||||
end)
|
||||
|
||||
it('should handle nil core in checkCollision', function()
|
||||
local result = house.checkCollision({ x = 0, y = 0, z = 0 }, { 1, 1, 1 })
|
||||
assert.is_false(result)
|
||||
end)
|
||||
|
||||
it('should handle nil core in interactWithFurniture', function()
|
||||
local furn = house.interactWithFurniture({ x = 0, y = 0, z = 0 })
|
||||
assert.are.equal(nil, furn)
|
||||
end)
|
||||
end)
|
||||
@@ -0,0 +1,146 @@
|
||||
describe('JSON Module', function()
|
||||
local json
|
||||
|
||||
setup(function()
|
||||
json = require('src.json')
|
||||
end)
|
||||
|
||||
it('should encode strings', function()
|
||||
assert.are.equal('"hello"', json.encode('hello'))
|
||||
assert.are.equal('"hello world"', json.encode('hello world'))
|
||||
end)
|
||||
|
||||
it('should encode numbers', function()
|
||||
assert.are.equal('123', json.encode(123))
|
||||
assert.are.equal('12.5', json.encode(12.5))
|
||||
assert.are.equal('-42', json.encode(-42))
|
||||
end)
|
||||
|
||||
it('should encode booleans', function()
|
||||
assert.are.equal('true', json.encode(true))
|
||||
assert.are.equal('false', json.encode(false))
|
||||
end)
|
||||
|
||||
it('should encode null', function()
|
||||
assert.are.equal('null', json.encode(nil))
|
||||
end)
|
||||
|
||||
it('should encode arrays', function()
|
||||
local arr = { 1, 2, 3 }
|
||||
local encoded = json.encode(arr)
|
||||
assert.are.equal('{ 1, 2, 3 }', encoded)
|
||||
end)
|
||||
|
||||
it('should encode objects', function()
|
||||
local obj = { name = 'test', value = 42 }
|
||||
local encoded = json.encode(obj)
|
||||
assert.are_not.equal(nil, encoded:find('name'))
|
||||
assert.are_not.equal(nil, encoded:find('value'))
|
||||
end)
|
||||
|
||||
it('should decode strings', function()
|
||||
local decoded = json.decode('"hello"')
|
||||
assert.are.equal('hello', decoded)
|
||||
end)
|
||||
|
||||
it('should decode numbers', function()
|
||||
local decoded = json.decode('123')
|
||||
assert.are.equal(123, decoded)
|
||||
local decoded2 = json.decode('12.5')
|
||||
assert.are.equal(12.5, decoded2)
|
||||
end)
|
||||
|
||||
it('should decode booleans', function()
|
||||
local decoded = json.decode('true')
|
||||
assert.are.equal(true, decoded)
|
||||
local decoded2 = json.decode('false')
|
||||
assert.are.equal(false, decoded2)
|
||||
end)
|
||||
|
||||
it('should decode null', function()
|
||||
local decoded = json.decode('null')
|
||||
assert.are.equal(nil, decoded)
|
||||
end)
|
||||
|
||||
it('should decode arrays', function()
|
||||
local decoded = json.decode('{ 1, 2, 3 }')
|
||||
assert.are.equal(1, decoded[1])
|
||||
assert.are.equal(2, decoded[2])
|
||||
assert.are.equal(3, decoded[3])
|
||||
end)
|
||||
|
||||
it('should decode objects', function()
|
||||
local decoded = json.decode('{ "name": "test", "value": 42 }')
|
||||
assert.are.equal('test', decoded.name)
|
||||
assert.are.equal(42, decoded.value)
|
||||
end)
|
||||
|
||||
it('should handle round-trip encoding/decoding', function()
|
||||
local original = {
|
||||
name = 'Test Cat',
|
||||
age = 3,
|
||||
active = true,
|
||||
tags = { 'cute', 'playful' },
|
||||
stats = { comfort = 0.8, hunger = 0.6 }
|
||||
}
|
||||
local encoded = json.encode(original)
|
||||
local decoded = json.decode(encoded)
|
||||
assert.are.equal(original.name, decoded.name)
|
||||
assert.are.equal(original.age, decoded.age)
|
||||
assert.are.equal(original.active, decoded.active)
|
||||
assert.are.equal(original.tags[1], decoded.tags[1])
|
||||
assert.are.equal(original.stats.comfort, decoded.stats.comfort)
|
||||
end)
|
||||
|
||||
it('should handle escaped characters in strings', function()
|
||||
local original = 'hello\nworld\t"quoted"'
|
||||
local encoded = json.encode(original)
|
||||
local decoded = json.decode(encoded)
|
||||
assert.are.equal(original, decoded)
|
||||
end)
|
||||
|
||||
it('should handle empty arrays', function()
|
||||
local original = {}
|
||||
local encoded = json.encode(original)
|
||||
local decoded = json.decode(encoded)
|
||||
assert.are.equal(0, #decoded)
|
||||
end)
|
||||
|
||||
it('should handle nested structures', function()
|
||||
local original = {
|
||||
cats = {
|
||||
{ name = 'Cat1', personality = { id = 'social' } },
|
||||
{ name = 'Cat2', personality = { id = 'lone_wolf' } }
|
||||
}
|
||||
}
|
||||
local encoded = json.encode(original)
|
||||
local decoded = json.decode(encoded)
|
||||
assert.are.equal('Cat1', decoded.cats[1].name)
|
||||
assert.are.equal('social', decoded.cats[1].personality.id)
|
||||
end)
|
||||
|
||||
it('should handle non-string keys by converting to strings', function()
|
||||
local original = { [123] = 'value', [true] = 'bool' }
|
||||
local encoded = json.encode(original)
|
||||
local decoded = json.decode(encoded)
|
||||
assert.are.equal('value', decoded['123'])
|
||||
assert.are.equal('bool', decoded['true'])
|
||||
end)
|
||||
|
||||
it('should handle NaN and infinity as null', function()
|
||||
assert.are.equal('null', json.encode(0/0))
|
||||
assert.are.equal('null', json.encode(math.huge))
|
||||
assert.are.equal('null', json.encode(-math.huge))
|
||||
end)
|
||||
|
||||
it('should return error for invalid JSON', function()
|
||||
local decoded, err = json.decode('{ invalid }')
|
||||
assert.are_not.equal(nil, err)
|
||||
assert.are.equal(nil, decoded)
|
||||
end)
|
||||
|
||||
it('should return error for non-string input', function()
|
||||
local decoded, err = json.encode({ function() end })
|
||||
assert.are.equal('null', decoded)
|
||||
end)
|
||||
end)
|
||||
@@ -0,0 +1,98 @@
|
||||
local test_helper = require('test.test_helper')
|
||||
local mocks = test_helper.mocks
|
||||
local utils = test_helper.utils
|
||||
|
||||
describe('UI Module', function()
|
||||
local ui
|
||||
local core
|
||||
|
||||
before_each(function()
|
||||
ui = require('src.ui.init')
|
||||
mocks.setupFilesystem()
|
||||
core = require('src.core')
|
||||
end)
|
||||
|
||||
it('should render HUD', function()
|
||||
local pass = utils.createMockPass()
|
||||
ui.renderHUD(pass)
|
||||
end)
|
||||
|
||||
it('should render HUD with zero cats', function()
|
||||
core.state.cats = {}
|
||||
local pass = utils.createMockPass()
|
||||
ui.renderHUD(pass)
|
||||
end)
|
||||
|
||||
it('should render inventory', function()
|
||||
local pass = utils.createMockPass()
|
||||
ui.renderInventory(pass, 1280, 720, 0.72)
|
||||
end)
|
||||
|
||||
it('should render cat log', function()
|
||||
local pass = utils.createMockPass()
|
||||
ui.renderCatLog(pass, 1280, 0.72)
|
||||
end)
|
||||
|
||||
it('should render placement preview', function()
|
||||
local pass = utils.createMockPass()
|
||||
ui.renderPlacementPreview(pass, 1280, 720)
|
||||
end)
|
||||
|
||||
it('should get world ray', function()
|
||||
local start, endPos = ui.getWorldRay(1280, 720)
|
||||
assert.are.equal('table', type(start))
|
||||
assert.are.equal('table', type(endPos))
|
||||
end)
|
||||
|
||||
it('should handle mouse press in inventory', function()
|
||||
ui.showInventory = true
|
||||
ui.selectedFurniture = nil
|
||||
ui.handleMousePress(1000, 500, 1)
|
||||
end)
|
||||
|
||||
it('should handle mouse press on close button', function()
|
||||
ui.showInventory = true
|
||||
ui.handleMousePress(1200, 300, 1)
|
||||
assert.is_false(ui.showInventory)
|
||||
end)
|
||||
|
||||
it('should toggle inventory', function()
|
||||
ui.toggleInventory()
|
||||
assert.is_true(ui.showInventory)
|
||||
ui.toggleInventory()
|
||||
assert.is_false(ui.showInventory)
|
||||
end)
|
||||
|
||||
it('should toggle cat log', function()
|
||||
ui.toggleCatLog()
|
||||
assert.is_false(ui.showCatLog)
|
||||
ui.toggleCatLog()
|
||||
assert.is_true(ui.showCatLog)
|
||||
end)
|
||||
|
||||
it('should handle nil core in renderHUD', function()
|
||||
_G.core = nil
|
||||
local pass = utils.createMockPass()
|
||||
ui.renderHUD(pass)
|
||||
end)
|
||||
|
||||
it('should handle nil pass in renderHUD', function()
|
||||
ui.renderHUD(nil)
|
||||
end)
|
||||
|
||||
it('should handle nil pass in renderInventory', function()
|
||||
ui.renderInventory(nil, 1280, 720, 0.72)
|
||||
end)
|
||||
|
||||
it('should handle nil pass in renderCatLog', function()
|
||||
ui.renderCatLog(nil, 1280, 0.72)
|
||||
end)
|
||||
|
||||
it('should handle nil pass in renderPlacementPreview', function()
|
||||
ui.renderPlacementPreview(nil, 1280, 720)
|
||||
end)
|
||||
|
||||
it('should handle nil core in handleMousePress', function()
|
||||
ui.handleMousePress(100, 100, 1)
|
||||
end)
|
||||
end)
|
||||
@@ -0,0 +1,101 @@
|
||||
local test_helper = require('test.test_helper')
|
||||
local mocks = test_helper.mocks
|
||||
local utils = test_helper.utils
|
||||
|
||||
describe('Voxel Module', function()
|
||||
local voxel
|
||||
|
||||
before_each(function()
|
||||
-- Mock LOVR
|
||||
_G.lovr = mocks.lovr
|
||||
voxel = require('src.voxel.init')
|
||||
end)
|
||||
|
||||
it('should create voxel texture', function()
|
||||
local color = { 1, 0, 0 }
|
||||
local texture = voxel.createVoxelTexture(color)
|
||||
assert.are.equal('texture', texture.type)
|
||||
assert.are.equal(64, texture.image.width)
|
||||
assert.are.equal(64, texture.image.height)
|
||||
end)
|
||||
|
||||
it('should initialize textures for furniture', function()
|
||||
mocks.setupFilesystem()
|
||||
require('src.core')
|
||||
voxel.init()
|
||||
assert.are_not.equal(nil, voxel.textures['bed'])
|
||||
assert.are_not.equal(nil, voxel.textures['cat_tree'])
|
||||
end)
|
||||
|
||||
it('should create cube mesh', function()
|
||||
local mesh = voxel.cubeMesh
|
||||
assert.are.equal('mesh', mesh.type)
|
||||
assert.are.equal(36, #mesh.indices) -- 12 triangles
|
||||
end)
|
||||
|
||||
it('should render world', function()
|
||||
mocks.setupFilesystem()
|
||||
require('src.core')
|
||||
local pass = utils.createMockPass()
|
||||
voxel.renderWorld(pass)
|
||||
-- Should draw floor and walls
|
||||
end)
|
||||
|
||||
it('should render furniture', function()
|
||||
mocks.setupFilesystem()
|
||||
require('src.core')
|
||||
local pass = utils.createMockPass()
|
||||
voxel.renderFurniture(pass)
|
||||
end)
|
||||
|
||||
it('should render cats', function()
|
||||
mocks.setupFilesystem()
|
||||
require('src.core')
|
||||
local pass = utils.createMockPass()
|
||||
voxel.renderCats(pass)
|
||||
end)
|
||||
|
||||
it('should raycast furniture', function()
|
||||
local start = { 0, 0, 0 }
|
||||
local direction = { 0, 0, -1 }
|
||||
local maxDistance = 10
|
||||
|
||||
local result = voxel.raycast(start, direction, maxDistance)
|
||||
assert.are.equal('table', type(result))
|
||||
end)
|
||||
|
||||
it('should raycast cats', function()
|
||||
local start = { 0, 0, 0 }
|
||||
local direction = { 0, 0, -1 }
|
||||
local maxDistance = 10
|
||||
|
||||
local result = voxel.raycast(start, direction, maxDistance)
|
||||
assert.are.equal('table', type(result))
|
||||
end)
|
||||
|
||||
it('should handle nil inputs', function()
|
||||
local result = voxel.raycast(nil, nil, nil)
|
||||
assert.are.equal('table', type(result))
|
||||
assert.are.equal(0, #result)
|
||||
end)
|
||||
|
||||
it('should handle zero distance direction', function()
|
||||
local start = { 0, 0, 0 }
|
||||
local direction = { 0, 0, 0 }
|
||||
local maxDistance = 10
|
||||
|
||||
local result = voxel.raycast(start, direction, maxDistance)
|
||||
assert.are.equal('table', type(result))
|
||||
end)
|
||||
|
||||
it('should sort raycast results by distance', function()
|
||||
local start = { 0, 0, 0 }
|
||||
local direction = { 0, 0, -1 }
|
||||
local maxDistance = 10
|
||||
|
||||
local result = voxel.raycast(start, direction, maxDistance)
|
||||
for i = 2, #result do
|
||||
assert.is_true(result[i].distance >= result[i-1].distance)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
@@ -0,0 +1,243 @@
|
||||
-- Cat Haven - Test Helper
|
||||
-- LOVR API mocks and test utilities
|
||||
|
||||
local mocks = {}
|
||||
|
||||
-- Mock LOVR graphics API
|
||||
mocks.lovr = {
|
||||
graphics = {
|
||||
newImage = function(pixels, width, height, options)
|
||||
return { type = 'image', pixels = pixels, width = width, height = height }
|
||||
end,
|
||||
newTexture = function(image)
|
||||
return { type = 'texture', image = image }
|
||||
end,
|
||||
newMesh = function(vertices, indices, mode)
|
||||
return { type = 'mesh', vertices = vertices, indices = indices, mode = mode }
|
||||
end,
|
||||
newMaterial = function(texture)
|
||||
return { type = 'material', texture = texture }
|
||||
end,
|
||||
newModel = function(mesh)
|
||||
return { type = 'model', mesh = mesh }
|
||||
end,
|
||||
box = function(mode, x, y, z, w, h, d, color, thickness)
|
||||
return { type = 'box', mode = mode, x = x, y = y, z = z, w = w, h = h, d = d, color = color }
|
||||
end,
|
||||
text = function(text, x, y, z, size, color, align)
|
||||
return { type = 'text', text = text, x = x, y = y, z = z, size = size, color = color, align = align }
|
||||
end
|
||||
},
|
||||
audio = {
|
||||
newSource = function(data, bufferType, sampleRate, channels)
|
||||
return {
|
||||
type = 'source',
|
||||
data = data,
|
||||
bufferType = bufferType,
|
||||
sampleRate = sampleRate,
|
||||
channels = channels,
|
||||
spatial = false,
|
||||
looping = false,
|
||||
volume = 1.0,
|
||||
position = { 0, 0, 0 },
|
||||
rolloff = 1.0,
|
||||
maxDistance = 10.0
|
||||
}
|
||||
end
|
||||
},
|
||||
system = {
|
||||
getWindowDimensions = function()
|
||||
return 1280, 720
|
||||
end,
|
||||
getMousePosition = function()
|
||||
return 640, 360
|
||||
end
|
||||
},
|
||||
headset = {
|
||||
isDown = function(button)
|
||||
return false
|
||||
end
|
||||
},
|
||||
filesystem = {
|
||||
newFile = function(filename, mode)
|
||||
return {
|
||||
filename = filename,
|
||||
mode = mode,
|
||||
content = '',
|
||||
write = function(self, data)
|
||||
self.content = self.content .. data
|
||||
end,
|
||||
read = function(self, spec)
|
||||
if spec == '*a' then return self.content end
|
||||
end,
|
||||
close = function(self)
|
||||
end
|
||||
}
|
||||
end
|
||||
},
|
||||
data = {
|
||||
newImage = function(pixels, width, height, options)
|
||||
return { type = 'image', pixels = pixels, width = width, height = height }
|
||||
end,
|
||||
newModelData = function()
|
||||
return { type = 'modeldata' }
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
-- Mock LOVR filesystem for save/load tests
|
||||
mocks.setupFilesystem = function(saveData)
|
||||
local originalNewFile = mocks.lovr.filesystem.newFile
|
||||
mocks.lovr.filesystem.newFile = function(filename, mode)
|
||||
if filename == 'savegame.json' then
|
||||
if mode == 'w' then
|
||||
return {
|
||||
filename = filename,
|
||||
mode = mode,
|
||||
content = '',
|
||||
write = function(self, data)
|
||||
self.content = data
|
||||
end,
|
||||
read = function(self, spec)
|
||||
return saveData or ''
|
||||
end,
|
||||
close = function(self)
|
||||
end
|
||||
}
|
||||
elseif mode == 'r' then
|
||||
return {
|
||||
filename = filename,
|
||||
mode = mode,
|
||||
content = saveData or '',
|
||||
write = function(self, data)
|
||||
end,
|
||||
read = function(self, spec)
|
||||
if spec == '*a' then return self.content end
|
||||
end,
|
||||
close = function(self)
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
return originalNewFile(filename, mode)
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset mocks
|
||||
mocks.reset = function()
|
||||
mocks.lovr.filesystem.newFile = function(filename, mode)
|
||||
return {
|
||||
filename = filename,
|
||||
mode = mode,
|
||||
content = '',
|
||||
write = function(self, data)
|
||||
self.content = self.content .. data
|
||||
end,
|
||||
read = function(self, spec)
|
||||
if spec == '*a' then return self.content end
|
||||
end,
|
||||
close = function(self)
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Test utilities
|
||||
local utils = {}
|
||||
|
||||
utils.assertDeepEqual = function(actual, expected, msg)
|
||||
if type(actual) ~= 'table' or type(expected) ~= 'table' then
|
||||
assert(actual == expected, msg or 'Values not equal')
|
||||
return
|
||||
end
|
||||
|
||||
for k, v in pairs(actual) do
|
||||
if type(v) == 'table' then
|
||||
utils.assertDeepEqual(v, expected[k], msg .. ': table[' .. k .. ']')
|
||||
else
|
||||
assert(v == expected[k], msg .. ': field[' .. k .. '] expected ' .. tostring(expected[k]) .. ', got ' .. tostring(v))
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(expected) do
|
||||
if actual[k] == nil then
|
||||
assert(false, msg .. ': missing field[' .. k .. ']')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
utils.createMockPass = function()
|
||||
return {
|
||||
box = function(mode, x, y, z, w, h, d, color, thickness)
|
||||
return { mode = mode, x = x, y = y, z = z, w = w, h = h, d = d, color = color }
|
||||
end,
|
||||
text = function(text, x, y, z, size, color, align)
|
||||
return { text = text, x = x, y = y, z = z, size = size, color = color, align = align }
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
utils.createMockCat = function(overrides)
|
||||
return {
|
||||
id = overrides.id or 1,
|
||||
name = overrides.name or 'Test Cat',
|
||||
personality = overrides.personality or { id = 'social', color = { 0.9, 0.6, 0.7 } },
|
||||
position = overrides.position or { x = 0, y = 0, z = 0 },
|
||||
targetPosition = overrides.targetPosition,
|
||||
state = overrides.state or 'idle',
|
||||
mood = overrides.mood or 0.8,
|
||||
needs = overrides.needs or { comfort = 0.7, social = 0.7, hunger = 0.7, fun = 0.7 },
|
||||
favoriteSpots = overrides.favoriteSpots or {},
|
||||
history = overrides.history or { arrivals = 1, departures = 0, totalHappiness = 0 },
|
||||
animationTime = overrides.animationTime or 0,
|
||||
isSleeping = overrides.isSleeping or false,
|
||||
sleepTimer = overrides.sleepTimer or 0,
|
||||
update = function(self, dt) end,
|
||||
updateNeeds = function(self) end,
|
||||
serialize = function(self)
|
||||
return {
|
||||
id = self.id,
|
||||
name = self.name,
|
||||
personalityId = self.personality.id,
|
||||
position = self.position,
|
||||
state = self.state,
|
||||
mood = self.mood,
|
||||
needs = self.needs,
|
||||
favoriteSpots = self.favoriteSpots,
|
||||
history = self.history,
|
||||
isSleeping = self.isSleeping
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
utils.createMockFurniture = function(overrides)
|
||||
return {
|
||||
type = overrides.type or { id = 'bed', name = 'Cozy Bed', baseHappiness = 0.7, comfortBonus = 0.6, socialBonus = 0.1, funBonus = 0.1, maxOccupants = 2, color = { 0.8, 0.6, 0.7 }, size = { 1, 0.3, 1 } },
|
||||
position = overrides.position or { x = 0, y = 0, z = 0 },
|
||||
state = overrides.state or 'placed',
|
||||
upgradeLevel = overrides.upgradeLevel or 1,
|
||||
occupant = overrides.occupant,
|
||||
happinessContribution = overrides.happinessContribution or 0.7,
|
||||
upgrade = function(self)
|
||||
if self.upgradeLevel < 3 then
|
||||
self.upgradeLevel = self.upgradeLevel + 1
|
||||
self.happinessContribution = self.type.baseHappiness * self.upgradeLevel * 0.8
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end,
|
||||
serialize = function(self)
|
||||
return {
|
||||
typeId = self.type.id,
|
||||
position = self.position,
|
||||
state = self.state,
|
||||
upgradeLevel = self.upgradeLevel,
|
||||
occupant = self.occupant and self.occupant.id or nil,
|
||||
happinessContribution = self.happinessContribution
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
return { mocks = mocks, utils = utils }
|
||||
Reference in New Issue
Block a user