A vJass unit testing library

krainert

Member
Reaction score
10
Does one exist?
If I were to make one, what should I keep in mind?
EDIT: One thing I can't immediately figure out how to do it terminate the execution of a test method once an assert within it fails. Is there an elegant solution, or will I have to go with something like
JASS:
if (assertNotNull(hurfdurf)) then
    return
endif

?
I'm considering using jUnit as my inspirational base, but I should probably introduce some adjustments to fine-tune the system for vJass's personality.
 

krainert

Member
Reaction score
10
Okay, here is a first attempt:
JASS:
library VUnit

//********************************************************************************************************************************
//* VUnit
//*  
//*  ABOUT
//*   Author:   krainert
//*   Version:  1.0
//*   
//*  DESCRIPTION
//*   
//*   
//*  CLASSES
//*   ======== Vector2D ========
//*    -- Description
//*     
//*    -- Static methods
//*     
//*    -- Methods
//*     
//*     
//*  CHANGELOG
//*   1.0  YYYY-MM-DD  Original release
//*   
//********************************************************************************************************************************

globals
    private constant integer MAX_SUITES = 8191
    private constant integer NULL_SUITE = -1
endglobals

function interface VUnitInitializer takes nothing returns nothing
function interface VUnitTerminator  takes nothing returns nothing
function interface VUnitTest        takes nothing returns nothing

struct VUnitSuite[MAX_SUITES]
    private static thistype currentSuite = NULL_SUITE
    private static boolean  success
    
    private string    name
    private integer   initCount = 0
    private integer   termCount = 0
    private integer   testCount = 0
    private hashtable tests     = InitHashtable()
    private hashtable inits     = InitHashtable()
    private hashtable terms     = InitHashtable()
    
    static method create takes string name returns thistype
        local thistype r = thistype.allocate()
        set r.name = name
        return r
    endmethod
    
    private static method fail takes string violation returns nothing
        set thistype.success = false
        call BJDebugMsg(violation)
    endmethod
    
    method addInitializer takes VUnitInitializer init returns nothing
        call SaveInteger(.inits, .initCount, 0, init)
        set .initCount = .initCount + 1
    endmethod
    
    method addTerminatr takes VUnitTerminator term returns nothing
        call SaveInteger(.terms, .termCount, 0, term)
        set .termCount = .termCount + 1
    endmethod
    
    method addTest takes VUnitTest test, string name returns nothing
        call SaveInteger(.tests, .testCount, 0, test)
        call SaveStr(.tests, .testCount, 1, name)
        set .testCount = .testCount + 1
    endmethod
    
    method run takes nothing returns nothing
        local boolean success = true
        local integer i = 0
        local integer j
        local VUnitInitializer init
        local VUnitTerminator  term
        local VUnitTest        test
        local string           name
        call BJDebugMsg("[VUnit] Suite: " + .name + " (" + I2S(.testCount) + " tests)")
        set thistype.currentSuite = this
        loop
            exitwhen i == .testCount
            
            //initializers
            set j = 0
            loop
                exitwhen j == .initCount
                set init = LoadInteger(.inits, j, 0)
                call init.evaluate()
                set j = j + 1
            endloop
            
            //test
            set test = LoadInteger(.tests, i, 0)
            set name = LoadStr(.tests, i, 1)
            call BJDebugMsg("[VUnit] Test: " + name)
            set thistype.success = true
            call test.evaluate()
            if (thistype.success) then
                call BJDebugMsg("[VUnit] Test passed")
            else
                set success = false
                call BJDebugMsg("[VUnit] Test failed")
            endif
            
            //terminators
            set j = 0
            loop
                exitwhen j == .initCount
                set init = LoadInteger(.inits, j, 0)
                call init.evaluate()
                set j = j + 1
            endloop
            
            set i = i + 1
        endloop
        set thistype.currentSuite = NULL_SUITE
        if (success) then
            call BJDebugMsg("[VUnit] Suite passed")
        else
            call BJDebugMsg("[VUnit] Suite failed")
        endif
    endmethod
    
    static method assertTrue takes boolean bool returns boolean
        if (bool) then
            return true
        endif
        call thistype.fail("[VUnit] assertTrue violated")
        return false
    endmethod
    
    static method assertIntegerEquals takes integer expected, integer actual returns boolean
        if (expected == actual) then
            return true
        endif
        call thistype.fail("[VUnit] assertIntegerEquals violated; expected: " + I2S(expected) + "; actual: " + I2S(actual))
        return false
    endmethod
endstruct

endlibrary


The following is some simple test code to be put in a trigger named "TestVUnit":

JASS:
library TestVUnit requires VUnit

function TestVUnit_testGenericIntegerEquals takes nothing returns nothing
    call VUnitSuite.assertIntegerEquals(42, 42)
endfunction
function TestVUnit_testGenericIntegerEquals2 takes nothing returns nothing
    call VUnitSuite.assertIntegerEquals(42, 41)
endfunction
function TestVUnit_testGenericIntegerEquals3 takes nothing returns nothing
    call VUnitSuite.assertIntegerEquals(41, 41)
endfunction

function InitTrig_TestVUnit takes nothing returns nothing
    local VUnitSuite suite = VUnitSuite.create("Generic VUnitSuite")
    call suite.addTest(TestVUnit_testGenericIntegerEquals, "42==42")
    call suite.addTest(TestVUnit_testGenericIntegerEquals2, "41==42")
    call suite.addTest(TestVUnit_testGenericIntegerEquals3, "41==41")
    call suite.run()
endfunction

endlibrary


It seems to work thus far :)

Comments? Suggestions? Anything?
 

krainert

Member
Reaction score
10
JASS:
library VUnit

//********************************************************************************************************************************
//* VUnit
//*  
//*  ABOUT
//*   Author:   krainert
//*   Version:  1.0
//*   
//*  DESCRIPTION
//*   A jUnit inspired unit testing framework
//*   
//*  CLASSES
//*   ======== VUnitSuite ========
//*    -- Description
//*     A runnable test suite
//*    -- Static methods
//*     static VUnitSuite create(string name)                                                           - instantiates a new test suite named name
//*     static boolean    assertTrue(boolean condition, string description)                             - asserts condition==true with description description and returns whether the assertion holds
//*     static boolean    assertFalse(boolean condition, string description)                            - asserts condition==false with description description and returns whether the assertion holds
//*     static boolean    assertBooleanEquals(boolean expected, boolean actual, string description)     - asserts actual==expected with description description and returns whether the assertion holds
//*     static boolean    assertIntegerEquals(integer expected, integer actual, string description)     - asserts actual==expected with description description and returns whether the assertion holds
//*     static boolean    assertRealEquals(real expected, real actual, real delta, string description)  - asserts actual==expected with a precision of delta and description description and returns whether the assertion holds
//*     static boolean    assertStringEquals(string expected, string actual, string description)        - asserts actual==expected with description description and returns whether the assertion holds
//*     static boolean    assertNull(integer value, string description)                                 - asserts value==null with description description and returns whether the assertion holds
//*     static boolean    assertNotNull(integer value, string description)                              - asserts value!=null with description description and returns whether the assertion holds
//*    -- Methods
//*     void addInitializer(VUnitInitializer init)                                                      - adds initializer init
//*     void addTerminator(VUnitTerminator term)                                                        - adds terminator term
//*     void addTest(VUnitTest test, string name)                                                       - adds test test named name
//*     void run()                                                                                      - runs all tests
//*     
//*  FUNCTION INTERFACES
//*   void VUnitInitializer()
//*   void VUnitTerminator()
//*   void VUnitTest()
//*   
//*  CHANGELOG
//*   1.0  YYYY-MM-DD  Original release
//*   
//********************************************************************************************************************************

globals
    private constant integer MAX_SUITES = 8191
    private constant integer NULL_SUITE = -1
endglobals

function interface VUnitInitializer takes nothing returns nothing
function interface VUnitTerminator  takes nothing returns nothing
function interface VUnitTest        takes nothing returns nothing

struct VUnitSuite[MAX_SUITES]
    private static thistype currentSuite = NULL_SUITE
    private static boolean  success
    
    private string    name
    private integer   initCount = 0
    private integer   termCount = 0
    private integer   testCount = 0
    private hashtable inits     = InitHashtable()
    private hashtable terms     = InitHashtable()
    private hashtable tests     = InitHashtable()
    
    static method create takes string name returns thistype
        local thistype r = thistype.allocate()
        set r.name = name
        return r
    endmethod
    
    method onDestroy takes nothing returns nothing
        call FlushParentHashtable(.inits)
        call FlushParentHashtable(.terms)
        call FlushParentHashtable(.tests)
    endmethod
    
    method addInitializer takes VUnitInitializer init returns nothing
        call SaveInteger(.inits, .initCount, 0, init)
        set .initCount = .initCount + 1
    endmethod
    
    method addTerminator takes VUnitTerminator term returns nothing
        call SaveInteger(.terms, .termCount, 0, term)
        set .termCount = .termCount + 1
    endmethod
    
    method addTest takes VUnitTest test, string name returns nothing
        call SaveInteger(.tests, .testCount, 0, test)
        call SaveStr(.tests, .testCount, 1, name)
        set .testCount = .testCount + 1
    endmethod
    
    method run takes nothing returns nothing
        local boolean success = true
        local integer i = 0
        local integer j
        local VUnitInitializer init
        local VUnitTerminator  term
        local VUnitTest        test
        local string           name
        call BJDebugMsg("[VUnit] Suite: " + .name + " (" + I2S(.testCount) + " tests)")
        set thistype.currentSuite = this
        loop
            exitwhen i == .testCount
            
            //initializers
            set j = 0
            loop
                exitwhen j == .initCount
                set init = LoadInteger(.inits, j, 0)
                call init.evaluate()
                set j = j + 1
            endloop
            
            //test
            set test = LoadInteger(.tests, i, 0)
            set name = LoadStr(.tests, i, 1)
            call BJDebugMsg("[VUnit] Test: " + name)
            set thistype.success = true
            call test.evaluate()
            if (thistype.success) then
                call BJDebugMsg("[VUnit] Test " + name + " passed")
            else
                set success = false
                call BJDebugMsg("[VUnit] Test " + name + " failed")
            endif
            
            //terminators
            set j = 0
            loop
                exitwhen j == .termCount
                set term = LoadInteger(.terms, j, 0)
                call term.evaluate()
                set j = j + 1
            endloop
            
            set i = i + 1
        endloop
        set thistype.currentSuite = NULL_SUITE
        if (success) then
            call BJDebugMsg("[VUnit] Suite " + .name + " passed")
        else
            call BJDebugMsg("[VUnit] Suite " + .name + " failed")
        endif
    endmethod
    
    private static method assert takes boolean condition, string name, string description returns boolean
        call BJDebugMsg("[VUnit] Assertion: " + name + " (" + description + ")")
        if (condition) then
            call BJDebugMsg("[VUnit] Assertion " + name + " satisfied")
            return true
        else
            set thistype.success = false
            call BJDebugMsg("[VUnit] Assertion " + name + " violated")
            return false
        endif
    endmethod
    
    private static method B2S takes boolean bool returns string
        if (bool) then
            return "true"
        else
            return "false"
        endif
    endmethod
    
    static method assertTrue takes boolean condition, string description returns boolean
        return thistype.assert(condition, "assertTrue(" + B2S(condition) + ")", description)
    endmethod
    
    static method assertFalse takes boolean condition, string description  returns boolean
        return thistype.assert(not(condition), "assertFalse(" + B2S(condition) + ")", description)
    endmethod
    
    static method assertBooleanEquals takes boolean expected, boolean actual, string description returns boolean
        return thistype.assert(actual == expected, "assertBooleanEquals(" + B2S(expected) + "," + B2S(actual) + ")", description)
    endmethod
    
    static method assertIntegerEquals takes integer expected, integer actual, string description returns boolean
        return thistype.assert(actual == expected, "assertIntegerEquals(" + I2S(expected) + "," + I2S(actual) + ")", description)
    endmethod
    
    static method assertRealEquals takes real expected, real actual, real delta, string description returns boolean
        return thistype.assert((actual >= expected - delta) and (actual <= expected + delta), "assertRealEquals(" + R2S(expected) + "," + R2S(actual) + "," + R2S(delta) + ")", description)
    endmethod
    
    static method assertStringEquals takes string expected, string actual, string description returns boolean
        return thistype.assert(actual == expected, "assertRealEquals(" + expected + "," + actual + ")", description)
    endmethod
    
    static method assertNull takes integer value, string description returns boolean
        return thistype.assert(value == 0, "assertNull(" + I2S(value) + ")", description)
    endmethod
    
    static method assertNotNull takes integer value, string description returns boolean
        return thistype.assert(value != 0, "assertNotNull(" + I2S(value) + ")", description)
    endmethod
endstruct

endlibrary


Testing the newest (unreleased) version of Vectors:

JASS:
library VectorsVUnit initializer VectorsVUnit_init requires Vectors, VUnit

private function vector2DCreateXY takes nothing returns nothing
    local Vector2D v = Vector2D.createXY(3., 7.)
    call VUnitSuite.assertRealEquals(3., v.getX(), .001, "x")
    call VUnitSuite.assertRealEquals(7., v.getY(), .001, "y")
    call v.destroy()
endfunction

private function vector2DCreate takes nothing returns nothing
    local Vector2D v1 = Vector2D.createXY(3., 7.)
    local Vector2D v2 = Vector2D.create(v1)
    call VUnitSuite.assertRealEquals(3., v2.getX(), .001, "x")
    call VUnitSuite.assertRealEquals(7., v2.getY(), .001, "y")
    call v1.destroy()
    call v2.destroy()
endfunction

private function vector2DCreateDM takes nothing returns nothing
    local Vector2D v = Vector2D.createDM(.5*3.1416, 2.)
    call VUnitSuite.assertRealEquals(0., v.getX(), .001, "x")
    call VUnitSuite.assertRealEquals(2., v.getY(), .001, "y")
    call v.destroy()
endfunction

private function vector3DCreateXYZ takes nothing returns nothing
    local Vector3D v = Vector3D.createXYZ(3., 7., 4.)
    call VUnitSuite.assertRealEquals(3., v.getX(), .001, "x")
    call VUnitSuite.assertRealEquals(7., v.getY(), .001, "y")
    call VUnitSuite.assertRealEquals(4., v.getZ(), .001, "z")
    call v.destroy()
endfunction

private function vector3DCreate takes nothing returns nothing
    local Vector3D v1 = Vector3D.createXYZ(3., 7., 4.)
    local Vector3D v2 = Vector3D.create(v1)
    call VUnitSuite.assertRealEquals(3., v2.getX(), .001, "x")
    call VUnitSuite.assertRealEquals(7., v2.getY(), .001, "y")
    call VUnitSuite.assertRealEquals(4., v2.getZ(), .001, "z")
    call v1.destroy()
    call v2.destroy()
endfunction

function VectorsVUnit_init takes nothing returns nothing
    local VUnitSuite vector2D = VUnitSuite.create("Vector2D")
    local VUnitSuite vector3D = VUnitSuite.create("Vector3D")
    
    call vector2D.addTest(vector2DCreateXY, "static createXY")
    call vector2D.addTest(vector2DCreate, "static create")
    call vector2D.addTest(vector2DCreateDM, "static createDM")
    
    call vector3D.addTest(vector3DCreateXYZ, "static createXYZ")
    call vector3D.addTest(vector3DCreate, "static create")
    
    call vector2D.run()
    call vector3D.run()
endfunction

endlibrary


Simple and easily maintainable. It liberates the coder from doing too much boiler-plate code while testing while rigidly enforcing a standardized structure for unit tests.

EDIT: Now I have to figure out a sensible way to output VUnit results...
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top