Skip to content

Commit

Permalink
Add initial unit testing logic (#708)
Browse files Browse the repository at this point in the history
Adds initial logic to allow for some form of basic unit testing.
  • Loading branch information
ASpoonPlaysGames authored Apr 11, 2024
1 parent 11787b3 commit f427583
Show file tree
Hide file tree
Showing 2 changed files with 315 additions and 0 deletions.
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"
}
},
{
"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 )
}

0 comments on commit f427583

Please sign in to comment.