├── .hgtags ├── manifest ├── source ├── examples │ └── test_examples.brs ├── test_testresult.brs ├── main.brs ├── test_testsuite.brs ├── test_testloader.brs ├── brstest.brs └── test_testcase.brs ├── bower.json ├── TODO ├── Makefile ├── README └── LICENSE /.hgtags: -------------------------------------------------------------------------------- 1 | 58b1e14acddcf8eefccff785c1bf16fdac6e0b93 brstest_v0.1.0 2 | bc7bf72c1d10986c76df1fdbb7855e740d5c72d3 brstest_v0.1.1 3 | -------------------------------------------------------------------------------- /manifest: -------------------------------------------------------------------------------- 1 | title=brstest 2 | subtitle=Bright Script Testing Framework 3 | major_version=0 4 | minor_version=1 5 | subminor_version=1 6 | build_version=00000 7 | -------------------------------------------------------------------------------- /source/examples/test_examples.brs: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sub testAdd(t as object) 4 | t.assertEqual( (1+2), 3) 5 | t.assertEqual( 0+1, 1) 6 | End Sub 7 | 8 | Sub testMultiply(t as object) 9 | t.assertEqual( (0*10), 0) 10 | t.assertEqual( (5*8), 40) 11 | End Sub 12 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "brs-test", 3 | 4 | "version": "3.0.3", 5 | "homepage": "https://github.com/MarkRoddy/brstest", 6 | "authors": [ 7 | "https://github.com/MarkRoddy" 8 | ], 9 | "description": "brsttest - A unit testing framework for the BrightScript programming language used in development for the Roku Digital Video Player", 10 | "main": "main.brs", 11 | "moduleType": [ 12 | "amd" 13 | ], 14 | "ignore": ["source/**/*", "!source/brstest.brs"], 15 | "keywords": [ 16 | "Brightscript", 17 | "Roku", 18 | "Testing", 19 | "Test", 20 | "Xunit", 21 | "TDD" 22 | ], 23 | "license": "APACHE" 24 | } 25 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | -*- outline -*- 2 | 3 | 4 | * Future Features 5 | 6 | ** Specify halt on error/failure 7 | Let users specify by their choosen test execution context 8 | if errors should be recorded or automatically fail into 9 | the debugger 10 | 11 | ** Print Methods on test runner 12 | Now has bare print commands that can't be overridden 13 | Should delegate to a method which can be patched 14 | 15 | ** Timing 16 | Add calculation and printing of the time it to complete 17 | unit test execution 18 | 19 | ** Specified test discovery 20 | Execute a test fixture specified by some string 21 | identifier, probably just the fixture name 22 | Also needs a convience method for use to call 23 | with the test name 24 | 25 | ** Test loader Discover / Load Files 26 | Must be able to handle loading scripts not in 27 | the 'pkg:/source' directory as these will not 28 | be in the global name space by default 29 | 30 | ** Asserts 31 | 32 | *** Add new asserts 33 | assertIn(item, iterable) - specified item is found in the enumerable object 34 | assertCausesError(callable, error_code) - calling specified method results in the specified error number 35 | 36 | *** Upgrade existing asserts 37 | Add support for 'types' in assertEqual and assertNotEqual (list, array, hash) 38 | 39 | 40 | -------------------------------------------------------------------------------- /source/test_testresult.brs: -------------------------------------------------------------------------------- 1 | 'Unit Tests for the TestResult object 2 | 3 | 4 | Sub testStopSetsShouldStopToTrue(m) 5 | 'stop() sets shouldStop to True 6 | tr = brstNewTestResult() 7 | tr.stop() 'fake test 8 | m.assertTrue(tr.shouldStop) 9 | End Sub 10 | 11 | Sub testShouldStopDefaultsToFalse(m) 12 | 'ShouldStop attribute defaults to False 13 | tr = brstNewTestResult() 14 | m.assertFalse(tr.shouldStop) 15 | End Sub 16 | 17 | Sub testStartTestIncrementsTestsRun(m) 18 | 'startTest() should increment testsRun attribute by 1 19 | tr = brstNewTestResult() 20 | tr.startTest("") 21 | tr.startTest("") 22 | tr.startTest("") 23 | m.assertEqual(3, tr.testsRun) 24 | End Sub 25 | 26 | Sub testTestRunDefaultsToZero(m) 27 | 'testsRun attribute should default to zero 28 | tr = brstNewTestResult() 29 | m.assertEqual(0, tr.testsRun) 30 | End Sub 31 | 32 | Sub testWasSuccessfulAfterFailure(m) 33 | 'wasSuccessful() returns false after a failure is added 34 | tr = brstNewTestResult() 35 | tr.addFailure("", "This is a simulated failure") 36 | m.assertFalse(tr.wasSuccessful()) 37 | End Sub 38 | 39 | Sub testWasSuccessfulAfterError(m) 40 | 'wasSuccessful() returns false after an error is added 41 | tr = brstNewTestResult() 42 | tr.addError("", "This is a simulated error") 43 | m.assertFalse(tr.wasSuccessful()) 44 | End Sub 45 | 46 | Sub testWasSuccessfulTrueByDefault(m) 47 | 'wasSuccessful() returns true of no errors or failures were added 48 | tr = brstNewTestResult() 49 | m.assertTrue(tr.wasSuccessful()) 50 | End Sub -------------------------------------------------------------------------------- /source/main.brs: -------------------------------------------------------------------------------- 1 | 'Run tests on brstest 2 | 3 | ' Version using regular print statements 4 | function main() 5 | BrsTestMain() 6 | end function 7 | 8 | ' Version using tcp socket to stream output - will wait for connection before starting tests 9 | ' function main() 10 | ' print "Waiting for socket connection ..." 11 | ' messagePort = CreateObject("roMessagePort") 12 | ' tcpListen = CreateObject("roStreamSocket") 13 | ' tcpListen.setMessagePort(messagePort) 14 | ' addr = CreateObject("roSocketAddress") 15 | ' addr.setPort(54321) 16 | ' 17 | ' tcpListen.setAddress(addr) 18 | ' tcpListen.notifyReadable(true) 19 | ' x = tcpListen.listen(1) 20 | ' 21 | ' if not tcpListen.eOK() 22 | ' print "Error creating listen socket" 23 | ' stop 24 | ' end if 25 | ' 26 | ' while True 27 | ' msg = wait(0, messagePort) 28 | ' 29 | ' if type(msg) = "roSocketEvent" 30 | ' changedID = msg.getSocketID() 31 | ' 32 | ' if changedID = tcpListen.getID() and tcpListen.isReadable() 33 | ' newConnection = tcpListen.accept() 34 | ' 35 | ' if newConnection = Invalid 36 | ' print "accept failed" 37 | ' else 38 | ' print "accepted new connection " newConnection.getID() 39 | ' newConnection.sendStr("Running tests ...") 40 | ' 41 | ' BrsTestMain(newConnection) 42 | ' 43 | ' newConnection.sendStr("Tests complete. ") 44 | ' newConnection.close() 45 | ' 46 | ' End 47 | ' end if 48 | ' 49 | ' else 50 | ' if closed or not newConnection.eOK() 51 | ' print "closing connection " changedID 52 | ' newConnection.close() 53 | ' end if 54 | ' end if 55 | ' end if 56 | ' end while 57 | ' 58 | ' print "Main loop exited" 59 | ' tcpListen.close() 60 | ' end function 61 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Simple makefile for packaging Roku Audio Test Application example 3 | # 4 | # Makefile Usage: 5 | # > make 6 | # > make install 7 | # > make remove 8 | # 9 | # Important Notes: 10 | # To use the "install" and "remove" targets to install your 11 | # application directly from the shell, you must do the following: 12 | # 13 | # 1) Make sure that you have the curl command line executable in your path 14 | # 2) Set the variable ROKU_DEV_TARGET in your environment to the IP 15 | # address of your Roku box. (e.g. export ROKU_DEV_TARGET=192.168.1.1. 16 | # Set in your this variable in your shell startup (e.g. .bashrc) 17 | ########################################################################## 18 | PKGREL = ../packages 19 | ZIPREL = ../zips 20 | SOURCEREL = .. 21 | DISTREL = ../dist 22 | APPNAME = brstest 23 | VERMAJOR=$(shell cat manifest|grep ^major_version|cut -d '=' -f 2) 24 | VERMINOR=$(shell cat manifest|grep ^minor_version|cut -d '=' -f 2) 25 | VERSUBMINOR=$(shell cat manifest|grep ^subminor_version|cut -d '=' -f 2) 26 | VERSION = $(VERMAJOR).$(VERMINOR).$(VERSUBMINOR) 27 | 28 | .PHONY: all brstest 29 | 30 | brstest: 31 | @echo "*** Creating $(APPNAME).zip ***" 32 | 33 | @echo " >> removing old application zip $(ZIPREL)/$(APPNAME).zip" 34 | @if [ -e "$(ZIPREL)/$(APPNAME).zip" ]; \ 35 | then \ 36 | rm $(ZIPREL)/$(APPNAME).zip; \ 37 | fi 38 | 39 | @echo " >> creating destination directory $(ZIPREL)" 40 | @if [ ! -d $(ZIPREL) ]; \ 41 | then \ 42 | mkdir -p $(ZIPREL); \ 43 | fi 44 | 45 | @echo " >> setting directory permissions for $(ZIPREL)" 46 | @if [ ! -w $(ZIPREL) ]; \ 47 | then \ 48 | chmod 755 $(ZIPREL); \ 49 | fi 50 | 51 | @echo " >> creating application zip $(ZIPREL)/$(APPNAME).zip" 52 | @if [ -d $(SOURCEREL)/$(APPNAME) ]; \ 53 | then \ 54 | (zip -9 -r "$(ZIPREL)/$(APPNAME).zip" . -x \*.svn\* ); \ 55 | (zip -d "$(ZIPREL)/$(APPNAME).zip" Makefile); \ 56 | else \ 57 | echo "Source for $(APPNAME) not found at $(SOURCEREL)/$(APPNAME)"; \ 58 | fi 59 | 60 | @echo "*** developer zip $(APPNAME) complete ***" 61 | 62 | install: brstest 63 | @echo "Installing $(APPNAME) to host $(ROKU_DEV_TARGET)" 64 | @curl -s -S -F "mysubmit=Install" -F "archive=@$(ZIPREL)/$(APPNAME).zip" -F "passwd=" http://$(ROKU_DEV_TARGET)/plugin_install | grep "//" 65 | 66 | dist: brstest 67 | @echo "Creating distributables for $(APPNAME)" 68 | @echo " >> creating destination directory $(DISTREL)" 69 | @if [ ! -d $(DISTREL) ]; \ 70 | then \ 71 | mkdir -p $(DISTREL); \ 72 | fi 73 | cp "$(ZIPREL)/$(APPNAME).zip" "$(DISTREL)/$(APPNAME)_v$(VERSION).zip" 74 | zip -u "$(DISTREL)/$(APPNAME)_v$(VERSION).zip" Makefile 75 | cp source/brstest.brs "$(DISTREL)/brstest_v$(VERSION).brs" 76 | 77 | pkg: install 78 | @echo "*** Creating Package ***" 79 | 80 | @echo " >> creating destination directory $(PKGREL)" 81 | @if [ ! -d $(PKGREL) ]; \ 82 | then \ 83 | mkdir -p $(PKGREL); \ 84 | fi 85 | 86 | @echo " >> setting directory permissions for $(PKGREL)" 87 | @if [ ! -w $(PKGREL) ]; \ 88 | then \ 89 | chmod 755 $(PKGREL); \ 90 | fi 91 | 92 | @echo "Packaging $(APPNAME) on host $(ROKU_DEV_TARGET)" 93 | @read -p "Password: " REPLY ; echo $$REPLY | xargs -i curl -s -S -Fmysubmit=Package -Fapp_name=$(APPNAME)/$(VERSION) -Fpasswd={} -Fpkg_time=`expr \`date +%s\` \* 1000` "http://$(ROKU_DEV_TARGET)/plugin_package" | grep '^//" 99 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | brsttest - A unit testing framework for the BrightScript programming 2 | language used in development for the Roku Digital Video Player inspired 3 | by xUnit style testing systems. 4 | 5 | * Writing a Test Fixture 6 | Test fixtures must conform to the two following behaviors: 7 | 1) The sub/function name must begin with 'test' 8 | 2) The sub/function must take a single 'object' type argument 9 | 10 | An example: 11 | Sub testAddition(t as object) 12 | a = 1 13 | b = 2 14 | t.assertEqual(3, a + b) 15 | End Sub 16 | 17 | The 'test' prefix to the method name is required so that the testing 18 | framework may be able to discover the test in order to execute it, 19 | and the 't' argument is the test case from which all assertions are 20 | performed. If the prefix is missing the test will not be found and 21 | there for not executed. And iff the argument signature is not which 22 | is expected, the test will fail as the incorrect number of arguments 23 | will be passed. 24 | 25 | * Using the TestCase 26 | The test case object passed into test methods contains a number of 27 | methods which can be used to assert expected state as it is expected 28 | to exist after some operations. These methods include: 29 | assertTrue 30 | assertFalse 31 | assertEqual 32 | assertNotEqual 33 | assertInvalid 34 | assertNotInvalid 35 | 36 | In the case of assertEqual and assertNotEqual, there is currently a 37 | limitation in the number of types that can be handled. These include 38 | integer, float, and string types. Comparing integer and floats is 39 | also acceptable. More robust type handling, including built in 40 | BrightScript components such as roList, roArray and roAssociativeArray, 41 | is planned for a future release. 42 | 43 | * Identifying Files with Tests 44 | By default, brsttest assumes all files in the 'sources' directory 45 | whose names begin with 'test' will contain test fixtures and parses 46 | for named test cases. As such any tests you wish to be executed 47 | should be in files that conform to this protocol. 48 | 49 | * Running Tests 50 | First, obtain a copy of brsttest.brs and place it in your 'sources' 51 | directory. Second, at which ever point you'd like to execute your 52 | tests call the BrsTestMain() subroutine. All test files following 53 | the protocol described above will be parsed for test fixtures, and 54 | all tests found will be executed with their results being printed 55 | to the debug console. If an instace of roStreamSocket is passed to 56 | BrsTestMain then test output will be streamed to the socket as well 57 | as well as to the debug console. 58 | 59 | * Limitations 60 | Due to the nature of the BrightScript language some features found 61 | in traditional xUnit style frameworks are not present. As the 62 | BrightScript language evolves and expands some of these features 63 | will hopefully be added. An incomplete list of typical xUnit 64 | features which are not supported follows. 65 | 66 | 1) Detailed Error Reporting: Due to the nature of error handling 67 | within BrightScript, only a minimal amount of information about 68 | a failing test can be reported. Currently, in the case of a 69 | failed assertion, only a message about the failure (such as the 70 | two values which are not equal) is displayed. For test errors, 71 | a short description of the error in question is displayed. 72 | Unfortunately, as there is no formal exception mechanism in 73 | BrightScript, information such as the line the error occured or 74 | a stacktrace can not be displayed. 75 | 76 | 2) OOP Style Test Cases 77 | It has been a decision in the implementation of brstest to not 78 | support an object oriented approach to authoring test cases ( 79 | collections of related test fixtures). An intial prototype 80 | of brstest took this approach but was scrapped due to the 81 | overhead of having to write both the test fixture methods and 82 | support a 'constructor' which attached all of the methods 83 | to a created roAssociativeArray object. If it is shown that 84 | there is a significant demand for being able to write this 85 | style of tests, this decision will be re-evaluated. 86 | 87 | 3) TestCase setUp/tearDown Methods 88 | Due to the divergance of brstest from traditional xUnit 89 | frameworks in the area of class based TestCases, setUp and 90 | tearDown methods are not currently supported. If there is 91 | a high demand for these features, this decision will also 92 | be re-evaluated. 93 | -------------------------------------------------------------------------------- /source/test_testsuite.brs: -------------------------------------------------------------------------------- 1 | 'Unit tests for the TestSuite class 2 | 3 | 4 | Sub testTestSuite_addTests_EmptySet(t as object) 5 | 'No tests added when empty collection passed to addTests() 6 | ts = brstNewTestSuite(CreateObject("roList")) 7 | t.assertEqual(0, ts.countTestCases()) 8 | 9 | ts.addTests(CreateObject("roList")) 10 | t.assertEqual(0, ts.countTestCases()) 11 | 12 | ts.addTests(CreateObject("roAssociativeArray")) 13 | t.assertEqual(0, ts.countTestCases()) 14 | 15 | ts.addTests(CreateObject("roArray", 0, False)) 16 | t.assertEqual(0, ts.countTestCases()) 17 | 18 | End Sub 19 | 20 | Sub testTestSuite_EmptySetToConstructor(t as Object) 21 | 'No tests should be added if created with an empty collection 22 | ts = brstNewTestSuite(CreateObject("roList")) 23 | t.assertEqual(0, ts.countTestCases()) 24 | ts = brstNewTestSuite(CreateObject("roArray", 0, False)) 25 | t.assertEqual(0, ts.countTestCases()) 26 | ts = brstNewTestSuite(CreateObject("roAssociativeArray")) 27 | t.assertEqual(0, ts.countTestCases()) 28 | 29 | End Sub 30 | 31 | Sub testtsAddTestsAllTestsAreRun(t as object) 32 | 'All tests added via addTests() should be run via the suite's run() method 33 | mock_result = CreateObject("roAssociativeArray") 34 | mock_result.TotalRunCalls = 0 35 | mock_result.shouldStop = False 36 | 37 | mock_test1 = CreateObject("roAssociativeArray") 38 | mock_test1.countTestCases = function() 39 | return 1 40 | end function 41 | mock_test1.run = function(r) 42 | r.TotalRunCalls = r.TotalRunCalls + 1 43 | end function 44 | 45 | mock_test2 = CreateObject("roAssociativeArray") 46 | mock_test2.countTestCases = function() 47 | return 1 48 | end function 49 | mock_test2.run = function(r) 50 | r.TotalRunCalls = r.TotalRunCalls + 1 51 | end function 52 | 53 | ts = brstNewTestSuite(CreateObject("roList")) 54 | test_set = CreateObject("roArray", 2, false) 55 | test_set.push(mock_test1) 56 | test_set.push(mock_test2) 57 | ts.addTests(test_set) 58 | ts.run(mock_result) 59 | t.assertEqual(2, mock_result.TotalRunCalls) 60 | End Sub 61 | 62 | Sub testtsAddTestNewTestIsRun(t as object) 63 | 'After adding test case to suite it should be run with the suite 64 | mock_result = CreateObject("roAssociativeArray") 65 | mock_result.mock_test_run = False 66 | mock_result.shouldstop = False 67 | 68 | mock_test1 = CreateObject("roAssociativeArray") 69 | mock_test1.countTestCases = function() 70 | return 1 71 | end function 72 | mock_test1.run = function(r) 73 | r.mock_test_run = True 74 | end function 75 | 76 | ts = brstNewTestSuite(CreateObject("roList")) 77 | ts.addTest(mock_test1) 78 | ts.run(mock_result) 79 | t.assertTrue(mock_result.mock_test_run) 80 | End Sub 81 | 82 | 83 | Sub testtsRunNothingAfterShouldStopSetToTrue(t as object) 84 | 'run() should stop running tests after ShouldStop on result is true 85 | mock_result = CreateObject("roAssociativeArray") 86 | mock_result.TotalRunCalls = 0 87 | mock_result.shouldStop = False 88 | 89 | mock_test1 = CreateObject("roAssociativeArray") 90 | mock_test1.countTestCases = function() 91 | return 1 92 | end function 93 | mock_test1.run = function(r) 94 | r.TotalRunCalls = r.TotalRunCalls + 1 95 | r.shouldStop = True 96 | end function 97 | 98 | mock_test2 = CreateObject("roAssociativeArray") 99 | mock_test2.countTestCases = function() 100 | return 1 101 | end function 102 | mock_test2.run = function(r) 103 | r.TotalRunCalls = r.TotalRunCalls + 1 104 | end function 105 | 106 | ts = brstNewTestSuite(CreateObject("roList")) 107 | ts.addTest(mock_test1) 108 | ts.addTest(mock_test2) 109 | ts.run(mock_result) 110 | t.assertEqual(1, mock_result.TotalRunCalls) 111 | End Sub 112 | 113 | Sub testtsRunCalledForEachTest(t as object) 114 | 'run() should call run for each test case 115 | mock_result = CreateObject("roAssociativeArray") 116 | mock_result.TotalRunCalls = 0 117 | mock_result.shouldStop = False 118 | 119 | mock_test1 = CreateObject("roAssociativeArray") 120 | mock_test1.countTestCases = function() 121 | return 1 122 | end function 123 | mock_test1.run = function(r) 124 | r.TotalRunCalls = r.TotalRunCalls + 1 125 | end function 126 | 127 | mock_test2 = CreateObject("roAssociativeArray") 128 | mock_test2.countTestCases = function() 129 | return 1 130 | end function 131 | mock_test2.run = function(r) 132 | r.TotalRunCalls = r.TotalRunCalls + 1 133 | end function 134 | 135 | ts = brstNewTestSuite(CreateObject("roList")) 136 | ts.addTest(mock_test1) 137 | ts.addTest(mock_test2) 138 | ts.run(mock_result) 139 | t.assertEqual(2, mock_result.TotalRunCalls) 140 | 141 | End Sub 142 | 143 | 144 | Sub testtsCountTestCasesSumsTestsAdded(t as object) 145 | 'CountTestCases() returns sum of same method for all test 146 | mock_test1 = CreateObject("roAssociativeArray") 147 | mock_test1.countTestCases = function() 148 | return 4 149 | end function 150 | mock_test1.run = function() 151 | return "" 152 | end function 153 | 154 | mock_test2 = CreateObject("roAssociativeArray") 155 | mock_test2.countTestCases = function() 156 | return 3 157 | end function 158 | mock_test2.run = function() 159 | return "" 160 | end function 161 | 162 | expected_tests = 7 163 | ts = brstNewTestSuite(CreateObject("roList")) 164 | ts.addTest(mock_test1) 165 | ts.addTest(mock_test2) 166 | actual_tests = ts.countTestCases() 167 | t.assertEqual(expected_tests, actual_tests) 168 | End Sub 169 | 170 | 171 | Sub testtsCountTestCasesZeroIfNoTests(t as object) 172 | 'CountTestCases() returns 0 if no tests added 173 | faketests = CreateObject("roList") 174 | ts = brstNewTestSuite(faketests) 175 | actual_tests = ts.countTestCases() 176 | t.assertEqual(0, actual_tests) 177 | End Sub 178 | 179 | Sub testttsPrintFileNameOnFailure(t as object) 180 | tl = brstNewTestLoader("Test", "test") 181 | fixtures = tl.fixturesFromScript("pkg:/source/examples/test_examples.brs") 182 | t.assertEqual("pkg:/source/examples/test_examples.brs", fixtures[0].testScriptPath) 183 | End Sub 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /source/test_testloader.brs: -------------------------------------------------------------------------------- 1 | 'Unit tests for the TestLoader class 2 | 3 | Sub testTestLoader_findTestScripts_CompiledIfNotInSources(t as object) 4 | 'Files not in the pkg:/sources directory should be compiled 5 | tl = brstNewTestLoader("Test", "test") 6 | tl.ListDir = function (fromdirectory as string) 7 | return ["TestFile1.brs", "TestFile2.brs", "TestFooBar.txt"] 8 | end function 9 | tl.CompiledFiles = 0 10 | tl.compileScript = function(scriptpath as string) 11 | m.CompiledFiles = m.CompiledFiles + 1 12 | return true 13 | End Function 14 | files = tl.findTestScripts("foo bar") 15 | t.assertEqual(2, files.Count()) 16 | t.assertEqual(2, tl.CompiledFiles) 17 | End Sub 18 | 19 | Sub testTestLoader_findTestScripts_WithoutBrsIsIgnored(t as object) 20 | 'Files with prefix but not with .brs ending should be ignored 21 | tl = brstNewTestLoader("Test", "test") 22 | tl.ListDir = function (fromdirectory as string) 23 | return ["TestFile1.brs", "TestFile2.brs", "TestFooBar.txt"] 24 | end function 25 | files = tl.findTestScripts("pkg:/sources") 26 | t.assertEqual(2, files.Count()) 27 | End Sub 28 | 29 | Sub testTestLoader_findTestScripts_WithoutPrefixArentIncluded(t as object) 30 | 'Only files with the prefix are reported 31 | tl = brstNewTestLoader("Test", "test") 32 | tl.ListDir = function (fromdirectory as string) 33 | return ["TestFile1.brs", "TestFile2.brs", "FooBar.brs"] 34 | end function 35 | files = tl.findTestScripts("pkg:/sources") 36 | t.assertEqual(2, files.Count()) 37 | End Sub 38 | 39 | Sub testTestLoader_fixturesFromScriptContents_FunctionWithoutPrefixNotReturned(t as object) 40 | 'Discovered function that doesn't be with prefix shouldn't be returned 41 | sub_code = chr(10) + "Sub MeaninglessSub(t as object)" 42 | sub_code = sub_code + chr(10) 43 | sub_code = sub_code + " i = 1 + 2" 44 | sub_code = sub_code + chr(10) 45 | sub_code = sub_code + "End Sub" 46 | sub_code = sub_code + chr(10) 47 | 48 | tl = brstNewTestLoader("Test", "test") 49 | fixtures = tl.fixturesFromScriptContents(sub_code, "") 50 | 51 | 'No fixtures should be returned 52 | t.assertEqual(0, fixtures.Count()) 53 | 54 | End Sub 55 | 56 | 57 | Sub testTestLoader_fixturesFromScriptContents_CommentedOutFunctionNotFound(t as object) 58 | 'No fixtures should be found for commentted out function 59 | sub_code = chr(10) + "'Sub testTestLoader_fixturesFromScriptContents_TestSubFound(t as object)" 60 | sub_code = sub_code + chr(10) 61 | sub_code = sub_code + "' i = 1 + 2" 62 | sub_code = sub_code + chr(10) 63 | sub_code = sub_code + "'End Sub" 64 | sub_code = sub_code + chr(10) 65 | tl = brstNewTestLoader("Test", "test") 66 | fixtures = tl.fixturesFromScriptContents(sub_code, "") 67 | 68 | 'No fixtures should be returned 69 | t.assertEqual(0, fixtures.Count()) 70 | 71 | End Sub 72 | 73 | Sub testTestLoader_fixturesFromScriptContents_TestSubFound(t as object) 74 | 'Sub declared with target prefix results in test fixture 75 | sub_code = chr(10) + "Sub testTestLoader_fixturesFromScriptContents_TestSubFound(t as object)" 76 | sub_code = sub_code + chr(10) 77 | sub_code = sub_code + " i = 1 + 2" 78 | sub_code = sub_code + chr(10) 79 | sub_code = sub_code + "End Sub" 80 | sub_code = sub_code + chr(10) 81 | sname = "testTestLoader_fixturesFromScriptContents_TestSubFound" 82 | subref = testTestLoader_fixturesFromScriptContents_TestSubFound 83 | sdesc = "" '<--Update once message parsing is added 84 | tl = brstNewTestLoader("Test", "test") 85 | fixtures = tl.fixturesFromScriptContents(sub_code, "") 86 | 87 | 'Only one fixture should be returned 88 | t.assertEqual(1, fixtures.Count()) 89 | 90 | fixture = fixtures[0] 91 | actual_name = fixture.FuncName 92 | actual_func = fixture.TestFunc 93 | actual_desc = fixture.Descript 94 | 95 | t.assertEqual(sname, actual_name) 96 | t.assertEqual(subref,actual_func) 97 | t.assertEqual(sdesc, actual_desc) 98 | End Sub 99 | 100 | Function testTestLoader_fixturesFromScriptContents_TestFunctionFound(t as object) as void 101 | 'Function declared with target prefix results in test fixture 102 | 'Purposely made a function so we have something to parse 103 | func_code = chr(10) + "Function testTestLoader_fixturesFromScriptContents_TestFunctionFound(t as object) as void" 104 | func_code = func_code + chr(10) 105 | func_code = func_code + " return 1.5" 106 | func_code = func_code + chr(10) 107 | func_code = func_code + "End Function" 108 | func_code = func_code + chr(10) 109 | fname = "testTestLoader_fixturesFromScriptContents_TestFunctionFound" 110 | func = testTestLoader_fixturesFromScriptContents_TestFunctionFound 111 | fdesc = "" '<--Update once message parsing is added 112 | tl = brstNewTestLoader("Test", "test") 113 | fixtures = tl.fixturesFromScriptContents(func_code, "") 114 | t.assertEqual(1, fixtures.Count()) 115 | 116 | fixture = fixtures[0] 117 | actual_name = fixture.FuncName 118 | actual_func = fixture.TestFunc 119 | actual_desc = fixture.Descript 120 | 121 | t.assertEqual(fname, actual_name) 122 | t.assertEqual(func,actual_func) 123 | t.assertEqual(fdesc, actual_desc) 124 | End Function 125 | 126 | Sub testTestLoader_fixturesFromScriptContents_NoneFound(t as object) 127 | 'Emtpy enumerable return if no tests were found 128 | tl = brstNewTestLoader("Test", "test") 129 | fixtures = tl.fixturesFromScriptContents("", "") 130 | t.assertEqual(0, fixtures.Count()) 131 | End Sub 132 | 133 | Sub testTestLoader_findTestScripts_InSubDirectories(t as object) 134 | tl = brstNewTestLoader("Test", "test") 135 | 136 | tl.ListDir = Function(fromdirectory as string) 137 | if fromdirectory = "directory" 138 | return ["subDirectory1", "TestFile1.brs"] 139 | else if fromdirectory = "directory/subDirectory1" 140 | return ["TestFile2.brs", "subDirectory2", "TestFile3.brs"] 141 | else if fromdirectory = "directory/subDirectory1/subDirectory2" 142 | return ["TestFile4.brs", "TestFile5.brs", "TestFooBar.txt"] 143 | end if 144 | end Function 145 | 146 | tl.CompiledFiles = 0 147 | 148 | tl.compileScript = function(scriptpath as string) 149 | m.CompiledFiles = m.CompiledFiles + 1 150 | return true 151 | End Function 152 | 153 | files = tl.findTestScripts("directory") 154 | t.assertEqual(5, files.Count()) 155 | t.assertEqual(5, tl.CompiledFiles) 156 | End Sub 157 | 158 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /source/brstest.brs: -------------------------------------------------------------------------------- 1 | ' 2 | 'brstest - A framework for writing xUnit style tests in the BrightScript programming language 3 | ' 4 | 'Example test fixture: 5 | ' 6 | 'Sub testAddition(t as object) 7 | ' a = 1 8 | ' b = 2 9 | ' t.assertEqual(3, a + b) 10 | 'End Sub 11 | ' 12 | ' 13 | ' Copyright (c) 2010 Mark Roddy 14 | ' 15 | ' Permission is hereby granted, free of charge, to any person 16 | ' obtaining a copy of this software and associated documentation 17 | ' files (the "Software"), to deal in the Software without 18 | ' restriction, including without limitation the rights to use, 19 | ' copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | ' copies of the Software, and to permit persons to whom the 21 | ' Software is furnished to do so, subject to the following 22 | ' conditions: 23 | ' 24 | ' The above copyright notice and this permission notice shall be 25 | ' included in all copies or substantial portions of the Software. 26 | ' 27 | ' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28 | ' EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 29 | ' OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 30 | ' NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 31 | ' HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 32 | ' WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 33 | ' FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 34 | ' OTHER DEALINGS IN THE SOFTWARE. 35 | 36 | Sub BrsTestMain(PropagateErrors=False as Boolean, Socket=Invalid as Object, TestFilePrefix="Test" as string, TestMethodPrefix="test" as string, TestDirectory="pkg:/source" as string, Verbosity=1 as Integer) 37 | 38 | if socket <> Invalid AND socket.isConnected() then m.socket = socket 39 | 40 | 'Run all test fixtures found in the package using 41 | 'the standard naming conventions 42 | 'Discovers and runs test fixtures based upon the supplied arguments 43 | tl = brstNewTestLoader(TestFilePrefix, TestMethodPrefix) 44 | suite=tl.suiteFromDirectory(TestDirectory, PropagateErrors) 45 | runner=brstNewTextTestRunner(Verbosity) 46 | runner.run(suite) 47 | End Sub 48 | 49 | '==================== 50 | 'Begin Class TestResult 51 | 'Holds information on the result of a series of executed tests. These are 52 | 'automatically managed TestRunner and TestCase classes so test writers 53 | 'should not need to consider them. 54 | Function brstNewTestResult() as object 55 | new_result=CreateObject("roAssociativeArray") 56 | new_result.init=brstTrInit 57 | new_result.init() 58 | return new_result 59 | End Function 60 | 61 | Sub brstTrInit() 62 | m.failures = [] 63 | m.errors = [] 64 | m.testsRun = 0 65 | m.shouldStop = False 66 | 67 | 'Add Functions 68 | m.startTest=brstTrStartTest 69 | m.stopTest=brstTrStopTest 70 | m.addError=brstTrAddError 71 | m.addFailure=brstTrAddFailure 72 | m.addSuccess=brstTrAddSuccess 73 | m.wasSuccessful=brstTrWasSuccessful 74 | m.errToSting=trerrToSting 75 | m.stop = brstTrStop 76 | EndSub 77 | 78 | Sub brstTrStartTest(test as object) 79 | 'Called when the given test is about to be run 80 | m.testsRun = m.testsRun + 1 81 | End Sub 82 | 83 | Sub brstTrStopTest(test) 84 | 'Called when the given test has been run 85 | End Sub 86 | 87 | Sub brstTrLogToSocket(msg as String) 88 | if m.socket <> Invalid AND m.socket.isConnected() 89 | m.socket.sendStr(msg) 90 | end if 91 | End Sub 92 | 93 | Sub brstPrint(msg as String, newLine=true) 94 | if newLine 95 | Print msg 96 | brstTrLogToSocket(msg + " \r\n ") 97 | else 98 | Print msg; 99 | brstTrLogToSocket(msg) 100 | end if 101 | End Sub 102 | 103 | Sub brstTrAddError(test as object, err as object) 104 | 'Called when an error has occurred. 'err' is a tuple of values as 105 | 'returned by sys.exc_info(). 106 | err_inf= CreateObject("roArray",2,false) 107 | err_inf[0]=test 108 | err_inf[1]=m.errToSting(err, test) 109 | m.errors.push(err_inf) 110 | End Sub 111 | 112 | Sub brstTrAddFailure(test as object, err as object) 113 | 'Called when an error has occurred. 'err' is a tuple of values as 114 | 'returned by sys.exc_info() 115 | err_inf= CreateObject("roArray",2,false) 116 | err_inf[0]=test 117 | err_inf[1]=m.errToSting(err, test) 118 | m.failures.push(err_inf) 119 | End Sub 120 | 121 | Sub brstTrAddSuccess(test as object) 122 | 'Called when a test has completed successfully" 123 | End Sub 124 | 125 | Function brstTrWasSuccessful() as object 126 | 'Tells whether or not this result was a success" 127 | if m.failures.Count() = 0 and m.errors.Count() = 0 Then 128 | return true 129 | else 130 | return false 131 | end if 132 | End Function 133 | 134 | Sub brstTrStop() 135 | 'Indicates that the tests should be aborted" 136 | m.shouldStop = True 137 | End Sub 138 | 139 | Function trerrToSting( err as object, test as object) as string 140 | 'Converts error representation to a string 141 | return err 142 | End Function 143 | 'End Class TestResult 144 | '==================== 145 | 146 | 147 | '======================= 148 | 'Begin Class TestFixture 149 | 'A single test to be executed which is utilized by the TestCase class 150 | 'for execution and the TestResult class for reporting on the result. 151 | Function brstNewTestFixture(TestFunc as Object, TestName as String, TestDescription as String, TestScriptPath as String) as Object 152 | new_fix = CreateObject("roAssociativeArray") 153 | new_fix.TestFunc = TestFunc 154 | new_fix.FuncName = TestName 155 | new_fix.Descript = TestDescription 156 | new_fix.TestScriptPath = TestScriptPath 157 | return new_fix 158 | End Function 159 | 'End Class TestFixture 160 | '===================== 161 | 162 | 163 | '==================== 164 | 'Begin Class TestCase 165 | 'A class that manages running a single test fixture as well 166 | 'as determining it's outcome. An instance of this class is 167 | 'passed to each test fixture method. 168 | Function brstNewTestCase(Fixture as object, PropagateErrors=false as Boolean) as object 169 | new_case=CreateObject("roAssociativeArray") 170 | new_case.init = brstTcInit 171 | new_case.init(Fixture, PropagateErrors) 172 | return new_case 173 | End Function 174 | 175 | Sub brstTcInit(Fixture as object, PropagateErrors=false as Boolean) 176 | 177 | 'Attributes 178 | m._Fixture = Fixture 179 | 'this will be constructor argument in future version 180 | m._PropagateErrors = PropagateErrors 181 | 182 | 'Assertion methods which determine test failure 183 | m.fail = brstTcFail 184 | m.assertFalse = brstTcAssertFalse 185 | m.assertTrue = brstTcAssertTrue 186 | m.assertEqual = brstTcAssertEqual 187 | m.assertNotEqual = brstTcAssertNotEqual 188 | m.assertInvalid = brstTcAssertInvalid 189 | m.assertNotInvalid = brstTcAssertNotInvalid 190 | 191 | 'Other general purpose methods 192 | m.endedInFailure = brstTcEndedInFailure 193 | m.countTestCases = brstTcCountTestCases 194 | m.shortDescription = brstTcShortDescription 195 | m.toString = brstTcToString 196 | m.run = brstTcRun 197 | 198 | 'String Casting Functionality 199 | m.valueToString = brstTcValueToString 200 | m.assocArrayToString = brstTcAssocArrayToString 201 | m.nodeToString = brstTcNodeToString 202 | m.numericToString = brstTcNumericToString 203 | m.stringToString = brstTcStringToString 204 | m.booleanToString = brstTcBooleanToString 205 | m.roListToString = brstTcRoListToString 206 | m.roArrayToString = brstTcRoArrayToString 207 | m.roFunctionToString = brstTcRoFunctionToString 208 | m.roInvalidToString = brstTcRoInvalidToString 209 | 210 | 'Type Comparison Functionality 211 | m.isNumericType = brstTcIsNumeric 212 | m.eqValues = brstTcEqValues 213 | m.eqArrayOrList = brstTcEqArrayOrList 214 | m.eqAssocArrays = brstTcEqAssocArray 215 | 216 | End Sub 217 | 218 | Function brstTcCountTestCases() as Integer 219 | 'Returns the number of TestCases an instance 220 | 'of this object intends to run. 221 | 'Always 1, as apposed to TestSuite objects 222 | return 1 223 | End Function 224 | 225 | Function brstTcShortDescription() as string 226 | 'Returns a one-line description of the test, or empty string if no 227 | 'description has been provided. 228 | doc = m._Fixture.Descript 229 | nl_idx = Instr(1, doc, Chr(10)) 230 | if 0 = nl_idx then 231 | return doc 232 | else 233 | doc_obj = box(doc) 234 | return doc_obj.Tokenize(chr(10))[0] 235 | end if 236 | End Function 237 | 238 | Function brstTcToString() as string 239 | 'Returns a string representation of the test 240 | 'to be execute 241 | return m._Fixture.FuncName 242 | End Function 243 | 244 | Function brstTcEndedInFailure() as Boolean 245 | 'True if the result of test was a failed assertion 246 | return m.DoesExist("ErrorMessage") 247 | End Function 248 | 249 | 250 | Sub brstTcRun(result as object) 251 | 'Execute the test in this instance, and record 252 | 'the outcome in 'result' 253 | result.startTest(m) 254 | testMethod = m._Fixture.TestFunc 255 | 256 | if m._propagateErrors then 257 | testMethod(m) 258 | eval_result = &hFC 259 | else 260 | eval_result = eval("testMethod(m)") 261 | end if 262 | 263 | if m.endedInFailure() 264 | result.addFailure(m,m.ErrorMessage) 265 | else if eval_result <> &hFC and eval_result <> &hE2 then 266 | result.addError(m, ErrorMessageFromCode(eval_result)) 267 | else 268 | result.addSuccess(m) 269 | end if 270 | 271 | result.stopTest(m) 272 | End Sub 273 | 274 | Sub brstTcFail(msg) 275 | 'Fail immediately, with the given message 276 | m.ErrorMessage=msg 277 | stop 278 | End Sub 279 | 280 | Sub brstTcAssertFalse(expr as boolean) 281 | 'Fail the test if the expression is true. 282 | if expr then 283 | m.fail("expression evaluates to true") 284 | end if 285 | End Sub 286 | 287 | Sub brstTcAssertTrue(expr as object) 288 | 'Fail the test unless the expression is true. 289 | if not expr then 290 | m.fail("expression evaluates to false") 291 | End if 292 | End Sub 293 | 294 | Sub brstTcAssertEqual(first as object, second as object) 295 | 'Fail if the two objects are unequal as determined by the '<>' operator. 296 | if not m.eqValues(first, second) then 297 | first_as_string = m.valueToString(first) 298 | second_as_string = m.valueToString(second) 299 | m.fail(first_as_string + " != " + second_as_string) 300 | end if 301 | End Sub 302 | 303 | Sub brstTcAssertNotEqual(first as object, second as object) 304 | 'Fail if the two objects are equal as determined by the '=' operator. 305 | if m.eqValues(first, second) then 306 | first_as_string = m.valueToString(first) 307 | second_as_string = m.valueToString(second) 308 | m.fail(first_as_string + " == " + second_as_string) 309 | end if 310 | End Sub 311 | 312 | Sub brstTcAssertInvalid(value as dynamic) 313 | 'Fail if the value is not invalid 314 | if value <> Invalid then 315 | expr_as_string = m.valueToString(value) 316 | m.fail(expr_as_string + " <> Invalid") 317 | end if 318 | End Sub 319 | 320 | Sub brstTcAssertNotInvalid(value as dynamic) 321 | 'Fail if the value is invalid 322 | if value = Invalid then 323 | expr_as_string = m.valueToString(value) 324 | m.fail(expr_as_string + " = Invalid") 325 | end if 326 | End Sub 327 | 328 | 'String conversion functions used to coerce types so that can 329 | 'be outputted upon test failure 330 | Function brstTcValueToString(SrcValue as Object) as String 331 | 'Converts an arbitrary value to a string representation 332 | 333 | 'A dispatch table would be better approach here than 334 | 'this switch/case like approach, but one was attempted 335 | 'and needed to be abandonded due to a bug in the only 336 | 'mechinism of doing so. See the following forum thread 337 | 'for more information on the issue: 338 | 'http://forums.roku.com/viewtopic.php?f=34&t=27338 339 | value_type = type(SrcValue) 340 | if m.isNumericType(value_type) 341 | return m.numericToString(SrcValue) 342 | else if value_type = "roString" then 343 | return m.stringToString(SrcValue) 344 | else if value_type = "roBoolean" then 345 | return m.booleanToString(SrcValue) 346 | else if value_type = "roList" then 347 | return m.roListToString(SrcValue) 348 | else if value_type = "roAssociativeArray" then 349 | return m.assocArrayToString(SrcValue) 350 | else if value_type = "roSGNode" then 351 | return m.nodeToString(SrcValue) 352 | else if value_type = "roArray" then 353 | return m.roArrayToString(SrcValue) 354 | else if value_type = "roFunction" then 355 | return m.roFunctionToString(SrcValue) 356 | else if value_type = "roInvalid" then 357 | return m.roInvalidToString(SrcValue) 358 | else 359 | return "Unknown type: " + value_type 360 | end if 361 | End Function 362 | 363 | Function brstTcNumericToString(SrcNumeric as Object) as String 364 | 'Converts a numeric literal to a string 365 | return SrcNumeric.ToStr() 366 | End Function 367 | 368 | Function brstTcStringToString(SrcStr as Object) as String 369 | 'Convert an roString object to a string 370 | return Chr(34) + SrcStr.GetString() + Chr(34) 371 | 'Exists so that any object can be passed to the ValueToString() 372 | 'method, otherwise doesn't serve any real purpose 373 | End Function 374 | 375 | Function brstTcBooleanToString(SrcBool as Object) as String 376 | 'Convert roBoolean value to string 377 | if SrcBool then 378 | return "True" 379 | else 380 | return "False" 381 | end if 382 | End Function 383 | 384 | Function brstTcRoFunctionToString(SrcFunction as Object) as String 385 | 'Convert roFunction value to string 386 | return SrcFunction.toStr() 387 | End Function 388 | 389 | Function brstTcRoInvalidToString(InvalidObj as Object) as String 390 | 'Convert the roInvalid global into a string 391 | return "roInvalid" 392 | End Function 393 | 394 | Function brstTcRoListToString(SrcList as Object) as String 395 | 'Convert an roList object to a string 396 | if SrcList.IsEmpty() then 397 | return "->/" 398 | end if 399 | strvalue = "" 400 | 401 | 'Not using enum interface as this results in intermintant 402 | 'crashes or the DVP box 403 | SrcList.ResetIndex() 404 | i = SrcList.GetIndex() 405 | while i <> invalid 406 | strvalue = strvalue + m.ValueToString(i) 407 | strvalue = strvalue + " -> " 408 | i = SrcList.GetIndex() 409 | end while 410 | strvalue = strvalue + "/" 411 | return strvalue 412 | End Function 413 | 414 | Function brstTcNodeToString(SrcNode as Object) as String 415 | 'Converts an roSGNode to a string representation 416 | strvalue = "{ " 417 | first_entry = True 418 | keys = SrcNode.getFields() 419 | for each k in keys 420 | if not first_entry then 421 | strvalue = strvalue + ", " 422 | else 423 | first_entry = False 424 | end if 425 | strvalue = strvalue + k 426 | strvalue = strvalue + " : " 427 | 428 | 'handle basic self-refs 429 | if type(SrcNode[k]) = "roSGNode" and SrcNode[k].isSameNode(SrcNode) then 430 | strvalue = strvalue + "(self)" 431 | else 432 | strvalue = strvalue + m.ValueToString(SrcNode[k]) 433 | end if 434 | end for 435 | strvalue = strvalue + " }" 436 | return strvalue 437 | return "{}" 438 | End Function 439 | 440 | 441 | Function brstTcAssocArrayToString(SrcAssocArray as Object) as String 442 | 'Converts an roAssociativeArray to a string representation 443 | strvalue = "{ " 444 | first_entry = True 445 | keys = SrcAssocArray.keys() 446 | for each k in keys 447 | if not first_entry then 448 | strvalue = strvalue + ", " 449 | else 450 | first_entry = False 451 | end if 452 | strvalue = strvalue + k 453 | strvalue = strvalue + " : " 454 | strvalue = strvalue + m.ValueToString(SrcAssocArray[k]) 455 | end for 456 | strvalue = strvalue + " }" 457 | return strvalue 458 | return "{}" 459 | End Function 460 | 461 | Function brstTcRoArrayToString(SrcArray as Object, DisplayTrailingInvalid=False as boolean) as String 462 | 'Convert an roArray to a string representation 463 | strvalue = "[" 464 | first_entry = True 465 | 466 | 'Purposely not using ifEnum interface for the roArray. 467 | 'Bug in Roku firmware causes crash when 468 | 'iterating over array that has an uninitialized 469 | 'element. See discussion here: 470 | 'http://forums.roku.com/viewtopic.php?f=34&t=25979 471 | array_length = SrcArray.Count() 472 | for i = 0 to array_length - 1 step 1 473 | entry = SrcArray[i] 474 | if first_entry then 475 | first_entry = False 476 | else 477 | strvalue = strvalue + "," 478 | end if 479 | strvalue = strvalue + " " + m.ValueToString(entry) 480 | end for 481 | strvalue = strvalue + " ]" 482 | return strvalue 483 | End Function 484 | 485 | Function brstTcEqValues(Value1 as Object, Value2 as Object) as Boolean 486 | 'Compare two arbtrary values to eachother 487 | 488 | valtype = type(Value1) 489 | 490 | if (m.isNumericType(valType) AND m.isNumericType(type(Value2))) 491 | return Value1 = Value2 492 | else if valtype = type(Value2) 493 | if valtype = "roArray" or valtype = "roList" or valType = "roByteArray" 494 | return m.eqArrayOrList(Value1, Value2) 495 | else if valtype = "roAssociativeArray" 496 | return m.eqAssocArrays(Value1, Value2) 497 | else if valtype = "roSGNode" 498 | return Value1.isSameNode(Value2) 499 | else 500 | return Value1 = Value2 501 | end if 502 | end if 503 | 504 | return False 505 | End Function 506 | 507 | Function brstTcIsNumeric(valType as String) as Boolean 508 | return valType = "roInt" or valType = "roFloat" or valType = "roDouble" or valType = "Double" or valType = "LongInteger" 509 | End Function 510 | 511 | Function brstTcEqArrayOrList(Value1 as Object, Value2 as Object) as Boolean 512 | 'Compare to roList objects for equality 513 | l1 = Value1.Count() 514 | l2 = Value2.Count() 515 | if l1 <> l2 then 516 | return False 517 | else 518 | for i = 0 to l1 - 1 step 1 519 | v1 = Value1[i] 520 | v2 = Value2[i] 521 | if not m.eqValues(v1, v2) then 522 | return False 523 | end if 524 | end for 525 | return True 526 | end if 527 | End Function 528 | 529 | Function brstTcEqAssocArray(Value1 as Object, Value2 as Object) as Boolean 530 | 'Compare to roAssociativeArray objects for equality 531 | if Value1.count() = Value2.count() 532 | for each k in Value1 533 | if not Value2.DoesExist(k) then 534 | return False 535 | else 536 | v1 = Value1[k] 537 | v2 = Value2[k] 538 | if not m.eqValues(v1, v2) then 539 | return False 540 | end if 541 | end if 542 | end for 543 | return True 544 | else 545 | return False 546 | end if 547 | End Function 548 | 549 | 550 | 'End Class TestCase 551 | '================== 552 | 553 | 554 | '===================== 555 | 'Begin Class TestSuite 556 | 'A test suite is an aggregation of TestCase objects that is runnable with 557 | 'the same semantics. 558 | Function brstNewTestSuite(tests as object) as object 559 | new_suite=CreateObject("roAssociativeArray") 560 | new_suite._tests = CreateObject("roArray", 10, true) 561 | 562 | new_suite.countTestCases = brstTsCountTestCases 563 | new_suite.addTest = brstTsAddTest 564 | new_suite.addTests = brstTsAddTests 565 | new_suite.run = brstTsRun 566 | new_suite.toString = brstTsToString 567 | new_suite.addTests(tests) 568 | 569 | return new_suite 570 | End Function 571 | 572 | Function brstTsToString() as String 573 | 'Return a string representaiton of the test suite 574 | st_str = "" 579 | return st_str 580 | End Function 581 | 582 | Function brstTsCountTestCases() as integer 583 | 'Return the number of tests encapsulated 584 | 'by the test suite 585 | cases = 0 586 | for each test in m._tests 587 | cases = cases + test.countTestCases() 588 | end for 589 | return cases 590 | End Function 591 | 592 | Sub brstTsAddTest(test as object) 593 | 'Add a single test to be in the test suite 594 | m._tests.push(test) 595 | End Sub 596 | 597 | Sub brstTsAddTests(tests as object) 598 | 'Add each item in an enumberable object 599 | 'to the suite 600 | for each test in tests 601 | m.addTest(test) 602 | end for 603 | End Sub 604 | 605 | Function brstTsRun(result as object) as object 606 | 'Execute each test in the suite, collecting 607 | 'their outcomes in the supplied result object 608 | for each test in m._tests 609 | if result.shouldStop then 610 | return result 611 | else 612 | test.run(result) 613 | end if 614 | end for 615 | return result 616 | End Function 617 | 'End Class TestSuite 618 | '=================== 619 | 620 | 621 | '========================== 622 | 'Begin Class TextTestResult 623 | 'A 'sub-class' of the TestResult class which prints 624 | 'the result of each test executed as it is reported 625 | Function brstNewTextTestResult(descriptions as object, verbosity as integer) as object 626 | new_result=CreateObject("roAssociativeArray") 627 | new_result.init=brstTrInit 628 | new_result.init() 629 | new_result.init=brstTtrInit 630 | new_result.init(descriptions, verbosity) 631 | 632 | return new_result 633 | End Function 634 | 635 | Sub brstTtrInit(show_descriptions as integer, verbosity as integer) 636 | if verbosity > 1 then 637 | m.showAll = true 638 | else 639 | m.showAll = false 640 | end if 641 | if verbosity = 1 then 642 | m.dots = true 643 | else 644 | m.dots = false 645 | end if 646 | 647 | m.show_descriptions = show_descriptions 648 | m.separator1 = string(70, "=") 649 | m.separator2 = string(70, "-") 650 | 651 | 'Attach functions 652 | m.getDescription = brstTtrGetDescription 653 | m.startTest = brstTtrStartTest 654 | m.addSuccess = brstTtrAddSuccess 655 | m.addError = brstTtrAddError 656 | m.addFailure =brstTtrAddFailure 657 | m.printErrors = brstTtrPrintErrors 658 | m.printErrorList = brstTtrPrintErrorList 659 | m.write = brstTtrWrite 660 | m.writeline = brstTtrWriteline 661 | 662 | End Sub 663 | 664 | Function brstTtrGetDescription(test as object) as string 665 | if not m.show_descriptions then 666 | return test.toString() 667 | else 668 | desc = test.shortDescription() 669 | if desc <> "" then 670 | return desc 671 | else 672 | return test.toString() 673 | end if 674 | end if 675 | end Function 676 | 677 | Sub brstTtrStartTest(test as object) 678 | m.startTest=brstTrstartTest 679 | m.startTest(test) 680 | m.startTest = brstTtrStartTest 681 | if m.showAll then 682 | m.write(m.getDescription(test) + " ... ") 683 | end if 684 | End Sub 685 | 686 | Sub brstTtrAddSuccess(test as object) 687 | m.addSuccess=brstTrAddSuccess 688 | m.addSuccess(test) 689 | m.addSuccess=brstTtrAddSuccess 690 | if m.showAll then 691 | m.writeline("ok") 692 | elseif m.dots then 693 | m.write( ".") 694 | end if 695 | end sub 696 | 697 | Sub brstTtrAddError(test as object, err as object) 698 | m.addError=brstTrAddError 699 | m.addError(test, err) 700 | m.addError=brstTtrAddError 701 | if m.showAll then 702 | m.writeline("ERROR") 703 | else if m.dots then 704 | m.write("E") 705 | end if 706 | End Sub 707 | 708 | Sub brstTtrAddFailure(test as object, err as object) 709 | m.addFailure=brstTrAddFailure 710 | m.addFailure(test,err) 711 | m.addFailure=brstTtrAddFailure 712 | if m.showAll then 713 | m.writeline("FAIL") 714 | elseif m.dots then 715 | m.write( "F") 716 | end if 717 | end sub 718 | 719 | Sub brstTtrPrintErrors() 720 | if m.dots or m.showAll then 721 | m.writeline("") 722 | End If 723 | m.printErrorList("ERROR", m.errors) 724 | m.printErrorList("FAIL", m.failures) 725 | End Sub 726 | 727 | Sub brstTtrPrintErrorList(flavour as string, errors as object) 728 | for each err_item in errors 729 | test=err_item[0] 730 | err=err_item[1] 731 | m.writeline(m.separator1) 732 | m.writeline(test._fixture.testScriptPath) 733 | m.writeline(flavour + ": " + m.getDescription(test)) 734 | m.writeline(m.separator2) 735 | m.writeline(err) 736 | m.writeline("") 737 | end for 738 | end sub 739 | 740 | Sub brstTtrWrite(item as object) 741 | brstPrint(item, false) 742 | End Sub 743 | 744 | Sub brstTtrWriteline(item as object) 745 | brstPrint(item) 746 | End Sub 747 | 'End Class TextTestResult 748 | '======================== 749 | 750 | 751 | 752 | 753 | '========================== 754 | 'Begin Class TextTestResult 755 | 'A class which runs tests and reports their results in a 756 | 'textual format to the debug console 757 | 'verbosity: if <2 Do not print method names; if >1 print the method names 758 | Function brstNewTextTestRunner(verbosity as Integer) as object 759 | new_runner = CreateObject("roAssociativeArray") 760 | new_runner.init = brstTtrnInit 761 | new_runner.init(1,verbosity) 762 | return new_runner 763 | End Function 764 | 765 | Sub brstTtrnInit(show_descriptions as integer, verbosity as integer) 766 | m.show_descriptions = show_descriptions 767 | m.verbosity = verbosity 768 | m.makeresult = brstTtrnMakeresult 769 | m.run = brstTtrnRun 770 | End Sub 771 | 772 | Function brstTtrnMakeresult() as object 773 | return brstNewTextTestResult(m.show_descriptions, m.verbosity) 774 | End Function 775 | 776 | Function brstTtrnRun(test as object) as object 777 | 'Run a test case or suite and report their 778 | 'result to the debug console 779 | 780 | 'todo: Break this up into individual methods 781 | result = m.makeResult() 782 | test.run(result) 783 | result.printErrors() 784 | brstPrint(result.separator2) 785 | testsrun = result.testsRun 786 | brstPrint("Ran" + Str(testsrun) + " tests" + chr(10)) 787 | if not result.wasSuccessful() then 788 | brstPrint("FAILED (") 789 | failed=result.failures.Count() 790 | errored=result.errors.Count() 791 | if failed <> 0 792 | brstPrint("failures=" + Stri(failed)) 793 | end if 794 | if errored <> 0 then 795 | if failed <> 0 796 | brstPrint(", ") 797 | end if 798 | brstPrint("errors=" + Stri(errored)) 799 | brstPrint(")") 800 | else 801 | brstPrint(")") 802 | end if 803 | else: 804 | brstPrint("OK") 805 | end if 806 | brstPrint("") 807 | return result 808 | End Function 809 | 'End Class TextTestResult 810 | '======================== 811 | 812 | 813 | '====================== 814 | 'Begin Class TestLoader 815 | 'Locates test fixtures and loads information about them into 816 | 'TestCase objects where are ready to run 817 | Function brstNewTestLoader(TestFilePrefix as string, TestMethodPrefix as string) as Object 818 | ldr=CreateObject("roAssociativeArray") 819 | 820 | ldr.testFilePrefix = TestFilePrefix 821 | ldr.testMethodPrefix = TestMethodPrefix 822 | ldr.NewSuite = brstNewTestSuite 823 | ldr.NewTestCase = brstNewTestCase 824 | 825 | 'Method Attachment 826 | ldr.fixturesFromScriptContents = brstTlFixturesFromScriptContents 827 | ldr.findTestScripts = brstTlFindTestScripts 828 | ldr.ListDir = brstTlListDir 829 | ldr.recursiveListDir = brstlrecursiveListDir 830 | ldr.fixturesFromScript = brstTlFixturesFromScript 831 | ldr.readFile = brstTlReadFile 832 | ldr.fixturesFromDirectory = brstTlFixturesFromDirectory 833 | ldr.suiteFromDirectory = brstTlSuiteFromDirectory 834 | ldr.compileScript = brstTlCompileScript 835 | return ldr 836 | End Function 837 | 838 | Function brstTlSuiteFromDirectory(fromdirectory as String, PropagateErrors=false as Boolean) as object 839 | 'Returns a test suite containing all test fixtures found in 840 | 'the specified path 841 | cases = CreateObject("roList") 842 | for each fixture in m.fixturesFromDirectory(fromdirectory) 843 | case = m.NewTestCase(fixture, PropagateErrors) 844 | cases.addtail(case) 845 | end for 846 | suite = m.NewSuite(cases) 847 | return suite 848 | End Function 849 | 850 | Function brstTlFixturesFromDirectory(fromdirectory as String) as object 851 | 'Returns an enumerable of TestFixture objects from a 852 | 'a directory containing test files 853 | ret = CreateObject("roList") 854 | for each file in m.findTestScripts(fromdirectory) 855 | for each fixture in m.fixturesFromScript(file) 856 | ret.addtail(fixture) 857 | end for 858 | end for 859 | return ret 860 | End Function 861 | 862 | Function brstlRecursiveListDir(directory as string, files as Object) as Object 863 | paths = m.ListDir(directory) 864 | for each path in paths 865 | if path.instr(".") = -1 866 | m.recursiveListDir(directory + "/" + path, files) 867 | else 868 | files.push({ 869 | name: path 870 | path: directory 871 | }) 872 | end if 873 | end for 874 | return files 875 | End Function 876 | 877 | Function brstTlFindTestScripts(fromdirectory as string) as Object 878 | 'Returns an enumerable of paths to scripts that contain 879 | 'test fixtures based upon the naming convention supplied 880 | 'at object creation time 881 | ret = CreateObject("roList") 882 | SrcsDir = "pkg:/source" 883 | if Left(fromdirectory, len(SrcsDir)) = SrcsDir then 884 | ShouldCompile = False 885 | else 886 | ShouldCompile = True 887 | end if 888 | for each f in m.recursiveListDir(fromDirectory, []) 889 | if Left(UCase(f.name),len(m.testFilePrefix)) = UCase(m.testFilePrefix) then 890 | 'Has the desired prefix 891 | if Right(Ucase(f.name),4) = ".BRS" then 892 | 'Is a bright script file 893 | full_path = f.path + "/" + f.name 894 | if ShouldCompile then 895 | if m.compileScript(full_path) then 896 | ret.AddTail(full_path) 897 | else 898 | 'print full_path + " failed to compile" 899 | 'Don't bother as run() seems to expect a main() to be 900 | 'present, doesn't seem a way to add this feature yet, 901 | 'but will deal with it in time 902 | end if 903 | else 904 | ret.AddTail(full_path) 905 | end if 906 | end if 907 | end if 908 | end for 909 | return ret 910 | End Function 911 | 912 | Function brstTlCompileScript(scriptpath as string) as boolean 913 | 'Compile a script and return True if the compilation 914 | 'was successful 915 | Run(scriptpath) 916 | el=GetLastRunCompileError() 917 | if el=invalid then 918 | return True 919 | else 920 | return false 921 | end if 922 | End Function 923 | 924 | Function brstTlListDir(fromdirectory as string) as object 925 | 'Redirect over builtin ListDir so it can be 926 | 'overriden. Mainly used for testing 927 | return ListDir(fromdirectory) 928 | End Function 929 | 930 | Function brstTlFixturesFromScript(scriptpath as string) as Object 931 | 'Returns an enumerable of TestFixture objects from the 932 | 'contents of a script file designated 933 | code = m.readFile(scriptpath) 934 | return m.fixturesFromScriptContents(code, scriptpath) 935 | End Function 936 | 937 | Function brstTlReadFile(fromfile as string) as string 938 | 'Redirect over the builtin ReadAsciiFile so that 939 | 'it can be overriden. Mainly for testing. 940 | return ReadAsciiFile(fromfile) 941 | End Function 942 | 943 | Function brstTlFixturesFromScriptContents(scriptstr as string, scriptpath as string) as Object 944 | 'Returns an enumerable of TestFixture objects from the contents 945 | 'of a test script file 946 | 'Assumes that the code file has already been compiled and the 947 | 'functions/subs have been loaded into memory 948 | code = box(scriptstr) 949 | fixtures = CreateObject("roList") 950 | for each line in code.Tokenize(chr(10)) 951 | boxedline = box(UCase(line)) 952 | boxedline.Trim() 953 | fobj = invalid 954 | if UCase("Function") = boxedline.trim().Left(8) or UCase("Sub") = boxedline.trim().Left(3) then 955 | func_def = box(line) 956 | func_def.Trim() 957 | tokens = func_def.Tokenize(chr(32)) 958 | fname = tokens[1] 959 | tokens = fname.Tokenize("(") 960 | fname = tokens[0] 961 | if UCase(Left(fname, len(m.testMethodPrefix))) = UCase(m.testMethodPrefix) then 962 | eval("fobj=" + fname) 963 | fixt = brstNewTestFixture(fobj, fname, "", scriptpath) 964 | fixtures.AddTail(fixt) 965 | end if 966 | end if 967 | end for 968 | return fixtures 969 | End Function 970 | 'End Class TestLoader 971 | '==================== 972 | 973 | Function ErrorMessageFromCode(err_code as integer) as string 974 | 'Translate a BrightScript error code as returned by eval() into a meaningful 975 | 'error message. 976 | 'Ref: https://forums.roku.com/viewtopic.php?t=33193#p211138 977 | if m.err_map = invalid then 978 | m.err_map = {} 979 | m.err_map[str(&hfc)] = "ERR_OKAY" 980 | m.err_map[str(&hFC)] = "[Not an error] Normal, but terminate execution; END, shell 'exit', window closed, etc. (ERR_NORMAL_END)" 981 | m.err_map[str(&hE2)] = "[Not an error] Return executed, and a value returned on the stack (ERR_VALUE_RETURN)" 982 | m.err_map[str(&hFE)] = "ERR_INTERNAL" 983 | m.err_map[str(&hFD)] = "Opcode that Roku does not handle (ERR_UNDEFINED_OPCD)" 984 | m.err_map[str(&hFB)] = "Expression operator that Roku does not handle (ERR_UNDEFINED_OP)" 985 | m.err_map[str(&hFA)] = "ERR_MISSING_PARN" 986 | m.err_map[str(&hF9)] = "Nothing on stack to pop (ERR_STACK_UNDER)" 987 | m.err_map[str(&hF8)] = "scriptBreak() called (ERR_BREAK)" 988 | m.err_map[str(&hF7)] = "Explicit 'STOP' command encountered (ERR_STOP)" 989 | m.err_map[str(&hF6)] = "bscNewComponent failed because object class not found (ERR_RO0)" 990 | m.err_map[str(&hF5)] = "ro function call does not have the right number of parameters (ERR_RO1)" 991 | m.err_map[str(&hF4)] = "Member function not found in BrightScript Component or interface (ERR_RO2)" 992 | m.err_map[str(&hF3)] = "Interface not a member of object (ERR_RO3)" 993 | m.err_map[str(&hF2)] = "Too many function parameters for BrightScript to handle (ERR_TOO_MANY_PARAM)" 994 | m.err_map[str(&hF1)] = "Wrong number of function parameters (ERR_WRONG_NUM_PARAM)" 995 | m.err_map[str(&hF0)] = "Function returns a value, but is ignored (ERR_RVIG)" 996 | m.err_map[str(&hEF)] = "Non-printable value (ERR_NOTPRINTABLE)" 997 | m.err_map[str(&hEE)] = "Tried to Wait on a function without the ifMessagePort interface (ERR_NOTWAITABLE)" 998 | m.err_map[str(&hED)] = "Interface calls from rotINTERFACE must be static (ERR_MUST_BE_STATIC)" 999 | m.err_map[str(&hEC)] = "'Dot' Operator attempted with invalid BrightScript Component or interface reference (ERR_RO4)" 1000 | m.err_map[str(&hEB)] = "Operation on two typeless operands attempted (ERR_NOTYPEOP)" 1001 | m.err_map[str(&hE9)] = "Use of uninitialized variable (ERR_USE_OF_UNINIT_VAR)" 1002 | m.err_map[str(&hE8)] = "Non-numeric array index (ERR_TM2)" 1003 | m.err_map[str(&hE7)] = "ERR_ARRAYNOTDIMMED" 1004 | m.err_map[str(&hE6)] = "Uninitialized reference to SUB (ERR_USE_OF_UNINIT_BRSUBREF)" 1005 | m.err_map[str(&hE5)] = "ERR_MUST_HAVE_RETURN" 1006 | m.err_map[str(&hE4)] = "Invalid left side of expression (ERR_INVALID_LVALUE)" 1007 | m.err_map[str(&hE3)] = "Invalid number of array indices (ERR_INVALID_NUM_ARRAY_IDX)" 1008 | m.err_map[str(&hE1)] = "ERR_UNICODE_NOT_SUPPORTED" 1009 | m.err_map[str(&hE0)] = "Function Call Operator ( ) attempted on non-function (ERR_NOTFUNOPABLE)" 1010 | m.err_map[str(&hDF)] = "Stack overflow (ERR_STACK_OVERFLOW)" 1011 | m.err_map[str(&h02)] = "Syntax error (ERR_SYNTAX)" 1012 | m.err_map[str(&h14)] = "Divide by zero (ERR_DIV_ZERO)" 1013 | m.err_map[str(&h0E)] = "ERR_MISSING_LN" 1014 | m.err_map[str(&h0C)] = "ERR_OUTOFMEM" 1015 | m.err_map[str(&h1C)] = "ERR_STRINGTOLONG" 1016 | m.err_map[str(&h18)] = "Type Mismatch (ERR_TM)" 1017 | m.err_map[str(&h1A)] = "Out of string space (ERR_OS)" 1018 | m.err_map[str(&h04)] = "Return without Gosub (ERR_RG)" 1019 | m.err_map[str(&h00)] = "Next statement encountered without matching For (ERR_NF)" 1020 | m.err_map[str(&h08)] = "Invalid parameter passed to function or array (ERR_FC)" 1021 | m.err_map[str(&h12)] = "Attempted to redim an array (ERR_DD)" 1022 | m.err_map[str(&h10)] = "Array subscript out of bounds (ERR_BS)" 1023 | m.err_map[str(&h06)] = "Out of data during READ operation (ERR_OD)" 1024 | m.err_map[str(&h20)] = "Continue not allowed (ERR_CN)" 1025 | m.err_map[str(&hBF)] = "EndWhile statement encountered without matching While (ERR_NW)" 1026 | m.err_map[str(&hBE)] = "While statement encountered without matching EndWhile (ERR_MISSING_ENDWHILE)" 1027 | m.err_map[str(&hBC)] = "If statement encountered without matching EndIf (ERR_MISSING_ENDIF)" 1028 | m.err_map[str(&hBB)] = "No line number found (ERR_NOLN)" 1029 | m.err_map[str(&hBA)] = "Line number sequence error (ERR_LNSEQ)" 1030 | m.err_map[str(&hB9)] = "Error loading a file (ERR_LOADFILE)" 1031 | m.err_map[str(&hB8)] = "'Match' statement did not match (ERR_NOMATCH)" 1032 | m.err_map[str(&hB7)] = "String being compiled ended unexpectedly - missing end of block? (ERR_UNEXPECTED_EOF)" 1033 | m.err_map[str(&hB6)] = "Variable on NEXT does not match the corresponding FOR (ERR_FOR_NEXT_MISMATCH)" 1034 | m.err_map[str(&hB5)] = "ERR_NO_BLOCK_END" 1035 | m.err_map[str(&hB4)] = "Label defined more than once (ERR_LABELTWICE)" 1036 | m.err_map[str(&hB3)] = "Literal string does not have ending quote (ERR_UNTERMED_STRING)" 1037 | m.err_map[str(&hB2)] = "ERR_FUN_NOT_EXPECTED" 1038 | m.err_map[str(&hB1)] = "ERR_TOO_MANY_CONST" 1039 | m.err_map[str(&hB0)] = "ERR_TOO_MANY_VAR" 1040 | m.err_map[str(&hAF)] = "ERR_EXIT_WHILE_NOT_IN_WHILE" 1041 | m.err_map[str(&hAE)] = "ERR_INTERNAL_LIMIT_EXCEDED" 1042 | m.err_map[str(&hAD)] = "ERR_SUB_DEFINED_TWICE" 1043 | m.err_map[str(&hAC)] = "ERR_NOMAIN" 1044 | m.err_map[str(&hAB)] = "ERR_FOREACH_INDEX_TM" 1045 | m.err_map[str(&hAA)] = "ERR_RET_CANNOT_HAVE_VALUE" 1046 | m.err_map[str(&hA9)] = "ERR_RET_MUST_HAVE_VALUE" 1047 | m.err_map[str(&hA8)] = "ERR_FUN_MUST_HAVE_RET_TYPE" 1048 | m.err_map[str(&hA7)] = "ERR_INVALID_TYPE" 1049 | m.err_map[str(&hA6)] = "No longer supported (ERR_NOLONGER)" 1050 | m.err_map[str(&hA5)] = "ERR_EXIT_FOR_NOT_IN_FOR" 1051 | m.err_map[str(&hA4)] = "ERR_MISSING_INITILIZER" 1052 | m.err_map[str(&hA3)] = "ERR_IF_TOO_LARGE" 1053 | m.err_map[str(&hA2)] = "ERR_RO_NOT_FOUND" 1054 | m.err_map[str(&hA1)] = "ERR_TOO_MANY_LABELS" 1055 | m.err_map[str(&hA0)] = "ERR_VAR_CANNOT_BE_SUBNAME" 1056 | m.err_map[str(&h9F)] = "ERR_INVALID_CONST_NAME" 1057 | end if 1058 | 1059 | errMsg = m.err_map[str(err_code)] 1060 | if errMsg = invalid then 1061 | errMsg = "Unknown Error: " + str(err_code) 1062 | end if 1063 | 1064 | return errMsg 1065 | End Function 1066 | -------------------------------------------------------------------------------- /source/test_testcase.brs: -------------------------------------------------------------------------------- 1 | 'Unit tests for the test case class 2 | 3 | 4 | Sub testTestCase_ToString_EqualToMethodName(t as object) 5 | 'To string should return the test method name 6 | methodname = "testfoobar" 7 | fixture = brstNewTestFixture("", methodname, "", "") 8 | tc = brstNewTestCase(fixture) 9 | t.assertEqual(methodname, tc.toString()) 10 | End Sub 11 | 12 | Sub testTestCase_ShortDescription_EmptyIfNoDescSpecified(t as object) 13 | 'Empty string return if no description specified 14 | fixture = brstNewTestFixture("", "", "", "") 15 | tc = brstNewTestCase(fixture) 16 | t.assertEqual("", tc.shortDescription()) 17 | End Sub 18 | 19 | Sub testTestCase_ShortDescription_OnlyFirstLineReturned(t as object) 20 | 'Only the first line of the test description should be returned 21 | expected_desc = "This is the first line of the test description" 22 | full_desc = expected_desc + chr(10) + "This is the second line" 23 | full_desc = full_desc + chr(10) + "This is the third line" 24 | fixture = brstNewTestFixture("", "", full_desc, "") 25 | tc = brstNewTestCase(fixture) 26 | t.assertEqual(expected_desc, tc.shortDescription()) 27 | End Sub 28 | 29 | Sub testTestCase_ShortDescription_SingleLineDescHandled(t as object) 30 | 'Single line description should be returned as itself 31 | expected_desc = "This is the first line of the test description" 32 | fixture = brstNewTestFixture("", "", expected_desc, "") 33 | tc = brstNewTestCase(fixture) 34 | t.assertEqual(expected_desc, tc.shortDescription()) 35 | End Sub 36 | 37 | Sub assertEqualMessageForNotEquals(t as object, val1 as object, val2 as object, ExpectedMessage as object) 38 | fixture = brstNewTestFixture("", "", "", "") 39 | tc = brstNewTestCase(fixture) 40 | tc.ErrorMessage = "" 41 | tc.fail = function(msg as string) 42 | m.ErrorMessage = msg 43 | end function 44 | tc.assertEqual(val1, val2) 45 | 46 | ' Manually compare strings instead of using assertEqual() as we are testing 47 | ' the assertEqual() method 48 | if "" = tc.ErrorMessage then 49 | t.fail("No error message was set") 50 | else if ExpectedMessage <> tc.ErrorMessage then 51 | err_msg = "Expected Msg: " + chr(34) + ExpectedMessage + chr(34) + chr(10) 52 | err_msg = err_msg + "Actual Msg: " + chr(34) + tc.ErrorMessage + chr(34) 53 | t.fail(err_msg) 54 | end if 55 | End Sub 56 | 57 | Sub assertEqualMessageForEquals(t as object, val1 as object, val2 as object) 58 | fixture = brstNewTestFixture("", "", "", "") 59 | tc = brstNewTestCase(fixture) 60 | tc.ErrorMessage = "" 61 | tc.fail = function(msg as string) 62 | m.ErrorMessage = msg 63 | end function 64 | tc.assertEqual(val1, val2) 65 | if "" <> tc.ErrorMessage then 66 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 67 | end if 68 | End Sub 69 | 70 | Sub assertNotEqualMessageForEquals(t as object, val1 as object, val2 as object, ExpectedMessage as string) 71 | fixture = brstNewTestFixture("", "", "", "") 72 | tc = brstNewTestCase(fixture) 73 | tc.ErrorMessage = "" 74 | tc.fail = function(msg as string) 75 | m.ErrorMessage = msg 76 | end function 77 | tc.assertNotEqual(val1, val2) 78 | 79 | ' Ok to use assertEqual() in this case as we are testting 80 | ' assertNotEqual() 81 | t.assertEqual(ExpectedMessage, tc.ErrorMessage) 82 | End Sub 83 | 84 | Sub assertNotEqualNoMessageForNotEquals(t as object, val1 as object, val2 as object) 85 | fixture = brstNewTestFixture("", "", "", "") 86 | tc = brstNewTestCase(fixture) 87 | tc.ErrorMessage = "" 88 | tc.fail = function(msg as string) 89 | m.ErrorMessage = msg 90 | end function 91 | tc.assertNotEqual(val1, val2) 92 | if "" <> tc.ErrorMessage then 93 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 94 | end if 95 | End Sub 96 | 97 | Sub testTestCase_assertEqual_TwoStrings_NotEqual(t as object) 98 | 'Error message for two unequal strings yields expected message 99 | s1 = "This is the first string" 100 | s2 = "This is the second string" 101 | expected_msg = Chr(34) + s1 + Chr(34) + " != " + Chr(34) + s2 + Chr(34) 102 | assertEqualMessageForNotEquals(t, s1, s2, expected_msg) 103 | End Sub 104 | 105 | Sub testTestCase_assertEqual_TwoStrings_AreEqual(t as object) 106 | 'No error message for two equal strings 107 | s1 = "This is the first string" 108 | s2 = "This is the first string" 109 | assertEqualMessageForEquals(t, s1, s2) 110 | End Sub 111 | 112 | Sub testTestCase_assertNotEqual_TwoStrings_AreEqual(t as object) 113 | 'Equal string values produces expected error message 114 | s1 = "This is the first string" 115 | s2 = "This is the first string" 116 | expected_msg = Chr(34) + s1 + Chr(34) + " == " + Chr(34) + s2 + Chr(34) 117 | assertNotEqualMessageForEquals(t, s1, s2, expected_msg) 118 | End Sub 119 | 120 | Sub testTestCase_assertNotEqual_TwoStrings_NotEqual(t as object) 121 | 'No error message for two different strings 122 | s1 = "This is the first string" 123 | s2 = "This is the second string" 124 | assertNotEqualNoMessageForNotEquals(t, s1, s2) 125 | End Sub 126 | 127 | Sub testTestCase_assertEqual_TwoFloats_NotEqual(t as object) 128 | 'Unequal float values produce expected error message 129 | f1 = 3.14 130 | f2 = 3.15 131 | expected_msg = Str(f1) + " !=" + Str(f2) 132 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 133 | assertEqualMessageForNotEquals(t, f1, f2, expected_msg) 134 | End Sub 135 | 136 | Sub testTestCase_assertEqual_TwoFloats_AreEqual(t as object) 137 | 'No error message for two equal floats 138 | f1 = 3.14 139 | f2 = 3.14 140 | assertEqualMessageForEquals(t, f1, f2) 141 | End Sub 142 | 143 | Sub testTestCase_assertNotEqual_TwoFloats_AreEqual(t as object) 144 | 'Equal float values produces expected error message 145 | f1 = 3.14 146 | f2 = 3.14 147 | expected_msg = Str(f1) + " ==" + Str(f2) 148 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 149 | assertNotEqualMessageForEquals(t, f1, f2, expected_msg) 150 | End Sub 151 | 152 | Sub testTestCase_assertNotEqual_TwoFloats_NotEqual(t as object) 153 | 'No error message for two different floats 154 | f1 = 3.14 155 | f2 = 3.15 156 | assertNotEqualNoMessageForNotEquals(t, f1, f2) 157 | End Sub 158 | 159 | Sub testTestCase_assertEqual_TwoDoubles_NotEqual(t as object) 160 | 'Unequal double values produce expected error message 161 | f1 = 3.14# 162 | f2 = 3.15# 163 | expected_msg = Str(f1) + " !=" + Str(f2) 164 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 165 | assertEqualMessageForNotEquals(t, f1, f2, expected_msg) 166 | End Sub 167 | 168 | Sub testTestCase_assertEqual_TwoDoubles_AreEqual(t as object) 169 | 'No error message for two equal doubles 170 | f1 = 3.14# 171 | f2 = 3.14# 172 | assertEqualMessageForEquals(t, f1, f2) 173 | End Sub 174 | 175 | Sub testTestCase_assertNotEqual_TwoDoubles_AreEqual(t as object) 176 | 'Equal double values produces expected error message 177 | f1 = 3.14# 178 | f2 = 3.14# 179 | expected_msg = Str(f1) + " ==" + Str(f2) 180 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 181 | assertNotEqualMessageForEquals(t, f1, f2, expected_msg) 182 | End Sub 183 | 184 | Sub testTestCase_assertNotEqual_TwoDoubles_NotEqual(t as object) 185 | 'No error message for two different doubles 186 | f1 = 3.14# 187 | f2 = 3.15# 188 | assertNotEqualNoMessageForNotEquals(t, f1, f2) 189 | End Sub 190 | 191 | Sub testTestCase_assertEqual_TwoInts_NotEqual(t as object) 192 | 'Unequal integer values produce expected error message 193 | i1 = 3 194 | i2 = 4 195 | expected_msg = Stri(i1) + " !=" + Stri(i2) 196 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 197 | assertEqualMessageForNotEquals(t, i1, i2, expected_msg) 198 | End Sub 199 | 200 | Sub testTestCase_assertEqual_TwoInts_AreEqual(t as object) 201 | 'No error message for two equal integer values 202 | i1 = 3 203 | i2 = 3 204 | assertEqualMessageForEquals(t, i1, i2) 205 | End Sub 206 | 207 | Sub testTestCase_assertNotEqual_TwoInts_AreEqual(t as object) 208 | 'Equal integer values produces expected error message 209 | i1 = 3 210 | i2 = 3 211 | expected_msg = Str(i1) + " ==" + Str(i2) 212 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 213 | assertNotEqualMessageForEquals(t, i1, i2, expected_msg) 214 | End Sub 215 | 216 | Sub testTestCase_assertNotEqual_TwoInts_NotEqual(t as object) 217 | 'No error message for two different integers 218 | i1 = 3 219 | i2 = 4 220 | assertNotEqualNoMessageForNotEquals(t, i1, i2) 221 | End Sub 222 | 223 | Sub testTestCase_assertEqual_TwoLongIntegers_NotEqual(t as object) 224 | 'Unequal long integer values produce expected error message 225 | i1 = 3& 226 | i2 = 4& 227 | expected_msg = Stri(i1) + " !=" + Stri(i2) 228 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 229 | assertEqualMessageForNotEquals(t, i1, i2, expected_msg) 230 | End Sub 231 | 232 | Sub testTestCase_assertEqual_TwoLongIntegers_AreEqual(t as object) 233 | 'No error message for two equal long integer values 234 | i1 = 3& 235 | i2 = 3& 236 | assertEqualMessageForEquals(t, i1, i2) 237 | End Sub 238 | 239 | Sub testTestCase_assertNotEqual_TwoLongIntegers_AreEqual(t as object) 240 | 'Equal long integer values produces expected error message 241 | i1 = 3& 242 | i2 = 3& 243 | expected_msg = Str(i1) + " ==" + Str(i2) 244 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 245 | assertNotEqualMessageForEquals(t, i1, i2, expected_msg) 246 | End Sub 247 | 248 | Sub testTestCase_assertNotEqual_TwoLongIntegers_NotEqual(t as object) 249 | 'No error message for two different long integers 250 | i1 = 3& 251 | i2 = 4& 252 | assertNotEqualNoMessageForNotEquals(t, i1, i2) 253 | End Sub 254 | 255 | Sub testTestCase_assertEqual_StrAndInt(t as object) 256 | 'String and Int values yield expected error message 257 | v1 = "Foo Bar" 258 | v2 = 3 259 | expected_msg = Chr(34) + v1 + Chr(34) + " !=" + Str(v2) 260 | assertEqualMessageForNotEquals(t, v1, v2, expected_msg) 261 | End Sub 262 | 263 | Sub testTestCase_assertEqual_StrAndFloat(t as object) 264 | 'String and Float values yield expected error message 265 | v1 = "Foo Bar" 266 | v2 = 3.14 267 | expected_msg = Chr(34) + v1 + Chr(34) + " !=" + Str(v2) 268 | assertEqualMessageForNotEquals(t, v1, v2, expected_msg) 269 | End Sub 270 | 271 | Sub testTestCase_assertEqual_StrAndDouble(t as object) 272 | 'String and Double values yield expected error message 273 | v1 = "Foo Bar" 274 | v2 = 3.14# 275 | expected_msg = Chr(34) + v1 + Chr(34) + " !=" + Str(v2) 276 | assertEqualMessageForNotEquals(t, v1, v2, expected_msg) 277 | End Sub 278 | 279 | Sub testTestCase_assertEqual_StrAndLongInteger(t as object) 280 | 'String and Int values yield expected error message 281 | v1 = "Foo Bar" 282 | v2 = 3& 283 | expected_msg = Chr(34) + v1 + Chr(34) + " !=" + Str(v2) 284 | assertEqualMessageForNotEquals(t, v1, v2, expected_msg) 285 | End Sub 286 | 287 | Sub testTestCase_assertEqual_IntAndStr(t as object) 288 | 'Int and string values yield expected error message 289 | v1 = 3 290 | v2 = "Foo Bar" 291 | expected_msg = Str(v1) + " != " + Chr(34) + v2 + Chr(34) 292 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 293 | assertEqualMessageForNotEquals(t, v1, v2, expected_msg) 294 | End Sub 295 | 296 | Sub testTestCase_assertEqual_FloatAndStr(t as object) 297 | 'Float and string values yield expected error message 298 | v1 = 3.14 299 | v2 = "Foo Bar" 300 | expected_msg = Str(v1) + " != " + Chr(34) + v2 + Chr(34) 301 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 302 | assertEqualMessageForNotEquals(t, v1, v2, expected_msg) 303 | End Sub 304 | 305 | Sub testTestCase_assertEqual_DoubleAndStr(t as object) 306 | 'Double and string values yield expected error message 307 | v1 = 3.14# 308 | v2 = "Foo Bar" 309 | expected_msg = Str(v1) + " != " + Chr(34) + v2 + Chr(34) 310 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 311 | assertEqualMessageForNotEquals(t, v1, v2, expected_msg) 312 | End Sub 313 | 314 | Sub testTestCase_assertEqual_LongIntegerAndStr(t as object) 315 | 'LongInteger and string values yield expected error message 316 | v1 = 3& 317 | v2 = "Foo Bar" 318 | expected_msg = Str(v1) + " != " + Chr(34) + v2 + Chr(34) 319 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 320 | assertEqualMessageForNotEquals(t, v1, v2, expected_msg) 321 | End Sub 322 | 323 | Sub testTestCase_assertEqual_FloatAndInt(t as object) 324 | 'Equivalent float and int values should successfully compare 325 | f = 3.0 326 | i = 3 327 | assertEqualMessageForEquals(t, f, i) 328 | End Sub 329 | 330 | Sub testTestCase_assertEqual_DoubleAndInt(t as object) 331 | 'Equivalent double and int values should successfully compare 332 | d = 3.0# 333 | i = 3 334 | assertEqualMessageForEquals(t, d, i) 335 | End Sub 336 | 337 | Sub testTestCase_assertEqual_LongIntegerAndInt(t as object) 338 | 'Equivalent long integer and int values should successfully compare 339 | l = 3& 340 | i = 3 341 | assertEqualMessageForEquals(t, l, i) 342 | End Sub 343 | 344 | Sub testTestCase_assertEqual_IntAndFloat(t as object) 345 | 'Equivalent int and float values should successfully compare 346 | f = 3.0 347 | i = 3 348 | assertEqualMessageForEquals(t, i, f) 349 | End Sub 350 | 351 | Sub testTestCase_assertEqual_DoubleAndFloat(t as object) 352 | 'Equivalent double and float values should successfully compare 353 | f = 3.0 354 | d = 3.0# 355 | assertEqualMessageForEquals(t, d, f) 356 | End Sub 357 | 358 | Sub testTestCase_assertEqual_LongIntegerAndFloat(t as object) 359 | 'Equivalent long integer and float values should successfully compare 360 | f = 3.0 361 | l = 3& 362 | assertEqualMessageForEquals(t, l, f) 363 | End Sub 364 | 365 | Sub testTestCase_assertEqual_IntAndDouble(t as object) 366 | 'Equivalent int and double values should successfully compare 367 | d = 3.0 368 | i = 3 369 | assertEqualMessageForEquals(t, i, d) 370 | End Sub 371 | 372 | Sub testTestCase_assertEqual_FloatAndDouble(t as object) 373 | 'Equivalent float and double values should successfully compare 374 | d = 3.0# 375 | f = 3.0 376 | assertEqualMessageForEquals(t, f, d) 377 | End Sub 378 | 379 | Sub testTestCase_assertEqual_LongIntegerAndDouble(t as object) 380 | 'Equivalent long integer and double values should successfully compare 381 | d = 3.0# 382 | l = 3& 383 | assertEqualMessageForEquals(t, l, d) 384 | End Sub 385 | 386 | Sub testTestCase_assertEqual_IntAndLongInteger(t as object) 387 | 'Equivalent int and long integer values should successfully compare 388 | l = 3& 389 | i = 3 390 | assertEqualMessageForEquals(t, i, l) 391 | End Sub 392 | 393 | Sub testTestCase_assertEqual_FloatAndLongInteger(t as object) 394 | 'Equivalent float and long integer values should successfully compare 395 | l = 3& 396 | f = 3.0 397 | assertEqualMessageForEquals(t, f, l) 398 | End Sub 399 | 400 | Sub testTestCase_assertEqual_DoubleAndLongInteger(t as object) 401 | 'Equivalent double and long integer values should successfully compare 402 | l = 3& 403 | d = 3.0# 404 | assertEqualMessageForEquals(t, d, l) 405 | End Sub 406 | 407 | Sub testTestCase_assertNotEqual_StrAndInt(t as object) 408 | 'No error message for string and int value 409 | 'assertNotEqual() should handle two different types 410 | v1 = "foo bar" 411 | v2 = 3 412 | assertNotEqualNoMessageForNotEquals(t, v1, v2) 413 | End Sub 414 | 415 | Sub testTestCase_assertNotEqual_StrAndFloat(t as object) 416 | 'No error message for string and float value 417 | 'assertNotEqual() should handle two different types 418 | v1 = "foo bar" 419 | v2 = 3.14 420 | assertNotEqualNoMessageForNotEquals(t, v1, v2) 421 | End Sub 422 | 423 | Sub testTestCase_assertNotEqual_StrAndDouble(t as object) 424 | 'No error message for string and double value 425 | 'assertNotEqual() should handle two different types 426 | v1 = "foo bar" 427 | v2 = 3.14# 428 | assertNotEqualNoMessageForNotEquals(t, v1, v2) 429 | End Sub 430 | 431 | Sub testTestCase_assertNotEqual_StrAndLongInteger(t as object) 432 | 'No error message for string and long integer value 433 | 'assertNotEqual() should handle two different types 434 | v1 = "foo bar" 435 | v2 = 3& 436 | assertNotEqualNoMessageForNotEquals(t, v1, v2) 437 | End Sub 438 | 439 | Sub testTestCase_assertNotEqual_FloatAndInt(t as object) 440 | 'Equivalent float and int values should cause error 441 | f = 3.0 442 | i = 3 443 | expected_msg = Str(f) + " ==" + Str(i) 444 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 445 | assertNotEqualMessageForEquals(t, f, i, expected_msg) 446 | End Sub 447 | 448 | Sub testTestCase_assertNotEqual_DoubleAndInt(t as object) 449 | 'Equivalent double and int values should cause error 450 | d = 3.0# 451 | i = 3 452 | expected_msg = Str(d) + " ==" + Str(i) 453 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 454 | assertNotEqualMessageForEquals(t, d, i, expected_msg) 455 | End Sub 456 | 457 | Sub testTestCase_assertNotEqual_LongIntegerAndInt(t as object) 458 | 'Equivalent long integer and int values should cause error 459 | l = 3& 460 | i = 3 461 | expected_msg = Str(l) + " ==" + Str(i) 462 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 463 | assertNotEqualMessageForEquals(t, l, i, expected_msg) 464 | End Sub 465 | 466 | Sub testTestCase_assertNotEqual_IntAndFloat(t as object) 467 | 'Equivalent int and float values should cause error 468 | i = 3 469 | f = 3.0 470 | expected_msg = Str(i) + " ==" + Str(f) 471 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 472 | assertNotEqualMessageForEquals(t, i, f, expected_msg) 473 | End Sub 474 | 475 | Sub testTestCase_assertNotEqual_DoubleAndFloat(t as object) 476 | 'Equivalent double and float values should cause error 477 | d = 3.0 478 | f = 3.0 479 | expected_msg = Str(d) + " ==" + Str(f) 480 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 481 | assertNotEqualMessageForEquals(t, d, f, expected_msg) 482 | End Sub 483 | 484 | Sub testTestCase_assertNotEqual_LongIntegerAndFloat(t as object) 485 | 'Equivalent long integer and float values should cause error 486 | l = 3& 487 | f = 3.0 488 | expected_msg = Str(l) + " ==" + Str(f) 489 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 490 | assertNotEqualMessageForEquals(t, l, f, expected_msg) 491 | End Sub 492 | 493 | Sub testTestCase_assertNotEqual_IntAndDouble(t as object) 494 | 'Equivalent int and double values should cause error 495 | i = 3 496 | d = 3.0# 497 | expected_msg = Str(i) + " ==" + Str(d) 498 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 499 | assertNotEqualMessageForEquals(t, i, d, expected_msg) 500 | End Sub 501 | 502 | Sub testTestCase_assertNotEqual_FloatAndDouble(t as object) 503 | 'Equivalent float and double values should cause error 504 | f = 3.0 505 | d = 3.0# 506 | expected_msg = Str(d) + " ==" + Str(f) 507 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 508 | assertNotEqualMessageForEquals(t, f, d, expected_msg) 509 | End Sub 510 | 511 | Sub testTestCase_assertNotEqual_LongIntegerAndDouble(t as object) 512 | 'Equivalent long integer and double values should cause error 513 | l = 3& 514 | d = 3.0# 515 | expected_msg = Str(l) + " ==" + Str(d) 516 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 517 | assertNotEqualMessageForEquals(t, l, d, expected_msg) 518 | End Sub 519 | 520 | Sub testTestCase_assertNotEqual_IntAndLongInteger(t as object) 521 | 'Equivalent int and long integer values should cause error 522 | i = 3 523 | l = 3& 524 | expected_msg = Str(i) + " ==" + Str(l) 525 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 526 | assertNotEqualMessageForEquals(t, i, l, expected_msg) 527 | End Sub 528 | 529 | Sub testTestCase_assertNotEqual_FloatAndLongInteger(t as object) 530 | 'Equivalent float and long integer values should cause error 531 | f = 3.0 532 | l = 3& 533 | expected_msg = Str(f) + " ==" + Str(l) 534 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 535 | assertNotEqualMessageForEquals(t, f, l, expected_msg) 536 | End Sub 537 | 538 | Sub testTestCase_assertNotEqual_DoubleAndLongInteger(t as object) 539 | 'Equivalent double and long integer values should cause error 540 | d = 3.0# 541 | l = 3& 542 | expected_msg = Str(d) + " ==" + Str(l) 543 | expected_msg = Right(expected_msg, Len(expected_msg) - 1) ' Remove sign padding 544 | assertNotEqualMessageForEquals(t, d, l, expected_msg) 545 | End Sub 546 | 547 | Sub testTestCase_assertInvalid_Invalid(t as object) 548 | fixture = brstNewTestFixture("", "", "", "") 549 | tc = brstNewTestCase(fixture) 550 | tc.ErrorMessage = "" 551 | tc.fail = function(msg as string) 552 | m.ErrorMessage = msg 553 | end function 554 | tc.assertInvalid(Invalid) 555 | if "" <> tc.ErrorMessage then 556 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 557 | end if 558 | End Sub 559 | 560 | Sub testTestCase_assertInvalid_MissingProperty(t as object) 561 | fixture = brstNewTestFixture("", "", "", "") 562 | tc = brstNewTestCase(fixture) 563 | tc.ErrorMessage = "" 564 | tc.fail = function(msg as string) 565 | m.ErrorMessage = msg 566 | end function 567 | o = {} 568 | tc.assertInvalid(o.missing) 569 | if "" <> tc.ErrorMessage then 570 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 571 | end if 572 | End Sub 573 | 574 | Sub testTestCase_assertInvalid_Integer(t as object) 575 | fixture = brstNewTestFixture("", "", "", "") 576 | tc = brstNewTestCase(fixture) 577 | tc.ErrorMessage = "" 578 | tc.fail = function(msg as string) 579 | m.ErrorMessage = msg 580 | end function 581 | tc.assertInvalid(0) 582 | if "0 <> Invalid" <> tc.ErrorMessage then 583 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 584 | end if 585 | End Sub 586 | 587 | Sub testTestCase_assertInvalid_Float(t as object) 588 | fixture = brstNewTestFixture("", "", "", "") 589 | tc = brstNewTestCase(fixture) 590 | tc.ErrorMessage = "" 591 | tc.fail = function(msg as string) 592 | m.ErrorMessage = msg 593 | end function 594 | tc.assertInvalid(0.0) 595 | if "0 <> Invalid" <> tc.ErrorMessage then 596 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 597 | end if 598 | End Sub 599 | 600 | Sub testTestCase_assertInvalid_Double(t as object) 601 | fixture = brstNewTestFixture("", "", "", "") 602 | tc = brstNewTestCase(fixture) 603 | tc.ErrorMessage = "" 604 | tc.fail = function(msg as string) 605 | m.ErrorMessage = msg 606 | end function 607 | tc.assertInvalid(0.0#) 608 | if "0 <> Invalid" <> tc.ErrorMessage then 609 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 610 | end if 611 | End Sub 612 | 613 | Sub testTestCase_assertInvalid_LongInteger(t as object) 614 | fixture = brstNewTestFixture("", "", "", "") 615 | tc = brstNewTestCase(fixture) 616 | tc.ErrorMessage = "" 617 | tc.fail = function(msg as string) 618 | m.ErrorMessage = msg 619 | end function 620 | tc.assertInvalid(0&) 621 | if "0 <> Invalid" <> tc.ErrorMessage then 622 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 623 | end if 624 | End Sub 625 | 626 | Sub testTestCase_assertInvalid_Object(t as object) 627 | fixture = brstNewTestFixture("", "", "", "") 628 | tc = brstNewTestCase(fixture) 629 | tc.ErrorMessage = "" 630 | tc.fail = function(msg as string) 631 | m.ErrorMessage = msg 632 | end function 633 | o = {} 634 | tc.assertInvalid({}) 635 | if "{ } <> Invalid" <> tc.ErrorMessage then 636 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 637 | end if 638 | End Sub 639 | 640 | Sub testTestCase_assertInvalid_string(t as object) 641 | fixture = brstNewTestFixture("", "", "", "") 642 | tc = brstNewTestCase(fixture) 643 | tc.ErrorMessage = "" 644 | tc.fail = function(msg as string) 645 | m.ErrorMessage = msg 646 | end function 647 | o = {} 648 | tc.assertInvalid("") 649 | if Chr(34) + Chr(34) + " <> Invalid" <> tc.ErrorMessage then 650 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 651 | end if 652 | End Sub 653 | 654 | Sub testTestCase_assertNotInvalid_Invalid(t as object) 655 | fixture = brstNewTestFixture("", "", "", "") 656 | tc = brstNewTestCase(fixture) 657 | tc.ErrorMessage = "" 658 | tc.fail = function(msg as string) 659 | m.ErrorMessage = msg 660 | end function 661 | tc.assertNotInvalid(Invalid) 662 | if "roInvalid = Invalid" <> tc.ErrorMessage then 663 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 664 | end if 665 | End Sub 666 | 667 | Sub testTestCase_assertNotInvalid_MissingProperty(t as object) 668 | fixture = brstNewTestFixture("", "", "", "") 669 | tc = brstNewTestCase(fixture) 670 | tc.ErrorMessage = "" 671 | tc.fail = function(msg as string) 672 | m.ErrorMessage = msg 673 | end function 674 | o = {} 675 | tc.assertNotInvalid(o.missing) 676 | if "roInvalid = Invalid" <> tc.ErrorMessage then 677 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 678 | end if 679 | End Sub 680 | 681 | Sub testTestCase_assertNotInvalid_Integer(t as object) 682 | fixture = brstNewTestFixture("", "", "", "") 683 | tc = brstNewTestCase(fixture) 684 | tc.ErrorMessage = "" 685 | tc.fail = function(msg as string) 686 | m.ErrorMessage = msg 687 | end function 688 | tc.assertNotInvalid(0) 689 | if "" <> tc.ErrorMessage then 690 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 691 | end if 692 | End Sub 693 | 694 | Sub testTestCase_assertNotInvalid_Float(t as object) 695 | fixture = brstNewTestFixture("", "", "", "") 696 | tc = brstNewTestCase(fixture) 697 | tc.ErrorMessage = "" 698 | tc.fail = function(msg as string) 699 | m.ErrorMessage = msg 700 | end function 701 | tc.assertNotInvalid(0.0) 702 | if "" <> tc.ErrorMessage then 703 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 704 | end if 705 | End Sub 706 | 707 | Sub testTestCase_assertNotInvalid_Double(t as object) 708 | fixture = brstNewTestFixture("", "", "", "") 709 | tc = brstNewTestCase(fixture) 710 | tc.ErrorMessage = "" 711 | tc.fail = function(msg as string) 712 | m.ErrorMessage = msg 713 | end function 714 | tc.assertNotInvalid(0.0#) 715 | if "" <> tc.ErrorMessage then 716 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 717 | end if 718 | End Sub 719 | 720 | Sub testTestCase_assertNotInvalid_LongInteger(t as object) 721 | fixture = brstNewTestFixture("", "", "", "") 722 | tc = brstNewTestCase(fixture) 723 | tc.ErrorMessage = "" 724 | tc.fail = function(msg as string) 725 | m.ErrorMessage = msg 726 | end function 727 | tc.assertNotInvalid(0&) 728 | if "" <> tc.ErrorMessage then 729 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 730 | end if 731 | End Sub 732 | 733 | Sub testTestCase_assertNotInvalid_Object(t as object) 734 | fixture = brstNewTestFixture("", "", "", "") 735 | tc = brstNewTestCase(fixture) 736 | tc.ErrorMessage = "" 737 | tc.fail = function(msg as string) 738 | m.ErrorMessage = msg 739 | end function 740 | o = {} 741 | tc.assertNotInvalid({}) 742 | if "" <> tc.ErrorMessage then 743 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 744 | end if 745 | End Sub 746 | 747 | Sub testTestCase_assertNotInvalid_string(t as object) 748 | fixture = brstNewTestFixture("", "", "", "") 749 | tc = brstNewTestCase(fixture) 750 | tc.ErrorMessage = "" 751 | tc.fail = function(msg as string) 752 | m.ErrorMessage = msg 753 | end function 754 | o = {} 755 | tc.assertNotInvalid("") 756 | if "" <> tc.ErrorMessage then 757 | t.fail("Unexpected Error message: " + tc.ErrorMessage) 758 | end if 759 | End Sub 760 | 761 | Sub testTestCase_ValueTostring_Integer(t as object) 762 | 'Can convert an integer using the ValueToString method 763 | i = 4 764 | expected = "4" 765 | actual = t.ValueToString(i) 766 | t.assertEqual(expected, actual) 767 | End Sub 768 | 769 | Sub testTestCase_ValueTostring_Integer_Negative(t as object) 770 | 'Can convert a negative integer using the ValueToString method 771 | i = -4 772 | expected = "-4" 773 | actual = t.ValueToString(i) 774 | t.assertEqual(expected, actual) 775 | End Sub 776 | 777 | Sub testTestCase_ValueTostring_LongInteger(t as object) 778 | 'Can convert an integer using the ValueToString method 779 | i = 4& 780 | expected = "4" 781 | actual = t.ValueToString(i) 782 | t.assertEqual(expected, actual) 783 | End Sub 784 | 785 | Sub testTestCase_ValueTostring_LongInteger_Negative(t as object) 786 | 'Can convert a negative integer using the ValueToString method 787 | i = -4& 788 | expected = "-4" 789 | actual = t.ValueToString(i) 790 | t.assertEqual(expected, actual) 791 | End Sub 792 | 793 | Sub testTestCase_ValueTostring_Float(t as Object) 794 | 'Can convert a float value using ValueToString method 795 | f = 4.3 796 | expected = "4.3" 797 | actual = t.ValueToString(f) 798 | t.assertEqual(expected, actual) 799 | End Sub 800 | 801 | Sub testTestCase_ValueTostring_Float_Negative(t as Object) 802 | 'Can convert a negative float value using ValueToString method 803 | f = -4.3 804 | expected = "-4.3" 805 | actual = t.ValueToString(f) 806 | t.assertEqual(expected, actual) 807 | End Sub 808 | 809 | Sub testTestCase_ValueTostring_Double(t as Object) 810 | 'Can convert a float value using ValueToString method 811 | f = 4.3# 812 | expected = "4.3" 813 | actual = t.ValueToString(f) 814 | t.assertEqual(expected, actual) 815 | End Sub 816 | 817 | Sub testTestCase_ValueTostring_Double_Negative(t as Object) 818 | 'Can convert a negative float value using ValueToString method 819 | f = -4.3# 820 | expected = "-4.3" 821 | actual = t.ValueToString(f) 822 | t.assertEqual(expected, actual) 823 | End Sub 824 | 825 | Sub testTestCase_ValueTostring_String(t as object) 826 | 'Can pass a string to the ValueToString method 827 | s = "Foo Bar" 828 | expected = Chr(34) + "Foo Bar" + Chr(34) 829 | actual = t.ValueToString(s) 830 | t.assertEqual(expected, actual) 831 | End Sub 832 | 833 | Sub testTestCase_ValueTostring_Bool_True(t as object) 834 | 'Convert a True value w/the ValueToString method 835 | b = True 836 | expected = "True" 837 | actual = t.ValueToString(b) 838 | t.assertEqual(expected, actual) 839 | End Sub 840 | 841 | Sub testTestCase_ValueTostring_Bool_False(t as object) 842 | 'Convert a False value w/the ValueToString method 843 | b = False 844 | expected = "False" 845 | actual = t.ValueToString(b) 846 | t.assertEqual(expected, actual) 847 | End Sub 848 | 849 | Sub testTestCase_ValueTostring_roInvalid(t as Object) 850 | 'Convert the roInvalid constant to a string 851 | invalid_val = CreateObject("roInvalid") 852 | expected = "roInvalid" 853 | actual = t.ValueToString(invalid_val) 854 | t.assertEqual(expected, actual) 855 | End Sub 856 | 857 | Sub testTestCase_ValueTostring_roAssociativeArray_Empty(t as object) 858 | 'Appropriate converstion of an empty associative array 859 | aa = {} 860 | expected = "{ }" 861 | actual = t.ValueToString(aa) 862 | t.assertEqual(expected, actual) 863 | End Sub 864 | 865 | Sub testTestCase_ValueTostring_roAssociativeArray_HandlesNumerics(t as object) 866 | 'Int values in an associative array are converted 867 | aa = {foo1:1, foo2:2.0, foo3: 3.0#, foo4: 4&} 868 | expected = "{ foo1 : 1, foo2 : 2, foo3 : 3, foo4 : 4 }" 869 | actual = t.ValueToString(aa) 870 | t.assertEqual(expected, actual) 871 | End Sub 872 | 873 | Sub testTestCase_ValueTostring_roAssociativeArray_NestedWithAnotherAA(t as object) 874 | 'roAssociativeArray with value that is another roAssociativeArray 875 | aa = {Foo:1, Bar:{Baz:4} } 876 | expected = "{ bar : { baz : 4 }, foo : 1 }" 877 | actual = t.ValueToString(aa) 878 | t.assertEqual(expected, actual) 879 | End Sub 880 | 881 | Sub testTestCase_ValueTostring_roList_Empty(t as Object) 882 | 'Convert an empty roList to a string w/the ValueToString method 883 | l = CreateObject("roList") 884 | expected = "->/" 885 | actual = t.ValueToString(l) 886 | t.assertEqual(expected, actual) 887 | End Sub 888 | 889 | Sub testTestCase_ValueTostring_roList_Numerics(t as Object) 890 | 'Convert roList of ints w/the ValueToString method 891 | l = CreateObject("roList") 892 | l.AddTail(1) 893 | l.AddTail(2.0) 894 | l.AddTail(3.0#) 895 | l.AddTail(4&) 896 | expected = "1 -> 2 -> 3 -> 4 -> /" 897 | actual = t.ValueToString(l) 898 | t.assertEqual(expected, actual) 899 | End Sub 900 | 901 | Sub testTestCase_ValueTostring_roArray_Empty(t as object) 902 | 'Proper conversion of an empty roArray object 903 | array = [] 904 | expected = "[ ]" 905 | actual = t.ValueToString(array) 906 | t.assertEqual(expected, actual) 907 | End Sub 908 | 909 | Sub testTestCase_ValueTostring_roArray_Numerics(t as object) 910 | 'Conversion of roArray with int entries 911 | array = [1,2.0,3.0#,4&] 912 | expected = "[ 1, 2, 3, 4 ]" 913 | actual = t.ValueToString(array) 914 | t.assertEqual(expected, actual) 915 | End Sub 916 | 917 | Sub testTestCase_ValueTostring_roArray_NestedArrays(t as object) 918 | 'Convert an array which has another array as one of its elements 919 | array = [1,[2,3],4] 920 | expected = "[ 1, [ 2, 3 ], 4 ]" 921 | actual = t.ValueToString(array) 922 | t.assertEqual(expected, actual) 923 | End Sub 924 | 925 | Sub testTestCase_EqValues_Integers_AreEqual(t as object) 926 | 'True if two integer values are equal 927 | x = 4 928 | y = 4 929 | result = t.eqValues(x, y) 930 | t.assertTrue(result) 931 | End Sub 932 | 933 | Sub testTestCase_EqValues_Integers_NotEqual(t as object) 934 | 'False if two integer values are not equal 935 | x = 4 936 | y = 5 937 | result = t.eqValues(x, y) 938 | t.assertFalse(result) 939 | End Sub 940 | 941 | Sub testTestCase_EqValues_LongIntegers_AreEqual(t as object) 942 | 'True if two long integer values are equal 943 | x = 4& 944 | y = 4& 945 | result = t.eqValues(x, y) 946 | t.assertTrue(result) 947 | End Sub 948 | 949 | Sub testTestCase_EqValues_LongIntegers_NotEqual(t as object) 950 | 'False if two long integer values are not equal 951 | x = 4& 952 | y = 5& 953 | result = t.eqValues(x, y) 954 | t.assertFalse(result) 955 | End Sub 956 | 957 | Sub testTestCase_EqValues_Floats_AreEqual(t as object) 958 | 'True if two float values are equal 959 | x = 4.5 960 | y = 4.5 961 | result = t.eqValues(x, y) 962 | t.assertTrue(result) 963 | End Sub 964 | 965 | Sub testTestCase_EqValues_Floats_NotEqual(t as object) 966 | 'False if two float values are not equal 967 | x = 4.5 968 | y = 5.5 969 | result = t.eqValues(x, y) 970 | t.assertFalse(result) 971 | End Sub 972 | 973 | Sub testTestCase_EqValues_Doubles_AreEqual(t as object) 974 | 'True if two doubles values are equal 975 | x = 4.5# 976 | y = 4.5# 977 | result = t.eqValues(x, y) 978 | t.assertTrue(result) 979 | End Sub 980 | 981 | Sub testTestCase_EqValues_Doubles_NotEqual(t as object) 982 | 'False if two doubles values are not equal 983 | x = 4.5# 984 | y = 5.5# 985 | result = t.eqValues(x, y) 986 | t.assertFalse(result) 987 | End Sub 988 | 989 | Sub testTestCase_EqValues_Equal_FloatAndIntValues(t as object) 990 | 'True if equal float and int values are supplied 991 | x = 3.0 992 | y = 3 993 | result = t.eqValues(x, y) 994 | t.assertTrue(result) 995 | End Sub 996 | 997 | Sub testTestCase_EqValues_Equal_DoubleAndIntValues(t as object) 998 | 'True if equal double and int values are supplied 999 | x = 3.0# 1000 | y = 3 1001 | result = t.eqValues(x, y) 1002 | t.assertTrue(result) 1003 | End Sub 1004 | 1005 | Sub testTestCase_EqValues_Equal_LongIntegerAndIntValues(t as object) 1006 | 'True if equal long integer and int values are supplied 1007 | x = 3& 1008 | y = 3 1009 | result = t.eqValues(x, y) 1010 | t.assertTrue(result) 1011 | End Sub 1012 | 1013 | Sub testTestCase_EqValues_Equal_IntAndFloatValues(t as object) 1014 | 'True if equal int and float values are supplied 1015 | x = 3 1016 | y = 3.0 1017 | result = t.eqValues(x, y) 1018 | t.assertTrue(result) 1019 | End Sub 1020 | 1021 | Sub testTestCase_EqValues_Equal_DoubleAndFloatValues(t as object) 1022 | 'True if equal double and float values are supplied 1023 | x = 3.0# 1024 | y = 3.0 1025 | result = t.eqValues(x, y) 1026 | t.assertTrue(result) 1027 | End Sub 1028 | 1029 | Sub testTestCase_EqValues_Equal_LongIntegerAndFloatValues(t as object) 1030 | 'True if equal long integer and float values are supplied 1031 | x = 3& 1032 | y = 3.0 1033 | result = t.eqValues(x, y) 1034 | t.assertTrue(result) 1035 | End Sub 1036 | 1037 | Sub testTestCase_EqValues_Equal_IntAndDoubleValues(t as object) 1038 | 'True if equal int and double values are supplied 1039 | x = 3 1040 | y = 3.0# 1041 | result = t.eqValues(x, y) 1042 | t.assertTrue(result) 1043 | End Sub 1044 | 1045 | Sub testTestCase_EqValues_Equal_FloatAndDoubleValues(t as object) 1046 | 'True if equal float and double values are supplied 1047 | x = 3.0 1048 | y = 3.0# 1049 | result = t.eqValues(x, y) 1050 | t.assertTrue(result) 1051 | End Sub 1052 | 1053 | Sub testTestCase_EqValues_Equal_LongIntegerAndDoubleValues(t as object) 1054 | 'True if equal long integer and double values are supplied 1055 | x = 3& 1056 | y = 3.0# 1057 | result = t.eqValues(x, y) 1058 | t.assertTrue(result) 1059 | End Sub 1060 | 1061 | Sub testTestCase_EqValues_Equal_IntAndLongIntegerValues(t as object) 1062 | 'True if equal int and double values are supplied 1063 | x = 3 1064 | y = 3& 1065 | result = t.eqValues(x, y) 1066 | t.assertTrue(result) 1067 | End Sub 1068 | 1069 | Sub testTestCase_EqValues_Equal_FloatAndLongIntegerValues(t as object) 1070 | 'True if equal float and double values are supplied 1071 | x = 3.0 1072 | y = 3& 1073 | result = t.eqValues(x, y) 1074 | t.assertTrue(result) 1075 | End Sub 1076 | 1077 | Sub testTestCase_EqValues_Equal_DoubleAndLongIntegerValues(t as object) 1078 | 'True if equal long integer and double values are supplied 1079 | x = 3.0# 1080 | y = 3& 1081 | result = t.eqValues(x, y) 1082 | t.assertTrue(result) 1083 | End Sub 1084 | 1085 | Sub testTestCase_EqValues_Strings_AreEqual(t as object) 1086 | 'True if two string values are equal 1087 | x = "Foo Bar" 1088 | y = "Foo Bar" 1089 | result = t.eqValues(x, y) 1090 | t.assertTrue(result) 1091 | End Sub 1092 | 1093 | Sub testTestCase_EqValues_Strings_NotEqual(t as object) 1094 | 'False if two string values are not equal 1095 | x = "Foo Bar" 1096 | y = "Foo Bar Baz" 1097 | result = t.eqValues(x, y) 1098 | t.assertFalse(result) 1099 | End Sub 1100 | 1101 | Sub testTestCase_EqValues_Bools_AreEqual_True(t as object) 1102 | 'True if two bool values are equal 1103 | x = True 1104 | y = True 1105 | result = t.eqValues(x, y) 1106 | t.assertTrue(result) 1107 | End Sub 1108 | 1109 | Sub testTestCase_EqValues_Bools_AreEqual_False(t as object) 1110 | 'True if two bool values are equal 1111 | x = False 1112 | y = False 1113 | result = t.eqValues(x, y) 1114 | t.assertTrue(result) 1115 | End Sub 1116 | 1117 | Sub testTestCase_EqValues_Bools_NotEqual(t as object) 1118 | 'False if two bool values are not equal 1119 | x = True 1120 | y = False 1121 | result = t.eqValues(x, y) 1122 | t.assertFalse(result) 1123 | End Sub 1124 | 1125 | Sub testTestCase_EqValues_AssocArray_NotEqual_FirstHasMoreKeys(t as object) 1126 | 'False if first of two roAssociativeArrays has more keys 1127 | x = {Foo:1, Bar:2, Baz:4} 1128 | y = {Foo:1, Bar:2} 1129 | result = t.eqValues(x, y) 1130 | t.assertFalse(result) 1131 | End Sub 1132 | 1133 | Sub testTestCase_EqValues_AssocArray_NotEqual_SecondHasMoreKeys(t as object) 1134 | 'False if second of two roAssociativeArrays has more keys 1135 | x = {Foo:1, Bar:2} 1136 | y = {Foo:1, Bar:2, Baz:4} 1137 | result = t.eqValues(x, y) 1138 | t.assertFalse(result) 1139 | End Sub 1140 | 1141 | Sub testTestCase_EqValues_AssocArray_NotEqual_DiffKeysSameCount(t as object) 1142 | 'False if two roAssociativeArrays have the same number keys but different sets of keys 1143 | x = {Foo:1, Bar:2} 1144 | y = {Foo:1, Baz:2} 1145 | result = t.eqValues(x, y) 1146 | t.assertFalse(result) 1147 | End Sub 1148 | 1149 | Sub testTestCase_EqValues_AssocArray_AreEqual(t as object) 1150 | 'True if two roAssociativeArrays have the same keys and point to equal values 1151 | x = {Foo:1, Bar:2} 1152 | y = {Foo:1, Bar:2} 1153 | result = t.eqValues(x, y) 1154 | t.assertTrue(result) 1155 | End Sub 1156 | 1157 | Sub testTestCase_EqValues_AssocArray_NotEqual_DifferentValues(t as object) 1158 | 'False if two roAssociativeArrays have the same keys and point to different values 1159 | x = {Foo:1, Bar:2} 1160 | y = {Foo:1, Bar:5} 1161 | result = t.eqValues(x, y) 1162 | t.assertFalse(result) 1163 | End Sub 1164 | 1165 | sub testTestCase_EqValues_Function_AreEqual(t as object) 1166 | 'True if two function arguments are the same function 1167 | x = Function () 1168 | Return 1 1169 | End Function 1170 | y = x 1171 | result = t.eqValues(x,y) 1172 | t.assertTrue(result) 1173 | End Sub 1174 | 1175 | sub testTestCase_EqValues_Function_AreNotEqual(t as object) 1176 | 'False if two function arguments are not the same function 1177 | x = Function () 1178 | Return 1 1179 | End Function 1180 | y = Function () 1181 | Return 1 1182 | End Function 1183 | result = t.eqValues(x,y) 1184 | t.assertFalse(result) 1185 | End Sub 1186 | 1187 | sub testTestCase_EqValues_FunctionOnObject_AreEqual(t as object) 1188 | 'True if two function arguments from an 'object' are the same function 1189 | x = t.assertEqual 1190 | y = t.assertEqual 1191 | result = t.eqValues(x,y) 1192 | t.assertTrue(result) 1193 | End Sub 1194 | 1195 | sub testTestCase_EqValues_FunctionOnObject_AreNotEqual(t as object) 1196 | 'False if two function arguments from the same 'object' are not the same function 1197 | x = t.assertEqual 1198 | y = t.assertNotEqual 1199 | result = t.eqValues(x,y) 1200 | t.assertFalse(result) 1201 | End Sub 1202 | 1203 | sub testTestCase_EqValues_FunctionOnDifferentObject_AreNotEqual(t as object) 1204 | 'True if two function arguments are from different 'objects' but the 1205 | 'same function 1206 | fixture = brstNewTestFixture("", "", "", "") 1207 | tc = brstNewTestCase(fixture) 1208 | x = t.assertEqual 1209 | y = tc.assertEqual 1210 | result = t.eqValues(x,y) 1211 | t.assertTrue(result) 1212 | End Sub 1213 | 1214 | Sub testTestCase_EqValues_Lists_AreEqual(t as object) 1215 | 'True if two list values are equal 1216 | x = CreateObject("roList") 1217 | x.AddTail(1) 1218 | x.AddTail(2) 1219 | x.AddTail(3) 1220 | y = CreateObject("roList") 1221 | y.AddTail(1) 1222 | y.AddTail(2) 1223 | y.AddTail(3) 1224 | result = t.eqValues(x, y) 1225 | t.assertTrue(result) 1226 | End Sub 1227 | 1228 | Sub testTestCase_EqValues_Lists_NotEqual_DifferentLength(t as object) 1229 | 'False if two list values are not equal due to having a different length 1230 | x = CreateObject("roList") 1231 | x.AddTail(1) 1232 | x.AddTail(2) 1233 | x.AddTail(3) 1234 | y = CreateObject("roList") 1235 | y.AddTail(1) 1236 | y.AddTail(2) 1237 | result = t.eqValues(x, y) 1238 | t.assertFalse(result) 1239 | End Sub 1240 | 1241 | Sub testTestCase_EqValues_Lists_NotEqual_DifferentValues(t as object) 1242 | 'False if two list are the same length, but have different entries 1243 | x = CreateObject("roList") 1244 | x.AddTail(1) 1245 | x.AddTail(2) 1246 | x.AddTail(3) 1247 | y = CreateObject("roList") 1248 | y.AddTail(1) 1249 | y.AddTail(2) 1250 | y.AddTail(12) 1251 | result = t.eqValues(x, y) 1252 | t.assertFalse(result) 1253 | End Sub 1254 | 1255 | Sub testTestCase_EqValues_Array_NotEqual_FirstIsLonger(t as object) 1256 | 'False if the first of two roArrays is longer 1257 | x = [1,2,3] 1258 | y = [1,2] 1259 | result = t.eqValues(x, y) 1260 | t.assertFalse(result) 1261 | End Sub 1262 | 1263 | Sub testTestCase_EqValues_Array_NotEqual_SecondIsLonger(t as object) 1264 | 'False if the second of two roArrays is longer 1265 | x = [1,2] 1266 | y = [1,2,4] 1267 | result = t.eqValues(x, y) 1268 | t.assertFalse(result) 1269 | End Sub 1270 | 1271 | Sub testTestCase_EqValues_Array_NotEqual_SameLength_DifferentValues(t as object) 1272 | 'False if two roArrays are the same length but contain different values 1273 | x = [1,2, 6] 1274 | y = [1,2,4] 1275 | result = t.eqValues(x, y) 1276 | t.assertFalse(result) 1277 | End Sub 1278 | 1279 | Sub testTestCase_EqReference_Node_AreEqual(t as object) 1280 | 'True if both nodes have the same reference -- not just all the same fields 1281 | x = CreateObject("roSGNode", "Node") 1282 | y = x 1283 | result = t.eqValues(x, y) 1284 | t.assertTrue(result) 1285 | End Sub 1286 | 1287 | Sub testTestCase_EqValues_Node_NotEqual_DifferentReference(t as object) 1288 | 'False if two nodes have a different reference, regardless of their field values 1289 | x = CreateObject("roSGNode", "Node") 1290 | y = CreateObject("roSGNode", "Node") 1291 | result = t.eqValues(x, y) 1292 | t.assertFalse(result) 1293 | End Sub 1294 | 1295 | Sub testTestCase_Node_NotEqual_DifferentValues(t as object) 1296 | 'False if two nodes have a different reference, regardless of their field values 1297 | x = CreateObject("roSGNode", "Node") 1298 | x.addFields( {"foo" : "bar"} ) 1299 | y = CreateObject("roSGNode", "Node") 1300 | x.addFields( {"foo" : "baz"} ) 1301 | result = t.eqValues(x, y) 1302 | t.assertFalse(result) 1303 | End Sub 1304 | 1305 | Sub testTestCase_Node_EqReference_CircularRef_Basic(t as object) 1306 | 'True if two nodes have the same reference, even if one contains a reference to itself 1307 | x = CreateObject("roSGNode", "Node") 1308 | x.addFields( {"foo" : x} ) 1309 | result = t.eqValues(x, x) 1310 | t.assertTrue(result) 1311 | End Sub --------------------------------------------------------------------------------