Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial unit testing logic #708

Merged
merged 16 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Northstar.Custom/mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,19 @@
"Path": "sh_northstar_safe_io.gnut",
"RunOn": "CLIENT || SERVER || UI"
},
{
"Path": "_testing.nut",
"RunOn": "CLIENT || SERVER || UI",
"ClientCallback": {
"Before": "Testing_Init"
},
"ServerCallback": {
"Before": "Testing_Init"
},
"UICallback": {
"Before": "Testing_Init"
GeckoEidechse marked this conversation as resolved.
Show resolved Hide resolved
}
},
{
"Path": "_event_models.gnut",
"RunOn": "SERVER && LOBBY",
Expand Down
302 changes: 302 additions & 0 deletions Northstar.Custom/mod/scripts/vscripts/_testing.nut
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
global function Testing_Init
global function RunAllTests
global function RunAllTests_SaveToFile
global function RunTestsByCategory
global function RunTestByCategoryAndName

global function AddTest

struct TestInfo
{
string testName
var functionref() callback
// whether the test completed successfully
// if this is true, actualResult is valid
bool completed
// var not string because then i can just set it to an exception
// which print can then handle
var error
// whether the test is considered successful
var expectedResult
var actualResult
bool passed
}

struct {
table< string, array< TestInfo > > tests = {}
} file

void function Testing_Init()
{
// tests for the testing functions :)
//AddTest( "Example Tests", "example succeeding test", ExampleTest_ReturnsTrue, true )
//AddTest( "Example Tests", "example failing test", ExampleTest_ReturnsFalse, true )
//AddTest( "Example Tests", "example erroring test", ExampleTest_ThrowsError, true )
//AddTest( "Example Tests", "example test with args", var function() {
// return ExampleTest_HasArgs_ReturnsNonVar( 2, 3 )
//}, 6 )
}

int function ExampleTest_HasArgs_ReturnsNonVar( int first, int second )
{
return first * second
}

var function ExampleTest_ReturnsFalse()
{
return false
}

var function ExampleTest_ReturnsTrue()
{
return true
}

var function ExampleTest_ThrowsError()
{
throw "Example exception"
return null
}

void function RunAllTests_SaveToFile()
{
RunAllTests()

#if UI
string fileName = "ns-unit-tests-UI.json"
#elseif CLIENT
string fileName = "ns-unit-tests-CLIENT.json"
#elseif SERVER
string fileName = "ns-unit-tests-SERVER.json"
#endif

// cant encode structs so have to reconstruct a table manually from the structs
table out = {}
foreach ( category, tests in file.tests )
{
array categoryResults = []
foreach ( test in tests )
{
table testTable = {}
testTable[ "name" ] <- test.testName
testTable[ "completed" ] <- test.completed
testTable[ "passed" ] <- test.passed
if ( !test.completed )
testTable[ "error" ] <- test.error
else if ( !test.passed )
{
testTable[ "expectedResult" ] <- test.expectedResult
testTable[ "actualResult" ] <- test.actualResult
}

categoryResults.append( testTable )
}
out[ category ] <- categoryResults
}

NSSaveJSONFile( fileName, out )
}

void function RunAllTests()
{
printt( "Running all tests!" )

foreach ( category, categoryTests in file.tests )
{
foreach ( test in categoryTests )
{
RunTest( test )
}
}

PrintAllTestResults()
}

void function RunTestsByCategory( string category )
{
if ( !( category in file.tests ) )
{
printt( format( "Category '%s' has no tests registered", category ) )
return
}

foreach ( categoryTest in file.tests[ category ] )
{
RunTest( categoryTest )
}
}

void function RunTestByCategoryAndName( string category, string testName )
{
// find test
if ( !( category in file.tests ) )
{
printt( format( "Category '%s' has no tests registered", category ) )
return
}

TestInfo ornull foundTest = null
foreach ( categoryTest in file.tests[ category ] )
{
if ( categoryTest.testName == testName )
{
foundTest = categoryTest
break
}
}

if ( !foundTest )
{
printt( format( "Category '%s' does not contain a test with name '%s'", category, testName ) )
return
}

expect TestInfo( foundTest )

printt( "Running test!" )
// run test
RunTest( foundTest )
// print result
PrintTestResult( foundTest )
}

void function RunTest( TestInfo test )
{
test.completed = false
test.passed = false
test.actualResult = null
test.error = ""

try
{
test.actualResult = test.callback()
test.completed = true
test.passed = test.actualResult == test.expectedResult
}
catch ( exception )
{
test.completed = false
test.error = exception
}
}

void function PrintAllTestResults()
{
int totalSucceeded = 0
int totalFailed = 0
int totalErrored = 0

foreach ( category, categoryTests in file.tests )
{
int categorySucceeded = 0
int categoryFailed = 0
int categoryErrored = 0

printt( format( "Results for category: '%s'", category ) )
foreach ( test in categoryTests )
{
if ( test.completed )
{
if ( test.passed )
{
printt( "\t", test.testName, "- Passed!" )
categorySucceeded++
}
else
{
printt( "\t", test.testName, "- Failed!" )
printt( "\t\tExpected:", test.expectedResult )
printt( "\t\tActual: ", test.actualResult )
categoryFailed++
}
}
else
{
printt( "\t", test.testName, "- Errored!" )
printt( "\t\tError:", test.error )
categoryErrored++
}
}

printt( "Succeeded:", categorySucceeded, "Failed:", categoryFailed, "Errored:", categoryErrored )

totalSucceeded += categorySucceeded
totalFailed += categoryFailed
totalErrored += categoryErrored
}

printt( "TOTAL SUCCEEDED:", totalSucceeded, "TOTAL FAILED:", totalFailed, "TOTAL ERRORED:", totalErrored )
}

void function PrintCategoryResults( string category )
{
int categorySucceeded = 0
int categoryFailed = 0
int categoryErrored = 0

printt( format( "Results for category: '%s'", category ) )
foreach ( test in file.tests[ category ] )
{
if ( test.completed )
{
if ( test.passed )
{
printt( "\t", test.testName, "- Passed!" )
categorySucceeded++
}
else
{
printt( "\t", test.testName, "- Failed!" )
printt( "\t\tExpected:", test.expectedResult )
printt( "\t\tActual: ", test.actualResult )
categoryFailed++
}
}
else
{
printt( "\t", test.testName, "- Errored!" )
printt( "\t\tError:", test.error )
categoryErrored++
}
}

printt( "Succeeded:", categorySucceeded, "Failed:", categoryFailed, "Errored:", categoryErrored )
}

void function PrintTestResult( TestInfo test )
{
string resultString = test.testName

if ( test.completed )
{
if ( test.passed )
resultString += " - Passed!"
else
{
resultString += " - Failed!"
resultString += "\n\tExpected: " + test.expectedResult
resultString += "\n\tActual: " + test.actualResult
}
}
else
{
resultString += " - Not completed!"
resultString += "\n\tError: " + test.error
}

printt( resultString )
}

void function AddTest( string testCategory, string testName, var functionref() testFunc, var expectedResult )
{
TestInfo newTest
newTest.testName = testName
newTest.callback = testFunc
newTest.expectedResult = expectedResult

// create the test category if it doesn't exist
if ( !( testCategory in file.tests ) )
file.tests[ testCategory ] <- [ newTest ]
else
file.tests[ testCategory ].append( newTest )
}
Loading