Merge pull request #1974 from ellraiser/12.0-testsuite

basic API test suite
This commit is contained in:
Sasha Szpakowski
2023-11-29 20:45:36 -04:00
committed by GitHub
99 changed files with 8896 additions and 3 deletions
+232 -2
View File
@@ -3,7 +3,13 @@ on: [push, pull_request]
jobs:
linux-os:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
permissions:
checks: write
pull-requests: write
env:
ALSOFT_CONF: love2d-${{ github.sha }}/testing/resources/alsoft.conf
DISPLAY: :99
steps:
- name: Update APT
run: sudo apt-get update
@@ -16,7 +22,8 @@ jobs:
libgl1-mesa-dev libdbus-1-dev libudev-dev libgles2-mesa-dev \
libegl1-mesa-dev libibus-1.0-dev fcitx-libs-dev libsamplerate0-dev \
libsndio-dev libwayland-dev libxkbcommon-dev libdrm-dev libgbm-dev \
libcurl4-openssl-dev
libcurl4-openssl-dev libfuse2 wmctrl openbox mesa-vulkan-drivers \
libvulkan1 vulkan-tools vulkan-validationlayers
- name: Checkout love-appimage-source
uses: actions/checkout@v3
with:
@@ -40,6 +47,83 @@ jobs:
run: make LOVE_BRANCH=${{ github.sha }}
- name: Print LuaJIT branch
run: git -C LuaJIT-v2.1 branch -v
# start xvfb for test running
- name: Start xvfb and openbox
run: |
echo "Starting XVFB on $DISPLAY"
Xvfb $DISPLAY -screen 0, 360x240x24 &
echo "XVFBPID=$!" >> $GITHUB_ENV
# wait for xvfb to startup (3s is the same amount xvfb-run waits by default)
sleep 3
openbox &
echo "OPENBOXPID=$!" >> $GITHUB_ENV
# linux opengl tests
- name: Run Test Suite (opengl)
run: |
chmod a+x love-${{ github.sha }}.AppImage
./love-${{ github.sha }}.AppImage love2d-${{ github.sha }}/testing/main.lua --runAllTests --isRunner
- name: Love Test Report (opengl)
id: report1
uses: ellraiser/love-test-report@main
with:
name: Love Testsuite Linux
title: test-report-linux-opengl
path: love2d-${{ github.sha }}/testing/output/lovetest_runAllTests.md
token: ${{ secrets.GITHUB_TOKEN }}
- name: Zip Test Output (opengl)
run: |
7z a -tzip test-output-linux-opengl.zip love2d-${{ github.sha }}/testing/output/
- name: Artifact Test Output (opengl)
uses: actions/upload-artifact@v3
with:
name: test-output-linux-opengl-${{ steps.report1.outputs.conclusion }}
path: test-output-linux-opengl.zip
# linux opengles tests
- name: Run Test Suite (opengles)
run: |
export LOVE_GRAPHICS_USE_OPENGLES=1
./love-${{ github.sha }}.AppImage love2d-${{ github.sha }}/testing/main.lua --runAllTests --isRunner
- name: Love Test Report (opengles)
uses: ellraiser/love-test-report@main
id: report2
with:
name: Love Testsuite Linux
title: test-report-linux-opengles
path: love2d-${{ github.sha }}/testing/output/lovetest_runAllTests.md
token: ${{ secrets.GITHUB_TOKEN }}
- name: Zip Test Output (opengles)
run: |
7z a -tzip test-output-linux-opengles.zip love2d-${{ github.sha }}/testing/output/
- name: Artifact Test Output (opengles)
uses: actions/upload-artifact@v3
with:
name: test-output-linux-opengles-${{ steps.report2.outputs.conclusion }}
path: test-output-linux-opengles.zip
# # linux vulkan tests
# - name: Run Test Suite (vulkan)
# run: |
# export LOVE_GRAPHICS_DEBUG=1
# ./love-${{ github.sha }}.AppImage love2d-${{ github.sha }}/testing/main.lua --runAllTests --isRunner --renderers vulkan
# - name: Love Test Report (vulkan)
# uses: ellraiser/love-test-report@main
# with:
# name: Love Testsuite Linux
# title: test-report-linux-vulkan
# path: love2d-${{ github.sha }}/testing/output/lovetest_runAllTests.md
# - name: Zip Test Output (vulkan)
# run: |
# 7z a -tzip test-output-linux-vulkan.zip love2d-${{ github.sha }}/testing/output/
# - name: Artifact Test Output (vulkan)
# uses: actions/upload-artifact@v3
# with:
# name: test-output-linux-vulkan
# path: test-output-linux-vulkan.zip
- name: Stop xvfb and openbox
# should always stop xvfb and openbox even if other steps failed
if: always()
run: |
kill $XVFBPID
kill $OPENBOXPID
- name: Artifact
uses: actions/upload-artifact@v3
with:
@@ -50,8 +134,21 @@ jobs:
with:
name: love-x86_64-AppImage-debug
path: love-${{ github.sha }}.AppImage-debug.tar.gz
- name: Check Tests Passing
if: steps.report1.outputs.conclusion == 'failure' || steps.report2.outputs.conclusion == 'failure'
run: |
echo "${{ steps.report1.outputs.failed }} opengl tests failed"
echo "${{ steps.report2.outputs.failed }} opengles tests failed"
exit 1
windows-os:
runs-on: windows-latest
permissions:
checks: write
pull-requests: write
env:
ALSOFT_CONF: megasource/libs/love/testing/resources/alsoft.conf
VK_ICD_FILENAMES: ${{ github.workspace }}\mesa\x64\lvp_icd.x86_64.json
VULKAN_SDK: C:/VulkanSDK/1.3.231.1
strategy:
matrix:
platform: [Win32, x64, ARM64]
@@ -206,8 +303,115 @@ jobs:
with:
name: love-windows-${{ steps.vars.outputs.arch }}${{ steps.vars.outputs.compatname }}-dbg
path: pdb/Release/*.pdb
# install mesa for graphic tests
- name: Install Mesa
if: steps.vars.outputs.arch != 'ARM64' && steps.vars.outputs.compatname != '-compat'
run: |
curl -L --output mesa.7z --url https://github.com/pal1000/mesa-dist-win/releases/download/23.2.1/mesa3d-23.2.1-release-msvc.7z
7z x mesa.7z -o*
powershell.exe mesa\systemwidedeploy.cmd 1
# build love to use for the tests
- name: Build Test Exe
if: steps.vars.outputs.arch != 'ARM64' && steps.vars.outputs.compatname != '-compat'
run: cmake --build build --config Release --target install
# windows opengl tests
- name: Run Tests (opengl)
if: steps.vars.outputs.arch != 'ARM64' && steps.vars.outputs.compatname != '-compat'
run: |
echo 'check dir'
ls
powershell.exe ./install/lovec.exe ./megasource/libs/love/testing/main.lua --runAllTests --isRunner
- name: Love Test Report (opengl)
id: report1
if: steps.vars.outputs.arch != 'ARM64' && steps.vars.outputs.compatname != '-compat'
uses: ellraiser/love-test-report@main
with:
name: Love Testsuite Windows ${{ steps.vars.outputs.arch }} ${{ steps.vars.outputs.compatname }} (opengl)
title: test-report-windows-${{ steps.vars.outputs.arch }}${{ steps.vars.outputs.compatname }}-opengl
path: megasource/libs/love/testing/output/lovetest_runAllTests.md
token: ${{ secrets.GITHUB_TOKEN }}
- name: Zip Test Output (opengl)
if: steps.vars.outputs.arch != 'ARM64' && steps.vars.outputs.compatname != '-compat'
run: |
7z a -tzip test-output-windows-${{ steps.vars.outputs.arch }}${{ steps.vars.outputs.compatname }}-opengl.zip megasource/libs/love/testing/output/
- name: Artifact Test Output (opengl)
if: steps.vars.outputs.arch != 'ARM64' && steps.vars.outputs.compatname != '-compat'
uses: actions/upload-artifact@v3
with:
name: test-output-windows-${{ steps.vars.outputs.arch }}-opengl-${{ steps.report1.outputs.conclusion }}
path: test-output-windows-${{ steps.vars.outputs.arch }}-opengl.zip
# windows opengles tests
- name: Run Tests (opengles)
if: steps.vars.outputs.arch != 'ARM64' && steps.vars.outputs.compatname != '-compat'
run: |
$ENV:LOVE_GRAPHICS_USE_OPENGLES=1
powershell.exe ./install/lovec.exe ./megasource/libs/love/testing/main.lua --runAllTests --isRunner
- name: Love Test Report (opengles)
id: report2
if: steps.vars.outputs.arch != 'ARM64' && steps.vars.outputs.compatname != '-compat'
uses: ellraiser/love-test-report@main
with:
name: Love Testsuite Windows ${{ steps.vars.outputs.arch }} ${{ steps.vars.outputs.compatname }} (opengles)
title: test-report-windows-${{ steps.vars.outputs.arch }}${{ steps.vars.outputs.compatname }}-opengles
path: megasource/libs/love/testing/output/lovetest_runAllTests.md
token: ${{ secrets.GITHUB_TOKEN }}
- name: Zip Test Output (opengles)
if: steps.vars.outputs.arch != 'ARM64' && steps.vars.outputs.compatname != '-compat'
run: |
7z a -tzip test-output-windows-${{ steps.vars.outputs.arch }}${{ steps.vars.outputs.compatname }}-opengles.zip megasource/libs/love/testing/output/
- name: Artifact Test Output (opengles)
if: steps.vars.outputs.arch != 'ARM64' && steps.vars.outputs.compatname != '-compat'
uses: actions/upload-artifact@v3
with:
name: test-output-windows-${{ steps.vars.outputs.arch }}-opengles-${{ steps.report2.outputs.conclusion }}
path: test-output-windows-${{ steps.vars.outputs.arch }}-opengles.zip
- name: Check Tests Passing
if: steps.report1.outputs.conclusion == 'failure' || steps.report2.outputs.conclusion == 'failure'
run: |
echo "${{ steps.report1.outputs.failed }} opengl tests failed"
echo "${{ steps.report2.outputs.failed }} opengles tests failed"
exit 1
# # install vulkan
# - name: Install Vulkan
# if: steps.vars.outputs.arch != 'ARM64'
# run: |
# curl -L --show-error --output VulkanSDK.exe https://sdk.lunarg.com/sdk/download/1.3.231.1/windows/VulkanSDK-1.3.231.1-Installer.exe
# ./VulkanSDK.exe --root C:/VulkanSDK/1.3.231.1 --accept-licenses --default-answer --confirm-command install com.lunarg.vulkan.core com.lunarg.vulkan.vma
# curl -L --show-error --output vulkan-runtime.zip https://sdk.lunarg.com/sdk/download/1.3.231.1/windows/vulkan-runtime-components.zip
# 7z e vulkan-runtime.zip -o"C:/VulkanSDK/1.3.231.1/runtime/x64" */x64
# copy "C:/VulkanSDK/1.3.231.1/runtime/x64/vulkan-1.dll" "mesa/x64"
# copy "C:/VulkanSDK/1.3.231.1/runtime/x64/vulkan-1.dll" "C:/Windows/System32"
# copy "C:/VulkanSDK/1.3.231.1/runtime/x64/vulkan-1.dll" "love-12.0-win64/love-12.0-win64"
# reg add HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\Vulkan\Drivers /v "${{ github.workspace }}\mesa\x64\lvp_icd.x86_64.json" /t REG_DWORD /d 0
# powershell.exe C:/VulkanSDK/1.3.231.1/runtime/x64/vulkaninfo.exe --summary
# # windows vulkan tests
# - name: Run Tests (vulkan)
# if: steps.vars.outputs.arch != 'ARM64'
# run: |
# $ENV:LOVE_GRAPHICS_DEBUG=1
# powershell.exe ./install/lovec.exe ./megasource/libs/love/testing/main.lua --runAllTests --isRunner --renderers vulkan
# - name: Love Test Report (vulkan)
# if: steps.vars.outputs.arch != 'ARM64'
# uses: ellraiser/love-test-report@main
# with:
# name: Love Testsuite Windows ${{ steps.vars.outputs.arch }} ${{ steps.vars.outputs.compatname }} (vulkan)
# title: test-report-windows-${{ steps.vars.outputs.arch }}${{ steps.vars.outputs.compatname }}-vulkan
# path: megasource/libs/love/testing/output/lovetest_runAllTests.md
# - name: Zip Test Output (vulkan)
# if: steps.vars.outputs.arch != 'ARM64'
# run: |
# 7z a -tzip test-output-windows-${{ steps.vars.outputs.arch }}${{ steps.vars.outputs.compatname }}-vulkan.zip megasource/libs/love/testing/output/
# - name: Artifact Test Output (vulkan)
# if: steps.vars.outputs.arch != 'ARM64'
# uses: actions/upload-artifact@v3
# with:
# name: test-output-windows-${{ steps.vars.outputs.arch }}${{ steps.vars.outputs.compatname }}-vulkan
# path: test-output-windows-${{ steps.vars.outputs.arch }}${{ steps.vars.outputs.compatname }}-vulkan.zip
macOS:
runs-on: macos-latest
permissions:
checks: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v3
@@ -234,6 +438,32 @@ jobs:
with:
name: love-macos
path: love-macos.zip
# macos opengl tests (metal not supported on runners)
- name: Run Test Suite
run: |
ls
love-macos/love.app/Contents/MacOS/love ./testing/main.lua --runAllTests --isRunner
- name: Love Test Report
id: report1
uses: ellraiser/love-test-report@main
with:
name: Love Testsuite MacOS
title: test-report-macos
path: testing/output/lovetest_runAllTests.md
token: ${{ secrets.GITHUB_TOKEN }}
- name: Zip Test Output
run: |
7z a -tzip test-output-macos-opengl.zip ./testing/output/
- name: Artifact Test Output
uses: actions/upload-artifact@v3
with:
name: test-output-macos-opengl-${{ steps.report1.outputs.conclusion }}
path: test-output-macos-opengl.zip
- name: Check Tests Passing
if: steps.report1.outputs.conclusion == 'failure'
run: |
echo "${{ steps.report1.outputs.failed }} opengl tests failed"
exit 1
iOS-Simulator:
runs-on: macos-latest
steps:
+5 -1
View File
@@ -68,4 +68,8 @@ stamp-h1
/src/love
/src/tags
.vs/
.vscode/
.vscode/
/testing/output/*.xml
/testing/output/*.html
/testing/output/*.md
/testing/output/actual/*.png
+540
View File
@@ -0,0 +1,540 @@
-- @class - TestMethod
-- @desc - used to run a specific method from a module's /test/ suite
-- each assertion is tracked and then printed to output
TestMethod = {
-- @method - TestMethod:new()
-- @desc - create a new TestMethod object
-- @param {string} method - string of method name to run
-- @param {TestMethod} testmethod - parent testmethod this test belongs to
-- @return {table} - returns the new Test object
new = function(self, method, testmodule)
local test = {
testmodule = testmodule,
method = method,
asserts = {},
start = love.timer.getTime(),
finish = 0,
count = 0,
passed = false,
skipped = false,
skipreason = '',
rgba_tolerance = 0,
pixel_tolerance = 0,
fatal = '',
message = nil,
result = {},
colors = {
red = {1, 0, 0, 1},
redpale = {1, 0.5, 0.5, 1},
red07 = {0.7, 0, 0, 1},
green = {0, 1, 0, 1},
greenhalf = {0, 0.5, 0, 1},
greenfade = {0, 1, 0, 0.5},
blue = {0, 0, 1, 1},
bluefade = {0, 0, 1, 0.5},
yellow = {1, 1, 0, 1},
pink = {1, 0, 1, 1},
black = {0, 0, 0, 1},
white = {1, 1, 1, 1},
lovepink = {214/255, 86/255, 151/255, 1},
loveblue = {83/255, 168/255, 220/255, 1}
},
imgs = 1,
delay = 0,
delayed = false,
store = {},
co = nil
}
setmetatable(test, self)
self.__index = self
return test
end,
-- @method - TestMethod:assertEquals()
-- @desc - used to assert two values are equals
-- @param {any} expected - expected value of the test
-- @param {any} actual - actual value of the test
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertEquals = function(self, expected, actual, label)
self.count = self.count + 1
table.insert(self.asserts, {
key = 'assert ' .. tostring(self.count),
passed = expected == actual,
message = 'expected \'' .. tostring(expected) .. '\' got \'' ..
tostring(actual) .. '\'',
test = label or 'no label given'
})
end,
-- @method - TestMethod:assertTrue()
-- @desc - used to assert a value is true
-- @param {any} value - value to test
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertTrue = function(self, value, label)
self.count = self.count + 1
table.insert(self.asserts, {
key = 'assert ' .. tostring(self.count),
passed = value == true,
message = 'expected \'true\' got \'' ..
tostring(value) .. '\'',
test = label or 'no label given'
})
end,
-- @method - TestMethod:assertFalse()
-- @desc - used to assert a value is false
-- @param {any} value - value to test
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertFalse = function(self, value, label)
self.count = self.count + 1
table.insert(self.asserts, {
key = 'assert ' .. tostring(self.count),
passed = value == false,
message = 'expected \'false\' got \'' ..
tostring(value) .. '\'',
test = label or 'no label given'
})
end,
-- @method - TestMethod:assertNotEquals()
-- @desc - used to assert two values are not equal
-- @param {any} expected - expected value of the test
-- @param {any} actual - actual value of the test
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertNotEquals = function(self, expected, actual, label)
self.count = self.count + 1
table.insert(self.asserts, {
key = 'assert ' .. tostring(self.count),
passed = expected ~= actual,
message = 'avoiding \'' .. tostring(expected) .. '\' got \'' ..
tostring(actual) .. '\'',
test = label or 'no label given'
})
end,
-- @method - TestMethod:assertPixels()
-- @desc - checks a list of coloured pixels agaisnt given imgdata
-- @param {ImageData} imgdata - image data to check
-- @param {table} pixels - map of colors to list of pixel coords, i.e.
-- { blue = { {1, 1}, {2, 2}, {3, 4} } }
-- @return {nil}
assertPixels = function(self, imgdata, pixels, label)
for i, v in pairs(pixels) do
local col = self.colors[i]
local pixels = v
for p=1,#pixels do
local coord = pixels[p]
local tr, tg, tb, ta = imgdata:getPixel(coord[1], coord[2])
local compare_id = tostring(coord[1]) .. ',' .. tostring(coord[2])
-- prevent us getting stuff like 0.501960785 for 0.5 red
tr = math.floor((tr*10)+0.5)/10
tg = math.floor((tg*10)+0.5)/10
tb = math.floor((tb*10)+0.5)/10
ta = math.floor((ta*10)+0.5)/10
col[1] = math.floor((col[1]*10)+0.5)/10
col[2] = math.floor((col[2]*10)+0.5)/10
col[3] = math.floor((col[3]*10)+0.5)/10
col[4] = math.floor((col[4]*10)+0.5)/10
self:assertEquals(col[1], tr, 'check pixel r for ' .. i .. ' at ' .. compare_id .. '(' .. label .. ')')
self:assertEquals(col[2], tg, 'check pixel g for ' .. i .. ' at ' .. compare_id .. '(' .. label .. ')')
self:assertEquals(col[3], tb, 'check pixel b for ' .. i .. ' at ' .. compare_id .. '(' .. label .. ')')
self:assertEquals(col[4], ta, 'check pixel a for ' .. i .. ' at ' .. compare_id .. '(' .. label .. ')')
end
end
end,
-- @method - TestMethod:assertRange()
-- @desc - used to check a value is within an expected range
-- @param {number} actual - actual value of the test
-- @param {number} min - minimum value the actual should be >= to
-- @param {number} max - maximum value the actual should be <= to
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertRange = function(self, actual, min, max, label)
self.count = self.count + 1
table.insert(self.asserts, {
key = 'assert ' .. tostring(self.count),
passed = actual >= min and actual <= max,
message = 'value \'' .. tostring(actual) .. '\' out of range \'' ..
tostring(min) .. '-' .. tostring(max) .. '\'',
test = label or 'no label given'
})
end,
-- @method - TestMethod:assertMatch()
-- @desc - used to check a value is within a list of values
-- @param {number} list - list of valid values for the test
-- @param {number} actual - actual value of the test to check is in the list
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertMatch = function(self, list, actual, label)
self.count = self.count + 1
local found = false
for l=1,#list do
if list[l] == actual then found = true end;
end
table.insert(self.asserts, {
key = 'assert ' .. tostring(self.count),
passed = found == true,
message = 'value \'' .. tostring(actual) .. '\' not found in \'' ..
table.concat(list, ',') .. '\'',
test = label or 'no label given'
})
end,
-- @method - TestMethod:assertGreaterEqual()
-- @desc - used to check a value is >= than a certain target value
-- @param {any} target - value to check the test agaisnt
-- @param {any} actual - actual value of the test
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertGreaterEqual = function(self, target, actual, label)
self.count = self.count + 1
local passing = false
if target ~= nil and actual ~= nil then
passing = actual >= target
end
table.insert(self.asserts, {
key = 'assert ' .. tostring(self.count),
passed = passing,
message = 'value \'' .. tostring(actual) .. '\' not >= \'' ..
tostring(target) .. '\'',
test = label or 'no label given'
})
end,
-- @method - TestMethod:assertLessEqual()
-- @desc - used to check a value is <= than a certain target value
-- @param {any} target - value to check the test agaisnt
-- @param {any} actual - actual value of the test
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertLessEqual = function(self, target, actual, label)
self.count = self.count + 1
local passing = false
if target ~= nil and actual ~= nil then
passing = actual <= target
end
table.insert(self.asserts, {
key = 'assert ' .. tostring(self.count),
passed = passing,
message = 'value \'' .. tostring(actual) .. '\' not <= \'' ..
tostring(target) .. '\'',
test = label or 'no label given'
})
end,
-- @method - TestMethod:assertObject()
-- @desc - used to check a table is a love object, this runs 3 seperate
-- tests to check table has the basic properties of an object
-- @note - actual object functionality tests have their own methods
-- @param {table} obj - table to check is a valid love object
-- @return {nil}
assertObject = function(self, obj)
self:assertNotNil(obj)
self:assertEquals('userdata', type(obj), 'check is userdata')
if obj ~= nil then
self:assertNotEquals(nil, obj:type(), 'check has :type()')
end
end,
-- @method - TestMethod:assertCoords()
-- @desc - used to check a pair of values (usually coordinates)
-- @param {table} obj - table to check is a valid love object
-- @return {nil}
assertCoords = function(self, expected, actual, label)
self.count = self.count + 1
local passing = false
if expected ~= nil and actual ~= nil then
if expected[1] == actual[1] and expected[2] == actual[2] then
passing = true
end
end
table.insert(self.asserts, {
key = 'assert ' .. tostring(self.count),
passed = passing,
message = 'expected \'' .. tostring(expected[1]) .. 'x,' ..
tostring(expected[2]) .. 'y\' got \'' ..
tostring(actual[1]) .. 'x,' .. tostring(actual[2]) .. 'y\'',
test = label or 'no label given'
})
end,
-- @method - TestMethod:assertNotNil()
-- @desc - quick assert for value not nil
-- @param {any} value - value to check not nil
-- @return {nil}
assertNotNil = function (self, value, err)
self:assertNotEquals(nil, value, 'check not nil')
if err ~= nil then
table.insert(self.asserts, {
key = 'assert ' .. tostring(self.count),
passed = false,
message = err,
test = 'assert not nil catch'
})
end
end,
-- @method - TestMethod:compareImg()
-- @desc - compares a given image to the 'expected' version, with a tolerance of
-- 1px in any direction, and then saves it as the 'actual' version for
-- report viewing
-- @param {table} imgdata - imgdata to save as a png
-- @return {nil}
compareImg = function(self, imgdata)
local expected = love.image.newImageData(
'tempoutput/expected/love.test.graphics.' .. self.method .. '-' ..
tostring(self.imgs) .. '.png'
)
local iw = imgdata:getWidth()-2
local ih = imgdata:getHeight()-2
local rgba_tolerance = self.rgba_tolerance * (1/255)
for ix=2,iw do
for iy=2,ih do
local ir, ig, ib, ia = imgdata:getPixel(ix, iy)
local points = {
{expected:getPixel(ix, iy)}
}
if self.pixel_tolerance > 0 then
table.insert(points, {expected:getPixel(ix-1, iy+1)})
table.insert(points, {expected:getPixel(ix-1, iy)})
table.insert(points, {expected:getPixel(ix-1, iy-1)})
table.insert(points, {expected:getPixel(ix, iy+1)})
table.insert(points, {expected:getPixel(ix, iy-1)})
table.insert(points, {expected:getPixel(ix+1, iy+1)})
table.insert(points, {expected:getPixel(ix+1, iy)})
table.insert(points, {expected:getPixel(ix+1, iy-1)})
end
local has_match_r = false
local has_match_g = false
local has_match_b = false
local has_match_a = false
for t=1,#points do
local epoint = points[t]
if ir >= epoint[1] - rgba_tolerance and ir <= epoint[1] + rgba_tolerance then has_match_r = true; end
if ig >= epoint[2] - rgba_tolerance and ig <= epoint[2] + rgba_tolerance then has_match_g = true; end
if ib >= epoint[3] - rgba_tolerance and ib <= epoint[3] + rgba_tolerance then has_match_b = true; end
if ia >= epoint[4] - rgba_tolerance and ia <= epoint[4] + rgba_tolerance then has_match_a = true; end
end
local matching = has_match_r and has_match_g and has_match_b and has_match_a
local ymatch = ''
local nmatch = ''
if has_match_r then ymatch = ymatch .. 'r' else nmatch = nmatch .. 'r' end
if has_match_g then ymatch = ymatch .. 'g' else nmatch = nmatch .. 'g' end
if has_match_b then ymatch = ymatch .. 'b' else nmatch = nmatch .. 'b' end
if has_match_a then ymatch = ymatch .. 'a' else nmatch = nmatch .. 'a' end
local pixel = tostring(ir)..','..tostring(ig)..','..tostring(ib)..','..tostring(ia)
self:assertEquals(true, matching, 'compare image pixel (' .. pixel .. ') at ' ..
tostring(ix) .. ',' .. tostring(iy) .. ', matching = ' .. ymatch ..
', not matching = ' .. nmatch .. ' (' .. self.method .. '-' .. tostring(self.imgs) .. ')'
)
end
end
local path = 'tempoutput/actual/love.test.graphics.' ..
self.method .. '-' .. tostring(self.imgs) .. '.png'
imgdata:encode('png', path)
self.imgs = self.imgs + 1
end,
-- @method - TestMethod:skipTest()
-- @desc - used to mark this test as skipped for a specific reason
-- @param {string} reason - reason why method is being skipped
-- @return {nil}
skipTest = function(self, reason)
self.skipped = true
self.skipreason = reason
end,
waitFrames = function(self, frames)
for i=1,frames do coroutine.yield() end
end,
-- @method - TestMethod:evaluateTest()
-- @desc - evaluates the results of all assertions for a final restult
-- @return {nil}
evaluateTest = function(self)
local failure = ''
local failures = 0
for a=1,#self.asserts do
-- @TODO just return first failed assertion msg? or all?
-- currently just shows the first assert that failed
if self.asserts[a].passed == false and self.skipped == false then
if failure == '' then failure = self.asserts[a] end
failures = failures + 1
end
end
if self.fatal ~= '' then failure = self.fatal end
local passed = tostring(#self.asserts - failures)
local total = '(' .. passed .. '/' .. tostring(#self.asserts) .. ')'
if self.skipped == true then
self.testmodule.skipped = self.testmodule.skipped + 1
love.test.totals[3] = love.test.totals[3] + 1
self.result = {
total = '',
result = "SKIP",
passed = false,
message = '(0/0) - method skipped [' .. self.skipreason .. ']'
}
else
if failure == '' and #self.asserts > 0 then
self.passed = true
self.testmodule.passed = self.testmodule.passed + 1
love.test.totals[1] = love.test.totals[1] + 1
self.result = {
total = total,
result = 'PASS',
passed = true,
message = nil
}
else
self.passed = false
self.testmodule.failed = self.testmodule.failed + 1
love.test.totals[2] = love.test.totals[2] + 1
if #self.asserts == 0 then
local msg = 'no asserts defined'
if self.fatal ~= '' then msg = self.fatal end
self.result = {
total = total,
result = 'FAIL',
passed = false,
key = 'test',
message = msg
}
else
local key = failure['key']
if failure['test'] ~= nil then
key = key .. ' [' .. failure['test'] .. ']'
end
local msg = failure['message']
if self.fatal ~= '' then
key = 'code'
msg = self.fatal
end
self.result = {
total = total,
result = 'FAIL',
passed = false,
key = key,
message = msg
}
end
end
end
self:printResult()
end,
-- @method - TestMethod:printResult()
-- @desc - prints the result of the test to the console as well as appends
-- the XML + HTML for the test to the testsuite output
-- @return {nil}
printResult = function(self)
-- get total timestamp
-- @TODO make nicer, just need a 3DP ms value
self.finish = love.timer.getTime() - self.start
love.test.time = love.test.time + self.finish
self.testmodule.time = self.testmodule.time + self.finish
local endtime = UtilTimeFormat(love.timer.getTime() - self.start)
-- get failure/skip message for output (if any)
local failure = ''
local output = ''
if self.passed == false and self.skipped == false then
failure = '\t\t\t<failure message="' .. self.result.key .. ' ' ..
self.result.message .. '">' .. self.result.key .. ' ' .. self.result.message .. '</failure>\n'
output = self.result.key .. ' ' .. self.result.message
-- append failures if any to report md
love.test.mdfailures = love.test.mdfailures .. '> 🔴 ' .. self.method .. ' \n' ..
'> ' .. output .. ' \n\n'
end
if output == '' and self.skipped == true then
failure = '\t\t\t<skipped message="' .. self.skipreason .. '" />\n'
output = self.skipreason
end
-- append XML for the test class result
self.testmodule.xml = self.testmodule.xml .. '\t\t<testcase classname="' ..
self.method .. '" name="' .. self.method .. '" assertions="' .. tostring(#self.asserts) ..
'" time="' .. endtime .. '">\n' ..
failure .. '\t\t</testcase>\n'
-- unused currently, adds a preview image for certain graphics methods to the output
local preview = ''
if self.testmodule.module == 'graphics' then
local filename = 'love.test.graphics.' .. self.method
if love.filesystem.openFile('tempoutput/actual/' .. filename .. '-1.png', 'r') then
preview = '<div class="preview">' .. '<img src="expected/' .. filename .. '-1.png"/><p>Expected</p></div>' ..
'<div class="preview">' .. '<img src="actual/' .. filename .. '-1.png"/><p>Actual</p></div>'
end
if love.filesystem.openFile('tempoutput/actual/' .. filename .. '-2.png', 'r') then
preview = preview .. '<div class="preview">' .. '<img src="expected/' .. filename .. '-2.png"/><p>Expected</p></div>' ..
'<div class="preview">' .. '<img src="actual/' .. filename .. '-2.png"/><p>Actual</p></div>'
end
if love.filesystem.openFile('tempoutput/actual/' .. filename .. '-3.png', 'r') then
preview = preview .. '<div class="preview">' .. '<img src="expected/' .. filename .. '-3.png"/><p>Expected</p></div>' ..
'<div class="preview">' .. '<img src="actual/' .. filename .. '-3.png"/><p>Actual</p></div>'
end
end
-- append HTML for the test class result
local status = '🔴'
local cls = 'red'
if self.passed == true then status = '🟢'; cls = '' end
if self.skipped == true then status = '🟡'; cls = '' end
self.testmodule.html = self.testmodule.html ..
'<tr class=" ' .. cls .. '">' ..
'<td>' .. status .. '</td>' ..
'<td>' .. self.method .. '</td>' ..
'<td>' .. endtime .. 's</td>' ..
'<td>' .. output .. preview .. '</td>' ..
'</tr>'
-- add message if assert failed
local msg = ''
if self.result.message ~= nil and self.skipped == false then
msg = ' - ' .. self.result.key ..
' failed - (' .. self.result.message .. ')'
end
if self.skipped == true then
msg = self.result.message
end
-- log final test result to console
-- i know its hacky but its neat soz
local tested = 'love.' .. self.testmodule.module .. '.' .. self.method .. '()'
local matching = string.sub(self.testmodule.spacer, string.len(tested), 40)
self.testmodule:log(
self.testmodule.colors[self.result.result],
' ' .. tested .. matching,
' ==> ' .. self.result.result .. ' - ' .. endtime .. 's ' ..
self.result.total .. msg
)
end
}
+120
View File
@@ -0,0 +1,120 @@
-- @class - TestModule
-- @desc - used to run tests for a given module, each test method will spawn
-- a love.test.Test object
TestModule = {
-- @method - TestModule:new()
-- @desc - create a new Suite object
-- @param {string} module - string of love module the suite is for
-- @return {table} - returns the new Suite object
new = function(self, module, method)
local testmodule = {
time = 0,
spacer = ' ',
colors = {
PASS = 'green', FAIL = 'red', SKIP = 'grey'
},
colormap = {
grey = '\27[37m',
green = '\27[32m',
red = '\27[31m',
yellow = '\27[33m'
},
xml = '',
html = '',
tests = {},
running = {},
called = {},
passed = 0,
failed = 0,
skipped = 0,
module = module,
method = method,
index = 1,
start = false,
}
setmetatable(testmodule, self)
self.__index = self
return testmodule
end,
-- @method - TestModule:log()
-- @desc - log to console with specific colors, split out to make it easier
-- to adjust all console output across the tests
-- @param {string} color - color key to use for the log
-- @param {string} line - main message to write (LHS)
-- @param {string} result - result message to write (RHS)
-- @return {nil}
log = function(self, color, line, result)
if result == nil then result = '' end
print(self.colormap[color] .. line .. result)
end,
-- @method - TestModule:runTests()
-- @desc - starts the running of tests and sets up the list of methods to test
-- @param {string} module - module to set for the test suite
-- @param {string} method - specific method to test, if nil all methods tested
-- @return {nil}
runTests = function(self)
self.running = {}
self.passed = 0
self.failed = 0
if self.method ~= nil then
table.insert(self.running, self.method)
else
for i,_ in pairs(love.test[self.module]) do
table.insert(self.running, i)
end
table.sort(self.running)
end
self.index = 1
self.start = true
self:log('yellow', '\nlove.' .. self.module .. '.testmodule.start')
end,
-- @method - TestModule:printResult()
-- @desc - prints the result of the module to the console as well as appends
-- the XML + HTML for the test to the testsuite output
-- @return {nil}
printResult = function(self)
local finaltime = UtilTimeFormat(self.time)
local status = '🔴'
if self.failed == 0 then status = '🟢' end
-- add md row to main output
love.test.mdrows = love.test.mdrows .. '| ' .. status ..
' ' .. self.module ..
' | ' .. tostring(self.passed) ..
' | ' .. tostring(self.failed) ..
' | ' .. tostring(self.skipped) ..
' | ' .. finaltime .. 's |' .. '\n'
-- add xml to main output
love.test.xml = love.test.xml .. '\t<testsuite name="love.' .. self.module ..
'" tests="' .. tostring(self.passed) ..
'" failures="' .. tostring(self.failed) ..
'" skipped="' .. tostring(self.skipped) ..
'" time="' .. finaltime .. '">\n' .. self.xml .. '\t</testsuite>\n'
-- add html to main output
love.test.html = love.test.html .. '<h2>' .. status .. '&nbsp;love.' .. self.module .. '</h2><ul class="summary">' ..
'<li>🟢&nbsp;' .. tostring(self.passed) .. ' Tests</li>' ..
'<li>🔴&nbsp;' .. tostring(self.failed) .. ' Failures</li>' ..
'<li>🟡&nbsp;' .. tostring(self.skipped) .. ' Skipped</li>' ..
'<li>' .. finaltime .. 's</li>' .. '<ul><br/><br/>' ..
'<table><thead><tr><td width="20px"></td><td width="100px">Method</td><td width="60px">Time</td><td>Details</td></tr></thead><tbody>' ..
self.html .. '</tbody></table>'
-- print module results to console
self:log('yellow', 'love.' .. self.module .. '.testmodule.end')
local failedcol = '\27[31m'
if self.failed == 0 then failedcol = '\27[37m' end
self:log('green', tostring(self.passed) .. ' PASSED' .. ' || ' ..
failedcol .. tostring(self.failed) .. ' FAILED || \27[37m' ..
tostring(self.skipped) .. ' SKIPPED || ' .. finaltime .. 's')
self.start = false
self.fakequit = false
end
}
+183
View File
@@ -0,0 +1,183 @@
TestSuite = {
-- @method - TestSuite:new()
-- @desc - creates a new TestSuite object that handles all the tests
-- @return {table} - returns the new TestSuite object
new = function(self)
local test = {
-- testsuite internals
modules = {},
module = nil,
test = nil,
testcanvas = nil,
current = 1,
output = '',
totals = {0, 0, 0},
time = 0,
xml = '',
html = '',
mdrows = '',
mdfailures = '',
delayed = nil,
fakequit = false,
windowmode = true,
-- love modules to test
audio = {},
data = {},
event = {},
filesystem = {},
font = {},
graphics = {},
image = {},
joystick = {},
math = {},
mouse = {},
physics = {},
sound = {},
system = {},
thread = {},
timer = {},
touch = {},
video = {},
window = {}
}
setmetatable(test, self)
self.__index = self
return test
end,
-- @method - TestSuite:runSuite()
-- @desc - called in love.update, runs through every method or every module
-- @param {number} delta - delta from love.update to track time elapsed
-- @return {nil}
runSuite = function(self, delta)
-- stagger between tests
if self.module ~= nil then
if self.module.start == true then
-- work through each test method 1 by 1
if self.module.index <= #self.module.running then
-- run method once
if self.module.called[self.module.index] == nil then
self.module.called[self.module.index] = true
local method = self.module.running[self.module.index]
self.test = TestMethod:new(method, self.module)
TextRun:set('love.' .. self.module.module .. '.' .. method)
self.test.co = coroutine.create(function()
local ok, chunk, err = pcall(love.test[love.test.module.module][method], love.test.test)
if ok == false then
love.test.test.passed = false
love.test.test.fatal = tostring(chunk) .. tostring(err)
end
end)
-- once called we have a corouting, so just call resume every frame
-- until we have finished
else
-- move onto next yield if any
-- pauses can be set with TestMethod:waitFrames(frames)
coroutine.resume(self.test.co)
-- when wait finished (or no yields)
if coroutine.status(self.test.co) == 'dead' then
-- now we're all done evaluate the test
local ok, chunk, err = pcall(self.test.evaluateTest, self.test)
if ok == false then
self.test.passed = false
self.test.fatal = tostring(chunk) .. tostring(err)
end
-- save having to :release() anything we made in the last test
collectgarbage("collect")
-- move onto the next test
self.module.index = self.module.index + 1
end
end
-- once all tests have run
else
-- print module results and add to output
self.module:printResult()
-- if we have more modules to go run the next one
self.current = self.current + 1
if #self.modules >= self.current then
self.module = self.modules[self.current]
self.module:runTests()
-- otherwise print the final results and export output
else
self:printResult()
love.event.quit(0)
end
end
end
end
end,
-- @method - TestSuite:printResult()
-- @desc - prints the result of the whole test suite as well as writes
-- the MD, XML + HTML of the testsuite output
-- @return {nil}
printResult = function(self)
local finaltime = UtilTimeFormat(self.time)
local name, version, vendor, device = love.graphics.getRendererInfo()
local md = '<!-- PASSED ' .. tostring(self.totals[1]) ..
' || FAILED ' .. tostring(self.totals[2]) ..
' || SKIPPED ' .. tostring(self.totals[3]) ..
' || TIME ' .. finaltime .. ' -->\n\n### Info\n' ..
'**' .. tostring(self.totals[1] + self.totals[2] + self.totals[3]) .. '** tests were completed in **' ..
finaltime .. 's** with **' ..
tostring(self.totals[1]) .. '** passed, **' ..
tostring(self.totals[2]) .. '** failed, and **' ..
tostring(self.totals[3]) .. '** skipped\n\n' ..
'Renderer: ' .. name .. ' | ' .. version .. ' | ' .. vendor .. ' | ' .. device .. '\n\n' ..
'### Report\n' ..
'| Module | Pass | Fail | Skip | Time |\n' ..
'| --------------------- | ------ | ------ | ------- | ------ |\n' ..
self.mdrows .. '\n### Failures\n' .. self.mdfailures
local xml = '<testsuites name="love.test" tests="' .. tostring(self.totals[1]) ..
'" failures="' .. tostring(self.totals[2]) ..
'" skipped="' .. tostring(self.totals[3]) ..
'" time="' .. finaltime .. '">\n'
local status = '🔴'
if self.totals[2] == 0 then status = '🟢' end
local html = '<html><head><style>* { font-family: monospace; margin: 0; font-size: 11px; padding: 0; } body { margin: 50px; } h1 { padding-bottom: 10px; font-size: 13px; } h2 { padding: 20px 0 10px 0; font-size: 12px; } .summary { list-style: none; margin: 0; padding: 0; } .summary li { float: left; background: #eee; padding: 5px; margin-right: 10px; } table { background: #eee; margin-top: 10px; width: 100%; max-width: 800px; border-collapse: collapse } table thead { background: #ddd; } table th, table td { padding: 2px; } tr.red { color: red } .wrap { max-width: 800px; margin: auto; } .preview { width: 64px; height: 80px; float: left; margin-right: 10px; } .preview img { width: 100% } .preview p { text-align: center; }</style></head><body><div class="wrap"><h1>' .. status .. '&nbsp;love.test</h1><ul class="summary">'
html = html ..
'<li>🟢&nbsp;' .. tostring(self.totals[1]) .. ' Tests</li>' ..
'<li>🔴&nbsp;' .. tostring(self.totals[2]) .. ' Failures</li>' ..
'<li>🟡&nbsp;' .. tostring(self.totals[3]) .. ' Skipped</li>' ..
'<li>' .. finaltime .. 's</li></ul><br/><br/>'
love.filesystem.write('tempoutput/' .. self.output .. '.xml', xml .. self.xml .. '</testsuites>')
love.filesystem.write('tempoutput/' .. self.output .. '.html', html .. self.html .. '</div></body></html>')
love.filesystem.write('tempoutput/' .. self.output .. '.md', md)
self.module:log('grey', '\nFINISHED - ' .. finaltime .. 's\n')
local failedcol = '\27[31m'
if self.totals[2] == 0 then failedcol = '\27[37m' end
self.module:log('green', tostring(self.totals[1]) .. ' PASSED' .. ' || ' .. failedcol .. tostring(self.totals[2]) .. ' FAILED || \27[37m' .. tostring(self.totals[3]) .. ' SKIPPED')
end
}
+24
View File
@@ -0,0 +1,24 @@
function love.conf(t)
t.console = true
t.window.name = 'love.test'
t.window.width = 360
t.window.height = 240
t.window.resizable = true
t.renderers = {"opengl"}
t.modules.audio = true
t.modules.data = true
t.modules.event = true
t.modules.filesystem = true
t.modules.font = true
t.modules.graphics = true
t.modules.image = true
t.modules.math = true
t.modules.objects = true
t.modules.physics = true
t.modules.sound = true
t.modules.system = true
t.modules.thread = true
t.modules.timer = true
t.modules.video = true
t.modules.window = true
end
File diff suppressed because one or more lines are too long
+27
View File
@@ -0,0 +1,27 @@
<!-- PASSED 287 || FAILED 0 || SKIPPED 14 || TIME 14.726 -->
### Info
**301** tests were completed in **14.726s** with **287** passed, **0** failed, and **14** skipped
Renderer: OpenGL | 4.1 Metal - 76.3 | Apple | Apple M1 Max
### Report
| Module | Pass | Fail | Skip | Time |
| --------------------- | ------ | ------ | ------- | ------ |
| 🟢 audio | 28 | 0 | 0 | 0.894s |
| 🟢 data | 12 | 0 | 0 | 0.209s |
| 🟢 event | 4 | 0 | 2 | 0.103s |
| 🟢 filesystem | 29 | 0 | 2 | 0.550s |
| 🟢 font | 7 | 0 | 0 | 0.124s |
| 🟢 graphics | 102 | 0 | 3 | 3.231s |
| 🟢 image | 5 | 0 | 0 | 0.098s |
| 🟢 math | 20 | 0 | 0 | 0.353s |
| 🟢 physics | 23 | 0 | 3 | 0.449s |
| 🟢 sound | 4 | 0 | 0 | 0.074s |
| 🟢 system | 6 | 0 | 2 | 0.144s |
| 🟢 thread | 5 | 0 | 0 | 0.377s |
| 🟢 timer | 6 | 0 | 0 | 2.093s |
| 🟢 video | 2 | 0 | 0 | 0.040s |
| 🟢 window | 34 | 0 | 2 | 5.986s |
### Failures
+648
View File
@@ -0,0 +1,648 @@
<testsuites name="love.test" tests="287" failures="0" skipped="14" time="14.726">
<testsuite name="love.audio" tests="28" failures="0" skipped="0" time="0.894">
<testcase classname="RecordingDevice" name="RecordingDevice" assertions="20" time="0.398">
</testcase>
<testcase classname="Source" name="Source" assertions="54" time="0.024">
</testcase>
<testcase classname="getActiveEffects" name="getActiveEffects" assertions="3" time="0.017">
</testcase>
<testcase classname="getActiveSourceCount" name="getActiveSourceCount" assertions="3" time="0.018">
</testcase>
<testcase classname="getDistanceModel" name="getDistanceModel" assertions="3" time="0.018">
</testcase>
<testcase classname="getDopplerScale" name="getDopplerScale" assertions="2" time="0.019">
</testcase>
<testcase classname="getEffect" name="getEffect" assertions="4" time="0.019">
</testcase>
<testcase classname="getMaxSceneEffects" name="getMaxSceneEffects" assertions="1" time="0.016">
</testcase>
<testcase classname="getMaxSourceEffects" name="getMaxSourceEffects" assertions="1" time="0.017">
</testcase>
<testcase classname="getOrientation" name="getOrientation" assertions="6" time="0.019">
</testcase>
<testcase classname="getPosition" name="getPosition" assertions="3" time="0.019">
</testcase>
<testcase classname="getRecordingDevices" name="getRecordingDevices" assertions="1" time="0.018">
</testcase>
<testcase classname="getVelocity" name="getVelocity" assertions="3" time="0.019">
</testcase>
<testcase classname="getVolume" name="getVolume" assertions="1" time="0.018">
</testcase>
<testcase classname="isEffectsSupported" name="isEffectsSupported" assertions="1" time="0.018">
</testcase>
<testcase classname="newQueueableSource" name="newQueueableSource" assertions="3" time="0.019">
</testcase>
<testcase classname="newSource" name="newSource" assertions="6" time="0.019">
</testcase>
<testcase classname="pause" name="pause" assertions="2" time="0.020">
</testcase>
<testcase classname="play" name="play" assertions="1" time="0.018">
</testcase>
<testcase classname="setDistanceModel" name="setDistanceModel" assertions="7" time="0.017">
</testcase>
<testcase classname="setDopplerScale" name="setDopplerScale" assertions="2" time="0.019">
</testcase>
<testcase classname="setEffect" name="setEffect" assertions="3" time="0.018">
</testcase>
<testcase classname="setMixWithSystem" name="setMixWithSystem" assertions="1" time="0.018">
</testcase>
<testcase classname="setOrientation" name="setOrientation" assertions="6" time="0.018">
</testcase>
<testcase classname="setPosition" name="setPosition" assertions="3" time="0.017">
</testcase>
<testcase classname="setVelocity" name="setVelocity" assertions="3" time="0.018">
</testcase>
<testcase classname="setVolume" name="setVolume" assertions="1" time="0.018">
</testcase>
<testcase classname="stop" name="stop" assertions="2" time="0.019">
</testcase>
</testsuite>
<testsuite name="love.data" tests="12" failures="0" skipped="0" time="0.209">
<testcase classname="ByteData" name="ByteData" assertions="10" time="0.015">
</testcase>
<testcase classname="CompressedData" name="CompressedData" assertions="12" time="0.018">
</testcase>
<testcase classname="compress" name="compress" assertions="45" time="0.019">
</testcase>
<testcase classname="decode" name="decode" assertions="4" time="0.014">
</testcase>
<testcase classname="decompress" name="decompress" assertions="18" time="0.018">
</testcase>
<testcase classname="encode" name="encode" assertions="15" time="0.019">
</testcase>
<testcase classname="getPackedSize" name="getPackedSize" assertions="3" time="0.019">
</testcase>
<testcase classname="hash" name="hash" assertions="6" time="0.016">
</testcase>
<testcase classname="newByteData" name="newByteData" assertions="3" time="0.017">
</testcase>
<testcase classname="newDataView" name="newDataView" assertions="3" time="0.017">
</testcase>
<testcase classname="pack" name="pack" assertions="4" time="0.018">
</testcase>
<testcase classname="unpack" name="unpack" assertions="3" time="0.018">
</testcase>
</testsuite>
<testsuite name="love.event" tests="4" failures="0" skipped="2" time="0.103">
<testcase classname="clear" name="clear" assertions="1" time="0.016">
</testcase>
<testcase classname="poll" name="poll" assertions="1" time="0.018">
</testcase>
<testcase classname="pump" name="pump" assertions="0" time="0.018">
<skipped message="used internally" />
</testcase>
<testcase classname="push" name="push" assertions="1" time="0.016">
</testcase>
<testcase classname="quit" name="quit" assertions="1" time="0.016">
</testcase>
<testcase classname="wait" name="wait" assertions="0" time="0.019">
<skipped message="used internally" />
</testcase>
</testsuite>
<testsuite name="love.filesystem" tests="29" failures="0" skipped="2" time="0.550">
<testcase classname="File" name="File" assertions="36" time="0.019">
</testcase>
<testcase classname="FileData" name="FileData" assertions="12" time="0.017">
</testcase>
<testcase classname="append" name="append" assertions="6" time="0.020">
</testcase>
<testcase classname="areSymlinksEnabled" name="areSymlinksEnabled" assertions="1" time="0.017">
</testcase>
<testcase classname="createDirectory" name="createDirectory" assertions="3" time="0.017">
</testcase>
<testcase classname="getAppdataDirectory" name="getAppdataDirectory" assertions="1" time="0.017">
</testcase>
<testcase classname="getCRequirePath" name="getCRequirePath" assertions="1" time="0.018">
</testcase>
<testcase classname="getDirectoryItems" name="getDirectoryItems" assertions="2" time="0.018">
</testcase>
<testcase classname="getIdentity" name="getIdentity" assertions="1" time="0.017">
</testcase>
<testcase classname="getInfo" name="getInfo" assertions="3" time="0.019">
</testcase>
<testcase classname="getRealDirectory" name="getRealDirectory" assertions="1" time="0.019">
</testcase>
<testcase classname="getRequirePath" name="getRequirePath" assertions="1" time="0.018">
</testcase>
<testcase classname="getSaveDirectory" name="getSaveDirectory" assertions="1" time="0.017">
</testcase>
<testcase classname="getSource" name="getSource" assertions="0" time="0.017">
<skipped message="used internally" />
</testcase>
<testcase classname="getSourceBaseDirectory" name="getSourceBaseDirectory" assertions="1" time="0.020">
</testcase>
<testcase classname="getUserDirectory" name="getUserDirectory" assertions="1" time="0.018">
</testcase>
<testcase classname="getWorkingDirectory" name="getWorkingDirectory" assertions="1" time="0.018">
</testcase>
<testcase classname="isFused" name="isFused" assertions="1" time="0.018">
</testcase>
<testcase classname="lines" name="lines" assertions="6" time="0.020">
</testcase>
<testcase classname="load" name="load" assertions="4" time="0.018">
</testcase>
<testcase classname="mount" name="mount" assertions="5" time="0.018">
</testcase>
<testcase classname="newFileData" name="newFileData" assertions="1" time="0.018">
</testcase>
<testcase classname="openFile" name="openFile" assertions="4" time="0.018">
</testcase>
<testcase classname="read" name="read" assertions="6" time="0.017">
</testcase>
<testcase classname="remove" name="remove" assertions="5" time="0.018">
</testcase>
<testcase classname="setCRequirePath" name="setCRequirePath" assertions="1" time="0.017">
</testcase>
<testcase classname="setIdentity" name="setIdentity" assertions="1" time="0.015">
</testcase>
<testcase classname="setRequirePath" name="setRequirePath" assertions="1" time="0.016">
</testcase>
<testcase classname="setSource" name="setSource" assertions="0" time="0.016">
<skipped message="used internally" />
</testcase>
<testcase classname="unmount" name="unmount" assertions="2" time="0.018">
</testcase>
<testcase classname="write" name="write" assertions="3" time="0.017">
</testcase>
</testsuite>
<testsuite name="love.font" tests="7" failures="0" skipped="0" time="0.124">
<testcase classname="GlyphData" name="GlyphData" assertions="17" time="0.016">
</testcase>
<testcase classname="Rasterizer" name="Rasterizer" assertions="13" time="0.016">
</testcase>
<testcase classname="newBMFontRasterizer" name="newBMFontRasterizer" assertions="3" time="0.018">
</testcase>
<testcase classname="newGlyphData" name="newGlyphData" assertions="3" time="0.018">
</testcase>
<testcase classname="newImageRasterizer" name="newImageRasterizer" assertions="3" time="0.018">
</testcase>
<testcase classname="newRasterizer" name="newRasterizer" assertions="3" time="0.018">
</testcase>
<testcase classname="newTrueTypeRasterizer" name="newTrueTypeRasterizer" assertions="6" time="0.020">
</testcase>
</testsuite>
<testsuite name="love.graphics" tests="102" failures="0" skipped="3" time="3.231">
<testcase classname="Canvas" name="Canvas" assertions="44" time="0.019">
</testcase>
<testcase classname="Font" name="Font" assertions="90" time="0.021">
</testcase>
<testcase classname="Image" name="Image" assertions="40" time="0.020">
</testcase>
<testcase classname="Mesh" name="Mesh" assertions="0" time="0.018">
<skipped message="test class needs writing" />
</testcase>
<testcase classname="ParticleSystem" name="ParticleSystem" assertions="0" time="0.017">
<skipped message="test class needs writing" />
</testcase>
<testcase classname="Quad" name="Quad" assertions="47" time="0.018">
</testcase>
<testcase classname="Shader" name="Shader" assertions="39" time="0.029">
</testcase>
<testcase classname="SpriteBatch" name="SpriteBatch" assertions="203" time="0.027">
</testcase>
<testcase classname="Text" name="Text" assertions="90" time="0.018">
</testcase>
<testcase classname="Video" name="Video" assertions="48" time="1.040">
</testcase>
<testcase classname="applyTransform" name="applyTransform" assertions="4" time="0.006">
</testcase>
<testcase classname="arc" name="arc" assertions="264" time="0.019">
</testcase>
<testcase classname="captureScreenshot" name="captureScreenshot" assertions="1" time="0.183">
</testcase>
<testcase classname="circle" name="circle" assertions="192" time="0.020">
</testcase>
<testcase classname="clear" name="clear" assertions="20" time="0.031">
</testcase>
<testcase classname="discard" name="discard" assertions="0" time="0.016">
<skipped message="cant test this worked" />
</testcase>
<testcase classname="draw" name="draw" assertions="112" time="0.017">
</testcase>
<testcase classname="drawInstanced" name="drawInstanced" assertions="16" time="0.030">
</testcase>
<testcase classname="drawLayer" name="drawLayer" assertions="144" time="0.025">
</testcase>
<testcase classname="ellipse" name="ellipse" assertions="80" time="0.017">
</testcase>
<testcase classname="flushBatch" name="flushBatch" assertions="1" time="0.018">
</testcase>
<testcase classname="getBackgroundColor" name="getBackgroundColor" assertions="8" time="0.018">
</testcase>
<testcase classname="getBlendMode" name="getBlendMode" assertions="4" time="0.019">
</testcase>
<testcase classname="getCanvas" name="getCanvas" assertions="4" time="0.018">
</testcase>
<testcase classname="getColor" name="getColor" assertions="8" time="0.018">
</testcase>
<testcase classname="getColorMask" name="getColorMask" assertions="8" time="0.017">
</testcase>
<testcase classname="getDPIScale" name="getDPIScale" assertions="1" time="0.018">
</testcase>
<testcase classname="getDefaultFilter" name="getDefaultFilter" assertions="3" time="0.018">
</testcase>
<testcase classname="getDepthMode" name="getDepthMode" assertions="2" time="0.017">
</testcase>
<testcase classname="getDimensions" name="getDimensions" assertions="2" time="0.017">
</testcase>
<testcase classname="getFont" name="getFont" assertions="3" time="0.018">
</testcase>
<testcase classname="getFrontFaceWinding" name="getFrontFaceWinding" assertions="2" time="0.019">
</testcase>
<testcase classname="getHeight" name="getHeight" assertions="1" time="0.017">
</testcase>
<testcase classname="getLineJoin" name="getLineJoin" assertions="2" time="0.017">
</testcase>
<testcase classname="getLineStyle" name="getLineStyle" assertions="2" time="0.018">
</testcase>
<testcase classname="getLineWidth" name="getLineWidth" assertions="2" time="0.019">
</testcase>
<testcase classname="getMeshCullMode" name="getMeshCullMode" assertions="2" time="0.018">
</testcase>
<testcase classname="getPixelDimensions" name="getPixelDimensions" assertions="2" time="0.018">
</testcase>
<testcase classname="getPixelHeight" name="getPixelHeight" assertions="1" time="0.018">
</testcase>
<testcase classname="getPixelWidth" name="getPixelWidth" assertions="1" time="0.018">
</testcase>
<testcase classname="getPointSize" name="getPointSize" assertions="2" time="0.018">
</testcase>
<testcase classname="getRendererInfo" name="getRendererInfo" assertions="4" time="0.019">
</testcase>
<testcase classname="getScissor" name="getScissor" assertions="8" time="0.017">
</testcase>
<testcase classname="getShader" name="getShader" assertions="1" time="0.017">
</testcase>
<testcase classname="getStackDepth" name="getStackDepth" assertions="4" time="0.018">
</testcase>
<testcase classname="getStats" name="getStats" assertions="7" time="0.017">
</testcase>
<testcase classname="getStencilMode" name="getStencilMode" assertions="6" time="0.019">
</testcase>
<testcase classname="getSupported" name="getSupported" assertions="17" time="0.016">
</testcase>
<testcase classname="getSystemLimits" name="getSystemLimits" assertions="13" time="0.017">
</testcase>
<testcase classname="getTextureFormats" name="getTextureFormats" assertions="79" time="0.019">
</testcase>
<testcase classname="getTextureTypes" name="getTextureTypes" assertions="4" time="0.018">
</testcase>
<testcase classname="getWidth" name="getWidth" assertions="1" time="0.018">
</testcase>
<testcase classname="intersectScissor" name="intersectScissor" assertions="20" time="0.019">
</testcase>
<testcase classname="inverseTransformPoint" name="inverseTransformPoint" assertions="4" time="0.018">
</testcase>
<testcase classname="isActive" name="isActive" assertions="1" time="0.019">
</testcase>
<testcase classname="isGammaCorrect" name="isGammaCorrect" assertions="1" time="0.017">
</testcase>
<testcase classname="isWireframe" name="isWireframe" assertions="2" time="0.017">
</testcase>
<testcase classname="line" name="line" assertions="64" time="0.020">
</testcase>
<testcase classname="newArrayImage" name="newArrayImage" assertions="3" time="0.018">
</testcase>
<testcase classname="newCanvas" name="newCanvas" assertions="6" time="0.015">
</testcase>
<testcase classname="newCubeImage" name="newCubeImage" assertions="3" time="0.019">
</testcase>
<testcase classname="newFont" name="newFont" assertions="6" time="0.019">
</testcase>
<testcase classname="newImage" name="newImage" assertions="3" time="0.016">
</testcase>
<testcase classname="newImageFont" name="newImageFont" assertions="3" time="0.017">
</testcase>
<testcase classname="newMesh" name="newMesh" assertions="3" time="0.017">
</testcase>
<testcase classname="newParticleSystem" name="newParticleSystem" assertions="3" time="0.018">
</testcase>
<testcase classname="newQuad" name="newQuad" assertions="3" time="0.018">
</testcase>
<testcase classname="newShader" name="newShader" assertions="3" time="0.023">
</testcase>
<testcase classname="newSpriteBatch" name="newSpriteBatch" assertions="3" time="0.016">
</testcase>
<testcase classname="newTextBatch" name="newTextBatch" assertions="3" time="0.018">
</testcase>
<testcase classname="newVideo" name="newVideo" assertions="3" time="0.021">
</testcase>
<testcase classname="newVolumeImage" name="newVolumeImage" assertions="3" time="0.016">
</testcase>
<testcase classname="origin" name="origin" assertions="4" time="0.018">
</testcase>
<testcase classname="points" name="points" assertions="48" time="0.020">
</testcase>
<testcase classname="polygon" name="polygon" assertions="56" time="0.018">
</testcase>
<testcase classname="pop" name="pop" assertions="4" time="0.019">
</testcase>
<testcase classname="print" name="print" assertions="148" time="0.019">
</testcase>
<testcase classname="printf" name="printf" assertions="180" time="0.018">
</testcase>
<testcase classname="push" name="push" assertions="4" time="0.026">
</testcase>
<testcase classname="rectangle" name="rectangle" assertions="116" time="0.025">
</testcase>
<testcase classname="replaceTransform" name="replaceTransform" assertions="4" time="0.019">
</testcase>
<testcase classname="reset" name="reset" assertions="3" time="0.018">
</testcase>
<testcase classname="rotate" name="rotate" assertions="16" time="0.030">
</testcase>
<testcase classname="scale" name="scale" assertions="16" time="0.013">
</testcase>
<testcase classname="setBackgroundColor" name="setBackgroundColor" assertions="4" time="0.017">
</testcase>
<testcase classname="setBlendMode" name="setBlendMode" assertions="16" time="0.028">
</testcase>
<testcase classname="setCanvas" name="setCanvas" assertions="19" time="0.018">
</testcase>
<testcase classname="setColor" name="setColor" assertions="68" time="0.017">
</testcase>
<testcase classname="setColorMask" name="setColorMask" assertions="20" time="0.034">
</testcase>
<testcase classname="setDefaultFilter" name="setDefaultFilter" assertions="3" time="0.014">
</testcase>
<testcase classname="setDepthMode" name="setDepthMode" assertions="8" time="0.018">
</testcase>
<testcase classname="setFont" name="setFont" assertions="40" time="0.029">
</testcase>
<testcase classname="setFrontFaceWinding" name="setFrontFaceWinding" assertions="2" time="0.017">
</testcase>
<testcase classname="setLineJoin" name="setLineJoin" assertions="16" time="0.019">
</testcase>
<testcase classname="setLineStyle" name="setLineStyle" assertions="24" time="0.033">
</testcase>
<testcase classname="setLineWidth" name="setLineWidth" assertions="72" time="0.023">
</testcase>
<testcase classname="setMeshCullMode" name="setMeshCullMode" assertions="3" time="0.014">
</testcase>
<testcase classname="setScissor" name="setScissor" assertions="32" time="0.027">
</testcase>
<testcase classname="setShader" name="setShader" assertions="16" time="0.024">
</testcase>
<testcase classname="setStencilMode" name="setStencilMode" assertions="32" time="0.019">
</testcase>
<testcase classname="setWireframe" name="setWireframe" assertions="28" time="0.036">
</testcase>
<testcase classname="shear" name="shear" assertions="32" time="0.022">
</testcase>
<testcase classname="transformPoint" name="transformPoint" assertions="4" time="0.017">
</testcase>
<testcase classname="translate" name="translate" assertions="16" time="0.017">
</testcase>
<testcase classname="validateShader" name="validateShader" assertions="2" time="0.020">
</testcase>
</testsuite>
<testsuite name="love.image" tests="5" failures="0" skipped="0" time="0.098">
<testcase classname="CompressedImageData" name="CompressedImageData" assertions="11" time="0.017">
</testcase>
<testcase classname="ImageData" name="ImageData" assertions="14" time="0.036">
</testcase>
<testcase classname="isCompressed" name="isCompressed" assertions="1" time="0.013">
</testcase>
<testcase classname="newCompressedData" name="newCompressedData" assertions="3" time="0.016">
</testcase>
<testcase classname="newImageData" name="newImageData" assertions="6" time="0.016">
</testcase>
</testsuite>
<testsuite name="love.math" tests="20" failures="0" skipped="0" time="0.353">
<testcase classname="BezierCurve" name="BezierCurve" assertions="31" time="0.016">
</testcase>
<testcase classname="RandomGenerator" name="RandomGenerator" assertions="10" time="0.018">
</testcase>
<testcase classname="Transform" name="Transform" assertions="14" time="0.016">
</testcase>
<testcase classname="colorFromBytes" name="colorFromBytes" assertions="12" time="0.017">
</testcase>
<testcase classname="colorToBytes" name="colorToBytes" assertions="12" time="0.019">
</testcase>
<testcase classname="gammaToLinear" name="gammaToLinear" assertions="3" time="0.018">
</testcase>
<testcase classname="getRandomSeed" name="getRandomSeed" assertions="2" time="0.018">
</testcase>
<testcase classname="getRandomState" name="getRandomState" assertions="1" time="0.017">
</testcase>
<testcase classname="isConvex" name="isConvex" assertions="2" time="0.018">
</testcase>
<testcase classname="linearToGamma" name="linearToGamma" assertions="3" time="0.018">
</testcase>
<testcase classname="newBezierCurve" name="newBezierCurve" assertions="3" time="0.018">
</testcase>
<testcase classname="newRandomGenerator" name="newRandomGenerator" assertions="3" time="0.017">
</testcase>
<testcase classname="newTransform" name="newTransform" assertions="3" time="0.018">
</testcase>
<testcase classname="perlinNoise" name="perlinNoise" assertions="4" time="0.019">
</testcase>
<testcase classname="random" name="random" assertions="10" time="0.019">
</testcase>
<testcase classname="randomNormal" name="randomNormal" assertions="1" time="0.018">
</testcase>
<testcase classname="setRandomSeed" name="setRandomSeed" assertions="2" time="0.017">
</testcase>
<testcase classname="setRandomState" name="setRandomState" assertions="1" time="0.018">
</testcase>
<testcase classname="simplexNoise" name="simplexNoise" assertions="4" time="0.017">
</testcase>
<testcase classname="triangulate" name="triangulate" assertions="2" time="0.017">
</testcase>
</testsuite>
<testsuite name="love.physics" tests="23" failures="0" skipped="3" time="0.449">
<testcase classname="Body" name="Body" assertions="0" time="0.016">
<skipped message="test class needs writing" />
</testcase>
<testcase classname="Contact" name="Contact" assertions="0" time="0.017">
<skipped message="test class needs writing" />
</testcase>
<testcase classname="Joint" name="Joint" assertions="17" time="0.018">
</testcase>
<testcase classname="Shape" name="Shape" assertions="0" time="0.018">
<skipped message="test class needs writing" />
</testcase>
<testcase classname="World" name="World" assertions="33" time="0.016">
</testcase>
<testcase classname="getDistance" name="getDistance" assertions="1" time="0.018">
</testcase>
<testcase classname="getMeter" name="getMeter" assertions="1" time="0.018">
</testcase>
<testcase classname="newBody" name="newBody" assertions="3" time="0.018">
</testcase>
<testcase classname="newChainShape" name="newChainShape" assertions="3" time="0.017">
</testcase>
<testcase classname="newCircleShape" name="newCircleShape" assertions="3" time="0.017">
</testcase>
<testcase classname="newDistanceJoint" name="newDistanceJoint" assertions="3" time="0.018">
</testcase>
<testcase classname="newEdgeShape" name="newEdgeShape" assertions="3" time="0.017">
</testcase>
<testcase classname="newFrictionJoint" name="newFrictionJoint" assertions="3" time="0.017">
</testcase>
<testcase classname="newGearJoint" name="newGearJoint" assertions="3" time="0.017">
</testcase>
<testcase classname="newMotorJoint" name="newMotorJoint" assertions="3" time="0.015">
</testcase>
<testcase classname="newMouseJoint" name="newMouseJoint" assertions="3" time="0.018">
</testcase>
<testcase classname="newPolygonShape" name="newPolygonShape" assertions="3" time="0.016">
</testcase>
<testcase classname="newPrismaticJoint" name="newPrismaticJoint" assertions="3" time="0.017">
</testcase>
<testcase classname="newPulleyJoint" name="newPulleyJoint" assertions="3" time="0.017">
</testcase>
<testcase classname="newRectangleShape" name="newRectangleShape" assertions="6" time="0.018">
</testcase>
<testcase classname="newRevoluteJoint" name="newRevoluteJoint" assertions="3" time="0.018">
</testcase>
<testcase classname="newRopeJoint" name="newRopeJoint" assertions="3" time="0.018">
</testcase>
<testcase classname="newWeldJoint" name="newWeldJoint" assertions="3" time="0.016">
</testcase>
<testcase classname="newWheelJoint" name="newWheelJoint" assertions="3" time="0.017">
</testcase>
<testcase classname="newWorld" name="newWorld" assertions="3" time="0.018">
</testcase>
<testcase classname="setMeter" name="setMeter" assertions="2" time="0.018">
</testcase>
</testsuite>
<testsuite name="love.sound" tests="4" failures="0" skipped="0" time="0.074">
<testcase classname="Decoder" name="Decoder" assertions="14" time="0.017">
</testcase>
<testcase classname="SoundData" name="SoundData" assertions="20" time="0.019">
</testcase>
<testcase classname="newDecoder" name="newDecoder" assertions="3" time="0.019">
</testcase>
<testcase classname="newSoundData" name="newSoundData" assertions="6" time="0.019">
</testcase>
</testsuite>
<testsuite name="love.system" tests="6" failures="0" skipped="2" time="0.144">
<testcase classname="getClipboardText" name="getClipboardText" assertions="1" time="0.018">
</testcase>
<testcase classname="getOS" name="getOS" assertions="1" time="0.019">
</testcase>
<testcase classname="getPowerInfo" name="getPowerInfo" assertions="1" time="0.019">
</testcase>
<testcase classname="getProcessorCount" name="getProcessorCount" assertions="1" time="0.018">
</testcase>
<testcase classname="hasBackgroundMusic" name="hasBackgroundMusic" assertions="1" time="0.019">
</testcase>
<testcase classname="openURL" name="openURL" assertions="0" time="0.018">
<skipped message="cant test this worked" />
</testcase>
<testcase classname="setClipboardText" name="setClipboardText" assertions="1" time="0.017">
</testcase>
<testcase classname="vibrate" name="vibrate" assertions="0" time="0.017">
<skipped message="cant test this worked" />
</testcase>
</testsuite>
<testsuite name="love.thread" tests="5" failures="0" skipped="0" time="0.377">
<testcase classname="Channel" name="Channel" assertions="10" time="0.230">
</testcase>
<testcase classname="Thread" name="Thread" assertions="7" time="0.099">
</testcase>
<testcase classname="getChannel" name="getChannel" assertions="3" time="0.016">
</testcase>
<testcase classname="newChannel" name="newChannel" assertions="3" time="0.016">
</testcase>
<testcase classname="newThread" name="newThread" assertions="3" time="0.017">
</testcase>
</testsuite>
<testsuite name="love.timer" tests="6" failures="0" skipped="0" time="2.093">
<testcase classname="getAverageDelta" name="getAverageDelta" assertions="1" time="0.018">
</testcase>
<testcase classname="getDelta" name="getDelta" assertions="1" time="0.017">
</testcase>
<testcase classname="getFPS" name="getFPS" assertions="1" time="0.015">
</testcase>
<testcase classname="getTime" name="getTime" assertions="1" time="1.019">
</testcase>
<testcase classname="sleep" name="sleep" assertions="1" time="1.011">
</testcase>
<testcase classname="step" name="step" assertions="1" time="0.011">
</testcase>
</testsuite>
<testsuite name="love.video" tests="2" failures="0" skipped="0" time="0.040">
<testcase classname="VideoStream" name="VideoStream" assertions="9" time="0.019">
</testcase>
<testcase classname="newVideoStream" name="newVideoStream" assertions="3" time="0.021">
</testcase>
</testsuite>
<testsuite name="love.window" tests="34" failures="0" skipped="2" time="5.986">
<testcase classname="close" name="close" assertions="2" time="0.045">
</testcase>
<testcase classname="fromPixels" name="fromPixels" assertions="1" time="0.002">
</testcase>
<testcase classname="getDPIScale" name="getDPIScale" assertions="1" time="0.002">
</testcase>
<testcase classname="getDesktopDimensions" name="getDesktopDimensions" assertions="2" time="0.002">
</testcase>
<testcase classname="getDisplayCount" name="getDisplayCount" assertions="1" time="0.015">
</testcase>
<testcase classname="getDisplayName" name="getDisplayName" assertions="1" time="0.017">
</testcase>
<testcase classname="getDisplayOrientation" name="getDisplayOrientation" assertions="1" time="0.018">
</testcase>
<testcase classname="getFullscreen" name="getFullscreen" assertions="2" time="1.346">
</testcase>
<testcase classname="getFullscreenModes" name="getFullscreenModes" assertions="1" time="0.014">
</testcase>
<testcase classname="getIcon" name="getIcon" assertions="2" time="0.020">
</testcase>
<testcase classname="getMode" name="getMode" assertions="3" time="0.017">
</testcase>
<testcase classname="getPosition" name="getPosition" assertions="2" time="0.017">
</testcase>
<testcase classname="getSafeArea" name="getSafeArea" assertions="4" time="0.017">
</testcase>
<testcase classname="getTitle" name="getTitle" assertions="1" time="0.018">
</testcase>
<testcase classname="getVSync" name="getVSync" assertions="1" time="0.017">
</testcase>
<testcase classname="hasFocus" name="hasFocus" assertions="1" time="0.018">
</testcase>
<testcase classname="hasMouseFocus" name="hasMouseFocus" assertions="1" time="0.018">
</testcase>
<testcase classname="isDisplaySleepEnabled" name="isDisplaySleepEnabled" assertions="3" time="0.017">
</testcase>
<testcase classname="isMaximized" name="isMaximized" assertions="2" time="0.184">
</testcase>
<testcase classname="isMinimized" name="isMinimized" assertions="2" time="0.728">
</testcase>
<testcase classname="isOpen" name="isOpen" assertions="2" time="0.049">
</testcase>
<testcase classname="isVisible" name="isVisible" assertions="2" time="0.024">
</testcase>
<testcase classname="maximize" name="maximize" assertions="2" time="0.156">
</testcase>
<testcase classname="minimize" name="minimize" assertions="2" time="0.731">
</testcase>
<testcase classname="requestAttention" name="requestAttention" assertions="0" time="0.012">
<skipped message="cant test this worked" />
</testcase>
<testcase classname="restore" name="restore" assertions="1" time="0.864">
</testcase>
<testcase classname="setDisplaySleepEnabled" name="setDisplaySleepEnabled" assertions="2" time="0.019">
</testcase>
<testcase classname="setFullscreen" name="setFullscreen" assertions="2" time="1.336">
</testcase>
<testcase classname="setIcon" name="setIcon" assertions="1" time="0.014">
</testcase>
<testcase classname="setMode" name="setMode" assertions="4" time="0.022">
</testcase>
<testcase classname="setPosition" name="setPosition" assertions="2" time="0.180">
</testcase>
<testcase classname="setTitle" name="setTitle" assertions="1" time="0.019">
</testcase>
<testcase classname="setVSync" name="setVSync" assertions="1" time="0.016">
</testcase>
<testcase classname="showMessageBox" name="showMessageBox" assertions="0" time="0.002">
<skipped message="cant test this worked" />
</testcase>
<testcase classname="toPixels" name="toPixels" assertions="1" time="0.002">
</testcase>
<testcase classname="updateMode" name="updateMode" assertions="4" time="0.011">
</testcase>
</testsuite>
</testsuites>
+216
View File
@@ -0,0 +1,216 @@
-- & 'c:\Program Files\LOVE\love.exe' ./ --console
-- /Applications/love_12.app/Contents/MacOS/love ./testing
-- load test objs
require('classes.TestSuite')
require('classes.TestModule')
require('classes.TestMethod')
-- create testsuite obj
love.test = TestSuite:new()
-- load test scripts if module is active
if love.audio ~= nil then require('tests.audio') end
if love.data ~= nil then require('tests.data') end
if love.event ~= nil then require('tests.event') end
if love.filesystem ~= nil then require('tests.filesystem') end
if love.font ~= nil then require('tests.font') end
if love.graphics ~= nil then require('tests.graphics') end
if love.image ~= nil then require('tests.image') end
if love.math ~= nil then require('tests.math') end
if love.physics ~= nil then require('tests.physics') end
if love.sound ~= nil then require('tests.sound') end
if love.system ~= nil then require('tests.system') end
if love.thread ~= nil then require('tests.thread') end
if love.timer ~= nil then require('tests.timer') end
if love.video ~= nil then require('tests.video') end
if love.window ~= nil then require('tests.window') end
-- love.load
-- load given arguments and run the test suite
love.load = function(args)
-- setup basic img to display
if love.window ~= nil then
love.window.setMode(360, 240, {
fullscreen = false,
resizable = true,
centered = true
})
if love.graphics ~= nil then
love.graphics.setDefaultFilter("nearest", "nearest")
love.graphics.setLineStyle('rough')
love.graphics.setLineWidth(1)
Logo = {
texture = love.graphics.newImage('resources/love.png'),
img = nil
}
Logo.img = love.graphics.newQuad(0, 0, 64, 64, Logo.texture)
Font = love.graphics.newFont('resources/font.ttf', 8, 'normal')
local txtobj = love.graphics.newTextBatch or love.graphics.newText
TextCommand = txtobj(Font, 'Loading...')
TextRun = txtobj(Font, '')
end
end
-- mount for output later
if love.filesystem.mountFullPath then
love.filesystem.mountFullPath(love.filesystem.getSource() .. "/output", "tempoutput", "readwrite")
end
-- get all args with any comma lists split out as seperate
local arglist = {}
for a=1,#args do
local splits = UtilStringSplit(args[a], '([^,]+)')
for s=1,#splits do
table.insert(arglist, splits[s])
end
end
-- convert args to the cmd to run, modules, method (if any) and disabled
local testcmd = '--runAllTests'
local module = ''
local method = ''
local cmderr = 'Invalid flag used'
local modules = {
'audio', 'data', 'event', 'filesystem', 'font', 'graphics',
'image', 'math', 'physics', 'sound', 'system',
'thread', 'timer', 'video', 'window'
}
GITHUB_RUNNER = false
for a=1,#arglist do
if testcmd == '--runSpecificMethod' then
if module == '' and love[ arglist[a] ] ~= nil then
module = arglist[a]
table.insert(modules, module)
elseif module ~= '' and love[module] ~= nil and method == '' then
if love.test[module][arglist[a]] ~= nil then method = arglist[a] end
end
end
if testcmd == '--runSpecificModules' then
if love[ arglist[a] ] ~= nil and arglist[a] ~= '--isRunner' then
table.insert(modules, arglist[a])
end
end
if arglist[a] == '--runSpecificMethod' then
testcmd = arglist[a]
modules = {}
end
if arglist[a] == '--runSpecificModules' then
testcmd = arglist[a]
modules = {}
end
if arglist[a] == '--isRunner' then
GITHUB_RUNNER = true
end
end
-- runSpecificMethod uses the module + method given
if testcmd == '--runSpecificMethod' then
local testmodule = TestModule:new(module, method)
table.insert(love.test.modules, testmodule)
if module ~= '' and method ~= '' then
love.test.module = testmodule
love.test.module:log('grey', '--runSpecificMethod "' .. module .. '" "' .. method .. '"')
love.test.output = 'lovetest_runSpecificMethod_' .. module .. '_' .. method
else
if method == '' then cmderr = 'No valid method specified' end
if module == '' then cmderr = 'No valid module specified' end
end
end
-- runSpecificModules runs all methods for all the modules given
if testcmd == '--runSpecificModules' then
local modulelist = {}
for m=1,#modules do
local testmodule = TestModule:new(modules[m])
table.insert(love.test.modules, testmodule)
table.insert(modulelist, modules[m])
end
if #modulelist > 0 then
love.test.module = love.test.modules[1]
love.test.module:log('grey', '--runSpecificModules "' .. table.concat(modulelist, '" "') .. '"')
love.test.output = 'lovetest_runSpecificModules_' .. table.concat(modulelist, '_')
else
cmderr = 'No modules specified'
end
end
-- otherwise default runs all methods for all modules
if arglist[1] == nil or arglist[1] == '' or arglist[1] == '--runAllTests' then
for m=1,#modules do
local testmodule = TestModule:new(modules[m])
table.insert(love.test.modules, testmodule)
end
love.test.module = love.test.modules[1]
love.test.module:log('grey', '--runAllTests')
love.test.output = 'lovetest_runAllTests'
end
if GITHUB_RUNNER then
love.test.module:log('grey', '--isRunner')
end
-- invalid command
if love.test.module == nil then
print(cmderr)
love.event.quit(0)
else
-- start first module
TextCommand:set(testcmd)
love.test.module:runTests()
end
end
-- love.update
-- run test suite logic
love.update = function(delta)
love.test:runSuite(delta)
end
-- love.draw
-- draw a little logo to the screen
love.draw = function()
local lw = (love.graphics.getPixelWidth() - 128) / 2
local lh = (love.graphics.getPixelHeight() - 128) / 2
love.graphics.draw(Logo.texture, Logo.img, lw, lh, 0, 2, 2)
love.graphics.draw(TextCommand, 4, 12, 0, 2, 2)
love.graphics.draw(TextRun, 4, 32, 0, 2, 2)
end
-- love.quit
-- add a hook to allow test modules to fake quit
love.quit = function()
if love.test.module ~= nil and love.test.module.fakequit == true then
return true
else
return false
end
end
-- added so bad threads dont fail
function love.threaderror(thread, errorstr) end
-- string split helper
function UtilStringSplit(str, splitter)
local splits = {}
for word in string.gmatch(str, splitter) do
table.insert(splits, word)
end
return splits
end
-- string time formatter
function UtilTimeFormat(seconds)
return string.format("%.3f", tostring(seconds))
end
function UtilDebugLog(a, b, c)
if GITHUB_RUNNER == true then print("DEBUG ==> ", a, b, c) end
end
+2
View File
@@ -0,0 +1,2 @@
# Actual Graphics Output
The images generated by the tests
Binary file not shown.

After

Width:  |  Height:  |  Size: 92 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

+2
View File
@@ -0,0 +1,2 @@
# Expected Graphics Output
The images expected by the tests
+2
View File
@@ -0,0 +1,2 @@
# Testing Output
Any tests run will output an XML, MD, and HTML file here, assuming the tests are run with readwrite permissions for this repo
+155
View File
@@ -0,0 +1,155 @@
# Lövetest
Test suite for the [Löve](https://github.com/love2d/love) APIs, based off of [this issue](https://github.com/love2d/love/issues/1745).
Currently written for [Löve 12](https://github.com/love2d/love/tree/12.0-development), which is still in development. As such the test suite may fail if you try to run it with an older version of Löve due to it trying to call methods that don't exist.
While the test suite is part of the main Löve repo, the test suite has it's own repo [here](https://github.com/ellraiser/love-test) so that it can be used with other builds like [love-potion](https://github.com/lovebrew/lovepotion). If you would like to contribute to the test suite please raise a PR on the [love-test](https://github.com/ellraiser/love-test) repo.
---
## Features
- [x] Simple pass/fail tests written in Lua with minimal setup
- [x] Ability to run all tests with a simple command
- [x] Ability to see how many tests are passing/failing
- [x] Ability to run a subset of tests
- [x] Ability to easily run an individual test
- [x] Ability to see all visual results at a glance
- [x] Compare graphics test output with an expected output
- [x] Automatic testing that happens after every commit
- [x] No platform-specific dependencies / scripts
---
## Coverage
This is the status of all module tests.
See the **Todo** section for outstanding tasks if you want to contribute!
| Module | Done | Todo | Skip |
| ----------------- | ---- | ---- | ---- |
| 🟢 audio | 28 | 0 | 0 |
| 🟢 data | 12 | 0 | 0 |
| 🟢 event | 4 | 0 | 2 |
| 🟢 filesystem | 29 | 0 | 2 |
| 🟢 font | 7 | 0 | 0 |
| 🟢 graphics | 104 | 0 | 1 |
| 🟢 image | 5 | 0 | 0 |
| 🟢 math | 20 | 0 | 0 |
| 🟢 physics | 26 | 0 | 0 |
| 🟢 sound | 4 | 0 | 0 |
| 🟢 system | 6 | 0 | 2 |
| 🟢 thread | 5 | 0 | 0 |
| 🟢 timer | 6 | 0 | 0 |
| 🟢 video | 2 | 0 | 0 |
| 🟢 window | 34 | 0 | 2 |
> The following modules are not covered as we can't really emulate input nicely:
> `joystick`, `keyboard`, `mouse`, and `touch`
---
## Running Tests
The testsuite aims to keep things as simple as possible, and just runs all the tests inside Löve to match how they'd be used by developers in-engine.
To run the tests, download the repo and then run the main.lua as you would a Löve game, i.e:
WINDOWS: `& 'c:\Program Files\LOVE\love.exe' PATH_TO_TESTING_FOLDER/main.lua --console`
MACOS: `/Applications/love.app/Contents/MacOS/love PATH_TO_TESTING_FOLDER/main.lua`
LINUX: `./love.AppImage PATH_TO_TESTING_FOLDER/main.lua`
By default all tests will be run for all modules.
If you want to specify a module/s you can use:
`--runSpecificModules filesystem,audio`
If you want to specify only 1 specific method only you can use:
`--runSpecificMethod filesystem write`
All results will be printed in the console per method as PASS, FAIL, or SKIP with total assertions met on a module level and overall level.
When finished, the following files will be generated in the `/output` directory with a summary of the test results:
- an `XML` file in the style of [JUnit XML](https://www.ibm.com/docs/en/developer-for-zos/14.1?topic=formats-junit-xml-format)
- a `HTML` file that shows the report + any visual test results
- a `Markdown` file you can use with [this github action](https://github.com/ellraiser/love-test-report)
> An example of all types of output can be found in the `/examples`
> The visual results of any graphic tests can be found in `/output/actual`
---
## Architecture
Each method and object has it's own test method written in `/tests` under the matching module name.
When you run the tests, a single TestSuite object is created which handles the progress + totals for all the tests.
Each module has a TestModule object created, and each test method has a TestMethod object created which keeps track of assertions for that method. You can currently do the following assertions:
- **assertNotNil**(value)
- **assertEquals**(expected, actual, label)
- **assertTrue**(value, label)
- **assertFalse**(value, label)
- **assertNotEquals**(expected, actual, label)
- **assertRange**(actual, min, max, label)
- **assertMatch**({option1, option2, option3 ...}, actual, label)
- **assertGreaterEqual**(expected, actual, label)
- **assertLessEqual**(expected, actual, label)
- **assertObject**(table)
- **assertPixels**(imgdata, pixeltable, label)
- **assertCoords**(expected, actual, label)
Example test method:
```lua
-- love.filesystem.read test method
-- all methods should be put under love.test.MODULE.METHOD, matching the API
love.test.filesystem.read = function(test)
-- setup any data needed then run any asserts using the passed test object
local content, size = love.filesystem.read('resources/test.txt')
test:assertNotNil(content)
test:assertEquals('helloworld', content, 'check content match')
test:assertEquals(10, size, 'check size match')
content, size = love.filesystem.read('resources/test.txt', 5)
test:assertNotNil(content)
test:assertEquals('hello', content, 'check content match')
test:assertEquals(5, size, 'check size match')
-- no need to return anything or cleanup, GCC is called after each method
end
```
Each test is run inside it's own coroutine - you can use `test:waitFrames(frames)` to pause the test for a small period if you need to check things that won't happen for a few seconds.
After each test method is ran, the assertions are totalled up, printed, and we move onto the next method! Once all methods in the suite are run a total pass/fail/skip is given for that module and we move onto the next module (if any)
For sanity-checking, if it's currently not covered or it's not possible to test the method we can set the test to be skipped with `test:skipTest(reason)` - this way we still see the method listed in the test output without it affected the pass/fail totals
---
## Todo
If you would like to contribute to the test suite please raise a PR with the main [love-test](https://github.com/ellraiser/love-test) repo.
The following items are all the things still outstanding, expanding on any existing tests is also very welcome!
- [ ] check for any 12.0 methods in the changelog not yet covered in the test suite
- [ ] add BMfont alt. tests for font class tests (Rasterizer + GlyphData)
- [ ] graphics.isCompressed() should have an example of all compressed files
- [ ] graphics.Mesh should have some graphical tests ideally to check vertex settings w/ shaders
- [ ] ability to test loading different combinations of modules if needed
- [ ] more scenario based tests similar to some of the obj class tests
- [ ] performance tests? need to discuss what + how
---
## Graphics Tolerance
By default all graphic tests are run with pixel precision and 0 rgba tolerance.
However there are a couple of methods that on some platforms require some slight tolerance to allow for tiny differences in rendering.
| Test | OS | Exception | Reason |
| -------------------------- | --------- | ------------------- | ------ |
| love.graphics.drawInstanced | Windows | 1rgba tolerance | On Windows there's a couple pixels a tiny bit off, most likely due to complexity of the mesh drawn |
| love.graphics.setBlendMode | Win/Lin | 1rgba tolerance | Blendmodes have some small varience on some machines |
---
## Runner Exceptions
The automated tests through Github work for the most part however there are a few exceptions that have to be accounted for due to limitations of the VMs and the graphics emulation used.
These exceptions are either skipped, or handled by using a 1px or 1/255rgba tolerance - when run locally on real hardware, these tests pass fine at the default 0 tolerance.
You can specify the test suite is being run on a runner by adding the `--isRunner` flag in your workflow file, i.e.:
`& 'c:\Program Files\LOVE\love.exe' PATH_TO_TESTING_FOLDER/main.lua --console --runAllTests --isRunner`
| Test | OS | Exception | Reason |
| -------------------------- | --------- | ------------------- | ------ |
| love.graphics.points | MacOS | 1px tolerance | Points are offset by 1,1 when drawn |
| love.graphics.setWireframe | MacOS | 1px tolerance | Wireframes are offset by 1,1 when drawn |
| love.graphica.arc | MacOS | Skipped | Arc curves are drawn slightly off at really low scale |
| love.graphics.setLineStyle | Linux | 1rgba tolerance | 'Rough' lines blend differently with the background rgba |
| love.audio.RecordingDevice | All | Skipped | Recording devices can't be emulated on runners |
+491
View File
@@ -0,0 +1,491 @@
# OpenAL config file.
#
# Option blocks may appear multiple times, and duplicated options will take the
# last value specified. Environment variables may be specified within option
# values, and are automatically substituted when the config file is loaded.
# Environment variable names may only contain alpha-numeric characters (a-z,
# A-Z, 0-9) and underscores (_), and are prefixed with $. For example,
# specifying "$HOME/file.ext" would typically result in something like
# "/home/user/file.ext". To specify an actual "$" character, use "$$".
#
# Device-specific values may be specified by including the device name in the
# block name, with "general" replaced by the device name. That is, general
# options for the device "Name of Device" would be in the [Name of Device]
# block, while ALSA options would be in the [alsa/Name of Device] block.
# Options marked as "(global)" are not influenced by the device.
#
# The system-wide settings can be put in /etc/openal/alsoft.conf and user-
# specific override settings in $HOME/.alsoftrc.
# For Windows, these settings should go into $AppData\alsoft.ini
#
# Option and block names are case-senstive. The supplied values are only hints
# and may not be honored (though generally it'll try to get as close as
# possible). Note: options that are left unset may default to app- or system-
# specified values. These are the current available settings:
##
## General stuff
##
[general]
## disable-cpu-exts: (global)
# Disables use of specialized methods that use specific CPU intrinsics.
# Certain methods may utilize CPU extensions for improved performance, and
# this option is useful for preventing some or all of those methods from being
# used. The available extensions are: sse, sse2, sse3, sse4.1, and neon.
# Specifying 'all' disables use of all such specialized methods.
#disable-cpu-exts =
## drivers: (global)
# Sets the backend driver list order, comma-seperated. Unknown backends and
# duplicated names are ignored. Unlisted backends won't be considered for use
# unless the list is ended with a comma (e.g. 'oss,' will try OSS first before
# other backends, while 'oss' will try OSS only). Backends prepended with -
# won't be considered for use (e.g. '-oss,' will try all available backends
# except OSS). An empty list means to try all backends.
drivers = wave
## channels:
# Sets the output channel configuration. If left unspecified, one will try to
# be detected from the system, and defaulting to stereo. The available values
# are: mono, stereo, quad, surround51, surround51rear, surround61, surround71,
# ambi1, ambi2, ambi3. Note that the ambi* configurations provide ambisonic
# channels of the given order (using ACN ordering and SN3D normalization by
# default), which need to be decoded to play correctly on speakers.
#channels =
## sample-type:
# Sets the output sample type. Currently, all mixing is done with 32-bit float
# and converted to the output sample type as needed. Available values are:
# int8 - signed 8-bit int
# uint8 - unsigned 8-bit int
# int16 - signed 16-bit int
# uint16 - unsigned 16-bit int
# int32 - signed 32-bit int
# uint32 - unsigned 32-bit int
# float32 - 32-bit float
#sample-type = float32
## frequency:
# Sets the output frequency. If left unspecified it will try to detect a
# default from the system, otherwise it will default to 44100.
#frequency =
## period_size:
# Sets the update period size, in frames. This is the number of frames needed
# for each mixing update. Acceptable values range between 64 and 8192.
#period_size = 1024
## periods:
# Sets the number of update periods. Higher values create a larger mix ahead,
# which helps protect against skips when the CPU is under load, but increases
# the delay between a sound getting mixed and being heard. Acceptable values
# range between 2 and 16.
#periods = 3
## stereo-mode:
# Specifies if stereo output is treated as being headphones or speakers. With
# headphones, HRTF or crossfeed filters may be used for better audio quality.
# Valid settings are auto, speakers, and headphones.
#stereo-mode = auto
## stereo-encoding:
# Specifies the encoding method for non-HRTF stereo output. 'panpot' (default)
# uses standard amplitude panning (aka pair-wise, stereo pair, etc) between
# -30 and +30 degrees, while 'uhj' creates stereo-compatible two-channel UHJ
# output, which encodes some surround sound information into stereo output
# that can be decoded with a surround sound receiver. If crossfeed filters are
# used, UHJ is disabled.
#stereo-encoding = panpot
## ambi-format:
# Specifies the channel order and normalization for the "ambi*" set of channel
# configurations. Valid settings are: fuma, acn+sn3d, acn+n3d
#ambi-format = acn+sn3d
## hrtf:
# Controls HRTF processing. These filters provide better spatialization of
# sounds while using headphones, but do require a bit more CPU power. The
# default filters will only work with 44100hz or 48000hz stereo output. While
# HRTF is used, the cf_level option is ignored. Setting this to auto (default)
# will allow HRTF to be used when headphones are detected or the app requests
# it, while setting true or false will forcefully enable or disable HRTF
# respectively.
#hrtf = auto
## default-hrtf:
# Specifies the default HRTF to use. When multiple HRTFs are available, this
# determines the preferred one to use if none are specifically requested. Note
# that this is the enumerated HRTF name, not necessarily the filename.
#default-hrtf =
## hrtf-paths:
# Specifies a comma-separated list of paths containing HRTF data sets. The
# format of the files are described in docs/hrtf.txt. The files within the
# directories must have the .mhr file extension to be recognized. By default,
# OS-dependent data paths will be used. They will also be used if the list
# ends with a comma. On Windows this is:
# $AppData\openal\hrtf
# And on other systems, it's (in order):
# $XDG_DATA_HOME/openal/hrtf (defaults to $HOME/.local/share/openal/hrtf)
# $XDG_DATA_DIRS/openal/hrtf (defaults to /usr/local/share/openal/hrtf and
# /usr/share/openal/hrtf)
#hrtf-paths =
## cf_level:
# Sets the crossfeed level for stereo output. Valid values are:
# 0 - No crossfeed
# 1 - Low crossfeed
# 2 - Middle crossfeed
# 3 - High crossfeed (virtual speakers are closer to itself)
# 4 - Low easy crossfeed
# 5 - Middle easy crossfeed
# 6 - High easy crossfeed
# Users of headphones may want to try various settings. Has no effect on non-
# stereo modes.
#cf_level = 0
## resampler: (global)
# Selects the resampler used when mixing sources. Valid values are:
# point - nearest sample, no interpolation
# linear - extrapolates samples using a linear slope between samples
# cubic - extrapolates samples using a Catmull-Rom spline
# bsinc12 - extrapolates samples using a band-limited Sinc filter (varying
# between 12 and 24 points, with anti-aliasing)
# bsinc24 - extrapolates samples using a band-limited Sinc filter (varying
# between 24 and 48 points, with anti-aliasing)
#resampler = linear
## rt-prio: (global)
# Sets real-time priority for the mixing thread. Not all drivers may use this
# (eg. PortAudio) as they already control the priority of the mixing thread.
# 0 and negative values will disable it. Note that this may constitute a
# security risk since a real-time priority thread can indefinitely block
# normal-priority threads if it fails to wait. As such, the default is
# disabled.
#rt-prio = 0
## sources:
# Sets the maximum number of allocatable sources. Lower values may help for
# systems with apps that try to play more sounds than the CPU can handle.
#sources = 256
## slots:
# Sets the maximum number of Auxiliary Effect Slots an app can create. A slot
# can use a non-negligible amount of CPU time if an effect is set on it even
# if no sources are feeding it, so this may help when apps use more than the
# system can handle.
#slots = 64
## sends:
# Limits the number of auxiliary sends allowed per source. Setting this higher
# than the default has no effect.
#sends = 16
## front-stablizer:
# Applies filters to "stablize" front sound imaging. A psychoacoustic method
# is used to generate a front-center channel signal from the front-left and
# front-right channels, improving the front response by reducing the combing
# artifacts and phase errors. Consequently, it will only work with channel
# configurations that include front-left, front-right, and front-center.
#front-stablizer = false
## output-limiter:
# Applies a gain limiter on the final mixed output. This reduces the volume
# when the output samples would otherwise clamp, avoiding excessive clipping
# noise.
#output-limiter = true
## dither:
# Applies dithering on the final mix, for 8- and 16-bit output by default.
# This replaces the distortion created by nearest-value quantization with low-
# level whitenoise.
#dither = true
## dither-depth:
# Quantization bit-depth for dithered output. A value of 0 (or less) will
# match the output sample depth. For int32, uint32, and float32 output, 0 will
# disable dithering because they're at or beyond the rendered precision. The
# maximum dither depth is 24.
#dither-depth = 0
## volume-adjust:
# A global volume adjustment for source output, expressed in decibels. The
# value is logarithmic, so +6 will be a scale of (approximately) 2x, +12 will
# be a scale of 4x, etc. Similarly, -6 will be x1/2, and -12 is about x1/4. A
# value of 0 means no change.
#volume-adjust = 0
## excludefx: (global)
# Sets which effects to exclude, preventing apps from using them. This can
# help for apps that try to use effects which are too CPU intensive for the
# system to handle. Available effects are: eaxreverb,reverb,autowah,chorus,
# compressor,distortion,echo,equalizer,flanger,modulator,dedicated,pshifter,
# fshifter
#excludefx =
## default-reverb: (global)
# A reverb preset that applies by default to all sources on send 0
# (applications that set their own slots on send 0 will override this).
# Available presets are: None, Generic, PaddedCell, Room, Bathroom,
# Livingroom, Stoneroom, Auditorium, ConcertHall, Cave, Arena, Hangar,
# CarpetedHallway, Hallway, StoneCorridor, Alley, Forest, City, Moutains,
# Quarry, Plain, ParkingLot, SewerPipe, Underwater, Drugged, Dizzy, Psychotic.
#default-reverb =
## trap-alc-error: (global)
# Generates a SIGTRAP signal when an ALC device error is generated, on systems
# that support it. This helps when debugging, while trying to find the cause
# of a device error. On Windows, a breakpoint exception is generated.
#trap-alc-error = false
## trap-al-error: (global)
# Generates a SIGTRAP signal when an AL context error is generated, on systems
# that support it. This helps when debugging, while trying to find the cause
# of a context error. On Windows, a breakpoint exception is generated.
#trap-al-error = false
##
## Ambisonic decoder stuff
##
[decoder]
## hq-mode:
# Enables a high-quality ambisonic decoder. This mode is capable of frequency-
# dependent processing, creating a better reproduction of 3D sound rendering
# over surround sound speakers. Enabling this also requires specifying decoder
# configuration files for the appropriate speaker configuration you intend to
# use (see the quad, surround51, etc options below). Currently, up to third-
# order decoding is supported.
hq-mode = false
## distance-comp:
# Enables compensation for the speakers' relative distances to the listener.
# This applies the necessary delays and attenuation to make the speakers
# behave as though they are all equidistant, which is important for proper
# playback of 3D sound rendering. Requires the proper distances to be
# specified in the decoder configuration file.
distance-comp = true
## nfc:
# Enables near-field control filters. This simulates and compensates for low-
# frequency effects caused by the curvature of nearby sound-waves, which
# creates a more realistic perception of sound distance. Note that the effect
# may be stronger or weaker than intended if the application doesn't use or
# specify an appropriate unit scale, or if incorrect speaker distances are set
# in the decoder configuration file. Requires hq-mode to be enabled.
nfc = true
## nfc-ref-delay
# Specifies the reference delay value for ambisonic output. When channels is
# set to one of the ambi* formats, this option enables NFC-HOA output with the
# specified Reference Delay parameter. The specified value can then be shared
# with an appropriate NFC-HOA decoder to reproduce correct near-field effects.
# Keep in mind that despite being designed for higher-order ambisonics, this
# applies to first-order output all the same. When left unset, normal output
# is created with no near-field simulation.
nfc-ref-delay =
## quad:
# Decoder configuration file for Quadraphonic channel output. See
# docs/ambdec.txt for a description of the file format.
quad =
## surround51:
# Decoder configuration file for 5.1 Surround (Side and Rear) channel output.
# See docs/ambdec.txt for a description of the file format.
surround51 =
## surround61:
# Decoder configuration file for 6.1 Surround channel output. See
# docs/ambdec.txt for a description of the file format.
surround61 =
## surround71:
# Decoder configuration file for 7.1 Surround channel output. See
# docs/ambdec.txt for a description of the file format. Note: This can be used
# to enable 3D7.1 with the appropriate configuration and speaker placement,
# see docs/3D7.1.txt.
surround71 =
##
## Reverb effect stuff (includes EAX reverb)
##
[reverb]
## boost: (global)
# A global amplification for reverb output, expressed in decibels. The value
# is logarithmic, so +6 will be a scale of (approximately) 2x, +12 will be a
# scale of 4x, etc. Similarly, -6 will be about half, and -12 about 1/4th. A
# value of 0 means no change.
#boost = 0
##
## PulseAudio backend stuff
##
[pulse]
## spawn-server: (global)
# Attempts to autospawn a PulseAudio server whenever needed (initializing the
# backend, enumerating devices, etc). Setting autospawn to false in Pulse's
# client.conf will still prevent autospawning even if this is set to true.
#spawn-server = true
## allow-moves: (global)
# Allows PulseAudio to move active streams to different devices. Note that the
# device specifier (seen by applications) will not be updated when this
# occurs, and neither will the AL device configuration (sample rate, format,
# etc).
#allow-moves = false
## fix-rate:
# Specifies whether to match the playback stream's sample rate to the device's
# sample rate. Enabling this forces OpenAL Soft to mix sources and effects
# directly to the actual output rate, avoiding a second resample pass by the
# PulseAudio server.
#fix-rate = false
##
## ALSA backend stuff
##
[alsa]
## device: (global)
# Sets the device name for the default playback device.
#device = default
## device-prefix: (global)
# Sets the prefix used by the discovered (non-default) playback devices. This
# will be appended with "CARD=c,DEV=d", where c is the card id and d is the
# device index for the requested device name.
#device-prefix = plughw:
## device-prefix-*: (global)
# Card- and device-specific prefixes may be used to override the device-prefix
# option. The option may specify the card id (eg, device-prefix-NVidia), or
# the card id and device index (eg, device-prefix-NVidia-0). The card id is
# case-sensitive.
#device-prefix- =
## capture: (global)
# Sets the device name for the default capture device.
#capture = default
## capture-prefix: (global)
# Sets the prefix used by the discovered (non-default) capture devices. This
# will be appended with "CARD=c,DEV=d", where c is the card id and d is the
# device number for the requested device name.
#capture-prefix = plughw:
## capture-prefix-*: (global)
# Card- and device-specific prefixes may be used to override the
# capture-prefix option. The option may specify the card id (eg,
# capture-prefix-NVidia), or the card id and device index (eg,
# capture-prefix-NVidia-0). The card id is case-sensitive.
#capture-prefix- =
## mmap:
# Sets whether to try using mmap mode (helps reduce latencies and CPU
# consumption). If mmap isn't available, it will automatically fall back to
# non-mmap mode. True, yes, on, and non-0 values will attempt to use mmap. 0
# and anything else will force mmap off.
#mmap = true
## allow-resampler:
# Specifies whether to allow ALSA's built-in resampler. Enabling this will
# allow the playback device to be set to a different sample rate than the
# actual output, causing ALSA to apply its own resampling pass after OpenAL
# Soft resamples and mixes the sources and effects for output.
#allow-resampler = false
##
## OSS backend stuff
##
[oss]
## device: (global)
# Sets the device name for OSS output.
#device = /dev/dsp
## capture: (global)
# Sets the device name for OSS capture.
#capture = /dev/dsp
##
## Solaris backend stuff
##
[solaris]
## device: (global)
# Sets the device name for Solaris output.
#device = /dev/audio
##
## QSA backend stuff
##
[qsa]
##
## JACK backend stuff
##
[jack]
## spawn-server: (global)
# Attempts to autospawn a JACK server whenever needed (initializing the
# backend, opening devices, etc).
#spawn-server = false
## buffer-size:
# Sets the update buffer size, in samples, that the backend will keep buffered
# to handle the server's real-time processing requests. This value must be a
# power of 2, or else it will be rounded up to the next power of 2. If it is
# less than JACK's buffer update size, it will be clamped. This option may
# be useful in case the server's update size is too small and doesn't give the
# mixer time to keep enough audio available for the processing requests.
#buffer-size = 0
##
## WASAPI backend stuff
##
[wasapi]
##
## DirectSound backend stuff
##
[dsound]
##
## Windows Multimedia backend stuff
##
[winmm]
##
## PortAudio backend stuff
##
[port]
## device: (global)
# Sets the device index for output. Negative values will use the default as
# given by PortAudio itself.
#device = -1
## capture: (global)
# Sets the device index for capture. Negative values will use the default as
# given by PortAudio itself.
#capture = -1
##
## Wave File Writer stuff
##
[wave]
## file: (global)
# Sets the filename of the wave file to write to. An empty name prevents the
# backend from opening, even when explicitly requested.
# THIS WILL OVERWRITE EXISTING FILES WITHOUT QUESTION!
file = output.wav
## bformat: (global)
# Creates AMB format files using first-order ambisonics instead of a standard
# single- or multi-channel .wav file.
#bformat = false
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 B

Binary file not shown.
+1
View File
@@ -0,0 +1 @@
helloworld
Binary file not shown.
+478
View File
@@ -0,0 +1,478 @@
-- love.audio
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------OBJECTS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- RecordingDevice (love.audio.getRecordingDevices)
love.test.audio.RecordingDevice = function(test)
-- skip recording device on runners, they cant emulate it
if GITHUB_RUNNER == true then
return test:skipTest('cant emulate recording devices in CI')
end
-- check devices first
local devices = love.audio.getRecordingDevices()
if #devices == 0 then
return test:skipTest('cant test this works: no recording devices found')
end
-- check object created and basics
local device = devices[1]
test:assertObject(device)
test:assertMatch({1, 2}, device:getChannelCount(), 'check channel count is 1 or 2')
test:assertNotEquals(nil, device:getName(), 'check has name')
-- check initial data is empty as we haven't recorded anything yet
test:assertNotNil(device:getBitDepth())
test:assertEquals(nil, device:getData(), 'check initial data empty')
test:assertEquals(0, device:getSampleCount(), 'check initial sample empty')
test:assertNotNil(device:getSampleRate())
test:assertFalse(device:isRecording(), 'check not recording')
-- start recording for a short time
local startrecording = device:start(32000, 4000, 16, 1)
test:waitFrames(10)
test:assertTrue(startrecording, 'check recording started')
test:assertTrue(device:isRecording(), 'check now recording')
test:assertEquals(4000, device:getSampleRate(), 'check sample rate set')
test:assertEquals(16, device:getBitDepth(), 'check bit depth set')
test:assertEquals(1, device:getChannelCount(), 'check channel count set')
local recording = device:stop()
test:waitFrames(10)
-- after recording
test:assertFalse(device:isRecording(), 'check not recording')
test:assertEquals(nil, device:getData(), 'using stop should clear buffer')
test:assertObject(recording)
end
-- Source (love.audio.newSource)
love.test.audio.Source = function(test)
-- create stereo source
local stereo = love.audio.newSource('resources/click.ogg', 'static')
test:assertObject(stereo)
-- check stereo props
test:assertEquals(2, stereo:getChannelCount(), 'check stereo src')
test:assertRange(stereo:getDuration("seconds"), 0, 0.1, 'check stereo seconds')
test:assertNotNil(stereo:getFreeBufferCount())
test:assertEquals('static', stereo:getType(), 'check stereo type')
-- check cloning a stereo
local clone = stereo:clone()
test:assertEquals(2, clone:getChannelCount(), 'check clone stereo src')
test:assertRange(clone:getDuration("seconds"), 0, 0.1, 'check clone stereo seconds')
test:assertNotNil(clone:getFreeBufferCount())
test:assertEquals('static', clone:getType(), 'check cloned stereo type')
-- mess with stereo playing
test:assertFalse(stereo:isPlaying(), 'check not playing')
stereo:setLooping(true)
stereo:play()
test:assertTrue(stereo:isPlaying(), 'check now playing')
test:assertTrue(stereo:isLooping(), 'check now playing')
stereo:pause()
stereo:seek(0.01, 'seconds')
test:assertEquals(0.01, stereo:tell('seconds'), 'check seek/tell')
stereo:stop()
test:assertFalse(stereo:isPlaying(), 'check stopped playing')
-- check volume limits
stereo:setVolumeLimits(0.1, 0.5)
local min, max = stereo:getVolumeLimits()
test:assertRange(min, 0.1, 0.2, 'check min limit')
test:assertRange(max, 0.5, 0.6, 'check max limit')
-- check setting volume
stereo:setVolume(1)
test:assertEquals(1, stereo:getVolume(), 'check set volume')
stereo:setVolume(0)
test:assertEquals(0, stereo:getVolume(), 'check set volume')
-- change some get/set props that can apply to stereo
stereo:setPitch(2)
test:assertEquals(2, stereo:getPitch(), 'check pitch change')
-- create mono source
local mono = love.audio.newSource('resources/clickmono.ogg', 'stream')
test:assertObject(mono)
test:assertEquals(1, mono:getChannelCount(), 'check mono src')
test:assertEquals(2927, mono:getDuration("samples"), 'check mono seconds')
test:assertEquals('stream', mono:getType(), 'check mono type')
-- air absorption
test:assertEquals(0, mono:getAirAbsorption(), 'get air absorption')
mono:setAirAbsorption(1)
test:assertEquals(1, mono:getAirAbsorption(), 'set air absorption')
-- cone
mono:setCone(0, 90*(math.pi/180), 1)
local ia, oa, ov = mono:getCone()
test:assertEquals(0, ia, 'check cone ia')
test:assertEquals(math.floor(9000*(math.pi/180)), math.floor(oa*100), 'check cone oa')
test:assertEquals(1, ov, 'check cone ov')
-- direction
mono:setDirection(3, 1, -1)
local x, y, z = mono:getDirection()
test:assertEquals(3, x, 'check direction x')
test:assertEquals(1, y, 'check direction y')
test:assertEquals(-1, z, 'check direction z')
-- relative
mono:setRelative(true)
test:assertTrue(mono:isRelative(), 'check set relative')
-- position
mono:setPosition(1, 2, 3)
x, y, z = mono:getPosition()
test:assertEquals(x, 1, 'check pos x')
test:assertEquals(y, 2, 'check pos y')
test:assertEquals(z, 3, 'check pos z')
-- velocity
mono:setVelocity(1, 3, 4)
x, y, z = mono:getVelocity()
test:assertEquals(x, 1, 'check velocity x')
test:assertEquals(y, 3, 'check velocity x')
test:assertEquals(z, 4, 'check velocity x')
-- rolloff
mono:setRolloff(1)
test:assertEquals(1, mono:getRolloff(), 'check rolloff set')
-- create queue source
local queue = love.audio.newQueueableSource(44100, 16, 1, 3)
local sdata = love.sound.newSoundData(1024, 44100, 16, 1)
test:assertObject(queue)
local run = queue:queue(sdata)
test:assertTrue(run, 'check queued sound')
queue:stop()
-- check making a filer
local setfilter = stereo:setFilter({
type = 'lowpass',
volume = 0.5,
highgain = 0.3
})
test:assertTrue(setfilter, 'check filter applied')
local filter = stereo:getFilter()
test:assertEquals('lowpass', filter.type, 'check filter type')
test:assertEquals(0.5, filter.volume, 'check filter volume')
test:assertRange(filter.highgain, 0.3, 0.4, 'check filter highgain')
test:assertEquals(nil, filter.lowgain, 'check filter lowgain')
-- add an effect
local effsource = love.audio.newSource('resources/click.ogg', 'static')
love.audio.setEffect('testeffect', {
type = 'flanger',
volume = 10
})
local seteffect, err = effsource:setEffect('testeffect', {
type = 'highpass',
volume = 0.3,
lowgain = 0.1
})
-- both these fail on 12 using stereo or mono, no err
test:assertTrue(seteffect, 'check effect was applied')
local filtersettings = effsource:getEffect('effectthatdoesntexist', {})
test:assertNotNil(filtersettings)
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------METHODS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.audio.getActiveEffects
love.test.audio.getActiveEffects = function(test)
-- check we get a value
test:assertNotNil(love.audio.getActiveEffects())
-- check setting an effect active
love.audio.setEffect('testeffect', {
type = 'chorus',
volume = 10
})
test:assertEquals(1, #love.audio.getActiveEffects(), 'check 1 effect running')
test:assertEquals('testeffect', love.audio.getActiveEffects()[1], 'check effect details')
end
-- love.audio.getActiveSourceCount
love.test.audio.getActiveSourceCount = function(test)
-- check we get a value
test:assertNotNil(love.audio.getActiveSourceCount())
-- check source isn't active by default
local testsource = love.audio.newSource('resources/click.ogg', 'static')
test:assertEquals(0, love.audio.getActiveSourceCount(), 'check not active')
-- check playing a source marks it as active
love.audio.play(testsource)
test:assertEquals(1, love.audio.getActiveSourceCount(), 'check now active')
love.audio.pause()
end
-- love.audio.getDistanceModel
love.test.audio.getDistanceModel = function(test)
-- check we get a value
test:assertNotNil(love.audio.getDistanceModel())
-- check default value from documentation
test:assertEquals('inverseclamped', love.audio.getDistanceModel(), 'check default value')
-- check get correct value after setting
love.audio.setDistanceModel('inverse')
test:assertEquals('inverse', love.audio.getDistanceModel(), 'check setting model')
end
-- love.audio.getDopplerScale
love.test.audio.getDopplerScale = function(test)
-- check default value
test:assertEquals(1, love.audio.getDopplerScale(), 'check default 1')
-- check correct value after setting to 0
love.audio.setDopplerScale(0)
test:assertEquals(0, love.audio.getDopplerScale(), 'check setting to 0')
love.audio.setDopplerScale(1)
end
-- love.audio.getEffect
love.test.audio.getEffect = function(test)
-- check getting a non-existent effect
test:assertEquals(nil, love.audio.getEffect('madeupname'), 'check wrong name')
-- check getting a valid effect
love.audio.setEffect('testeffect', {
type = 'chorus',
volume = 10
})
test:assertNotNil(love.audio.getEffect('testeffect'))
-- check effect values match creation values
test:assertEquals('chorus', love.audio.getEffect('testeffect').type, 'check effect type')
test:assertEquals(10, love.audio.getEffect('testeffect').volume, 'check effect volume')
end
-- love.audio.getMaxSceneEffects
-- @NOTE feel like this is platform specific number so best we can do is a nil?
love.test.audio.getMaxSceneEffects = function(test)
test:assertNotNil(love.audio.getMaxSceneEffects())
end
-- love.audio.getMaxSourceEffects
-- @NOTE feel like this is platform specific number so best we can do is a nil?
love.test.audio.getMaxSourceEffects = function(test)
test:assertNotNil(love.audio.getMaxSourceEffects())
end
-- love.audio.getOrientation
-- @NOTE is there an expected default listener pos?
love.test.audio.getOrientation = function(test)
-- checking getting values matches what was set
love.audio.setOrientation(1, 2, 3, 4, 5, 6)
local fx, fy, fz, ux, uy, uz = love.audio.getOrientation()
test:assertEquals(1, fx, 'check fx orientation')
test:assertEquals(2, fy, 'check fy orientation')
test:assertEquals(3, fz, 'check fz orientation')
test:assertEquals(4, ux, 'check ux orientation')
test:assertEquals(5, uy, 'check uy orientation')
test:assertEquals(6, uz, 'check uz orientation')
end
-- love.audio.getPosition
-- @NOTE is there an expected default listener pos?
love.test.audio.getPosition = function(test)
-- check getting values matches what was set
love.audio.setPosition(1, 2, 3)
local x, y, z = love.audio.getPosition()
test:assertEquals(1, x, 'check x position')
test:assertEquals(2, y, 'check y position')
test:assertEquals(3, z, 'check z position')
end
-- love.audio.getRecordingDevices
-- @NOTE hardware dependent so best can do is not nil check
love.test.audio.getRecordingDevices = function(test)
test:assertNotNil(love.audio.getRecordingDevices())
end
-- love.audio.getVelocity
love.test.audio.getVelocity = function(test)
-- check getting values matches what was set
love.audio.setVelocity(1, 2, 3)
local x, y, z = love.audio.getVelocity()
test:assertEquals(1, x, 'check x velocity')
test:assertEquals(2, y, 'check y velocity')
test:assertEquals(3, z, 'check z velocity')
end
-- love.audio.getVolume
love.test.audio.getVolume = function(test)
-- check getting values matches what was set
love.audio.setVolume(0.5)
test:assertEquals(0.5, love.audio.getVolume(), 'check matches set')
end
-- love.audio.isEffectsSupported
love.test.audio.isEffectsSupported = function(test)
test:assertNotNil(love.audio.isEffectsSupported())
end
-- love.audio.newQueueableSource
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.audio.newQueueableSource = function(test)
test:assertObject(love.audio.newQueueableSource(32, 8, 1, 8))
end
-- love.audio.newSource
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.audio.newSource = function(test)
test:assertObject(love.audio.newSource('resources/click.ogg', 'static'))
test:assertObject(love.audio.newSource('resources/click.ogg', 'stream'))
end
-- love.audio.pause
love.test.audio.pause = function(test)
-- check nothing paused (as should be nothing playing)
local nopauses = love.audio.pause()
test:assertEquals(0, #nopauses, 'check nothing paused')
-- check 1 source paused after playing/pausing 1
local source = love.audio.newSource('resources/click.ogg', 'static')
love.audio.play(source)
local onepause = love.audio.pause()
test:assertEquals(1, #onepause, 'check 1 paused')
end
-- love.audio.play
love.test.audio.play = function(test)
-- check playing source is detected
local source = love.audio.newSource('resources/click.ogg', 'static')
love.audio.play(source)
test:assertTrue(source:isPlaying(), 'check something playing')
love.audio.pause()
end
-- love.audio.setDistanceModel
love.test.audio.setDistanceModel = function(test)
-- check setting each of the distance models is accepted and val returned
local distancemodel = {
'none', 'inverse', 'inverseclamped', 'linear', 'linearclamped',
'exponent', 'exponentclamped'
}
for d=1,#distancemodel do
love.audio.setDistanceModel(distancemodel[d])
test:assertEquals(distancemodel[d], love.audio.getDistanceModel(),
'check model set to ' .. distancemodel[d])
end
end
-- love.audio.setDopplerScale
love.test.audio.setDopplerScale = function(test)
-- check setting value is returned properly
love.audio.setDopplerScale(0)
test:assertEquals(0, love.audio.getDopplerScale(), 'check set to 0')
love.audio.setDopplerScale(1)
test:assertEquals(1, love.audio.getDopplerScale(), 'check set to 1')
end
-- love.audio.setEffect
love.test.audio.setEffect = function(test)
-- check effect is set correctly
local effect = love.audio.setEffect('testeffect', {
type = 'chorus',
volume = 10
})
test:assertTrue(effect, 'check effect created')
-- check values set match
local settings = love.audio.getEffect('testeffect')
test:assertEquals('chorus', settings.type, 'check effect type')
test:assertEquals(10, settings.volume, 'check effect volume')
end
-- love.audio.setMixWithSystem
love.test.audio.setMixWithSystem = function(test)
test:assertNotNil(love.audio.setMixWithSystem(true))
end
-- love.audio.setOrientation
love.test.audio.setOrientation = function(test)
-- check setting orientation vals are returned
love.audio.setOrientation(1, 2, 3, 4, 5, 6)
local fx, fy, fz, ux, uy, uz = love.audio.getOrientation()
test:assertEquals(1, fx, 'check fx orientation')
test:assertEquals(2, fy, 'check fy orientation')
test:assertEquals(3, fz, 'check fz orientation')
test:assertEquals(4, ux, 'check ux orientation')
test:assertEquals(5, uy, 'check uy orientation')
test:assertEquals(6, uz, 'check uz orientation')
end
-- love.audio.setPosition
love.test.audio.setPosition = function(test)
-- check setting position vals are returned
love.audio.setPosition(1, 2, 3)
local x, y, z = love.audio.getPosition()
test:assertEquals(1, x, 'check x position')
test:assertEquals(2, y, 'check y position')
test:assertEquals(3, z, 'check z position')
end
-- love.audio.setVelocity
love.test.audio.setVelocity = function(test)
-- check setting velocity vals are returned
love.audio.setVelocity(1, 2, 3)
local x, y, z = love.audio.getVelocity()
test:assertEquals(1, x, 'check x velocity')
test:assertEquals(2, y, 'check y velocity')
test:assertEquals(3, z, 'check z velocity')
end
-- love.audio.setVolume
love.test.audio.setVolume = function(test)
-- check setting volume works
love.audio.setVolume(0.5)
test:assertEquals(0.5, love.audio.getVolume(), 'check set to 0.5')
end
-- love.audio.stop
love.test.audio.stop = function(test)
-- check source is playing first
local source = love.audio.newSource('resources/click.ogg', 'static')
love.audio.play(source)
test:assertTrue(source:isPlaying(), 'check is playing')
-- check source is then stopped
love.audio.stop()
test:assertFalse(source:isPlaying(), 'check stopped playing')
end
+251
View File
@@ -0,0 +1,251 @@
-- love.data
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------OBJECTS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- ByteData (love.data.newByteData)
love.test.data.ByteData = function(test)
-- create new obj
local data = love.data.newByteData('helloworld')
test:assertObject(data)
-- check properties match expected
test:assertEquals('helloworld', data:getString(), 'check data string')
test:assertEquals(10, data:getSize(), 'check data size')
-- check cloning the bytedata
local cloneddata = data:clone()
test:assertObject(cloneddata)
test:assertEquals('helloworld', cloneddata:getString(), 'check cloned data')
test:assertEquals(10, cloneddata:getSize(), 'check cloned size')
-- check pointer access if allowed
if data:getFFIPointer() ~= nil and ffi ~= nil then
local pointer = data:getFFIPointer()
local ptr = ffi.cast('uint8_t*', pointer)
local byte5 = ptr[4]
test:assertEquals('o', byte5)
end
end
-- CompressedData (love.data.compress)
love.test.data.CompressedData = function(test)
-- create new compressed data
local cdata = love.data.compress('data', 'zlib', 'helloworld', -1)
test:assertObject(cdata)
test:assertEquals('zlib', cdata:getFormat(), 'check format used')
-- check properties match expected
test:assertEquals(18, cdata:getSize())
test:assertEquals('helloworld', love.data.decompress('data', cdata):getString())
-- check cloning the data
local clonedcdata = cdata:clone()
test:assertObject(clonedcdata)
test:assertEquals('zlib', clonedcdata:getFormat())
test:assertEquals(18, clonedcdata:getSize())
test:assertEquals('helloworld', love.data.decompress('data', clonedcdata):getString())
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------METHODS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.data.compress
love.test.data.compress = function(test)
-- here just testing each combo 'works' - in decompress's test method
-- we actually check the compress + decompress give the right value back
local compressions = {
{ love.data.compress('string', 'lz4', 'helloworld', -1), 'string'},
{ love.data.compress('string', 'lz4', 'helloworld', 0), 'string'},
{ love.data.compress('string', 'lz4', 'helloworld', 9), 'string'},
{ love.data.compress('string', 'zlib', 'helloworld', -1), 'string'},
{ love.data.compress('string', 'zlib', 'helloworld', 0), 'string'},
{ love.data.compress('string', 'zlib', 'helloworld', 9), 'string'},
{ love.data.compress('string', 'gzip', 'helloworld', -1), 'string'},
{ love.data.compress('string', 'gzip', 'helloworld', 0), 'string'},
{ love.data.compress('string', 'gzip', 'helloworld', 9), 'string'},
{ love.data.compress('data', 'lz4', 'helloworld', -1), 'userdata'},
{ love.data.compress('data', 'lz4', 'helloworld', 0), 'userdata'},
{ love.data.compress('data', 'lz4', 'helloworld', 9), 'userdata'},
{ love.data.compress('data', 'zlib', 'helloworld', -1), 'userdata'},
{ love.data.compress('data', 'zlib', 'helloworld', 0), 'userdata'},
{ love.data.compress('data', 'zlib', 'helloworld', 9), 'userdata'},
{ love.data.compress('data', 'gzip', 'helloworld', -1), 'userdata'},
{ love.data.compress('data', 'gzip', 'helloworld', 0), 'userdata'},
{ love.data.compress('data', 'gzip', 'helloworld', 9), 'userdata'}
}
for c=1,#compressions do
test:assertNotNil(compressions[c][1])
-- sense check return type and make sure bytedata returns are an object
test:assertEquals(compressions[c][2], type(compressions[c][1]), 'check is userdata')
if compressions[c][2] == 'userdata' then
test:assertNotEquals(nil, compressions[c][1]:type(), 'check has :type()')
end
end
end
-- love.data.decode
love.test.data.decode = function(test)
-- setup encoded strings
local str1 = love.data.encode('string', 'base64', 'helloworld', 0)
local str2 = love.data.encode('string', 'hex', 'helloworld')
local str3 = love.data.encode('data', 'base64', 'helloworld', 0)
local str4 = love.data.encode('data', 'hex', 'helloworld')
-- check value matches expected when decoded back
test:assertEquals('helloworld', love.data.decode('string', 'base64', str1), 'check string base64 decode')
test:assertEquals('helloworld', love.data.decode('string', 'hex', str2), 'check string hex decode')
test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decode('data', 'base64', str3):getString(), 'check data base64 decode')
test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decode('data', 'hex', str4):getString(), 'check data hex decode')
end
-- love.data.decompress
love.test.data.decompress = function(test)
-- setup compressed data for each combination
local str1 = love.data.compress('string', 'lz4', 'helloworld', -1)
local str2 = love.data.compress('string', 'lz4', 'helloworld', 0)
local str3 = love.data.compress('string', 'lz4', 'helloworld', 9)
local str4 = love.data.compress('string', 'zlib', 'helloworld', -1)
local str5 = love.data.compress('string', 'zlib', 'helloworld', 0)
local str6 = love.data.compress('string', 'zlib', 'helloworld', 9)
local str7 = love.data.compress('string', 'gzip', 'helloworld', -1)
local str8 = love.data.compress('string', 'gzip', 'helloworld', 0)
local str9 = love.data.compress('string', 'gzip', 'helloworld', 9)
local str10 = love.data.compress('data', 'lz4', 'helloworld', -1)
local str11 = love.data.compress('data', 'lz4', 'helloworld', 0)
local str12 = love.data.compress('data', 'lz4', 'helloworld', 9)
local str13 = love.data.compress('data', 'zlib', 'helloworld', -1)
local str14 = love.data.compress('data', 'zlib', 'helloworld', 0)
local str15 = love.data.compress('data', 'zlib', 'helloworld', 9)
local str16 = love.data.compress('data', 'gzip', 'helloworld', -1)
local str17 = love.data.compress('data', 'gzip', 'helloworld', 0)
local str18 = love.data.compress('data', 'gzip', 'helloworld', 9)
-- check decompressed value matches whats expected
test:assertEquals('helloworld', love.data.decompress('string', 'lz4', str1), 'check string lz4 decompress')
test:assertEquals('helloworld', love.data.decompress('string', 'lz4', str2), 'check string lz4 decompress')
test:assertEquals('helloworld', love.data.decompress('string', 'lz4', str3), 'check string lz4 decompress')
test:assertEquals('helloworld', love.data.decompress('string', 'zlib', str4), 'check string zlib decompress')
test:assertEquals('helloworld', love.data.decompress('string', 'zlib', str5), 'check string zlib decompress')
test:assertEquals('helloworld', love.data.decompress('string', 'zlib', str6), 'check string zlib decompress')
test:assertEquals('helloworld', love.data.decompress('string', 'gzip', str7), 'check string glib decompress')
test:assertEquals('helloworld', love.data.decompress('string', 'gzip', str8), 'check string glib decompress')
test:assertEquals('helloworld', love.data.decompress('string', 'gzip', str9), 'check string glib decompress')
test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'lz4', str10):getString(), 'check data lz4 decompress')
test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'lz4', str11):getString(), 'check data lz4 decompress')
test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'lz4', str12):getString(), 'check data lz4 decompress')
test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'zlib', str13):getString(), 'check data zlib decompress')
test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'zlib', str14):getString(), 'check data zlib decompress')
test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'zlib', str15):getString(), 'check data zlib decompress')
test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'gzip', str16):getString(), 'check data glib decompress')
test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'gzip', str17):getString(), 'check data glib decompress')
test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'gzip', str18):getString(), 'check data glib decompress')
end
-- love.data.encode
love.test.data.encode = function(test)
-- here just testing each combo 'works' - in decode's test method
-- we actually check the encode + decode give the right value back
local encodes = {
{ love.data.encode('string', 'base64', 'helloworld', 0), 'string'},
{ love.data.encode('string', 'base64', 'helloworld', 2), 'string'},
{ love.data.encode('string', 'hex', 'helloworld'), 'string'},
{ love.data.encode('data', 'base64', 'helloworld', 0), 'userdata'},
{ love.data.encode('data', 'base64', 'helloworld', 2), 'userdata'},
{ love.data.encode('data', 'hex', 'helloworld'), 'userdata'}
}
for e=1,#encodes do
test:assertNotNil(encodes[e][1])
-- sense check return type and make sure bytedata returns are an object
test:assertEquals(encodes[e][2], type(encodes[e][1]), 'check is usedata')
if encodes[e][2] == 'userdata' then
test:assertNotEquals(nil, encodes[e][1]:type(), 'check has :type()')
end
end
end
-- love.data.getPackedSize
love.test.data.getPackedSize = function(test)
local pack1 = love.data.getPackedSize('>xI3b')
local pack2 = love.data.getPackedSize('>I2B')
local pack3 = love.data.getPackedSize('>I4I4I4I4x')
test:assertEquals(5, pack1, 'check pack size 1')
test:assertEquals(3, pack2, 'check pack size 2')
test:assertEquals(17, pack3, 'check pack size 3')
end
-- love.data.hash
love.test.data.hash = function(test)
-- setup all the different hashing types
local str1 = love.data.hash('md5', 'helloworld')
local str2 = love.data.hash('sha1', 'helloworld')
local str3 = love.data.hash('sha224', 'helloworld')
local str4 = love.data.hash('sha256', 'helloworld')
local str5 = love.data.hash('sha384', 'helloworld')
local str6 = love.data.hash('sha512', 'helloworld')
-- check encoded hash value matches what's expected for that algo
test:assertEquals('fc5e038d38a57032085441e7fe7010b0', love.data.encode("string", "hex", str1), 'check md5 encode')
test:assertEquals('6adfb183a4a2c94a2f92dab5ade762a47889a5a1', love.data.encode("string", "hex", str2), 'check sha1 encode')
test:assertEquals('b033d770602994efa135c5248af300d81567ad5b59cec4bccbf15bcc', love.data.encode("string", "hex", str3), 'check sha224 encode')
test:assertEquals('936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af', love.data.encode("string", "hex", str4), 'check sha256 encode')
test:assertEquals('97982a5b1414b9078103a1c008c4e3526c27b41cdbcf80790560a40f2a9bf2ed4427ab1428789915ed4b3dc07c454bd9', love.data.encode("string", "hex", str5), 'check sha384 encode')
test:assertEquals('1594244d52f2d8c12b142bb61f47bc2eaf503d6d9ca8480cae9fcf112f66e4967dc5e8fa98285e36db8af1b8ffa8b84cb15e0fbcf836c3deb803c13f37659a60', love.data.encode("string", "hex", str6), 'check sha512 encode')
end
-- love.data.newByteData
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.data.newByteData = function(test)
test:assertObject(love.data.newByteData('helloworld'))
end
-- love.data.newDataView
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.data.newDataView = function(test)
test:assertObject(love.data.newDataView(love.data.newByteData('helloworld'), 0, 10))
end
-- love.data.pack
love.test.data.pack = function(test)
local packed1 = love.data.pack('string', '>I4I4I4I4', 9999, 1000, 1010, 2030)
local packed2 = love.data.pack('data', '>I4I4I4I4', 9999, 1000, 1010, 2030)
local a, b, c, d = love.data.unpack('>I4I4I4I4', packed1)
local e, f, g, h = love.data.unpack('>I4I4I4I4', packed2)
test:assertEquals(9999+9999, a+e, 'check packed 1')
test:assertEquals(1000+1000, b+f, 'check packed 2')
test:assertEquals(1010+1010, c+g, 'check packed 3')
test:assertEquals(2030+2030, d+h, 'check packed 4')
end
-- love.data.unpack
love.test.data.unpack = function(test)
local packed1 = love.data.pack('string', '>s5s4I3', 'hello', 'love', 100)
local packed2 = love.data.pack('data', '>s5I2', 'world', 20)
local a, b, c = love.data.unpack('>s5s4I3', packed1)
local d, e = love.data.unpack('>s5I2', packed2)
test:assertEquals(a .. ' ' .. d, 'hello world', 'check unpack 1')
test:assertEquals(b, 'love', 'check unpack 2')
test:assertEquals(c - e, 80, 'check unpack 3')
end
+80
View File
@@ -0,0 +1,80 @@
-- love.event
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------METHODS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.event.clear
love.test.event.clear = function(test)
-- push some events first
love.event.push('test', 1, 2, 3)
love.event.push('test', 1, 2, 3)
love.event.push('test', 1, 2, 3)
-- check after calling clear there are no events left
love.event.clear()
local count = 0
for n, a, b, c, d, e, f in love.event.poll() do
count = count + 1
end
test:assertEquals(0, count, 'check no events')
end
-- love.event.poll
love.test.event.poll = function(test)
-- push some events first
love.event.push('test', 1, 2, 3)
love.event.push('test', 1, 2, 3)
love.event.push('test', 1, 2, 3)
-- check poll recieves all events
local count = 0
for n, a, b, c, d, e, f in love.event.poll() do
count = count + 1
end
test:assertEquals(3, count, 'check 3 events')
end
-- love.event.pump
-- @NOTE dont think can really test as internally used
love.test.event.pump = function(test)
test:skipTest('used internally')
end
-- love.event.push
love.test.event.push = function(test)
-- check pushing some different types
love.event.push('add', 1, 2, 3)
love.event.push('ignore', 1, 2, 3)
love.event.push('add', 1, 2, 3)
love.event.push('ignore', 1, 2, 3)
local count = 0
for n, a, b, c, d, e, f in love.event.poll() do
if n == 'add' then
count = count + a + b + c
end
end
test:assertEquals(12, count, 'check total events')
end
-- love.event.quit
love.test.event.quit = function(test)
-- setting this overrides the quit hook to prevent actually quitting
love.test.module.fakequit = true
love.event.quit(0)
-- if it failed we'd have quit here
test:assertTrue(true, 'check quit hook called')
end
-- love.event.wait
-- @NOTE not sure best way to test this one
love.test.event.wait = function(test)
test:skipTest('used internally')
end
+451
View File
@@ -0,0 +1,451 @@
-- love.filesystem
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------OBJECTS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- File (love.filesystem.newFile)
love.test.filesystem.File = function(test)
-- setup a file to play with
local file1 = love.filesystem.openFile('data.txt', 'w')
file1:write('helloworld')
test:assertObject(file1)
file1:close()
-- test read mode
file1:open('r')
test:assertEquals('r', file1:getMode(), 'check read mode')
local contents, size = file1:read()
test:assertEquals('helloworld', contents)
test:assertEquals(10, size, 'check file read')
test:assertEquals(10, file1:getSize())
local ok1, err1 = file1:write('hello')
test:assertNotEquals(nil, err1, 'check cant write in read mode')
local iterator = file1:lines()
test:assertNotEquals(nil, iterator, 'check can read lines')
test:assertEquals('data.txt', file1:getFilename(), 'check filename matches')
file1:close()
-- test write mode
file1:open('w')
test:assertEquals('w', file1:getMode(), 'check write mode')
contents, size = file1:read()
test:assertEquals(nil, contents, 'check cant read file in write mode')
test:assertEquals('string', type(size), 'check err message shown')
local ok2, err2 = file1:write('helloworld')
test:assertTrue(ok2, 'check file write')
test:assertEquals(nil, err2, 'check no err writing')
-- test open/closing
file1:open('r')
test:assertTrue(file1:isOpen(), 'check file is open')
file1:close()
test:assertFalse(file1:isOpen(), 'check file gets closed')
file1:close()
-- test buffering and flushing
file1:open('w')
local ok3, err3 = file1:setBuffer('full', 10000)
test:assertTrue(ok3)
test:assertEquals('full', file1:getBuffer())
file1:write('replacedcontent')
file1:flush()
file1:close()
file1:open('r')
contents, size = file1:read()
test:assertEquals('replacedcontent', contents, 'check buffered content was written')
file1:close()
-- loop through file data with seek/tell until EOF
file1:open('r')
local counter = 0
for i=1,100 do
file1:seek(i)
test:assertEquals(i, file1:tell())
if file1:isEOF() == true then
counter = i
break
end
end
test:assertEquals(counter, 15)
file1:close()
end
-- FileData (love.filesystem.newFileData)
love.test.filesystem.FileData = function(test)
-- create new obj
local fdata = love.filesystem.newFileData('helloworld', 'test.txt')
test:assertObject(fdata)
test:assertEquals('test.txt', fdata:getFilename())
test:assertEquals('txt', fdata:getExtension())
-- check properties match expected
test:assertEquals('helloworld', fdata:getString(), 'check data string')
test:assertEquals(10, fdata:getSize(), 'check data size')
-- check cloning the bytedata
local clonedfdata = fdata:clone()
test:assertObject(clonedfdata)
test:assertEquals('helloworld', clonedfdata:getString(), 'check cloned data')
test:assertEquals(10, clonedfdata:getSize(), 'check cloned size')
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------METHODS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.filesystem.append
love.test.filesystem.append = function(test)
-- create a new file to test with
love.filesystem.write('filesystem.append.txt', 'foo')
-- try appending text and check new file contents/size matches
local success, message = love.filesystem.append('filesystem.append.txt', 'bar')
test:assertNotEquals(false, success, 'check success')
test:assertEquals(nil, message, 'check no error msg')
local contents, size = love.filesystem.read('filesystem.append.txt')
test:assertEquals(contents, 'foobar', 'check file contents')
test:assertEquals(size, 6, 'check file size')
-- check appending a specific no. of bytes
love.filesystem.append('filesystem.append.txt', 'foobarfoobarfoo', 6)
contents, size = love.filesystem.read('filesystem.append.txt')
test:assertEquals(contents, 'foobarfoobar', 'check appended contents')
test:assertEquals(size, 12, 'check appended size')
-- cleanup
love.filesystem.remove('filesystem.append.txt')
end
-- love.filesystem.areSymlinksEnabled
-- @NOTE best can do here is just check not nil
love.test.filesystem.areSymlinksEnabled = function(test)
test:assertNotNil(love.filesystem.areSymlinksEnabled())
end
-- love.filesystem.createDirectory
love.test.filesystem.createDirectory = function(test)
-- try creating a dir + subdir and check both exist
local success = love.filesystem.createDirectory('foo/bar')
test:assertNotEquals(false, success, 'check success')
test:assertNotEquals(nil, love.filesystem.getInfo('foo', 'directory'), 'check directory created')
test:assertNotEquals(nil, love.filesystem.getInfo('foo/bar', 'directory'), 'check subdirectory created')
-- cleanup
love.filesystem.remove('foo/bar')
love.filesystem.remove('foo')
end
-- love.filesystem.getAppdataDirectory
-- @NOTE i think this is too platform dependent to be tested nicely
love.test.filesystem.getAppdataDirectory = function(test)
test:assertNotNil(love.filesystem.getAppdataDirectory())
end
-- love.filesystem.getCRequirePath
love.test.filesystem.getCRequirePath = function(test)
-- check default value from documentation
test:assertEquals('??', love.filesystem.getCRequirePath(), 'check default value')
end
-- love.filesystem.getDirectoryItems
love.test.filesystem.getDirectoryItems = function(test)
-- create a dir + subdir with 2 files
love.filesystem.createDirectory('foo/bar')
love.filesystem.write('foo/file1.txt', 'file1')
love.filesystem.write('foo/bar/file2.txt', 'file2')
-- check both the file + subdir exist in the item list
local files = love.filesystem.getDirectoryItems('foo')
local hasfile = false
local hasdir = false
for _,v in ipairs(files) do
local info = love.filesystem.getInfo('foo/'..v)
if v == 'bar' and info.type == 'directory' then hasdir = true end
if v == 'file1.txt' and info.type == 'file' then hasfile = true end
end
test:assertTrue(hasfile, 'check file exists')
test:assertTrue(hasdir, 'check directory exists')
-- cleanup
love.filesystem.remove('foo/file1.txt')
love.filesystem.remove('foo/bar/file2.txt')
love.filesystem.remove('foo/bar')
love.filesystem.remove('foo')
end
-- love.filesystem.getIdentity
love.test.filesystem.getIdentity = function(test)
-- check setting identity matches
local original = love.filesystem.getIdentity()
love.filesystem.setIdentity('lover')
test:assertEquals('lover', love.filesystem.getIdentity(), 'check identity matches')
-- put back to original value
love.filesystem.setIdentity(original)
end
-- love.filesystem.getRealDirectory
love.test.filesystem.getRealDirectory = function(test)
-- make a test dir + file first
love.filesystem.createDirectory('foo')
love.filesystem.write('foo/test.txt', 'test')
-- check save dir matches the real dir we just wrote to
test:assertEquals(love.filesystem.getSaveDirectory(),
love.filesystem.getRealDirectory('foo/test.txt'), 'check directory matches')
-- cleanup
love.filesystem.remove('foo/test.txt')
love.filesystem.remove('foo')
end
-- love.filesystem.getRequirePath
love.test.filesystem.getRequirePath = function(test)
test:assertEquals('?.lua;?/init.lua',
love.filesystem.getRequirePath(), 'check default value')
end
-- love.filesystem.getSource
-- @NOTE i dont think we can test this cos love calls it first
love.test.filesystem.getSource = function(test)
test:skipTest('used internally')
end
-- love.filesystem.getSourceBaseDirectory
-- @NOTE i think this is too platform dependent to be tested nicely
love.test.filesystem.getSourceBaseDirectory = function(test)
test:assertNotNil(love.filesystem.getSourceBaseDirectory())
end
-- love.filesystem.getUserDirectory
-- @NOTE i think this is too platform dependent to be tested nicely
love.test.filesystem.getUserDirectory = function(test)
test:assertNotNil(love.filesystem.getUserDirectory())
end
-- love.filesystem.getWorkingDirectory
-- @NOTE i think this is too platform dependent to be tested nicely
love.test.filesystem.getWorkingDirectory = function(test)
test:assertNotNil(love.filesystem.getWorkingDirectory())
end
-- love.filesystem.getSaveDirectory
-- @NOTE i think this is too platform dependent to be tested nicely
love.test.filesystem.getSaveDirectory = function(test)
test:assertNotNil(love.filesystem.getSaveDirectory())
end
-- love.filesystem.getInfo
love.test.filesystem.getInfo = function(test)
-- create a dir and subdir with a file
love.filesystem.createDirectory('foo/bar')
love.filesystem.write('foo/bar/file2.txt', 'file2')
-- check getinfo returns the correct values
test:assertEquals(nil, love.filesystem.getInfo('foo/bar/file2.txt', 'directory'), 'check not directory')
test:assertNotEquals(nil, love.filesystem.getInfo('foo/bar/file2.txt'), 'check info not nil')
test:assertEquals(love.filesystem.getInfo('foo/bar/file2.txt').size, 5, 'check info size match')
-- @TODO test modified timestamp from info.modtime?
-- cleanup
love.filesystem.remove('foo/bar/file2.txt')
love.filesystem.remove('foo/bar')
love.filesystem.remove('foo')
end
-- love.filesystem.isFused
love.test.filesystem.isFused = function(test)
-- kinda assuming you'd run the testsuite in a non-fused game
test:assertEquals(love.filesystem.isFused(), false, 'check not fused')
end
-- love.filesystem.lines
love.test.filesystem.lines = function(test)
-- check lines returns the 3 lines expected
love.filesystem.write('file.txt', 'line1\nline2\nline3')
local linenum = 1
for line in love.filesystem.lines('file.txt') do
test:assertEquals('line' .. tostring(linenum), line, 'check line matches')
-- also check it removes newlines like the docs says it does
test:assertEquals(nil, string.find(line, '\n'), 'check newline removed')
linenum = linenum + 1
end
-- cleanup
love.filesystem.remove('file.txt')
end
-- love.filesystem.load
love.test.filesystem.load = function(test)
-- setup some fake lua files
love.filesystem.write('test1.lua', 'function test()\nreturn 1\nend\nreturn test()')
love.filesystem.write('test2.lua', 'function test()\nreturn 1')
-- check file that doesn't exist
local chunk, errormsg = love.filesystem.load('faker.lua')
test:assertEquals(nil, chunk, 'check file doesnt exist')
-- check valid lua file
chunk, errormsg = love.filesystem.load('test1.lua')
test:assertEquals(nil, errormsg, 'check no error message')
test:assertEquals(1, chunk(), 'check lua file runs')
-- check invalid lua file
local ok, chunk, err = pcall(love.filesystem.load, 'test2.lua')
test:assertFalse(ok, 'check invalid lua file')
-- cleanup
love.filesystem.remove('test1.lua')
love.filesystem.remove('test2.lua')
end
-- love.filesystem.mount
love.test.filesystem.mount = function(test)
-- write an example zip to savedir to use
local contents, size = love.filesystem.read('resources/test.zip') -- contains test.txt
love.filesystem.write('test.zip', contents, size)
-- check mounting file and check contents are mounted
local success = love.filesystem.mount('test.zip', 'test')
test:assertTrue(success, 'check success')
test:assertNotEquals(nil, love.filesystem.getInfo('test'), 'check mount not nil')
test:assertEquals('directory', love.filesystem.getInfo('test').type, 'check directory made')
test:assertNotEquals(nil, love.filesystem.getInfo('test/test.txt'), 'check file not nil')
test:assertEquals('file', love.filesystem.getInfo('test/test.txt').type, 'check file type')
-- cleanup
love.filesystem.remove('test/test.txt')
love.filesystem.remove('test')
love.filesystem.remove('test.zip')
end
-- love.filesystem.openFile
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.filesystem.openFile = function(test)
test:assertNotNil(love.filesystem.openFile('file2.txt', 'w'))
test:assertNotNil(love.filesystem.openFile('file2.txt', 'r'))
test:assertNotNil(love.filesystem.openFile('file2.txt', 'a'))
test:assertNotNil(love.filesystem.openFile('file2.txt', 'c'))
love.filesystem.remove('file2.txt')
end
-- love.filesystem.newFileData
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.filesystem.newFileData = function(test)
test:assertNotNil(love.filesystem.newFileData('helloworld', 'file1'))
end
-- love.filesystem.read
love.test.filesystem.read = function(test)
-- check reading a full file
local content, size = love.filesystem.read('resources/test.txt')
test:assertNotEquals(nil, content, 'check not nil')
test:assertEquals('helloworld', content, 'check content match')
test:assertEquals(10, size, 'check size match')
-- check reading partial file
content, size = love.filesystem.read('resources/test.txt', 5)
test:assertNotEquals(nil, content, 'check not nil')
test:assertEquals('hello', content, 'check content match')
test:assertEquals(5, size, 'check size match')
end
-- love.filesystem.remove
love.test.filesystem.remove = function(test)
-- create a dir + subdir with a file
love.filesystem.createDirectory('foo/bar')
love.filesystem.write('foo/bar/file2.txt', 'helloworld')
-- check removing files + dirs (should fail to remove dir if file inside)
test:assertFalse(love.filesystem.remove('foo'), 'check fail when file inside')
test:assertFalse(love.filesystem.remove('foo/bar'), 'check fail when file inside')
test:assertTrue(love.filesystem.remove('foo/bar/file2.txt'), 'check file removed')
test:assertTrue(love.filesystem.remove('foo/bar'), 'check subdirectory removed')
test:assertTrue(love.filesystem.remove('foo'), 'check directory removed')
-- cleanup not needed here hopefully...
end
-- love.filesystem.setCRequirePath
love.test.filesystem.setCRequirePath = function(test)
-- check setting path val is returned
love.filesystem.setCRequirePath('/??')
test:assertEquals('/??', love.filesystem.getCRequirePath(), 'check crequirepath value')
love.filesystem.setCRequirePath('??')
end
-- love.filesystem.setIdentity
love.test.filesystem.setIdentity = function(test)
-- check setting identity val is returned
local original = love.filesystem.getIdentity()
love.filesystem.setIdentity('lover')
test:assertEquals('lover', love.filesystem.getIdentity(), 'check indentity value')
-- return value to original
love.filesystem.setIdentity(original)
end
-- love.filesystem.setRequirePath
love.test.filesystem.setRequirePath = function(test)
-- check setting path val is returned
love.filesystem.setRequirePath('?.lua;?/start.lua')
test:assertEquals('?.lua;?/start.lua', love.filesystem.getRequirePath(), 'check require path')
-- reset to default
love.filesystem.setRequirePath('?.lua;?/init.lua')
end
-- love.filesystem.setSource
love.test.filesystem.setSource = function(test)
test:skipTest('used internally')
end
-- love.filesystem.unmount
love.test.filesystem.unmount = function(test)
-- create a zip file mounted to use
local contents, size = love.filesystem.read('resources/test.zip') -- contains test.txt
love.filesystem.write('test.zip', contents, size)
love.filesystem.mount('test.zip', 'test')
-- check mounted, unmount, then check its unmounted
test:assertNotEquals(nil, love.filesystem.getInfo('test/test.txt'), 'check mount exists')
love.filesystem.unmount('test.zip')
test:assertEquals(nil, love.filesystem.getInfo('test/test.txt'), 'check unmounted')
-- cleanup
love.filesystem.remove('test/test.txt')
love.filesystem.remove('test')
love.filesystem.remove('test.zip')
end
-- love.filesystem.write
love.test.filesystem.write = function(test)
-- check writing a bunch of files matches whats read back
love.filesystem.write('test1.txt', 'helloworld')
love.filesystem.write('test2.txt', 'helloworld', 10)
love.filesystem.write('test3.txt', 'helloworld', 5)
test:assertEquals('helloworld', love.filesystem.read('test1.txt'), 'check read file')
test:assertEquals('helloworld', love.filesystem.read('test2.txt'), 'check read all')
test:assertEquals('hello', love.filesystem.read('test3.txt'), 'check read partial')
-- cleanup
love.filesystem.remove('test1.txt')
love.filesystem.remove('test2.txt')
love.filesystem.remove('test3.txt')
end
+131
View File
@@ -0,0 +1,131 @@
-- love.font
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------OBJECTS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- GlyphData (love.font.newGlyphData)
love.test.font.GlyphData = function(test)
-- create obj
local rasterizer = love.font.newRasterizer('resources/font.ttf')
local gdata = love.font.newGlyphData(rasterizer, 97) -- 'a'
test:assertObject(gdata)
-- check properties match expected
test:assertNotNil(gdata:getString())
test:assertEquals(128, gdata:getSize(), 'check data size')
test:assertEquals(9, gdata:getAdvance(), 'check advance')
test:assertEquals('la8', gdata:getFormat(), 'check format')
-- @TODO
--[[
currently these will return 0 and '' respectively as not implemented
https://github.com/love2d/love/blob/12.0-development/src/modules/font/freetype/TrueTypeRasterizer.cpp#L140-L141
"basically I haven't decided what to do here yet, because of the more
advanced text shaping that happens in love 12 having a unicode codepoint
associated with a glyph probably doesn't make sense in the first place"
]]--
--test:assertEquals(97, gdata:getGlyph(), 'check glyph number') - returns 0
--test:assertEquals('a', gdata:getGlyphString(), 'check glyph string') - returns ''
-- check height + width
test:assertEquals(8, gdata:getHeight(), 'check height')
test:assertEquals(8, gdata:getWidth(), 'check width')
-- check boundary / dimensions
local x, y, w, h = gdata:getBoundingBox()
local dw, dh = gdata:getDimensions()
test:assertEquals(0, x, 'check bbox x')
test:assertEquals(-3, y, 'check bbox y')
test:assertEquals(8, w, 'check bbox w')
test:assertEquals(14, h, 'check bbox h')
test:assertEquals(8, dw, 'check dim width')
test:assertEquals(8, dh, 'check dim height')
-- check bearing
local bw, bh = gdata:getBearing()
test:assertEquals(0, bw, 'check bearing w')
test:assertEquals(11, bh, 'check bearing h')
end
-- Rasterizer (love.font.newRasterizer)
love.test.font.Rasterizer = function(test)
-- create obj
local rasterizer = love.font.newRasterizer('resources/font.ttf')
test:assertObject(rasterizer)
-- check advance
test:assertEquals(9, rasterizer:getAdvance(), 'check advance')
-- check ascent/descent
test:assertEquals(9, rasterizer:getAscent(), 'check ascent')
test:assertEquals(-3, rasterizer:getDescent(), 'check descent')
-- check glyphcount
test:assertEquals(77, rasterizer:getGlyphCount(), 'check glyph count')
-- check specific glyphs
test:assertObject(rasterizer:getGlyphData('L'))
test:assertTrue(rasterizer:hasGlyphs('L', 'O', 'V', 'E'), 'check LOVE')
-- check height + lineheight
test:assertEquals(12, rasterizer:getHeight(), 'check height')
test:assertEquals(15, rasterizer:getLineHeight(), 'check line height')
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------METHODS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.font.newBMFontRasterizer
love.test.font.newBMFontRasterizer = function(test)
local rasterizer = love.font.newBMFontRasterizer('resources/love.png');
test:assertObject(rasterizer)
end
-- love.font.newGlyphData
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.font.newGlyphData = function(test)
local img = love.image.newImageData('resources/love.png')
local rasterizer = love.font.newImageRasterizer(img, 'ABC', 0, 1);
local glyphdata = love.font.newGlyphData(rasterizer, 65)
test:assertObject(glyphdata)
end
-- love.font.newImageRasterizer
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.font.newImageRasterizer = function(test)
local img = love.image.newImageData('resources/love.png')
local rasterizer = love.font.newImageRasterizer(img, 'ABC', 0, 1);
test:assertObject(rasterizer)
end
-- love.font.newRasterizer
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.font.newRasterizer = function(test)
test:assertObject(love.font.newRasterizer('resources/font.ttf'))
end
-- love.font.newTrueTypeRasterizer
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.font.newTrueTypeRasterizer = function(test)
test:assertObject(love.font.newTrueTypeRasterizer(12, "normal", 1))
test:assertObject(love.font.newTrueTypeRasterizer('resources/font.ttf', 8, "normal", 1))
end
File diff suppressed because it is too large Load Diff
+119
View File
@@ -0,0 +1,119 @@
-- love.image
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------OBJECTS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- CompressedImageData (love.image.newCompressedImageData)
love.test.image.CompressedImageData = function(test)
-- create obj
local idata = love.image.newCompressedData('resources/love.dxt1')
test:assertObject(idata)
-- check string + size
test:assertNotEquals(nil, idata:getString(), 'check data string')
test:assertEquals(2744, idata:getSize(), 'check data size')
-- check img dimensions
local iw, ih = idata:getDimensions()
test:assertEquals(64, iw, 'check image dimension w')
test:assertEquals(64, ih, 'check image dimension h')
test:assertEquals(64, idata:getWidth(), 'check image direct w')
test:assertEquals(64, idata:getHeight(), 'check image direct h')
-- check format
test:assertEquals('DXT1', idata:getFormat(), 'check image format')
-- check mipmap count
test:assertEquals(7, idata:getMipmapCount(), 'check mipmap count')
end
-- ImageData (love.image.newImageData)
love.test.image.ImageData = function(test)
-- create obj
local idata = love.image.newImageData('resources/love.png')
test:assertObject(idata)
-- check string + size
test:assertNotEquals(nil, idata:getString(), 'check data string')
test:assertEquals(16384, idata:getSize(), 'check data size')
-- check img dimensions
local iw, ih = idata:getDimensions()
test:assertEquals(64, iw, 'check image dimension w')
test:assertEquals(64, ih, 'check image dimension h')
test:assertEquals(64, idata:getWidth(), 'check image direct w')
test:assertEquals(64, idata:getHeight(), 'check image direct h')
-- check format
test:assertEquals('rgba8', idata:getFormat(), 'check image format')
-- manipulate image data so white heart is black
local mapdata = function(x, y, r, g, b, a)
if r == 1 and g == 1 and b == 1 then
r = 0; g = 0; b = 0
end
return r, g, b, a
end
idata:mapPixel(mapdata, 0, 0, 64, 64)
local r1, g1, b1 = idata:getPixel(25, 25)
test:assertEquals(0, r1+g1+b1, 'check mapped black')
-- map some other data into the idata
local idata2 = love.image.newImageData('resources/loveinv.png')
idata:paste(idata2, 0, 0, 0, 0)
r1, g1, b1 = idata:getPixel(25, 25)
test:assertEquals(3, r1+g1+b1, 'check back to white')
-- set pixels directly
idata:setPixel(25, 25, 1, 0, 0, 1)
local r2, g2, b2 = idata:getPixel(25, 25)
test:assertEquals(1, r2+g2+b2, 'check set to red')
-- check encoding to an image
idata:encode('png', 'test-encode.png')
local read = love.filesystem.openFile('test-encode.png', 'r')
test:assertNotNil(read)
love.filesystem.remove('test-encode.png')
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------METHODS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.image.isCompressed
-- @NOTE really we need to test each of the files listed here:
-- https://love2d.org/wiki/CompressedImageFormat
-- also need to be platform dependent (e.g. dxt not suppored on phones)
love.test.image.isCompressed = function(test)
test:assertTrue(love.image.isCompressed('resources/love.dxt1'),
'check dxt1 valid compressed image')
end
-- love.image.newCompressedData
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.image.newCompressedData = function(test)
test:assertObject(love.image.newCompressedData('resources/love.dxt1'))
end
-- love.image.newImageData
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.image.newImageData = function(test)
test:assertObject(love.image.newImageData('resources/love.png'))
test:assertObject(love.image.newImageData(16, 16, 'rgba8', nil))
end
+375
View File
@@ -0,0 +1,375 @@
-- love.math
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------OBJECTS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- BezierCurve (love.math.newBezierCurve)
love.test.math.BezierCurve = function(test)
-- create obj
local curve = love.math.newBezierCurve(1, 1, 2, 2, 3, 1)
local px, py = curve:getControlPoint(2)
test:assertObject(curve)
-- check initial properties
test:assertCoords({2, 2}, {px, py}, 'check point x/y')
test:assertEquals(3, curve:getControlPointCount(), 'check 3 points')
test:assertEquals(2, curve:getDegree(), 'check degree is points-1')
-- check some values on the curve
test:assertEquals(1, curve:evaluate(0), 'check curve evaluation 0')
test:assertRange(curve:evaluate(0.1), 1.2, 1.3, 'check curve evaluation 0.1')
test:assertRange(curve:evaluate(0.2), 1.4, 1.5, 'check curve evaluation 0.2')
test:assertRange(curve:evaluate(0.5), 2, 2.1, 'check curve evaluation 0.5')
test:assertEquals(3, curve:evaluate(1), 'check curve evaluation 1')
-- check derivative
local deriv = curve:getDerivative()
test:assertObject(deriv)
test:assertEquals(2, deriv:getControlPointCount(), 'check deriv points')
test:assertRange(deriv:evaluate(0.1), 2, 2.1, 'check deriv evaluation 0.1')
-- check segment
local segment = curve:getSegment(0, 0.5)
test:assertObject(segment)
test:assertEquals(3, segment:getControlPointCount(), 'check segment points')
test:assertRange(segment:evaluate(0.1), 1, 1.1, 'check segment evaluation 0.1')
-- mess with control points
curve:removeControlPoint(2)
curve:insertControlPoint(4, 1, -1)
curve:insertControlPoint(5, 3, -1)
curve:insertControlPoint(6, 2, -1)
curve:setControlPoint(2, 3, 2)
test:assertEquals(5, curve:getControlPointCount(), 'check 3 points still')
local px1, py1 = curve:getControlPoint(1)
local px2, py2 = curve:getControlPoint(3)
local px3, py3 = curve:getControlPoint(5)
test:assertCoords({1, 1}, {px1, py1}, 'check modified point 1')
test:assertCoords({5, 3}, {px2, py2}, 'check modified point 1')
test:assertCoords({3, 1}, {px3, py3}, 'check modified point 1')
-- check render lists
local coords1 = curve:render(5)
local coords2 = curve:renderSegment(0, 0.1, 5)
test:assertEquals(196, #coords1, 'check coords')
test:assertEquals(20, #coords2, 'check segment coords')
-- check translation values
px, py = curve:getControlPoint(2)
test:assertCoords({3, 2}, {px, py}, 'check pretransform x/y')
curve:rotate(90 * (math.pi/180), 0, 0)
px, py = curve:getControlPoint(2)
test:assertCoords({-2, 3}, {px, py}, 'check rotated x/y')
curve:scale(2, 0, 0)
px, py = curve:getControlPoint(2)
test:assertCoords({-4, 6}, {px, py}, 'check scaled x/y')
curve:translate(5, -5)
px, py = curve:getControlPoint(2)
test:assertCoords({1, 1}, {px, py}, 'check translated x/y')
end
-- RandomGenerator (love.math.RandomGenerator)
-- @NOTE as this checks random numbers the chances this fails is very unlikely, but not 0...
-- if you've managed to proc it congrats! your prize is to rerun the testsuite again
love.test.math.RandomGenerator = function(test)
-- create object
local rng1 = love.math.newRandomGenerator(3418323524, 20529293)
test:assertObject(rng1)
-- check set properties
local low, high = rng1:getSeed()
test:assertEquals(3418323524, low, 'check seed low')
test:assertEquals(20529293, high, 'check seed high')
-- check states
local rng2 = love.math.newRandomGenerator(1448323524, 10329293)
test:assertNotEquals(rng1:random(), rng2:random(), 'check not matching states')
test:assertNotEquals(rng1:randomNormal(), rng2:randomNormal(), 'check not matching states')
-- check setting state works
rng2:setState(rng1:getState())
test:assertEquals(rng1:random(), rng2:random(), 'check now matching')
-- check overwriting seed works, should change output
rng1:setSeed(os.time())
test:assertNotEquals(rng1:random(), rng2:random(), 'check not matching states')
test:assertNotEquals(rng1:randomNormal(), rng2:randomNormal(), 'check not matching states')
end
-- Transform (love.math.Transform)
love.test.math.Transform = function(test)
-- create obj
local transform = love.math.newTransform(0, 0, 0, 1, 1, 0, 0, 0, 0)
test:assertObject(transform)
-- set some values and check the matrix and transformPoint values
transform:translate(10, 8)
transform:scale(2, 3)
transform:rotate(90*(math.pi/180))
transform:shear(1, 2)
local px, py = transform:transformPoint(1, 1)
test:assertCoords({4, 14}, {px, py}, 'check transformation methods')
transform:reset()
px, py = transform:transformPoint(1, 1)
test:assertCoords({1, 1}, {px, py}, 'check reset')
-- apply a transform to another transform
local transform2 = love.math.newTransform()
transform2:translate(5, 3)
transform:apply(transform2)
px, py = transform:transformPoint(1, 1)
test:assertCoords({6, 4}, {px, py}, 'check apply other transform')
-- check cloning a transform
local transform3 = transform:clone()
px, py = transform3:transformPoint(1, 1)
test:assertCoords({6, 4}, {px, py}, 'check clone transform')
-- check inverse and inverseTransform
transform:reset()
transform:translate(-14, 6)
local ipx, ipy = transform:inverseTransformPoint(0, 0)
transform:inverse()
px, py = transform:transformPoint(0, 0)
test:assertCoords({-px, -py}, {ipx, ipy}, 'check inverse points transform')
-- check matrix manipulation
transform:setTransformation(0, 0, 0, 1, 1, 0, 0, 0, 0)
transform:translate(4, 4)
local m1, m2, m3, m4, m5, m6, m7, m8,
m9, m10, m11, m12, m13, m14, m15, m16 = transform:getMatrix()
test:assertEquals(4, m4, 'check translate matrix x')
test:assertEquals(4, m8, 'check translate matrix y')
transform:setMatrix(m1, m2, m3, 3, m5, m6, m7, 1, m9, m10, m11, m12, m13, m14, m15, m16)
px, py = transform:transformPoint(1, 1)
test:assertCoords({4, 2}, {px, py}, 'check set matrix')
-- check affine vs non affine
transform:reset()
test:assertTrue(transform:isAffine2DTransform(), 'check affine 1')
transform:translate(4, 3)
test:assertTrue(transform:isAffine2DTransform(), 'check affine 2')
transform:setMatrix(1, 3, 4, 5.5, 1, 4.5, 2, 1, 3.4, 5.1, 4.1, 13, 1, 1, 2, 3)
test:assertFalse(transform:isAffine2DTransform(), 'check not affine')
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------METHODS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.math.colorFromBytes
love.test.math.colorFromBytes = function(test)
-- check random value
local r1, g1, b1, a1 = love.math.colorFromBytes(51, 51, 51, 51)
test:assertEquals(r1, 0.2, 'check r from bytes')
test:assertEquals(g1, 0.2, 'check g from bytes')
test:assertEquals(b1, 0.2, 'check b from bytes')
test:assertEquals(a1, 0.2, 'check a from bytes')
-- check "max" value
local r2, g2, b2, a2 = love.math.colorFromBytes(255, 255, 255, 255)
test:assertEquals(r2, 1, 'check r from bytes')
test:assertEquals(g2, 1, 'check g from bytes')
test:assertEquals(b2, 1, 'check b from bytes')
test:assertEquals(a2, 1, 'check a from bytes')
-- check "min" value
local r3, g3, b3, a3 = love.math.colorFromBytes(0, 0, 0, 0)
test:assertEquals(r3, 0, 'check r from bytes')
test:assertEquals(g3, 0, 'check g from bytes')
test:assertEquals(b3, 0, 'check b from bytes')
test:assertEquals(a3, 0, 'check a from bytes')
end
-- love.math.colorToBytes
love.test.math.colorToBytes = function(test)
-- check random value
local r1, g1, b1, a1 = love.math.colorToBytes(0.2, 0.2, 0.2, 0.2)
test:assertEquals(r1, 51, 'check bytes from r')
test:assertEquals(g1, 51, 'check bytes from g')
test:assertEquals(b1, 51, 'check bytes from b')
test:assertEquals(a1, 51, 'check bytes from a')
-- check "max" value
local r2, g2, b2, a2 = love.math.colorToBytes(1, 1, 1, 1)
test:assertEquals(r2, 255, 'check bytes from r')
test:assertEquals(g2, 255, 'check bytes from g')
test:assertEquals(b2, 255, 'check bytes from b')
test:assertEquals(a2, 255, 'check bytes from a')
-- check "min" value
local r3, g3, b3, a3 = love.math.colorToBytes(0, 0, 0, 0)
test:assertEquals(r3, 0, 'check bytes from r')
test:assertEquals(g3, 0, 'check bytes from g')
test:assertEquals(b3, 0, 'check bytes from b')
test:assertEquals(a3, 0, 'check bytes from a')
end
-- love.math.gammaToLinear
-- @NOTE I tried doing the same formula as the source from MathModule.cpp
-- but get test failues due to slight differences
love.test.math.gammaToLinear = function(test)
local lr, lg, lb = love.math.gammaToLinear(1, 0.8, 0.02)
--local eg = ((0.8 + 0.055) / 1.055)^2.4
--local eb = 0.02 / 12.92
test:assertGreaterEqual(0, lr, 'check gamma r to linear')
test:assertGreaterEqual(0, lg, 'check gamma g to linear')
test:assertGreaterEqual(0, lb, 'check gamma b to linear')
end
-- love.math.getRandomSeed
-- @NOTE whenever i run this high is always 0, is that intended?
love.test.math.getRandomSeed = function(test)
local low, high = love.math.getRandomSeed()
test:assertGreaterEqual(0, low, 'check random seed low')
test:assertGreaterEqual(0, high, 'check random seed high')
end
-- love.math.getRandomState
love.test.math.getRandomState = function(test)
test:assertNotNil(love.math.getRandomState())
end
-- love.math.isConvex
love.test.math.isConvex = function(test)
local isconvex = love.math.isConvex({0, 0, 1, 0, 1, 1, 1, 0, 0, 0}) -- square
local notconvex = love.math.isConvex({1, 2, 2, 4, 3, 4, 2, 1, 3, 1}) -- weird shape
test:assertTrue(isconvex, 'check polygon convex')
test:assertFalse(notconvex, 'check polygon not convex')
end
-- love.math.linearToGammer
-- @NOTE I tried doing the same formula as the source from MathModule.cpp
-- but get test failues due to slight differences
love.test.math.linearToGamma = function(test)
local gr, gg, gb = love.math.linearToGamma(1, 0.8, 0.001)
--local eg = 1.055 * (0.8^1/2.4) - 0.055
--local eb = 0.001 * 12.92
test:assertGreaterEqual(0, gr, 'check linear r to gamme')
test:assertGreaterEqual(0, gg, 'check linear g to gamme')
test:assertGreaterEqual(0, gb, 'check linear b to gamme')
end
-- love.math.newBezierCurve
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.math.newBezierCurve = function(test)
test:assertObject(love.math.newBezierCurve({0, 0, 0, 1, 1, 1, 2, 1}))
end
-- love.math.newRandomGenerator
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.math.newRandomGenerator = function(test)
test:assertObject(love.math.newRandomGenerator())
end
-- love.math.newTransform
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.math.newTransform = function(test)
test:assertObject(love.math.newTransform())
end
-- love.math.perlinNoise
love.test.math.perlinNoise = function(test)
-- check some noise values
-- output should be consistent if given the same input
local noise1 = love.math.perlinNoise(100)
local noise2 = love.math.perlinNoise(1, 10)
local noise3 = love.math.perlinNoise(1043, 31.123, 999)
local noise4 = love.math.perlinNoise(99.222, 10067, 8, 1843)
test:assertRange(noise1, 0.5, 0.51, 'check noise 1 dimension')
test:assertRange(noise2, 0.5, 0.51, 'check noise 2 dimensions')
test:assertRange(noise3, 0.56, 0.57, 'check noise 3 dimensions')
test:assertRange(noise4, 0.52, 0.53, 'check noise 4 dimensions')
end
-- love.math.simplexNoise
love.test.math.simplexNoise = function(test)
-- check some noise values
-- output should be consistent if given the same input
local noise1 = love.math.simplexNoise(100)
local noise2 = love.math.simplexNoise(1, 10)
local noise3 = love.math.simplexNoise(1043, 31.123, 999)
local noise4 = love.math.simplexNoise(99.222, 10067, 8, 1843)
-- rounded to avoid floating point issues
test:assertRange(noise1, 0.5, 0.51, 'check noise 1 dimension')
test:assertRange(noise2, 0.47, 0.48, 'check noise 2 dimensions')
test:assertRange(noise3, 0.26, 0.27, 'check noise 3 dimensions')
test:assertRange(noise4, 0.53, 0.54, 'check noise 4 dimensions')
end
-- love.math.random
love.test.math.random = function(test)
-- check some random ranges
test:assertRange(love.math.random(10), 1, 10, 'check within 1 - 10')
test:assertRange(love.math.random(10), 1, 10, 'check within 1 - 10')
test:assertRange(love.math.random(10), 1, 10, 'check within 1 - 10')
test:assertRange(love.math.random(10), 1, 10, 'check within 1 - 10')
test:assertRange(love.math.random(10), 1, 10, 'check within 1 - 10')
test:assertRange(love.math.random(5, 100), 5, 100, 'check within 5 - 100')
test:assertRange(love.math.random(5, 100), 5, 100, 'check within 5 - 100')
test:assertRange(love.math.random(5, 100), 5, 100, 'check within 5 - 100')
test:assertRange(love.math.random(5, 100), 5, 100, 'check within 5 - 100')
test:assertRange(love.math.random(5, 100), 5, 100, 'check within 5 - 100')
end
-- love.math.randomNormal
-- @NOTE i dont _really_ get the range expected based on stddev + mean
-- so feel free to change to be more accurate
love.test.math.randomNormal = function(test)
test:assertRange(love.math.randomNormal(1, 0), -3, 3, 'check within -3 - 3')
end
-- love.math.setRandomSeed
-- @NOTE same with getRandomSeed, high is always 0 when I tested it?
love.test.math.setRandomSeed = function(test)
love.math.setRandomSeed(9001)
local low, high = love.math.getRandomSeed()
test:assertEquals(9001, low, 'check seed low set')
test:assertEquals(0, high, 'check seed high set')
end
-- love.math.setRandomState
love.test.math.setRandomState = function(test)
-- check setting state matches value returned
local rs1 = love.math.getRandomState()
love.math.setRandomState(rs1)
local rs2 = love.math.getRandomState()
test:assertEquals(rs1, rs2, 'check random state set')
end
-- love.math.triangulate
love.test.math.triangulate = function(test)
local triangles1 = love.math.triangulate({0, 0, 1, 0, 1, 1, 1, 0, 0, 0}) -- square
local triangles2 = love.math.triangulate({1, 2, 2, 4, 3, 4, 2, 1, 3, 1}) -- weird shape
test:assertEquals(3, #triangles1, 'check polygon triangles')
test:assertEquals(3, #triangles2, 'check polygon triangles')
end
+824
View File
@@ -0,0 +1,824 @@
-- love.physics
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------OBJECTS---------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- Body (love.physics.newBody)
love.test.physics.Body = function(test)
-- create body
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 0, 0, 'static')
local body2 = love.physics.newBody(world, 30, 30, 'dynamic')
love.physics.newRectangleShape(body1, 5, 5, 10, 10)
love.physics.newRectangleShape(body2, 5, 5, 10, 10)
test:assertObject(body1)
-- check body active
test:assertTrue(body1:isActive(), 'check active by def')
-- check body bullet
test:assertFalse(body1:isBullet(), 'check not bullet by def')
body1:setBullet(true)
test:assertTrue(body1:isBullet(), 'check set bullet')
-- check fixed rotation
test:assertFalse(body1:isFixedRotation(), 'check fix rot def')
body1:setFixedRotation(true)
test:assertTrue(body1:isFixedRotation(), 'check set fix rot')
-- check sleeping/waking
test:assertTrue(body1:isSleepingAllowed(), 'check sleep def')
body1:setSleepingAllowed(false)
test:assertFalse(body1:isSleepingAllowed(), 'check set sleep')
body1:setSleepingAllowed(true)
world:update(1)
test:assertFalse(body1:isAwake(), 'check fell asleep')
body1:setSleepingAllowed(false)
body1:setType('dynamic')
test:assertTrue(body1:isAwake(), 'check waking up')
-- check touching
test:assertFalse(body1:isTouching(body2))
body2:setPosition(5, 5)
world:update(1)
test:assertTrue(body1:isTouching(body2))
-- check children lists
test:assertEquals(1, #body1:getContacts(), 'check contact list')
test:assertEquals(0, #body1:getJoints(), 'check joints list')
love.physics.newDistanceJoint(body1, body2, 5, 5, 10, 10, true)
test:assertEquals(1, #body1:getJoints(), 'check joints list')
-- check local points
local x, y = body1:getLocalCenter()
test:assertRange(x, 5, 6, 'check local center x')
test:assertRange(y, 5, 6, 'check local center y')
local lx, ly = body1:getLocalPoint(10, 10)
test:assertRange(lx, 10, 11, 'check local point x')
test:assertRange(ly, 9, 10, 'check local point y')
local lx1, ly1, lx2, ly2 = body1:getLocalPoints(0, 5, 5, 10)
test:assertRange(lx1, 0, 1, 'check local points x 1')
test:assertRange(ly1, 3, 4, 'check local points y 1')
test:assertRange(lx2, 5, 6, 'check local points x 2')
test:assertRange(ly2, 9, 10, 'check local points y 2')
-- check world points
local wx, wy = body1:getWorldPoint(10.4, 9)
test:assertRange(wx, 10, 11, 'check world point x')
test:assertRange(wy, 10, 11, 'check world point y')
local wx1, wy1, wx2, wy2 = body1:getWorldPoints(0.4, 4, 5.4, 9)
test:assertRange(wx1, 0, 1, 'check world points x 1')
test:assertRange(wy1, 5, 6, 'check world points y 1')
test:assertRange(wx2, 5, 6, 'check world points x 2')
test:assertRange(wy2, 10, 11, 'check world points y 2')
-- check angular damping + velocity
test:assertEquals(0, body1:getAngularDamping(), 'check angular damping')
test:assertEquals(0, body1:getAngularVelocity(), 'check angular velocity')
-- check world props
test:assertObject(body1:getWorld())
test:assertEquals(2, body1:getWorld():getBodyCount(), 'check world count')
local cx, cy = body1:getWorldCenter()
test:assertRange(cx, 4, 5, 'check world center x')
test:assertRange(cy, 6, 7, 'check world center y')
local vx, vy = body1:getWorldVector(5, 10)
test:assertEquals(5, vx, 'check vector x')
test:assertEquals(10, vy, 'check vector y')
-- check inertia
test:assertRange(body1:getInertia(), 5, 6, 'check inertia')
-- check angle
test:assertEquals(0, body1:getAngle(), 'check def angle')
body1:setAngle(90 * (math.pi/180))
test:assertEquals(math.floor(math.pi/2*100), math.floor(body1:getAngle()*100), 'check set angle')
-- check gravity scale
test:assertEquals(1, body1:getGravityScale(), 'check def grav')
body1:setGravityScale(2)
test:assertEquals(2, body1:getGravityScale(), 'check change grav')
-- check damping
test:assertEquals(0, body1:getLinearDamping(), 'check def lin damping')
body1:setLinearDamping(0.1)
test:assertRange(body1:getLinearDamping(), 0, 0.2, 'check change lin damping')
-- check velocity
local x2, y2 = body1:getLinearVelocity()
test:assertEquals(1, x2, 'check def lin velocity x')
test:assertEquals(1, y2, 'check def lin velocity y')
body1:setLinearVelocity(4, 5)
local x3, y3 = body1:getLinearVelocity()
test:assertEquals(4, x3, 'check change lin velocity x')
test:assertEquals(5, y3, 'check change lin velocity y')
-- check mass
test:assertRange(body1:getMass(), 0.1, 0.2, 'check def mass')
body1:setMass(10)
test:assertEquals(10, body1:getMass(), 'check change mass')
body1:setMassData(3, 5, 10, 1)
local x4, y4, mass4, inertia4 = body1:getMassData()
test:assertEquals(3, x4, 'check mass data change x')
test:assertEquals(5, y4, 'check mass data change y')
test:assertEquals(10, mass4, 'check mass data change mass')
test:assertRange(inertia4, 340, 341, 'check mass data change inertia')
body1:resetMassData()
local x5, y5, mass5, inertia5 = body1:getMassData()
test:assertRange(x5, 5, 6, 'check mass data reset x')
test:assertRange(y5, 5, 6, 'check mass data reset y')
test:assertRange(mass5, 0.1, 0.2, 'check mass data reset mass')
test:assertRange(inertia5, 5, 6, 'check mass data reset inertia')
-- check position
local x6, y6 = body1:getPosition()
test:assertRange(x6, -1, 0, 'check position x')
test:assertRange(y6, 0, 1, 'check position y')
body1:setPosition(10, 4)
local x7, y7 = body1:getPosition()
test:assertEquals(x7, 10, 'check set position x')
test:assertEquals(y7, 4, 'check set position y')
-- check type
test:assertEquals('dynamic', body1:getType(), 'check type match')
body1:setType('kinematic')
body1:setType('static')
test:assertEquals('static', body1:getType(), 'check type change')
-- check userdata
test:assertEquals(nil, body1:getUserData(), 'check user data')
body1:setUserData({ love = 'cool' })
test:assertEquals('cool', body1:getUserData().love, 'check set user data')
-- check x/y direct
test:assertEquals(10, math.floor(body1:getX()), 'check get x')
test:assertEquals(4, math.floor(body1:getY()), 'check get y')
body1:setX(0)
body1:setY(0)
test:assertEquals(0, body1:getX(), 'check get x')
test:assertEquals(0, body1:getY(), 'check get y')
-- apply angular impulse
local vel = body2:getAngularVelocity()
test:assertRange(vel, 0, 0, 'check velocity before')
body2:applyAngularImpulse(10)
local vel1 = body2:getAngularVelocity()
test:assertRange(vel1, 5, 6, 'check velocity after 1')
-- apply standard force
local ang1 = body2:getAngle()
test:assertRange(ang1, 0.1, 0.2, 'check initial angle 1')
body2:applyForce(2, 3)
world:update(2)
local vel2 = body2:getAngularVelocity()
local ang2 = body2:getAngle()
test:assertRange(ang2, -0.1, 0, 'check angle after 2')
test:assertRange(vel2, 1, 2, 'check velocity after 2')
-- apply linear impulse
body2:applyLinearImpulse(-4, -59)
world:update(1)
local ang3 = body2:getAngle()
local vel3 = body2:getAngularVelocity()
test:assertRange(ang3, -2, -1, 'check angle after 3')
test:assertRange(vel3, 0, 1, 'check velocity after 3')
-- apply torque
body2:applyTorque(4)
world:update(2)
local ang4 = body2:getAngle()
local vel4 = body2:getAngularVelocity()
test:assertRange(ang4, -1, 0, 'check angle after 4')
test:assertRange(vel4, 0, 1, 'check velocity after 4')
-- check destroy
test:assertFalse(body1:isDestroyed(), 'check not destroyed')
body1:destroy()
test:assertTrue(body1:isDestroyed(), 'check destroyed')
end
-- Contact (love.physics.World:getContacts)
love.test.physics.Contact = function(test)
-- create a setup so we can access some contact objects
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 0, 0, 'dynamic')
local body2 = love.physics.newBody(world, 10, 10, 'dynamic')
local rectangle1 = love.physics.newRectangleShape(body1, 0, 0, 10, 10)
local rectangle2 = love.physics.newRectangleShape(body2, 0, 0, 10, 10)
rectangle1:setUserData('rec1')
rectangle2:setUserData('rec2')
-- used to check for collisions + no. of collisions
local collided = false
local pass = 1
-- set callback for contact start
world:setCallbacks(
function(shape_a, shape_b, contact)
collided = true
-- check contact object
test:assertObject(contact)
-- check child indices
local indexA, indexB = contact:getChildren()
test:assertEquals(1, indexA, 'check child indice a')
test:assertEquals(1, indexB, 'check child indice b')
-- check shapes match using userdata
local shapeA, shapeB = contact:getShapes()
test:assertEquals(shape_a:getUserData(), shapeA:getUserData(), 'check shape a matches')
test:assertEquals(shape_b:getUserData(), shapeB:getUserData(), 'check shape b matches')
-- check normal pos
local nx, ny = contact:getNormal()
test:assertEquals(1, nx, 'check normal x')
test:assertEquals(0, ny, 'check normal y')
-- check actual pos
local px1, py1, px2, py2 = contact:getPositions()
test:assertRange(px1, 5, 6, 'check collide x 1')
test:assertRange(py1, 5, 6, 'check collide y 1')
test:assertRange(px2, 5, 6, 'check collide x 2')
test:assertRange(py2, 5, 6, 'check collide y 2')
-- check touching
test:assertTrue(contact:isTouching(), 'check touching')
-- check enabled (we pass through twice to test on/off)
test:assertEquals(pass == 1, contact:isEnabled(), 'check enabled for pass ' .. tostring(pass))
-- check friction
test:assertRange(contact:getFriction(), 0.2, 0.3, 'check def friction')
contact:setFriction(0.1)
test:assertRange(contact:getFriction(), 0.1, 0.2, 'check set friction')
contact:resetFriction()
test:assertRange(contact:getFriction(), 0.2, 0.3, 'check reset friction')
-- check restitution
test:assertEquals(0, contact:getRestitution(), 'check def restitution')
contact:setRestitution(1)
test:assertEquals(1, contact:getRestitution(), 'check set restitution')
contact:resetRestitution()
test:assertEquals(0, contact:getRestitution(), 'check reset restitution')
pass = pass + 1
end, function() end, function(shape_a, shape_b, contact)
if pass > 2 then
contact:setEnabled(false)
end
end, function() end
)
-- check bodies collided
world:update(1)
test:assertTrue(collided, 'check bodies collided')
-- update again for enabled check
world:update(1)
test:assertEquals(2, pass, 'check ran twice')
end
-- Joint (love.physics.newDistanceJoint)
love.test.physics.Joint = function(test)
-- make joint
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 10, 10, 'dynamic')
local body2 = love.physics.newBody(world, 20, 20, 'dynamic')
local joint = love.physics.newDistanceJoint(body1, body2, 10, 10, 20, 20, true)
test:assertObject(joint)
-- check type
test:assertEquals('distance', joint:getType(), 'check joint type')
-- check not destroyed
test:assertFalse(joint:isDestroyed(), 'check not destroyed')
-- check reaction props (sometimes nil on linux runners)
if joint:getReactionForce(1) ~= nil then
test:assertEquals(0, joint:getReactionForce(1), 'check reaction force')
end
if joint:getReactionTorque(1) ~= nil then
test:assertEquals(0, joint:getReactionTorque(1), 'check reaction torque')
end
-- check body pointer
local b1, b2 = joint:getBodies()
test:assertEquals(body1:getX(), b1:getX(), 'check body 1')
test:assertEquals(body2:getX(), b2:getX(), 'check body 2')
-- check joint anchors
local x1, y1, x2, y2 = joint:getAnchors()
test:assertRange(x1, 10, 11, 'check anchor x1')
test:assertRange(y1, 10, 11, 'check anchor y1')
test:assertRange(x2, 20, 21, 'check anchor x2')
test:assertRange(y2, 20, 21, 'check anchor y2')
test:assertTrue(joint:getCollideConnected(), 'check not colliding')
-- test userdata
test:assertEquals(nil, joint:getUserData(), 'check no data by def')
joint:setUserData('hello')
test:assertEquals('hello', joint:getUserData(), 'check set userdata')
-- destroy
joint:destroy()
test:assertTrue(joint:isDestroyed(), 'check destroyed')
end
-- Shape (love.physics.newCircleShape)
-- @NOTE in 12.0 fixtures have been merged into shapes
love.test.physics.Shape = function(test)
-- create shape
local world = love.physics.newWorld(0, 0, false)
local body1 = love.physics.newBody(world, 0, 0, 'dynamic')
local shape1 = love.physics.newRectangleShape(body1, 5, 5, 10, 10)
test:assertObject(shape1)
-- check child count
test:assertEquals(1, shape1:getChildCount(), 'check child count')
-- check radius
test:assertRange(shape1:getRadius(), 0, 0.4, 'check radius')
-- check type match
test:assertEquals('polygon', shape1:getType(), 'check rectangle type')
-- check body pointer
test:assertEquals(0, shape1:getBody():getX(), 'check body link')
-- check category
test:assertEquals(1, shape1:getCategory(), 'check def category')
shape1:setCategory(3, 5, 6)
local categories = {shape1:getCategory()}
test:assertEquals(14, categories[1] + categories[2] + categories[3], 'check set category')
-- check sensor prop
test:assertFalse(shape1:isSensor(), 'check sensor def')
shape1:setSensor(true)
test:assertTrue(shape1:isSensor(), 'check set sensor')
shape1:setSensor(false)
-- check not destroyed
test:assertFalse(shape1:isDestroyed(), 'check not destroyed')
-- check user data
test:assertEquals(nil, shape1:getUserData(), 'check no user data')
shape1:setUserData({ test = 14 })
test:assertEquals(14, shape1:getUserData().test, 'check user data set')
-- check bounding box
-- polygons have an additional skin radius to help with collisions
-- so this wont be 0, 0, 10, 10 as you'd think but has an additional 0.3 padding
local topLeftX, topLeftY, bottomRightX, bottomRightY = shape1:computeAABB(0, 0, 0, 1)
local tlx, tly, brx, bry = shape1:getBoundingBox(1)
test:assertEquals(topLeftX, tlx, 'check bbox methods match tlx')
test:assertEquals(topLeftY, tly, 'check bbox methods match tly')
test:assertEquals(bottomRightX, brx, 'check bbox methods match brx')
test:assertEquals(bottomRightY, bry, 'check bbox methods match bry')
test:assertEquals(topLeftX, topLeftY, 'check bbox tl 1')
test:assertRange(topLeftY, -0.3, -0.2, 'check bbox tl 2')
test:assertEquals(bottomRightX, bottomRightY, 'check bbox br 1')
test:assertRange(bottomRightX, 10.3, 10.4, 'check bbox br 2')
-- check density
test:assertEquals(1, shape1:getDensity(), 'check def density')
shape1:setDensity(5)
test:assertEquals(5, shape1:getDensity(), 'check set density')
-- check mass
local x1, y1, mass1, inertia1 = shape1:getMassData()
test:assertRange(x1, 5, 5.1, 'check shape mass pos x')
test:assertRange(y1, 5, 5.1, 'check shape mass pos y')
test:assertRange(mass1, 0.5, 0.6, 'check mass at 1 density')
test:assertRange(inertia1, 0, 0.1, 'check intertia at 1 density')
local x2, y2, mass2, inertia2 = shape1:computeMass(1000)
test:assertRange(mass2, 111, 112, 'check mass at 1000 density')
test:assertRange(inertia2, 7407, 7408, 'check intertia at 1000 density')
-- check friction
test:assertRange(shape1:getFriction(), 0.2, 0.3, 'check def friction')
shape1:setFriction(1)
test:assertEquals(1, shape1:getFriction(), 'check set friction')
-- check restitution
test:assertEquals(0, shape1:getRestitution(), 'check def restitution')
shape1:setRestitution(0.5)
test:assertRange(shape1:getRestitution(), 0.5, 0.6, 'check set restitution')
-- check points
local bodyp = love.physics.newBody(world, 0, 0, 'dynamic')
local shape2 = love.physics.newRectangleShape(bodyp, 5, 5, 10, 10)
test:assertTrue(shape2:testPoint(5, 5), 'check point 5,5')
test:assertTrue(shape2:testPoint(10, 10, 0, 15, 15), 'check point 15,15 after translate 10,10')
test:assertFalse(shape2:testPoint(5, 5, 90, 10, 10), 'check point 10,10 after translate 5,5,90')
test:assertFalse(shape2:testPoint(10, 10, 90, 5, 5), 'check point 5,5 after translate 10,10,90')
test:assertFalse(shape2:testPoint(15, 15), 'check point 15,15')
-- check ray cast
local xn1, yn1, fraction1 = shape2:rayCast(-20, -20, 20, 20, 100, 0, 0, 0, 1)
test:assertNotEquals(nil, xn1, 'check ray 1 x')
test:assertNotEquals(nil, xn1, 'check ray 1 y')
local xn2, yn2, fraction2 = shape2:rayCast(10, 10, -150, -150, 100, 0, 0, 0, 1)
test:assertEquals(nil, xn2, 'check ray 2 x')
test:assertEquals(nil, yn2, 'check ray 2 y')
-- check filtering
test:assertEquals(nil, shape2:getMask(), 'check no mask')
shape2:setMask(1, 2, 3)
test:assertEquals(3, #{shape2:getMask()}, 'check set mask')
test:assertEquals(0, shape2:getGroupIndex(), 'check no index')
shape2:setGroupIndex(-1)
test:assertEquals(-1, shape2:getGroupIndex(), 'check set index')
local cat, mask, group = shape2:getFilterData()
test:assertEquals(1, cat, 'check filter cat')
test:assertEquals(65528, mask, 'check filter mask')
test:assertEquals(-1, group, 'check filter group')
-- check destroyed
shape1:destroy()
test:assertTrue(shape1:isDestroyed(), 'check destroyed')
shape2:destroy()
-- run some collision checks using filters, setup new shapes
local body2 = love.physics.newBody(world, 5, 5, 'dynamic')
local shape3 = love.physics.newRectangleShape(body1, 0, 0, 10, 10)
local shape4 = love.physics.newRectangleShape(body2, 0, 0, 10, 10)
local collisions = 0
world:setCallbacks(
function() collisions = collisions + 1 end,
function() end,
function() end,
function() end
)
-- same positive group do collide
shape3:setGroupIndex(1)
shape4:setGroupIndex(1)
world:update(1)
test:assertEquals(1, collisions, 'check positive group collide')
-- check negative group dont collide
shape3:setGroupIndex(-1)
shape4:setGroupIndex(-1)
body2:setPosition(20, 20); world:update(1); body2:setPosition(0, 0); world:update(1)
test:assertEquals(1, collisions, 'check negative group collide')
-- check masks do collide
shape3:setGroupIndex(0)
shape4:setGroupIndex(0)
shape3:setCategory(2)
shape4:setMask(3)
body2:setPosition(20, 20); world:update(1); body2:setPosition(0, 0); world:update(1)
test:assertEquals(2, collisions, 'check mask collide')
-- check masks not colliding
shape3:setCategory(2)
shape4:setMask(2, 4, 6)
body2:setPosition(20, 20); world:update(1); body2:setPosition(0, 0); world:update(1)
test:assertEquals(2, collisions, 'check mask not collide')
end
-- World (love.physics.newWorld)
love.test.physics.World = function(test)
-- create new world
local world = love.physics.newWorld(0, 0, false)
local body1 = love.physics.newBody(world, 0, 0, 'dynamic')
local rectangle1 = love.physics.newRectangleShape(body1, 0, 0, 10, 10)
test:assertObject(world)
-- check bodies in world
test:assertEquals(1, #world:getBodies(), 'check 1 body')
test:assertEquals(0, world:getBodies()[1]:getX(), 'check body prop x')
test:assertEquals(0, world:getBodies()[1]:getY(), 'check body prop y')
world:translateOrigin(-10, -10) -- check affects bodies
test:assertRange(world:getBodies()[1]:getX(), 9, 11, 'check body prop change x')
test:assertRange(world:getBodies()[1]:getY(), 9, 11, 'check body prop change y')
test:assertEquals(1, world:getBodyCount(), 'check 1 body count')
-- check world status
test:assertFalse(world:isLocked(), 'check not updating')
test:assertFalse(world:isSleepingAllowed(), 'check no sleep (till brooklyn)')
world:setSleepingAllowed(true)
test:assertTrue(world:isSleepingAllowed(), 'check can sleep')
-- check world objects
test:assertEquals(0, #world:getJoints(), 'check no joints')
test:assertEquals(0, world:getJointCount(), 'check no joints count')
test:assertEquals(0, world:getGravity(), 'check def gravity')
test:assertEquals(0, #world:getContacts(), 'check no contacts')
test:assertEquals(0, world:getContactCount(), 'check no contact count')
-- check callbacks are called
local beginContact, endContact, preSolve, postSolve = world:getCallbacks()
test:assertEquals(nil, beginContact, 'check no begin contact callback')
test:assertEquals(nil, endContact, 'check no end contact callback')
test:assertEquals(nil, preSolve, 'check no pre solve callback')
test:assertEquals(nil, postSolve, 'check no post solve callback')
local beginContactCheck = false
local endContactCheck = false
local preSolveCheck = false
local postSolveCheck = false
local collisions = 0
world:setCallbacks(
function() beginContactCheck = true; collisions = collisions + 1 end,
function() endContactCheck = true end,
function() preSolveCheck = true end,
function() postSolveCheck = true end
)
-- setup so we can collide stuff to call the callbacks
local body2 = love.physics.newBody(world, 10, 10, 'dynamic')
local rectangle2 = love.physics.newRectangleShape(body2, 0, 0, 10, 10)
test:assertFalse(beginContactCheck, 'check world didnt update after adding body')
world:update(1)
test:assertTrue(beginContactCheck, 'check contact start')
test:assertTrue(preSolveCheck, 'check pre solve')
test:assertTrue(postSolveCheck, 'check post solve')
body2:setPosition(100, 100)
world:update(1)
test:assertTrue(endContactCheck, 'check contact end')
-- check point checking
local shapes = 0
world:queryShapesInArea(0, 0, 10, 10, function(x)
shapes = shapes + 1
end)
test:assertEquals(1, shapes, 'check shapes in area')
-- check raycast
world:rayCast(0, 0, 200, 200, function(x)
shapes = shapes + 1
return 1
end)
test:assertEquals(3, shapes, 'check shapes in raycast')
-- change collision logic
test:assertEquals(nil, world:getContactFilter(), 'check def filter')
world:update(1)
world:setContactFilter(function(s1, s2)
return false -- nothing collides
end)
body2:setPosition(10, 10)
world:update(1)
test:assertEquals(1, collisions, 'check collision logic change')
-- check gravity
world:setGravity(1, 1)
test:assertEquals(1, world:getGravity(), 'check grav change')
-- check destruction
test:assertFalse(world:isDestroyed(), 'check not destroyed')
world:destroy()
test:assertTrue(world:isDestroyed(), 'check world destroyed')
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------METHODS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.physics.getDistance
love.test.physics.getDistance = function(test)
-- setup two fixtues to check
local world = love.physics.newWorld(0, 0, false)
local body = love.physics.newBody(world, 10, 10, 'static')
local shape1 = love.physics.newEdgeShape(body, 0, 0, 5, 5)
local shape2 = love.physics.newEdgeShape(body, 10, 10, 15, 15)
-- check distance between them
test:assertRange(love.physics.getDistance(shape1, shape2), 6, 7, 'check distance matches')
end
-- love.physics.getMeter
love.test.physics.getMeter = function(test)
-- check value set is returned
love.physics.setMeter(30)
test:assertEquals(30, love.physics.getMeter(), 'check meter matches')
end
-- love.physics.newBody
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newBody = function(test)
local world = love.physics.newWorld(1, 1, true)
local body = love.physics.newBody(world, 10, 10, 'static')
test:assertObject(body)
end
-- love.physics.newChainShape
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newChainShape = function(test)
local world = love.physics.newWorld(1, 1, true)
local body = love.physics.newBody(world, 10, 10, 'static')
test:assertObject(love.physics.newChainShape(body, true, 0, 0, 1, 0, 1, 1, 0, 1))
end
-- love.physics.newCircleShape
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newCircleShape = function(test)
local world = love.physics.newWorld(1, 1, true)
local body = love.physics.newBody(world, 10, 10, 'static')
test:assertObject(love.physics.newCircleShape(body, 10))
end
-- love.physics.newDistanceJoint
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newDistanceJoint = function(test)
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 10, 10, 'static')
local body2 = love.physics.newBody(world, 20, 20, 'static')
local obj = love.physics.newDistanceJoint(body1, body2, 10, 10, 20, 20, true)
test:assertObject(obj)
end
-- love.physics.newEdgeShape
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newEdgeShape = function(test)
local world = love.physics.newWorld(1, 1, true)
local body = love.physics.newBody(world, 10, 10, 'static')
local obj = love.physics.newEdgeShape(body, 0, 0, 10, 10)
test:assertObject(obj)
end
-- love.physics.newFrictionJoint
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newFrictionJoint = function(test)
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 10, 10, 'static')
local body2 = love.physics.newBody(world, 20, 20, 'static')
local obj = love.physics.newFrictionJoint(body1, body2, 15, 15, true)
test:assertObject(obj)
end
-- love.physics.newGearJoint
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newGearJoint = function(test)
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 10, 10, 'dynamic')
local body2 = love.physics.newBody(world, 20, 20, 'dynamic')
local body3 = love.physics.newBody(world, 30, 30, 'dynamic')
local body4 = love.physics.newBody(world, 40, 40, 'dynamic')
local joint1 = love.physics.newPrismaticJoint(body1, body2, 10, 10, 20, 20, true)
local joint2 = love.physics.newPrismaticJoint(body3, body4, 30, 30, 40, 40, true)
local obj = love.physics.newGearJoint(joint1, joint2, 1, true)
test:assertObject(obj)
end
-- love.physics.newMotorJoint
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newMotorJoint = function(test)
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 10, 10, 'static')
local body2 = love.physics.newBody(world, 20, 20, 'static')
local obj = love.physics.newMotorJoint(body1, body2, 1)
test:assertObject(obj)
end
-- love.physics.newMouseJoint
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newMouseJoint = function(test)
local world = love.physics.newWorld(1, 1, true)
local body = love.physics.newBody(world, 10, 10, 'static')
local obj = love.physics.newMouseJoint(body, 10, 10)
test:assertObject(obj)
end
-- love.physics.newPolygonShape
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newPolygonShape = function(test)
local world = love.physics.newWorld(1, 1, true)
local body = love.physics.newBody(world, 10, 10, 'static')
local obj = love.physics.newPolygonShape(body, {0, 0, 2, 3, 2, 1, 3, 1, 5, 1})
test:assertObject(obj)
end
-- love.physics.newPrismaticJoint
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newPrismaticJoint = function(test)
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 10, 10, 'static')
local body2 = love.physics.newBody(world, 20, 20, 'static')
local obj = love.physics.newPrismaticJoint(body1, body2, 10, 10, 20, 20, true)
test:assertObject(obj)
end
-- love.physics.newPulleyJoint
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newPulleyJoint = function(test)
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 10, 10, 'static')
local body2 = love.physics.newBody(world, 20, 20, 'static')
local obj = love.physics.newPulleyJoint(body1, body2, 10, 10, 20, 20, 15, 15, 25, 25, 1, true)
test:assertObject(obj)
end
-- love.physics.newRectangleShape
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newRectangleShape = function(test)
local world = love.physics.newWorld(1, 1, true)
local body = love.physics.newBody(world, 10, 10, 'static')
local shape1 = love.physics.newRectangleShape(body, 10, 20)
local shape2 = love.physics.newRectangleShape(body, 10, 10, 40, 30, 10)
test:assertObject(shape1)
test:assertObject(shape2)
end
-- love.physics.newRevoluteJoint
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newRevoluteJoint = function(test)
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 10, 10, 'static')
local body2 = love.physics.newBody(world, 20, 20, 'static')
local obj = love.physics.newRevoluteJoint(body1, body2, 10, 10, true)
test:assertObject(obj)
end
-- love.physics.newRopeJoint
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newRopeJoint = function(test)
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 10, 10, 'static')
local body2 = love.physics.newBody(world, 20, 20, 'static')
local obj = love.physics.newRopeJoint(body1, body2, 10, 10, 20, 20, 50, true)
test:assertObject(obj)
end
-- love.physics.newWeldJoint
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newWeldJoint = function(test)
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 10, 10, 'static')
local body2 = love.physics.newBody(world, 20, 20, 'static')
local obj = love.physics.newWeldJoint(body1, body2, 10, 10, true)
test:assertObject(obj)
end
-- love.physics.newWheelJoint
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newWheelJoint = function(test)
local world = love.physics.newWorld(1, 1, true)
local body1 = love.physics.newBody(world, 10, 10, 'static')
local body2 = love.physics.newBody(world, 20, 20, 'static')
local obj = love.physics.newWheelJoint(body1, body2, 10, 10, 5, 5, true)
test:assertObject(obj)
end
-- love.physics.newWorld
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.physics.newWorld = function(test)
local world = love.physics.newWorld(1, 1, true)
test:assertObject(world)
end
-- love.physics.setMeter
love.test.physics.setMeter = function(test)
-- set initial meter
local world = love.physics.newWorld(1, 1, true)
love.physics.setMeter(30)
local body = love.physics.newBody(world, 300, 300, "dynamic")
-- check changing meter changes pos value relatively
love.physics.setMeter(10)
local x, y = body:getPosition()
test:assertEquals(100, x, 'check pos x')
test:assertEquals(100, y, 'check pos y')
end
+106
View File
@@ -0,0 +1,106 @@
-- love.sound
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------OBJECTS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- Decoder (love.sound.newDecoder)
love.test.sound.Decoder = function(test)
-- create obj
local decoder = love.sound.newDecoder('resources/click.ogg')
test:assertObject(decoder)
-- check bit depth
test:assertMatch({8, 16}, decoder:getBitDepth(), 'check bit depth')
-- check channel count
test:assertMatch({1, 2}, decoder:getChannelCount(), 'check channel count')
-- check duration
test:assertRange(decoder:getDuration(), 0.06, 0.07, 'check duration')
-- check sample rate
test:assertEquals(44100, decoder:getSampleRate(), 'check sample rate')
-- check makes sound data (test in method below)
test:assertObject(decoder:decode())
-- check cloning sound
local clone = decoder:clone()
test:assertMatch({8, 16}, clone:getBitDepth(), 'check cloned bit depth')
test:assertMatch({1, 2}, clone:getChannelCount(), 'check cloned channel count')
test:assertRange(clone:getDuration(), 0.06, 0.07, 'check cloned duration')
test:assertEquals(44100, clone:getSampleRate(), 'check cloned sample rate')
end
-- SoundData (love.sound.newSoundData)
love.test.sound.SoundData = function(test)
-- create obj
local sdata = love.sound.newSoundData('resources/click.ogg')
test:assertObject(sdata)
-- check data size + string
test:assertEquals(11708, sdata:getSize(), 'check size')
test:assertNotNil(sdata:getString())
-- check bit depth
test:assertMatch({8, 16}, sdata:getBitDepth(), 'check bit depth')
-- check channel count
test:assertMatch({1, 2}, sdata:getChannelCount(), 'check channel count')
-- check duration
test:assertRange(sdata:getDuration(), 0.06, 0.07, 'check duration')
-- check samples
test:assertEquals(44100, sdata:getSampleRate(), 'check sample rate')
test:assertEquals(2927, sdata:getSampleCount(), 'check sample count')
-- check cloning
local clone = sdata:clone()
test:assertEquals(11708, clone:getSize(), 'check clone size')
test:assertNotNil(clone:getString())
test:assertMatch({8, 16}, clone:getBitDepth(), 'check clone bit depth')
test:assertMatch({1, 2}, clone:getChannelCount(), 'check clone channel count')
test:assertRange(clone:getDuration(), 0.06, 0.07, 'check clone duration')
test:assertEquals(44100, clone:getSampleRate(), 'check clone sample rate')
test:assertEquals(2927, clone:getSampleCount(), 'check clone sample count')
-- check sample setting
test:assertRange(sdata:getSample(0.001), -0.1, 0, 'check sample 1')
test:assertRange(sdata:getSample(0.005), -0.1, 0, 'check sample 1')
sdata:setSample(0.002, 1)
test:assertEquals(1, sdata:getSample(0.002), 'check setting sample manually')
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------METHODS-------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.sound.newDecoder
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.sound.newDecoder = function(test)
test:assertObject(love.sound.newDecoder('resources/click.ogg'))
end
-- love.sound.newSoundData
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.sound.newSoundData = function(test)
test:assertObject(love.sound.newSoundData('resources/click.ogg'))
test:assertObject(love.sound.newSoundData(math.floor((1/32)*44100), 44100, 16, 1))
end
+83
View File
@@ -0,0 +1,83 @@
-- love.system
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------METHODS---------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.system.getClipboardText
love.test.system.getClipboardText = function(test)
-- ignore if not using window
if love.test.windowmode == false then
return test:skipTest('clipboard only available in window mode')
end
-- check clipboard value is set
love.system.setClipboardText('helloworld')
test:assertEquals('helloworld', love.system.getClipboardText(), 'check clipboard match')
end
-- love.system.getOS
love.test.system.getOS = function(test)
-- check os is in documented values
local os = love.system.getOS()
local options = {'OS X', 'Windows', 'Linux', 'Android', 'iOS'}
test:assertMatch(options, os, 'check value matches')
end
-- love.system.getPowerInfo
love.test.system.getPowerInfo = function(test)
-- check battery state is one of the documented states
local state, percent, seconds = love.system.getPowerInfo()
local states = {'unknown', 'battery', 'nobattery', 'charging', 'charged'}
test:assertMatch(states, state, 'check value matches')
-- if percent/seconds check within expected range
if percent ~= nil then
test:assertRange(percent, 0, 100, 'check battery percent within range')
end
if seconds ~= nil then
test:assertNotNil(seconds)
end
end
-- love.system.getProcessorCount
love.test.system.getProcessorCount = function(test)
test:assertNotNil(love.system.getProcessorCount()) -- youd hope right
end
-- love.system.hasBackgroundMusic
love.test.system.hasBackgroundMusic = function(test)
test:assertNotNil(love.system.hasBackgroundMusic())
end
-- love.system.openURL
love.test.system.openURL = function(test)
test:skipTest('cant test this worked')
--test:assertNotEquals(nil, love.system.openURL('https://love2d.org'), 'check open URL')
end
-- love.system.getClipboardText
love.test.system.setClipboardText = function(test)
-- ignore if not using window
if love.test.windowmode == false then
return test:skipTest('clipboard only available in window mode')
end
-- check value returned is what was set
love.system.setClipboardText('helloworld')
test:assertEquals('helloworld', love.system.getClipboardText(), 'check set text')
end
-- love.system.vibrate
-- @NOTE cant really test this
love.test.system.vibrate = function(test)
test:skipTest('cant test this worked')
end
+137
View File
@@ -0,0 +1,137 @@
-- love.thread
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------OBJECTS---------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- Channel (love.thread.newChannel)
love.test.thread.Channel = function(test)
-- create channel
local channel = love.thread.getChannel('test')
test:assertObject(channel)
-- setup thread to use
local threadcode1 = [[
require("love.timer")
love.timer.sleep(0.1)
love.thread.getChannel('test'):push('hello world')
love.timer.sleep(0.1)
love.thread.getChannel('test'):push('me again')
]]
local thread1 = love.thread.newThread(threadcode1)
thread1:start()
-- check message sent from thread to channel
local msg1 = channel:demand()
test:assertEquals('hello world', msg1, 'check 1st message was sent')
thread1:wait()
test:assertEquals(1, channel:getCount(), 'check still another message')
test:assertEquals('me again', channel:peek(), 'check 2nd message pending')
local msg2 = channel:pop()
test:assertEquals('me again', msg2, 'check 2nd message was sent')
channel:clear()
-- setup another thread for some ping pong
local threadcode2 = [[
local function setChannel(channel, value)
channel:clear()
return channel:push(value)
end
local channel = love.thread.getChannel('test')
local waiting = true
local sent = nil
while waiting == true do
if sent == nil then
sent = channel:performAtomic(setChannel, 'ping')
end
if channel:hasRead(sent) then
local msg = channel:demand()
if msg == 'pong' then
channel:push(msg)
waiting = false
end
end
end
]]
-- first we run a thread that will send 1 ping
local thread2 = love.thread.newThread(threadcode2)
thread2:start()
-- we wait for that ping to be sent and then send a pong back
local msg3 = channel:demand()
test:assertEquals('ping', msg3, 'check message recieved 1')
-- thread should be waiting for us, and checking is the ping was read
channel:supply('pong', 1)
-- if it was then it should send back our pong and thread should die
thread2:wait()
local msg4 = channel:pop()
test:assertEquals('pong', msg4, 'check message recieved 2')
test:assertEquals(0, channel:getCount())
end
-- Thread (love.thread.newThread)
love.test.thread.Thread = function(test)
-- create thread
local threadcode = [[
local b = 0
for a=1,100000 do
b = b + a
end
]]
local thread = love.thread.newThread(threadcode)
test:assertObject(thread)
-- check thread runs
thread:start()
test:assertTrue(thread:isRunning(), 'check started')
thread:wait()
test:assertFalse(thread:isRunning(), 'check finished')
test:assertEquals(nil, thread:getError(), 'check no errors')
-- check an invalid thread
local badthreadcode = 'local b = 0\nreturn b + "string" .. 10'
local badthread = love.thread.newThread(badthreadcode)
badthread:start()
badthread:wait()
test:assertNotNil(badthread:getError())
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------METHODS---------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.thread.getChannel
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.thread.getChannel = function(test)
test:assertObject(love.thread.getChannel('test'))
end
-- love.thread.newChannel
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.thread.newChannel = function(test)
test:assertObject(love.thread.newChannel())
end
-- love.thread.newThread
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.thread.newThread = function(test)
test:assertObject(love.thread.newThread('classes/TestSuite.lua'))
end
+52
View File
@@ -0,0 +1,52 @@
-- love.timer
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------METHODS---------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.timer.getAverageDelta
-- @NOTE not sure if you could reliably get a specific delta?
love.test.timer.getAverageDelta = function(test)
test:assertNotNil(love.timer.getAverageDelta())
end
-- love.timer.getDelta
-- @NOTE not sure if you could reliably get a specific delta?
love.test.timer.getDelta = function(test)
test:assertNotNil(love.timer.getDelta())
end
-- love.timer.getFPS
-- @NOTE not sure if you could reliably get a specific FPS?
love.test.timer.getFPS = function(test)
test:assertNotNil(love.timer.getFPS())
end
-- love.timer.getTime
love.test.timer.getTime = function(test)
local starttime = love.timer.getTime()
love.timer.sleep(1)
local endtime = love.timer.getTime() - starttime
test:assertRange(endtime, 1, 2, 'check 1s passes')
end
-- love.timer.sleep
love.test.timer.sleep = function(test)
local starttime = love.timer.getTime()
love.timer.sleep(1)
test:assertRange(love.timer.getTime() - starttime, 1, 2, 'check 1s passes')
end
-- love.timer.step
-- @NOTE not sure if you could reliably get a specific step val?
love.test.timer.step = function(test)
test:assertNotNil(love.timer.step())
end
+46
View File
@@ -0,0 +1,46 @@
-- love.video
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------OBJECTS---------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- VideoStream (love.thread.newVideoStream)
love.test.video.VideoStream = function(test)
-- create obj
local video = love.video.newVideoStream('resources/sample.ogv')
test:assertObject(video)
-- check def properties
test:assertEquals('resources/sample.ogv', video:getFilename(), 'check filename')
test:assertFalse(video:isPlaying(), 'check not playing by def')
-- check playing and pausing states
video:play()
test:assertTrue(video:isPlaying(), 'check now playing')
video:seek(0.3)
test:assertRange(video:tell(), 0.3, 0.4, 'check seek/tell')
video:rewind()
test:assertEquals(0, video:tell(), 'check rewind')
video:pause()
test:assertFalse(video:isPlaying(), 'check paused')
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------METHODS---------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.video.newVideoStream
-- @NOTE this is just basic nil checking, objs have their own test method
love.test.video.newVideoStream = function(test)
test:assertObject(love.video.newVideoStream('resources/sample.ogv'))
end
+371
View File
@@ -0,0 +1,371 @@
-- love.window
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------METHODS---------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- love.window.close
love.test.window.close = function(test)
-- closing window should cause graphics to not be active
love.window.close()
local active = love.graphics.isActive()
test:assertFalse(active, 'check window not active')
love.window.updateMode(360, 240) -- reset
active = love.graphics.isActive()
test:assertTrue(active, 'check window active again')
end
-- love.window.fromPixels
love.test.window.fromPixels = function(test)
-- check dpi/pixel ratio as expected
local dpi = love.window.getDPIScale()
local pixels = love.window.fromPixels(100)
test:assertEquals(100/dpi, pixels, 'check dpi ratio')
end
-- love.window.getDPIScale
-- @NOTE dependent on hardware so best can do is not nil
love.test.window.getDPIScale = function(test)
test:assertNotNil(test)
end
-- love.window.getDesktopDimensions
-- @NOTE dependent on hardware so best can do is not nil
love.test.window.getDesktopDimensions = function(test)
local w, h = love.window.getDesktopDimensions()
test:assertNotNil(w)
test:assertNotNil(h)
end
-- love.window.getDisplayCount
-- @NOTE cant wait for the test suite to be run headless and fail here
love.test.window.getDisplayCount = function(test)
test:assertGreaterEqual(1, love.window.getDisplayCount(), 'check 1 display')
end
-- love.window.getDisplayName
-- @NOTE dependent on hardware so best can do is not nil
love.test.window.getDisplayName = function(test)
test:assertNotNil(love.window.getDisplayName(1))
end
-- love.window.getDisplayOrientation
-- @NOTE dependent on hardware so best can do is not nil
love.test.window.getDisplayOrientation = function(test)
test:assertNotNil(love.window.getDisplayOrientation(1))
end
-- love.window.getFullscreen
love.test.window.getFullscreen = function(test)
-- check not fullscreen to start
test:assertFalse(love.window.getFullscreen(), 'check not fullscreen')
love.window.setFullscreen(true)
-- check now fullscreen
test:assertTrue(love.window.getFullscreen(), 'check now fullscreen')
love.window.setFullscreen(false) -- reset
end
-- love.window.getFullscreenModes
-- @NOTE dependent on hardware so best can do is not nil
love.test.window.getFullscreenModes = function(test)
test:assertNotNil(love.window.getFullscreenModes(1))
end
-- love.window.getIcon
love.test.window.getIcon = function(test)
-- check icon nil by default if not set
test:assertEquals(nil, love.window.getIcon(), 'check nil by default')
local icon = love.image.newImageData('resources/love.png')
-- check getting icon not nil after setting
love.window.setIcon(icon)
test:assertNotNil(love.window.getIcon())
end
-- love.window.getMode
-- @NOTE could prob add more checks on the flags here based on conf.lua
love.test.window.getMode = function(test)
local w, h, flags = love.window.getMode()
test:assertEquals(360, w, 'check w')
test:assertEquals(240, h, 'check h')
test:assertFalse(flags["fullscreen"], 'check fullscreen')
end
-- love.window.getPosition
-- @NOTE anything we could check display index agaisn't in getPosition return?
love.test.window.getPosition = function(test)
love.window.setPosition(100, 100, 1)
local x, y, _ = love.window.getPosition()
test:assertEquals(100, x, 'check position x')
test:assertEquals(100, y, 'check position y')
end
-- love.window.getSafeArea
-- @NOTE dependent on hardware so best can do is not nil
love.test.window.getSafeArea = function(test)
local x, y, w, h = love.window.getSafeArea()
test:assertNotNil(x)
test:assertNotNil(y)
test:assertNotNil(w)
test:assertNotNil(h)
end
-- love.window.getTitle
love.test.window.getTitle = function(test)
-- check title returned is what was set
love.window.setTitle('love.testing')
test:assertEquals('love.testing', love.window.getTitle(), 'check title match')
love.window.setTitle('love.test')
end
-- love.window.getVSync
love.test.window.getVSync = function(test)
test:assertNotNil(love.window.getVSync())
end
-- love.window.hasFocus
-- @NOTE cant really test as cant force focus
love.test.window.hasFocus = function(test)
test:assertNotNil(love.window.hasFocus())
end
-- love.window.hasMouseFocus
-- @NOTE cant really test as cant force focus
love.test.window.hasMouseFocus = function(test)
test:assertNotNil(love.window.hasMouseFocus())
end
-- love.window.isDisplaySleepEnabled
love.test.window.isDisplaySleepEnabled = function(test)
test:assertNotNil(love.window.isDisplaySleepEnabled())
-- check disabled
love.window.setDisplaySleepEnabled(false)
test:assertFalse(love.window.isDisplaySleepEnabled(), 'check sleep disabled')
-- check enabled
love.window.setDisplaySleepEnabled(true)
test:assertTrue(love.window.isDisplaySleepEnabled(), 'check sleep enabled')
end
-- love.window.isMaximized
love.test.window.isMaximized = function(test)
test:assertFalse(love.window.isMaximized(), 'check window not maximized')
love.window.maximize()
test:waitFrames(10)
-- on MACOS maximize wont get recognised immedietely so wait a few frames
test:assertTrue(love.window.isMaximized(), 'check window now maximized')
love.window.restore()
end
-- love.window.isMinimized
love.test.window.isMinimized = function(test)
-- check not minimized to start
test:assertFalse(love.window.isMinimized(), 'check window not minimized')
-- try to minimize
love.window.minimize()
test:waitFrames(10)
-- on linux minimize won't get recognized immediately, so wait a few frames
test:assertTrue(love.window.isMinimized(), 'check window minimized')
love.window.restore()
end
-- love.window.isOpen
love.test.window.isOpen = function(test)
-- check open initially
test:assertTrue(love.window.isOpen(), 'check window open')
-- try closing
love.window.close()
test:assertFalse(love.window.isOpen(), 'check window closed')
love.window.updateMode(360, 240) -- reset
end
-- love.window.isVisible
love.test.window.isVisible = function(test)
-- check visible initially
test:assertTrue(love.window.isVisible(), 'check window visible')
-- check closing makes window not visible
love.window.close()
test:assertFalse(love.window.isVisible(), 'check window not visible')
love.window.updateMode(360, 240) -- reset
end
-- love.window.maximize
love.test.window.maximize = function(test)
test:assertFalse(love.window.isMaximized(), 'check window not maximized')
-- check maximizing is set
love.window.maximize()
test:waitFrames(10)
-- on macos we need to wait a few frames
test:assertTrue(love.window.isMaximized(), 'check window maximized')
love.window.restore()
end
-- love.window.minimize
love.test.window.minimize = function(test)
test:assertFalse(love.window.isMinimized(), 'check window not minimized')
-- check minimizing is set
love.window.minimize()
test:waitFrames(10)
-- on linux we need to wait a few frames
test:assertTrue(love.window.isMinimized(), 'check window maximized')
love.window.restore()
end
-- love.window.requestAttention
love.test.window.requestAttention = function(test)
test:skipTest('cant test this worked')
end
-- love.window.restore
love.test.window.restore = function(test)
-- check minimized to start
love.window.minimize()
test:waitFrames(10)
love.window.restore()
test:waitFrames(10)
-- check restoring the state of the window
test:assertFalse(love.window.isMinimized(), 'check window restored')
end
-- love.window.setDisplaySleepEnabled
love.test.window.setDisplaySleepEnabled = function(test)
-- check disabling sleep
love.window.setDisplaySleepEnabled(false)
test:assertFalse(love.window.isDisplaySleepEnabled(), 'check sleep disabled')
-- check setting it back to enabled
love.window.setDisplaySleepEnabled(true)
test:assertTrue(love.window.isDisplaySleepEnabled(), 'check sleep enabled')
end
-- love.window.setFullscreen
love.test.window.setFullscreen = function(test)
-- check fullscreen is set
love.window.setFullscreen(true)
test:assertTrue(love.window.getFullscreen(), 'check fullscreen')
-- check setting back to normal
love.window.setFullscreen(false)
test:assertFalse(love.window.getFullscreen(), 'check not fullscreen')
end
-- love.window.setIcon
-- @NOTE could check the image data itself?
love.test.window.setIcon = function(test)
-- check setting an icon returns the val
local icon = love.image.newImageData('resources/love.png')
love.window.setIcon(icon)
test:assertNotEquals(nil, love.window.getIcon(), 'check icon not nil')
end
-- love.window.setMode
-- @NOTE same as getMode could be checking more flag properties
love.test.window.setMode = function(test)
-- set window mode
love.window.setMode(512, 512, {
fullscreen = false,
resizable = false
})
-- check what we set is returned
local width, height, flags = love.window.getMode()
test:assertEquals(512, width, 'check window w match')
test:assertEquals(512, height, 'check window h match')
test:assertFalse(flags["fullscreen"], 'check window not fullscreen')
test:assertFalse(flags["resizable"], 'check window not resizeable')
love.window.setMode(360, 240, {
fullscreen = false,
resizable = true
})
end
-- love.window.setPosition
love.test.window.setPosition = function(test)
-- check position is returned
love.window.setPosition(100, 100, 1)
test:waitFrames(10)
local x, y, _ = love.window.getPosition()
test:assertEquals(100, x, 'check position x')
test:assertEquals(100, y, 'check position y')
end
-- love.window.setTitle
love.test.window.setTitle = function(test)
-- check setting title val is returned
love.window.setTitle('love.testing')
test:assertEquals('love.testing', love.window.getTitle(), 'check title matches')
love.window.setTitle('love.test')
end
-- love.window.setVSync
love.test.window.setVSync = function(test)
love.window.setVSync(0)
test:assertNotNil(love.window.getVSync())
end
-- love.window.showMessageBox
-- @NOTE if running headless would need to skip anyway cos can't press it
love.test.window.showMessageBox = function(test)
test:skipTest('cant test this worked')
end
-- love.window.toPixels
love.test.window.toPixels = function(test)
-- check dpi/pixel ratio is as expected
local dpi = love.window.getDPIScale()
local pixels = love.window.toPixels(50)
test:assertEquals(50*dpi, pixels, 'check dpi ratio')
end
-- love.window.updateMode
love.test.window.updateMode = function(test)
-- set initial mode
love.window.setMode(512, 512, {
fullscreen = false,
resizable = false
})
-- update mode with some props but not others
love.window.updateMode(360, 240, nil)
-- check only changed values changed
local width, height, flags = love.window.getMode()
test:assertEquals(360, width, 'check window w match')
test:assertEquals(240, height, 'check window h match')
test:assertFalse(flags["fullscreen"], 'check window not fullscreen')
test:assertFalse(flags["resizable"], 'check window not resizeable')
love.window.setMode(360, 240, { -- reset
fullscreen = false,
resizable = true
})
end