From 67527eb009feeff109979146c81d177ea7ce8b14 Mon Sep 17 00:00:00 2001 From: George Cook Date: Fri, 26 Apr 2019 22:50:42 -0500 Subject: [PATCH] 2.2.0 - adds ability to pass node scope into the test runner, for non-node tests so node-scoped functions/vars can be accessed --- CHANGELOG.md | 14 + VERSION | 2 +- dist/rooibosDist.brs | 11 +- docs/index.md | 30 +- frameworkTests/source/main.brs | 2 +- frameworkTests/source/tests/BasicTests.brs | 17 + .../components/TestsScene.xml | 8 + outRun/.roku-deploy-staging/manifest | 4 + outRun/.roku-deploy-staging/source/main.brs | 57 + .../source/tests/AssertionTests.brs | 660 +++++ .../source/tests/BasicTests.brs | 17 + .../source/tests/ExpectTests.brs | 28 + .../source/tests/ParamsTests.brs | 32 + .../tests/issue_13_afterEach_not_running.brs | 48 + .../tests/issue_15_tests_only_on_groups.brs | 30 + .../tests/issue_5_duplicateGroupNames.brs | 28 + .../source/tests/rooibosDist.brs | 2210 +++++++++++++++++ .../source/tests/testConfig.json | 5 + .../source/tests/testUtils.brs | 8 + .../tests/verifyBeforeEachAfterEach.brs | 100 + package.json | 2 +- .../source/tests/rooibos/rooibosDist.brs | 11 +- src/Rooibos.brs | 8 +- src/Rooibos_TestRunner.brs | 3 +- 24 files changed, 3321 insertions(+), 14 deletions(-) create mode 100644 frameworkTests/source/tests/BasicTests.brs create mode 100644 outRun/.roku-deploy-staging/components/TestsScene.xml create mode 100644 outRun/.roku-deploy-staging/manifest create mode 100644 outRun/.roku-deploy-staging/source/main.brs create mode 100644 outRun/.roku-deploy-staging/source/tests/AssertionTests.brs create mode 100644 outRun/.roku-deploy-staging/source/tests/BasicTests.brs create mode 100644 outRun/.roku-deploy-staging/source/tests/ExpectTests.brs create mode 100644 outRun/.roku-deploy-staging/source/tests/ParamsTests.brs create mode 100644 outRun/.roku-deploy-staging/source/tests/issue_13_afterEach_not_running.brs create mode 100644 outRun/.roku-deploy-staging/source/tests/issue_15_tests_only_on_groups.brs create mode 100644 outRun/.roku-deploy-staging/source/tests/issue_5_duplicateGroupNames.brs create mode 100644 outRun/.roku-deploy-staging/source/tests/rooibosDist.brs create mode 100644 outRun/.roku-deploy-staging/source/tests/testConfig.json create mode 100644 outRun/.roku-deploy-staging/source/tests/testUtils.brs create mode 100644 outRun/.roku-deploy-staging/source/tests/verifyBeforeEachAfterEach.brs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bd3499d..f8524259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Rooibos CHANGELOG +## 2.2.0 + +### Added + + - sets the node property on non-node test suites. This allows you to access the global namespace, in case you are testing mixin methods, or other non-scoped code (i.e. the equivalent of accessing `method` as opposed to `m.method` or `myObject.method`) + +### Changed + +### Deprecated + +### Removed + +### Fixed + ## 2.1.4 ### Added diff --git a/VERSION b/VERSION index 7d2ed7c7..ccbccc3d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1.4 +2.2.0 diff --git a/dist/rooibosDist.brs b/dist/rooibosDist.brs index 25173ce1..9495ed55 100644 --- a/dist/rooibosDist.brs +++ b/dist/rooibosDist.brs @@ -1,19 +1,23 @@ '/** ' * rooibos - simple, flexible, fun brightscript test framework for roku scenegraph apps -' * @version v2.1.4 +' * @version v2.2.0 ' * @link https://github.com/georgejecook/rooibos#readme ' * @license MIT ' */ -function Rooibos__Init(preTestSetup = invalid, testUtilsDecoratorMethodName = invalid, testSceneName = "TestsScene") as void +function Rooibos__Init(preTestSetup = invalid, testUtilsDecoratorMethodName = invalid, testSceneName = invalid, nodeContext = invalid) as void args = {} if createObject("roAPPInfo").IsDev() <> true then ? " not running in dev mode! - rooibos tests only support sideloaded builds - aborting" return end if args.testUtilsDecoratorMethodName = testUtilsDecoratorMethodName + args.nodeContext = nodeContext screen = CreateObject("roSGScreen") m.port = CreateObject("roMessagePort") screen.setMessagePort(m.port) + if testSceneName = invalid + testSceneName = "TestsScene" + end if ? "Starting test using test scene with name TestsScene" ; testSceneName scene = screen.CreateScene(testSceneName) scene.id = "ROOT" @@ -1924,6 +1928,7 @@ end function function RBS_TR_TestRunner(args = {}) as object this = {} this.testScene = args.testScene + this.nodeContext = args.nodeContext fs = CreateObject("roFileSystem") defaultConfig = { logLevel : 1, @@ -2021,7 +2026,7 @@ sub RBS_TR_Run() if (metaTestSuite.hasIgnoredTests) totalStatObj.IgnoredTestNames.push("|-" + metaTestSuite.name) end if - RBS_RT_RunItGroups(metaTestSuite, totalStatObj, m.testUtilsDecoratorMethodName, m.config, m.runtimeConfig) + RBS_RT_RunItGroups(metaTestSuite, totalStatObj, m.testUtilsDecoratorMethodName, m.config, m.runtimeConfig, m.nodeContext) end if skipSuite: end for diff --git a/docs/index.md b/docs/index.md index 7d191de2..9bd0c30b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,6 @@ Simple, mocha-inspired, flexible, fun Brightscript test framework for ROKU apps ## FEATURES - [Easy to integrate](#easy-to-integrate) - - [Compatible with legacy unit testing framework](#compatible-with-legacy-framework) - [Simple, annotation-based, syntax for writing tests](#simple-syntax-for-writing-tests) - [No need for special file names, or method names](#no-need-for-special-file-or-method-names) - [Common TDD methods such as Setup/TearDown/BeforeEach/AfterEach](#common-tdd-methods) @@ -563,7 +562,34 @@ end function Note: The test utils decorator and all of it's dependencies must be visible to the current exeucting test. That means if you have a node test you must include the script file containing the `testCase.testUtils` method, and all other methods it invokes. The sample app contains an example. -Non-node tests should find all methods are automatically in scope +### Accessing global scope + +Non-node tests should find all methods are automatically in scope; however, if you need to access the node scope to test anonymous, or mixin methods, this is also supported. You can make your global scope available by calling `Rooibos__Init` and passing in a reference to your scope, as such + +``` +sub Main(args as dynamic) + if (type(Rooibos__Init) = "Function") then Rooibos__Init(SetupGlobals, "AddTestUtils", invalid, m) +end sub + +``` + +You can then access a node scoped method (i.e. one that is not on m, or on any other object) in the following way: + +``` + +'@Test +function BT_globalScope() as void + m.assertNotInvalid(m.node) + BT_doSomethingInNodeScope(true) + m.assertInvalid(m._isNodeScopeVarSet) + m.assertTrue(m.node. _isNodeScopeVarSet) +end function + +function BT_doSomethingInNodeScope(value) + m._isNodeScopeVarSet = value +end function +``` + ## Using mocks and stubs diff --git a/frameworkTests/source/main.brs b/frameworkTests/source/main.brs index 0253c0cd..c7639c93 100644 --- a/frameworkTests/source/main.brs +++ b/frameworkTests/source/main.brs @@ -1,5 +1,5 @@ sub Main(args as dynamic) - if (type(Rooibos__Init) = "Function") then Rooibos__Init(SetupGlobals, "AddTestUtils") + if (type(Rooibos__Init) = "Function") then Rooibos__Init(SetupGlobals, "AddTestUtils", invalid, m) InitScreen() end sub diff --git a/frameworkTests/source/tests/BasicTests.brs b/frameworkTests/source/tests/BasicTests.brs new file mode 100644 index 00000000..22789cde --- /dev/null +++ b/frameworkTests/source/tests/BasicTests.brs @@ -0,0 +1,17 @@ +'@TestSuite [BT] Basic tests + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests the node context is available for a Node scope function +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test +function BT_NodeScope() as void + m.assertNotInvalid(m.node) + BT_doSomethingInNodeScope(true) + m.assertInvalid(m._isNodeScopeVarSet) + m.assertTrue(m.node._isNodeScopeVarSet) +end function + +function BT_doSomethingInNodeScope(value) + m._isNodeScopeVarSet = value +end function \ No newline at end of file diff --git a/outRun/.roku-deploy-staging/components/TestsScene.xml b/outRun/.roku-deploy-staging/components/TestsScene.xml new file mode 100644 index 00000000..9d5b14a5 --- /dev/null +++ b/outRun/.roku-deploy-staging/components/TestsScene.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/outRun/.roku-deploy-staging/manifest b/outRun/.roku-deploy-staging/manifest new file mode 100644 index 00000000..9a12c564 --- /dev/null +++ b/outRun/.roku-deploy-staging/manifest @@ -0,0 +1,4 @@ +title=Rooibos +major_version=0 +minor_version=2 +build_version=0 diff --git a/outRun/.roku-deploy-staging/source/main.brs b/outRun/.roku-deploy-staging/source/main.brs new file mode 100644 index 00000000..c7639c93 --- /dev/null +++ b/outRun/.roku-deploy-staging/source/main.brs @@ -0,0 +1,57 @@ +sub Main(args as dynamic) + if (type(Rooibos__Init) = "Function") then Rooibos__Init(SetupGlobals, "AddTestUtils", invalid, m) + + InitScreen() +end sub + +function InitScreen() as void + 'this will be where you setup your typical roku app + 'it will not be launched when running unit tests + screen = CreateObject("roSGScreen") + m.port = CreateObject("roMessagePort") + screen.setMessagePort(m.port) + + rootScene = screen.CreateScene("TestsScene") + rootScene.id = "ROOT" + + screen.show() + + SetupGlobals(screen) + + while(true) + msg = wait(0, m.port) + msgType = type(msg) + + if msgType = "roSGScreenEvent" + if msg.isScreenClosed() + return + end if + end if + end while +end function + + +'************************************************************* +'** SetupGlobals +'** @param screen as roScreen - screen to set globals on +'************************************************************* +function SetupGlobals(screen) as void + ? "SETTTING UP GLOBALS - do your standard setup stuff here" + + m.global = screen.getGlobalNode() + + m.roDeviceInfo = CreateObject("roDeviceInfo") + + m.displayInfo = { + resolution: m.roDeviceInfo.GetUIResolution() + displayType: m.roDeviceInfo.GetDisplayType() + width: m.roDeviceInfo.GetDisplaySize().w + height: m.roDeviceInfo.GetDisplaySize().h + wFactor: m.roDeviceInfo.GetDisplaySize().w/1920 + hFactor: m.roDeviceInfo.GetDisplaySize().h/1080 + } + + m.modelLocator = {"displayInfo":m.displayInfo} ' contrived example : this would be a specifc modelLocator node/other setup thing + + m.global.addFields({"modelLocator": m.modelLocator}) +end function diff --git a/outRun/.roku-deploy-staging/source/tests/AssertionTests.brs b/outRun/.roku-deploy-staging/source/tests/AssertionTests.brs new file mode 100644 index 00000000..6eba2846 --- /dev/null +++ b/outRun/.roku-deploy-staging/source/tests/AssertionTests.brs @@ -0,0 +1,660 @@ +'@TestSuite [RBSA] Rooibos assertion tests + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests basic assertions +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test Fail +function Simp_basic_Fail() as void + + assertResult = m.Fail("reason") + + isFail = m.currentResult.isFail + m.currentResult.Reset() + + m.AssertFalse(assertResult) + m.AssertTrue(isFail) +end function + +'@Test AssertTrue +'@Params[true, true] +'@Params[false, false] +'@Params[invalid, false] +'@Params[0, false] +'@Params[1, false] +'@Params["test", false] +function Simp_basic_AssertTrue(value, expectedAssertResult) as void + + assertResult = m.AssertTrue(value) + isFail = m.currentResult.isFail + m.currentResult.Reset() + + m.AssertEqual(assertResult, expectedAssertResult) + m.AssertEqual(isFail, not expectedAssertResult) +end function + +'@Test AssertFalse +'@Params[false, true] +'@Params[true, false] +'@Params[invalid, false] +'@Params[0, false] +'@Params[1, false] +'@Params["test", false] +function Simp_basic_AssertFalse(value, expectedAssertResult) as void + + assertResult = m.AssertFalse(value) + + isFail = m.currentResult.isFail + m.currentResult.Reset() + + m.AssertEqual(assertResult, expectedAssertResult) + m.AssertEqual(isFail, not expectedAssertResult) +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests AssertArrayContainsAAs +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test Fail +'@Params[[{"one":1}], [{"one":2}]] +'@Params[[{"one":1}], [{"one":"a"}]] +'@Params[[{"one":1}], [{}]] +'@Params[[{"one":1}], [invalid]] +'@Params[[{"one":1}], []] +'@Params[[{"one":1}], invalid] +'@Params[[{"one":1}], [[]] ] +'@Params[[{"one":1}], ["wrong"] ] +'@Params[[{"one":1}], [2] ] +'@Params[[{"one":"a"}], [{"one":1}] ] +'@Params[[{"two":1}], [{"one":1}] ] +'@Params[[invalid], [{"one":1}] ] +'@Params[invalid, [{"one":1}] ] +'@Params[[{"one":1, "two":2}], [{"one":"1"}] ] +'@Params[[{"one":1}, {"two":2}], [{"one":"1"}, {"two":"a"}] ] +'@Params[[{"one":1}, {"two":2}], [{"a":1}, {"a":1}, {"a":1}] ] +function Simp_AssertArrayContainsAAs_Fail(expectedAAs, items) as void + + assertResult = m.AssertArrayContainsAAs(items, expectedAAs) + + isFail = m.currentResult.isFail + m.currentResult.Reset() + + m.AssertFalse(assertResult) + m.AssertTrue(isFail) +end function + + +'@Test pass +'@Params[[], []] +'@Params[[{}], [{}]] +'@Params[[{"one":1}], [{"one":1}]] +'@Params[[{"one":1, "two":2}], [{"one":1, "two":2}]] +'@Params[[{"one":1, "two":2}], [{ "two":2, "one":1}]] +'@Params[[{"one":1, "two":2}, {"one":1}], [{"one":1}, { "two":2, "one":1}]] +'@Params[[{"one":1, "two":2}, {"one":1}, {"three":3}], [{"one":1}, {"three":3}, { "two":2, "one":1}]] +function Simp_AssertArrayContainsAAs_Pass(expectedAAs, items) as void + + assertResult = m.AssertArrayContainsAAs(items, expectedAAs) + + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertTrue(assertResult) + m.AssertFalse(isFail) +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests global is present on testSuite +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@BeforeEach +function Simp_AssertGlobal_beforeEach() as void + m.beforeEachGlobal = m.global +end function + +'@AfterEach +function Simp_AssertGlobal_afterEach() as void + m.afterEachGlobal = m.global +end function + +'@Test global is in test +function Simp_AssertGlobalIsPassedIntoTest() as void + m.AssertNotInvalid(m.global) +end function + +'@Test global is in before each and after each +function Simp_AssertGlobalIsPassedInto_beforeEach_and_afterEach() as void + m.AssertNotInvalid(m.global) + m.AssertNotInvalid(m.beforeEachGlobal) + m.AssertNotInvalid(m.afterEachGlobal) +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests AssertArrayContainsOnlyValuesOfType +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test pass +'@Params[["one", "two", "three"], "String"] +'@Params[[1, 2, 3], "Integer"] +'@Params[[true, true, false], "Boolean"] +'@Params[[[true, true], [false, false]], "Array"] +'@Params[[{"test":1}, {"test":1}], "AssociativeArray"] +function Simp_AssertArrayContainsOnlyValuesOfType_Pass(values, typeName) as void + + assertResult = m.AssertArrayContainsOnlyValuesOfType(values, typeName) + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertTrue(assertResult) + m.AssertFalse(isFail) + +end function + +'@Test fail +'@Params[["one", 2, "three"], "String"] +'@Params[[1, "two", 3], "Integer"] +'@Params[[true, "true", false], "Boolean"] +'@Params[[[true, true], false, false], "Array"] +'@Params[[{"test":1}, "notAA"], "AssociativeArray"] +'@Params[["one", "two", "three"], "UnknownType"] +'@Params[["one", "two", "three"], "Integer"] +'@Params[[1, 2, 3], "String"] +'@Params[[true, true, false], "String"] +'@Params[[[true, true], [false, false]], "AssociativeArray"] +'@Params[[{"test":1}, {"test":1}], "Array"] +function Simp_AssertArrayContainsOnlyValuesOfType_Fail(values, typeName) as void + + assertResult = m.AssertArrayContainsOnlyValuesOfType(values, typeName) + isFail = m.currentResult.isFail + + isFail = m.currentResult.isFail + m.currentResult.Reset() + + m.AssertFalse(assertResult) + m.AssertTrue(isFail) + + +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests white spaces work with annotations +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'some comments to +'demonstrate +'@Test comments between tests +'that we can have comments +'between functions and tags +function Simp_whiteSpacing() as void + m.AssertTrue(true) +end function + +'some comments to +'demonstrate +'@Test comments between tests with params +'@Params[1] + +'@Params[2] +'that we can have comments +'@Params[3] +'between functions and tags +'@Params[4] + +function Simp_whiteSpacing_params(value) as void + m.AssertTrue(true) +end function + + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests EqArray +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test +'@Params[invalid, [], false] +'@Params[[], invalid, false] +'@Params[invalid, invalid, false] +'@Params[["one", "three"], [], false] +'@Params[[], ["one", "three"], false] +'@Params[[], [], true] +'@Params[["one", "two", "three"], ["one", "three"], false] +'@Params[["one", "two", "three"], ["one", "two", "three"], true] +'@Params[[1, 2, 3], ["one", "two", "three"], false] +'@Params[[1, 2, 3], [1, 2, 3], true] +'@Params[[1, invalid, "3"], [1, invalid, "3"], true] +'@Params[[3, 2, 1], [1, 2, 3], false] +function Simp_EqArray_Pass(values, values2, expected) as void + + result = m.EqArray(values, values2) + m.AssertEqual(result, expected) + +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests AssertNotEmpty +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test pass +'@Params[["one", "two", "three"]] +'@Params[[1, 2, 3]] +'@Params[[true]] +'@Params[[[true, true], [false, false]]] +'@Params[[{"test":1}]] +'@Params["not empty"] +'@Params[[invalid]] +function Simp_AssertNotEmpty_Pass(values) as void + + assertResult = m.AssertNotEmpty(values) + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertTrue(assertResult) + m.AssertFalse(isFail) + +end function + +'@Test fail +'@Params[invalid] +'@Params[[]] +'@Params[{}] +'@Params[1] +'@Params[""] +function Simp_AssertNotEmpty_Fail(values) as void + + assertResult = m.AssertNotEmpty(values) + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(assertResult) + m.AssertTrue(isFail) + +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests AssertEmpty +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test pass +'@Params[[]] +'@Params[{}] +'@Params[""] +function Simp_AssertEmpty_Pass(values) as void + + assertResult = m.AssertEmpty(values) + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertTrue(assertResult) + m.AssertFalse(isFail) + +end function + +'@Test fail +'@Params[1] +'@Params[invalid] +'@Params[["one", "two", "three"]] +'@Params[[1, 2, 3]] +'@Params[[true]] +'@Params[[[true, true], [false, false]]] +'@Params[[{"test":1}]] +'@Params["not empty"] +'@Params[[invalid]] +function Simp_AssertEmpty_Fail(values) as void + + assertResult = m.AssertEmpty(values) + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(assertResult) + m.AssertTrue(isFail) + +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests expect +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test multi return values +function Simp_expect_multiValues() + obj = {} + m.expect(obj, "mockMethod", 5, invalid, {"multiResult": ["one", 2, invalid, "last"]}, true) + + result = obj.mockMethod() + m.AssertEqual(result, "one") + + result = obj.mockMethod() + m.AssertEqual(result, 2) + + result = obj.mockMethod() + m.AssertEqual(result, invalid) + + result = obj.mockMethod() + m.AssertEqual(result, "last") + + result = obj.mockMethod() + m.AssertEqual(result, "last") + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(isFail) + +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests expect with overloaded expectOnce +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test simple test +function Simp_expect_multiExpect_success() + obj = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "mockMethod", [arg1], result1, true) + m.expectOnce(obj, "mockMethod", [arg2], result2, true) + m.expectOnce(obj, "mockMethod", [arg3], result3, true) + + result = obj.mockMethod(arg1) + m.AssertEqual(result, result1) + + result = obj.mockMethod(arg2) + m.AssertEqual(result, result2) + + result = obj.mockMethod(arg3) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(isFail) +end function + + +'@Test can set up multi expects on same method - one invocation with any args +function Simp_expect_multiExpect_success_oneCallsArgsNotTracked() + obj = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "mockMethod", [arg1], result1, true) + m.expectOnce(obj, "mockMethod", invalid, result2, true) + m.expectOnce(obj, "mockMethod", [arg3], result3, true) + + result = obj.mockMethod(arg1) + m.AssertEqual(result, result1) + + result = obj.mockMethod("do not care about args", "used in invocation", 2) + m.AssertEqual(result, result2) + + result = obj.mockMethod(arg3) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(isFail) + +end function + +'@Test can set up multi expects on same method - multi params +function Simp_expect_multiExpect_multi_args_success() + obj = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "mockMethod", [arg1, arg2, arg3], result1, true) + m.expectOnce(obj, "mockMethod", [arg2, arg3, arg1], result2, true) + m.expectOnce(obj, "mockMethod", [arg3, arg2, arg1], result3, true) + + result = obj.mockMethod(arg1, arg2, arg3) + m.AssertEqual(result, result1) + + result = obj.mockMethod(arg2, arg3, arg1) + m.AssertEqual(result, result2) + + result = obj.mockMethod(arg3, arg2, arg1) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(isFail) + +end function + +'@Test multi test, multi params with other expects +function Simp_expect_multiExpect_multi_args_others_success() + obj = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "anotherMockMethod", invalid, "another", true) + m.expectOnce(obj, "anotherMockMethod2", [1,2,3], "another2", true) + m.expectOnce(obj, "mockMethod", [arg1, arg2, arg3], result1, true) + m.expectOnce(obj, "mockMethod", [arg2, arg3, arg1], result2, true) + m.expectOnce(obj, "mockMethod", [arg3, arg2, arg1], result3, true) + + result = obj.anotherMockMethod() + m.AssertEqual(result, "another") + + result = obj.anotherMockMethod2(1, 2, 3) + m.AssertEqual(result, "another2") + + result = obj.mockMethod(arg1, arg2, arg3) + m.AssertEqual(result, result) + + result = obj.mockMethod(arg2, arg3, arg1) + m.AssertEqual(result, result2) + + result = obj.mockMethod(arg3, arg2, arg1) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(isFail) + +end function + +'@Test multi test, multi params with other expects - fail others +function Simp_expect_multiExpect_multi_args_others_fail() + obj = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "anotherMockMethod", ["not passed"], "another", true) + m.expectOnce(obj, "anotherMockMethod2", [1,2,3], "another2", true) + m.expectOnce(obj, "mockMethod", [arg1, arg2, arg3], result1, true) + m.expectOnce(obj, "mockMethod", [arg2, arg3, arg1], result2, true) + m.expectOnce(obj, "mockMethod", [arg3, arg2, arg1], result3, true) + + result = obj.anotherMockMethod() + m.AssertEqual(result, "another") + + result = obj.anotherMockMethod2(1, 2, 3) + m.AssertEqual(result, "another2") + + result = obj.mockMethod(arg1, arg2, arg3) + m.AssertEqual(result, result) + + result = obj.mockMethod(arg2, arg3, arg1) + m.AssertEqual(result, result2) + + result = obj.mockMethod(arg3, arg2, arg1) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertTrue(isFail) + +end function + +'@Test can set up multi expects on same method +'@Params["arg1_", "arg2", "arg3"] +'@Params["arg1", "arg2", "arg3_"] +'@Params["arg1", "arg2_", "arg3"] +'@Params["arg1", "arg2_", "arg3"] +'@Params["arg1_", "arg2_", "arg3"] +'@Params["arg1_", invalid, "arg3"] +function Simp_expect_multiExpect_fail(call1, call2, call3) + obj = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "mockMethod", [arg1], result1, true) + m.expectOnce(obj, "mockMethod", [arg2], result2, true) + m.expectOnce(obj, "mockMethod", [arg3], result3, true) + + result = obj.mockMethod(call1) + m.AssertEqual(result, result1) + + result = obj.mockMethod(call2) + m.AssertEqual(result, result2) + + result = obj.mockMethod(call2) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertTrue(isFail) +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests overloaded expectOnce on different objects +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test success +function Simp_expect_multiExpect_differentOnj_success() + obj = {} + obj2 = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "mockMethod", [arg1], result1, true) + m.expectOnce(obj, "mockMethod", [arg2], result2, true) + m.expectOnce(obj2, "mockMethod", [arg3], result3, true) + + result = obj.mockMethod(arg1) + m.AssertEqual(result, result1) + + result = obj.mockMethod(arg2) + m.AssertEqual(result, result2) + + result = obj2.mockMethod(arg3) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(isFail) + +end function + +'@Test fail to match +function Simp_expect_multiExpect_differentOnj_fail() + obj = {} + obj2 = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "mockMethod", [arg1], result1, true) + m.expectOnce(obj, "mockMethod", [arg2], result2, true) + m.expectOnce(obj2, "mockMethod", [arg3], result3, true) + + result = obj.mockMethod(arg1) + m.AssertEqual(result, result1) + + result = obj.mockMethod(arg2) + m.AssertEqual(result, result2) + + result = obj2.mockMethod(arg3) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(isFail) + +end function + +'ASSERTIONS TO WRITE TESTS FOR! + +'This is coming soon! + +' AssertEqual +' AssertLike +' AssertNotEqual +' AssertInvalid +' AssertNotInvalid +' AssertAAHasKey +' AssertAANotHasKey +' AssertAAHasKeys +' AssertAANotHasKeys +' AssertArrayNotContains +' AssertArrayContainsSubset +' AssertArrayNotContainsSubsetet +' AssertArrayCount +' AssertArrayNotCount +' AssertArrayContainsOnly +' AssertType +' AssertSubType +' +' 'Node extensions +' AssertNodeCount +' AssertNodeNotCount +' AssertNodeEmpty +' AssertNodeNotEmpty +' AssertNodeContains +' AssertNodeNotContains +' AssertNodeContainsFields +' AssertNodeNotContainsFields + +' AssertArray +' AssertAAContainsSubset +' +' 'Mocking and stubbing +' AssertMocks +' MockFail \ No newline at end of file diff --git a/outRun/.roku-deploy-staging/source/tests/BasicTests.brs b/outRun/.roku-deploy-staging/source/tests/BasicTests.brs new file mode 100644 index 00000000..22789cde --- /dev/null +++ b/outRun/.roku-deploy-staging/source/tests/BasicTests.brs @@ -0,0 +1,17 @@ +'@TestSuite [BT] Basic tests + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests the node context is available for a Node scope function +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test +function BT_NodeScope() as void + m.assertNotInvalid(m.node) + BT_doSomethingInNodeScope(true) + m.assertInvalid(m._isNodeScopeVarSet) + m.assertTrue(m.node._isNodeScopeVarSet) +end function + +function BT_doSomethingInNodeScope(value) + m._isNodeScopeVarSet = value +end function \ No newline at end of file diff --git a/outRun/.roku-deploy-staging/source/tests/ExpectTests.brs b/outRun/.roku-deploy-staging/source/tests/ExpectTests.brs new file mode 100644 index 00000000..114ce322 --- /dev/null +++ b/outRun/.roku-deploy-staging/source/tests/ExpectTests.brs @@ -0,0 +1,28 @@ +'@TestSuite ET ExpectTests + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests bug with expectOnce not matching values +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test reported case +'@Params[[52], true] +'@Params[invalid, false] +'@Params[[invalid], true] +'@Params[["42"], true] +'@Params[[42], false] +function ET_expectOnce_valuesBug_reported(expectedValue, expectMockFail) as void + obj = { + foo: function(arg0) : return arg0 : end function + } + + m.ExpectOnce(obj, "foo", expectedValue) + obj.foo(42) + m.isAutoAssertingMocks = false + m.AssertMocks() + + isFail = m.currentResult.isFail + m.currentResult.Reset() + m.CleanMocks() + m.AssertEqual(isFail, expectMockFail) +end function + diff --git a/outRun/.roku-deploy-staging/source/tests/ParamsTests.brs b/outRun/.roku-deploy-staging/source/tests/ParamsTests.brs new file mode 100644 index 00000000..a7ebed10 --- /dev/null +++ b/outRun/.roku-deploy-staging/source/tests/ParamsTests.brs @@ -0,0 +1,32 @@ +'@TestSuite PT ParamsTests + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests that nodes are created with RBSNode +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test +'@Params["#RBSNode", "ContentNode"] +'@Params["#RBSNode|Group", "Group"] +'@Params["#RBSNode|Label", "Label"] +function PT_RBSNodeDirective(node, expectedNodeType) as void + m.assertSubType(node, expectedNodeType) +end function + +'@Test node parsed as other arg index +'@Params[1, 2, 3, "#RBSNode", "ContentNode"] +'@Params[1, 2, 3, "#RBSNode|Group", "Group"] +'@Params[1, 2, 3, "#RBSNode|Label", "Label"] +function PT_RBSNodeDirective_otherArgs(arg1, arg2, arg3, node, expectedNodeType) as void + m.assertSubType(node, expectedNodeType) + m.assertEqual(arg1, 1) + m.assertEqual(arg2, 2) + m.assertEqual(arg3, 3) +end function + +'@Test does not affect regular params +'@Params["#someValue", "#someValue"] +'@Params[22, 22] +'@Params[[1,2,3], [1,2,3]] +function PT_RBSNodeDirective_noSideEffect(arg1, expectedValue) as void + m.assertEqual(arg1, expectedValue) +end function \ No newline at end of file diff --git a/outRun/.roku-deploy-staging/source/tests/issue_13_afterEach_not_running.brs b/outRun/.roku-deploy-staging/source/tests/issue_13_afterEach_not_running.brs new file mode 100644 index 00000000..228c7df5 --- /dev/null +++ b/outRun/.roku-deploy-staging/source/tests/issue_13_afterEach_not_running.brs @@ -0,0 +1,48 @@ +'@TestSuite [DGNT] Duplicate Group Name Tests + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It group1 +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test simple +function DGNT_group1_test() + m.AssertTrue(true) +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It group2 +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test simple +function DGNT_group2_test() + m.AssertTrue(true) +end function + + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It group2 +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test simple +function DGNT_group2_dupe_test() + m.AssertTrue(true) +end function + + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It group3 +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test simple +function DGNT_group3_test() + m.AssertTrue(true) +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It group1 +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test simple +function DGNT_group1_dupe_test() + m.AssertTrue(true) +end function \ No newline at end of file diff --git a/outRun/.roku-deploy-staging/source/tests/issue_15_tests_only_on_groups.brs b/outRun/.roku-deploy-staging/source/tests/issue_15_tests_only_on_groups.brs new file mode 100644 index 00000000..67b33e76 --- /dev/null +++ b/outRun/.roku-deploy-staging/source/tests/issue_15_tests_only_on_groups.brs @@ -0,0 +1,30 @@ +'ADD '@Only ON NEXT LINE TO TEST +'@TestSuite [RBSA] Rooibos before after tests + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests one +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test it one +function RBSA__it_test_one() as void + m.AssertTrue(true) +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests two +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test it two +function RBSA__it_test_two() as void + m.AssertTrue(true) +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'ADD '@Only ON NEXT LINE TO TEST +'@It tests three +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test it three +function RBSA__it_test_three() as void + m.AssertTrue(true) +end function \ No newline at end of file diff --git a/outRun/.roku-deploy-staging/source/tests/issue_5_duplicateGroupNames.brs b/outRun/.roku-deploy-staging/source/tests/issue_5_duplicateGroupNames.brs new file mode 100644 index 00000000..1725a4bb --- /dev/null +++ b/outRun/.roku-deploy-staging/source/tests/issue_5_duplicateGroupNames.brs @@ -0,0 +1,28 @@ +'@TestSuite [RBSA] Rooibos before after tests + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests before each and after each are running +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@BeforeEach +function RBSA__BeforeEach() as void + ? "!!! Before" +end function + + +'@AfterEach +function RBSA__AfterEach() as void + ? "!!! After" +end function + +'@Test before after +function RBSA__before_after() as void + + assertResult = m.Fail("reason") + + isFail = m.currentResult.isFail + m.currentResult.Reset() + + m.AssertFalse(assertResult) + m.AssertTrue(isFail) +end function \ No newline at end of file diff --git a/outRun/.roku-deploy-staging/source/tests/rooibosDist.brs b/outRun/.roku-deploy-staging/source/tests/rooibosDist.brs new file mode 100644 index 00000000..9495ed55 --- /dev/null +++ b/outRun/.roku-deploy-staging/source/tests/rooibosDist.brs @@ -0,0 +1,2210 @@ +'/** +' * rooibos - simple, flexible, fun brightscript test framework for roku scenegraph apps +' * @version v2.2.0 +' * @link https://github.com/georgejecook/rooibos#readme +' * @license MIT +' */ +function Rooibos__Init(preTestSetup = invalid, testUtilsDecoratorMethodName = invalid, testSceneName = invalid, nodeContext = invalid) as void + args = {} + if createObject("roAPPInfo").IsDev() <> true then + ? " not running in dev mode! - rooibos tests only support sideloaded builds - aborting" + return + end if + args.testUtilsDecoratorMethodName = testUtilsDecoratorMethodName + args.nodeContext = nodeContext + screen = CreateObject("roSGScreen") + m.port = CreateObject("roMessagePort") + screen.setMessagePort(m.port) + if testSceneName = invalid + testSceneName = "TestsScene" + end if + ? "Starting test using test scene with name TestsScene" ; testSceneName + scene = screen.CreateScene(testSceneName) + scene.id = "ROOT" + screen.show() + m.global = screen.getGlobalNode() + m.global.addFields({"testsScene": scene}) + if (preTestSetup <> invalid) + preTestSetup(screen) + end if + testId = args.TestId + if (testId = invalid) + testId = "UNDEFINED_TEST_ID" + end if + ? "#########################################################################" + ? "#TEST START : ###" ; testId ; "###" + args.testScene = scene + args.global = m.global + runner = RBS_TR_TestRunner(args) + runner.Run() + while(true) + msg = wait(0, m.port) + msgType = type(msg) + if msgType = "roSGScreenEvent" + if msg.isScreenClosed() + return + end if + end if + end while +end function +function BaseTestSuite() as object + this = {} + this.Name = "BaseTestSuite" + this.invalidValue = "#ROIBOS#INVALID_VALUE" ' special value used in mock arguments + this.ignoreValue = "#ROIBOS#IGNORE_VALUE" ' special value used in mock arguments + this.allowNonExistingMethodsOnMocks = true + this.isAutoAssertingMocks = true + this.TestCases = [] + this.AddTest = RBS_BTS_AddTest + this.CreateTest = RBS_BTS_CreateTest + this.GetLegacyCompatibleReturnValue = RBS_BTS_GetLegacyCompatibleReturnValue + this.Fail = RBS_BTS_Fail + this.AssertFalse = RBS_BTS_AssertFalse + this.AssertTrue = RBS_BTS_AssertTrue + this.AssertEqual = RBS_BTS_AssertEqual + this.AssertLike = RBS_BTS_AssertLike + this.AssertNotEqual = RBS_BTS_AssertNotEqual + this.AssertInvalid = RBS_BTS_AssertInvalid + this.AssertNotInvalid = RBS_BTS_AssertNotInvalid + this.AssertAAHasKey = RBS_BTS_AssertAAHasKey + this.AssertAANotHasKey = RBS_BTS_AssertAANotHasKey + this.AssertAAHasKeys = RBS_BTS_AssertAAHasKeys + this.AssertAANotHasKeys = RBS_BTS_AssertAANotHasKeys + this.AssertArrayContains = RBS_BTS_AssertArrayContains + this.AssertArrayNotContains = RBS_BTS_AssertArrayNotContains + this.AssertArrayContainsSubset = RBS_BTS_AssertArrayContainsSubset + this.AssertArrayContainsAAs = RBS_BTS_AssertArrayContainsAAs + this.AssertArrayNotContainsSubset = RBS_BTS_AssertArrayNotContainsSubset + this.AssertArrayCount = RBS_BTS_AssertArrayCount + this.AssertArrayNotCount = RBS_BTS_AssertArrayNotCount + this.AssertEmpty = RBS_BTS_AssertEmpty + this.AssertNotEmpty = RBS_BTS_AssertNotEmpty + this.AssertArrayContainsOnlyValuesOfType = RBS_BTS_AssertArrayContainsOnlyValuesOfType + this.AssertType = RBS_BTS_AssertType + this.AssertSubType = RBS_BTS_AssertSubType + this.AssertNodeCount = RBS_BTS_AssertNodeCount + this.AssertNodeNotCount = RBS_BTS_AssertNodeNotCount + this.AssertNodeEmpty = RBS_BTS_AssertNodeEmpty + this.AssertNodeNotEmpty = RBS_BTS_AssertNodenotEmpty + this.AssertNodeContains = RBS_BTS_AssertNodeContains + this.AssertNodeNotContains = RBS_BTS_AssertNodeNotContains + this.AssertNodeContainsFields = RBS_BTS_AssertNodeContainsFields + this.AssertNodeNotContainsFields = RBS_BTS_AssertNodeNotContainsFields + this.AssertAAContainsSubset = RBS_BTS_AssertAAContainsSubset + this.EqValues = RBS_BTS_EqValues + this.EqAssocArrays = RBS_BTS_EqAssocArray + this.EqArray = RBS_BTS_EqArray + this.Stub = RBS_BTS_Stub + this.Mock = RBS_BTS_Mock + this.AssertMocks = RBS_BTS_AssertMocks + this.CreateFake = RBS_BTS_CreateFake + this.CombineFakes = RBS_BTS_CombineFakes + this.MockFail = RBS_BTS_MockFail + this.CleanMocks = RBS_BTS_CleanMocks + this.CleanStubs = RBS_BTS_CleanStubs + this.ExpectOnce = RBS_BTS_ExpectOnce + this.ExpectNone = RBS_BTS_ExpectNone + this.Expect = RBS_BTS_Expect + this.ExpectOnceOrNone = RBS_BTS_ExpectOnceOrNone + this.MockCallback0 = RBS_BTS_MockCallback0 + this.MockCallback1 = RBS_BTS_MockCallback1 + this.MockCallback2 = RBS_BTS_MockCallback2 + this.MockCallback3 = RBS_BTS_MockCallback3 + this.MockCallback4 = RBS_BTS_MockCallback4 + this.MockCallback5 = RBS_BTS_MockCallback5 + this.StubCallback0 = RBS_BTS_StubCallback0 + this.StubCallback1 = RBS_BTS_StubCallback1 + this.StubCallback2 = RBS_BTS_StubCallback2 + this.StubCallback3 = RBS_BTS_StubCallback3 + this.StubCallback4 = RBS_BTS_StubCallback4 + this.StubCallback5 = RBS_BTS_StubCallback5 + this.pathAsArray_ = RBS_BTS_rodash_pathsAsArray_ + this.g = RBS_BTS_rodash_get_ + return this +end function +sub RBS_BTS_AddTest(name, func,funcName, setup = invalid, teardown = invalid) + m.testCases.Push(m.createTest(name, func, setup, teardown)) +end sub +function RBS_BTS_CreateTest(name, func, funcName, setup = invalid, teardown = invalid ) as object + if (func = invalid) + ? " ASKED TO CREATE TEST WITH INVALID FUNCITON POINTER FOR FUNCTION " ; funcName + end if + return { + Name: name + Func: func + FuncName: funcName + SetUp: setup + TearDown: teardown + } +end function +function RBS_BTS_Fail(msg = "Error" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) +end function +function RBS_BTS_GetLegacyCompatibleReturnValue(value) as object + if (value = true) + if (m.isLegacy = true) + return "" + else + return true + end if + else + if (m.isLegacy = true) + return "ERROR" + else + return false + end if + end if +end function +function RBS_BTS_AssertFalse(expr , msg = "Expression evaluates to true" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if not RBS_CMN_IsBoolean(expr) or expr + m.currentResult.AddResult(msg) + return m.fail(msg) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertTrue(expr , msg = "Expression evaluates to false" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if not RBS_CMN_IsBoolean(expr) or not expr then + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertEqual(first , second , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if not m.eqValues(first, second) + if msg = "" + first_as_string = RBS_CMN_AsString(first) + second_as_string = RBS_CMN_AsString(second) + msg = first_as_string + " != " + second_as_string + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertLike(first , second , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if first <> second + if msg = "" + first_as_string = RBS_CMN_AsString(first) + second_as_string = RBS_CMN_AsString(second) + msg = first_as_string + " != " + second_as_string + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertNotEqual(first , second , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if m.eqValues(first, second) + if msg = "" + first_as_string = RBS_CMN_AsString(first) + second_as_string = RBS_CMN_AsString(second) + msg = first_as_string + " == " + second_as_string + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertInvalid(value , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if value <> invalid + if msg = "" + expr_as_string = RBS_CMN_AsString(value) + msg = expr_as_string + " <> Invalid" + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertNotInvalid(value , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if value = invalid + if msg = "" + expr_as_string = RBS_CMN_AsString(value) + msg = expr_as_string + " = Invalid" + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertAAHasKey(array , key , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if RBS_CMN_IsAssociativeArray(array) + if not array.DoesExist(key) + if msg = "" + msg = "Array doesn't have the '" + key + "' key." + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an Associative Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertAANotHasKey(array , key , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if RBS_CMN_IsAssociativeArray(array) + if array.DoesExist(key) + if msg = "" + msg = "Array has the '" + key + "' key." + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an Associative Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertAAHasKeys(array , keys , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if RBS_CMN_IsAssociativeArray(array) and RBS_CMN_IsArray(keys) + for each key in keys + if not array.DoesExist(key) + if msg = "" + msg = "Array doesn't have the '" + key + "' key." + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + end for + else + msg = "Input value is not an Associative Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertAANotHasKeys(array , keys , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if RBS_CMN_IsAssociativeArray(array) and RBS_CMN_IsArray(keys) + for each key in keys + if array.DoesExist(key) + if msg = "" + msg = "Array has the '" + key + "' key." + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + end for + else + msg = "Input value is not an Associative Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertArrayContains(array , value , key = invalid , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if RBS_CMN_IsAssociativeArray(array) or RBS_CMN_IsArray(array) + if not RBS_CMN_ArrayContains(array, value, key) + msg = "Array doesn't have the '" + RBS_CMN_AsString(value) + "' value." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertArrayContainsAAs(array , values , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if not RBS_CMN_IsArray(values) + msg = "values to search for are not an Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + if RBS_CMN_IsArray(array) + for each value in values + isMatched = false + if not RBS_CMN_IsAssociativeArray(value) + msg = "Value to search for was not associativeArray "+ RBS_CMN_AsString(value) + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + for each item in array + if (RBS_CMN_IsAssociativeArray(item)) + isValueMatched = true + for each key in value + fieldValue = value[key] + itemValue = item[key] + if (not m.EqValues(fieldValue, itemValue)) + isValueMatched = false + exit for + end if + end for + if (isValueMatched) + isMatched = true + exit for + end if + end if + end for ' items in array + if not isMatched + msg = "array missing value: "+ RBS_CMN_AsString(value) + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + end for 'values to match + else + msg = "Input value is not an Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertArrayNotContains(array , value , key = invalid , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if RBS_CMN_IsAssociativeArray(array) or RBS_CMN_IsArray(array) + if RBS_CMN_ArrayContains(array, value, key) + msg = "Array has the '" + RBS_CMN_AsString(value) + "' value." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertArrayContainsSubset(array , subset , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if (RBS_CMN_IsAssociativeArray(array) and RBS_CMN_IsAssociativeArray(subset)) or (RBS_CMN_IsArray(array) and RBS_CMN_IsArray(subset)) + isAA = RBS_CMN_IsAssociativeArray(subset) + for each item in subset + key = invalid + value = item + if isAA + key = item + value = subset[key] + end if + if not RBS_CMN_ArrayContains(array, value, key) + msg = "Array doesn't have the '" + RBS_CMN_AsString(value) + "' value." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + end for + else + msg = "Input value is not an Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertArrayNotContainsSubset(array , subset , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if (RBS_CMN_IsAssociativeArray(array) and RBS_CMN_IsAssociativeArray(subset)) or (RBS_CMN_IsArray(array) and RBS_CMN_IsArray(subset)) + isAA = RBS_CMN_IsAssociativeArray(subset) + for each item in subset + key = invalid + value = item + if isAA + key = item + value = item[key] + end if + if RBS_CMN_ArrayContains(array, value, key) + msg = "Array has the '" + RBS_CMN_AsString(value) + "' value." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + end for + else + msg = "Input value is not an Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertArrayCount(array , count , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if RBS_CMN_IsAssociativeArray(array) or RBS_CMN_IsArray(array) + if array.Count() <> count + msg = "Array items count " + RBS_CMN_AsString(array.Count()) + " <> " + RBS_CMN_AsString(count) + "." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertArrayNotCount(array , count , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if RBS_CMN_IsAssociativeArray(array) or RBS_CMN_IsArray(array) + if array.Count() = count + msg = "Array items count = " + RBS_CMN_AsString(count) + "." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertEmpty(item , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if RBS_CMN_IsAssociativeArray(item) or RBS_CMN_IsArray(item) + if item.Count() > 0 + msg = "Array is not empty." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else if (RBS_CMN_IsString(item)) + if (RBS_CMN_AsString(item) <> "") + msg = "Input value is not empty." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "AssertEmpty: Input value was not an array or a string" + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertNotEmpty(item , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if RBS_CMN_IsAssociativeArray(item) or RBS_CMN_IsArray(item) + if item.Count() = 0 + msg = "Array is empty." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else if RBS_CMN_IsString(item) + if (item = "") + msg = "Input value is empty." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not a string or array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertArrayContainsOnlyValuesOfType(array , typeStr , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if typeStr <> "String" and typeStr <> "Integer" and typeStr <> "Boolean" and typeStr <> "Array" and typeStr <> "AssociativeArray" + msg = "Type must be Boolean, String, Array, Integer, or AssociativeArray" + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + if RBS_CMN_IsAssociativeArray(array) or RBS_CMN_IsArray(array) + methodName = "RBS_CMN_Is" + typeStr + typeCheckFunction = RBS_CMN_GetIsTypeFunction(methodName) + if (typeCheckFunction <> invalid) + for each item in array + if not typeCheckFunction(item) + msg = RBS_CMN_AsString(item) + "is not a '" + typeStr + "' type." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + end for + else + msg = "could not find comparator for type '" + typeStr + "' type." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an Array." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_CMN_GetIsTypeFunction(name) + if name = "RBS_CMN_IsFunction" + return RBS_CMN_IsFunction + else if name = "RBS_CMN_IsXmlElement" + return RBS_CMN_IsXmlElement + else if name = "RBS_CMN_IsInteger" + return RBS_CMN_IsInteger + else if name = "RBS_CMN_IsBoolean" + return RBS_CMN_IsBoolean + else if name = "RBS_CMN_IsFloat" + return RBS_CMN_IsFloat + else if name = "RBS_CMN_IsDouble" + return RBS_CMN_IsDouble + else if name = "RBS_CMN_IsLongInteger" + return RBS_CMN_IsLongInteger + else if name = "RBS_CMN_IsNumber" + return RBS_CMN_IsNumber + else if name = "RBS_CMN_IsList" + return RBS_CMN_IsList + else if name = "RBS_CMN_IsArray" + return RBS_CMN_IsArray + else if name = "RBS_CMN_IsAssociativeArray" + return RBS_CMN_IsAssociativeArray + else if name = "RBS_CMN_IsSGNode" + return RBS_CMN_IsSGNode + else if name = "RBS_CMN_IsString" + return RBS_CMN_IsString + else if name = "RBS_CMN_IsDateTime" + return RBS_CMN_IsDateTime + else if name = "RBS_CMN_IsUndefined" + return RBS_CMN_IsUndefined + else + return invalid + end if +end function +function RBS_BTS_AssertType(value , typeStr , msg ="" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if type(value) <> typeStr + if msg = "" + expr_as_string = RBS_CMN_AsString(value) + msg = expr_as_string + " was not expected type " + typeStr + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertSubType(value , typeStr , msg ="" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if type(value) <> "roSGNode" + if msg = "" + expr_as_string = RBS_CMN_AsString(value) + msg = expr_as_string + " was not a node, so could not match subtype " + typeStr + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + else if (value.subType() <> typeStr) + if msg = "" + expr_as_string = RBS_CMN_AsString(value) + msg = expr_as_string + "( type : " + value.subType() +") was not of subType " + typeStr + end if + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_EqValues(Value1 , Value2 ) as dynamic + val1Type = type(Value1) + val2Type = type(Value2) + if val1Type = "" or val2Type = "" or val1Type = "" or val2Type = "" + ? "ERROR!!!! - undefined value passed" + return false + end if + if val1Type = "roString" or val1Type = "String" + Value1 = RBS_CMN_AsString(Value1) + else + Value1 = box(Value1) + end if + if val2Type = "roString" or val2Type = "String" + Value2 = RBS_CMN_AsString(Value2) + else + Value2 = box(Value2) + end if + val1Type = type(Value1) + val2Type = type(Value2) + if val1Type = "roFloat" and val2Type = "roInt" + Value2 = box(Cdbl(Value2)) + else if val2Type = "roFloat" and val1Type = "roInt" + Value1 = box(Cdbl(Value1)) + end if + if val1Type <> val2Type + return false + else + valtype = val1Type + if valtype = "roList" + return RBS_BTS_EqArray(Value1, Value2) + else if valtype = "roAssociativeArray" + return RBS_BTS_EqAssocArray(Value1, Value2) + else if valtype = "roArray" + return RBS_BTS_EqArray(Value1, Value2) + else if (valtype = "roSGNode") + if (val2Type <> "roSGNode") + return false + else + return Value1.isSameNode(Value2) + end if + else + return Value1 = Value2 + end if + end if +end function +function RBS_BTS_EqAssocArray(Value1 , Value2 ) as dynamic + l1 = Value1.Count() + l2 = Value2.Count() + if not l1 = l2 + return false + else + for each k in Value1 + if not Value2.DoesExist(k) + return false + else + v1 = Value1[k] + v2 = Value2[k] + if not RBS_BTS_EqValues(v1, v2) + return false + end if + end if + end for + return true + end if +end function +function RBS_BTS_EqArray(Value1 , Value2 ) as dynamic + if not (RBS_CMN_IsArray(Value1)) or not RBS_CMN_IsArray(Value2) then return false + l1 = Value1.Count() + l2 = Value2.Count() + if not l1 = l2 + return false + else + for i = 0 to l1 - 1 + v1 = Value1[i] + v2 = Value2[i] + if not RBS_BTS_EqValues(v1, v2) then + return false + end if + end for + return true + end if +end function +function RBS_BTS_AssertNodeCount(node , count , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if type(node) = "roSGNode" + if node.getChildCount() <> count + msg = "node items count <> " + RBS_CMN_AsString(count) + ". Received " + RBS_CMN_AsString(node.getChildCount()) + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an node." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertNodeNotCount(node , count , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if type(node) = "roSGNode" + if node.getChildCount() = count + msg = "node items count = " + RBS_CMN_AsString(count) + "." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an node." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertNodeEmpty(node , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if type(node) = "roSGNode" + if node.getChildCount() > 0 + msg = "node is not empty." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertNodeNotEmpty(node , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if type(node) = "roSGNode" + if node.Count() = 0 + msg = "Array is empty." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertNodeContains(node , value , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if type(node) = "roSGNode" + if not RBS_CMN_NodeContains(node, value) + msg = "Node doesn't have the '" + RBS_CMN_AsString(value) + "' value." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an Node." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertNodeContainsOnly(node , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if type(node) = "roSGNode" + if not RBS_CMN_NodeContains(node, value) + msg = "Node doesn't have the '" + RBS_CMN_AsString(value) + "' value." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + else if node.getChildCount() <> 1 + msg = "Node Contains speicified value; but other values as well" + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an Node." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertNodeNotContains(node , value , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if type(node) = "roSGNode" + if RBS_CMN_NodeContains(node, value) + msg = "Node has the '" + RBS_CMN_AsString(value) + "' value." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + else + msg = "Input value is not an Node." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertNodeContainsFields(node , subset , ignoredFields=invalid, msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if ( type(node) = "roSGNode" and RBS_CMN_IsAssociativeArray(subset)) or ( type(node) = "roSGNode" and RBS_CMN_IsArray(subset)) + isAA = RBS_CMN_IsAssociativeArray(subset) + isIgnoredFields = RBS_CMN_IsArray(ignoredFields) + for each key in subset + if (key <> "") + if (not isIgnoredFields or not RBS_CMN_ArrayContains(ignoredFields, key)) + subsetValue = subset[key] + nodeValue = node[key] + if not m.eqValues(nodeValue, subsetValue) + msg = key + ": Expected '" + RBS_CMN_AsString(subsetValue) + "', got '" + RBS_CMN_AsString(nodeValue) + "'" + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + end if + else + ? "Found empty key!" + end if + end for + else + msg = "Input value is not an Node." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertNodeNotContainsFields(node , subset , msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if ( type(node) = "roSGNode" and RBS_CMN_IsAssociativeArray(subset)) or ( type(node) = "roSGNode" and RBS_CMN_IsArray(subset)) + isAA = RBS_CMN_IsAssociativeArray(subset) + for each item in subset + key = invalid + value = item + if isAA + key = item + value = item[key] + end if + if RBS_CMN_NodeContains(node, value, key) + msg = "Node has the '" + RBS_CMN_AsString(value) + "' value." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + end for + else + msg = "Input value is not an Node." + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_AssertAAContainsSubset(array , subset , ignoredFields = invalid, msg = "" ) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + if (RBS_CMN_IsAssociativeArray(array) and RBS_CMN_IsAssociativeArray(subset)) + isAA = RBS_CMN_IsAssociativeArray(subset) + isIgnoredFields = RBS_CMN_IsArray(ignoredFields) + for each key in subset + if (key <> "") + if (not isIgnoredFields or not RBS_CMN_ArrayContains(ignoredFields, key)) + subsetValue = subset[key] + arrayValue = array[key] + if not m.eqValues(arrayValue, subsetValue) + msg = key + ": Expected '" + RBS_CMN_AsString(subsetValue) + "', got '" + RBS_CMN_AsString(arrayValue) + "'" + m.currentResult.AddResult(msg) + return m.GetLegacyCompatibleReturnValue(false) + end if + end if + else + ? "Found empty key!" + end if + end for + else + msg = "Input values are not an Associative Array." + return m.GetLegacyCompatibleReturnValue(false) + end if + m.currentResult.AddResult("") + return m.GetLegacyCompatibleReturnValue(true) +end function +function RBS_BTS_Stub(target, methodName, returnValue = invalid, allowNonExistingMethods = false) as object + if (type(target) <> "roAssociativeArray") + m.Fail("could not create Stub provided target was null") + return {} + end if + if (m.stubs =invalid) + m.__stubId = -1 + m.stubs = {} + end if + m.__stubId++ + if (m.__stubId > 5) + ? "ERROR ONLY 6 STUBS PER TEST ARE SUPPORTED!!" + return invalid + end if + id = stri(m.__stubId).trim() + fake = m.CreateFake(id, target, methodName, 1, invalid, returnValue) + m.stubs[id] = fake + allowNonExisting = m.allowNonExistingMethodsOnMocks = true or allowNonExistingMethods + isMethodPresent = type(target[methodName]) = "Function" or type(target[methodName]) = "roFunction" + if (isMethodPresent or allowNonExisting) + target[methodName] = m["StubCallback" + id] + target.__stubs = m.stubs + if (not isMethodPresent) + ? "WARNING - stubbing call " ; methodName; " which did not exist on target object" + end if + else + ? "ERROR - could not create Stub : method not found "; target ; "." ; methodName + end if + return fake +end function +function RBS_BTS_ExpectOnce(target, methodName, expectedArgs = invalid, returnValue = invalid, allowNonExistingMethods = false) as object + return m.Mock(target, methodName, 1, expectedArgs, returnValue, allowNonExistingMethods) +end function +function RBS_BTS_ExpectOnceOrNone(target, methodName, isExpected, expectedArgs = invalid, returnValue = invalid, allowNonExistingMethods = false) as object + if isExpected + return m.ExpectOnce(target, methodName, expectedArgs, returnValue, allowNonExistingMethods) + else + return m.ExpectNone(target, methodName, allowNonExistingMethods) + end if +end function +function RBS_BTS_ExpectNone(target, methodName, allowNonExistingMethods = false) as object + return m.Mock(target, methodName, 0, invalid, invalid, allowNonExistingMethods) +end function +function RBS_BTS_Expect(target, methodName, expectedInvocations = 1, expectedArgs = invalid, returnValue = invalid, allowNonExistingMethods = false) as object + return m.Mock(target, methodName, expectedInvocations, expectedArgs, returnValue, allowNonExistingMethods) +end function +function RBS_BTS_Mock(target, methodName, expectedInvocations = 1, expectedArgs = invalid, returnValue = invalid, allowNonExistingMethods = false) as object + if not RBS_CMN_IsAssociativeArray(target) + m.Fail("mock args: target was not an AA") + else if not RBS_CMN_IsString(methodName) + m.Fail("mock args: methodName was not a string") + else if not RBS_CMN_IsNumber(expectedInvocations) + m.Fail("mock args: expectedInvocations was not an int") + else if not RBS_CMN_IsArray(expectedArgs) and RBS_CMN_IsValid(expectedArgs) + m.Fail("mock args: expectedArgs was not invalid or an array of args") + else if RBS_CMN_IsUndefined(expectedArgs) + m.Fail("mock args: expectedArgs undefined") + end if + if m.currentResult.isFail + ? "ERROR: "; m.currentResult.messages[m.currentResult.currentAssertIndex - 1] + return {} + end if + if (m.mocks = invalid) + m.__mockId = -1 + m.__mockTargetId = -1 + m.mocks = {} + end if + fake = invalid + if not target.doesExist("__rooibosTargetId") + m.__mockTargetId++ + target["__rooibosTargetId"] = m.__mockTargetId + end if + for i = 0 to m.__mockId + id = stri(i).trim() + mock = m.mocks[id] + if mock <> invalid and mock.methodName = methodName and mock.target.__rooibosTargetId = target.__rooibosTargetId + fake = mock + exit for + end if + end for + if fake = invalid + m.__mockId++ + id = stri(m.__mockId).trim() + if (m.__mockId > 6) + ? "ERROR ONLY 6 MOCKS PER TEST ARE SUPPORTED!! you're on # " ; m.__mockId + ? " Method was " ; methodName + return invalid + end if + fake = m.CreateFake(id, target, methodName, expectedInvocations, expectedArgs, returnValue) + m.mocks[id] = fake 'this will bind it to m + allowNonExisting = m.allowNonExistingMethodsOnMocks = true or allowNonExistingMethods + isMethodPresent = type(target[methodName]) = "Function" or type(target[methodName]) = "roFunction" + if (isMethodPresent or allowNonExisting) + target[methodName] = m["MockCallback" + id] + target.__mocks = m.mocks + if (not isMethodPresent) + ? "WARNING - mocking call " ; methodName; " which did not exist on target object" + end if + else + ? "ERROR - could not create Mock : method not found "; target ; "." ; methodName + end if + else + m.CombineFakes(fake, m.CreateFake(id, target, methodName, expectedInvocations, expectedArgs, returnValue)) + end if + return fake +end function +function RBS_BTS_CreateFake(id, target, methodName, expectedInvocations = 1, expectedArgs =invalid, returnValue=invalid ) as object + expectedArgsValues = [] + hasArgs = RBS_CMN_IsArray(expectedArgs) + if (hasArgs) + defaultValue = m.invalidValue + else + defaultValue = m.ignoreValue + expectedArgs = [] + end if + for i = 0 to 9 + if (hasArgs and expectedArgs.count() > i) + value = expectedArgs[i] + if not RBS_CMN_IsUndefined(value) + expectedArgsValues.push(expectedArgs[i]) + else + expectedArgsValues.push("#ERR-UNDEFINED!") + end if + else + expectedArgsValues.push(defaultValue) + end if + end for + fake = { + id : id, + target: target, + methodName: methodName, + returnValue: returnValue, + isCalled: false, + invocations: 0, + invokedArgs: [invalid, invalid, invalid, invalid, invalid, invalid, invalid, invalid, invalid], + expectedArgs: expectedArgsValues, + expectedInvocations: expectedInvocations, + callback: function (arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + if (m.allInvokedArgs = invalid) + m.allInvokedArgs = [] + end if + m.invokedArgs = [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 ] + m.allInvokedArgs.push ([arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 ]) + m.isCalled = true + m.invocations++ + if (type(m.returnValue) = "roAssociativeArray" and m.returnValue.doesExist("multiResult")) + returnValues = m.returnValue["multiResult"] + returnIndex = m.invocations -1 + if (type(returnValues) = "roArray" and returnValues.count() > 0) + if returnValues.count() <= m.invocations + returnIndex = returnValues.count() -1 + print "Multi return values all used up - repeating last value" + end if + return returnValues[returnIndex] + else + ? "Multi return value was specified; but no array of results were found" + return invalid + end if + else + return m.returnValue + end if + end function + } + return fake +end function +function RBS_BTS_CombineFakes(fake, otherFake) + if type(fake.expectedArgs) <> "roAssociativeArray" or not fake.expectedArgs.doesExist("multiInvoke") + currentExpectedArgsArgs = fake.expectedArgs + fake.expectedArgs = { + "multiInvoke": [currentExpectedArgsArgs] + } + end if + fake.expectedArgs.multiInvoke.push(otherFake.expectedArgs) + if type(fake.returnValue) <> "roAssociativeArray" or not fake.returnValue.doesExist("multiResult") + currentReturnValue = fake.returnValue + fake.returnValue = { + "multiResult": [currentReturnValue] + } + end if + fake.returnValue.multiResult.push(otherFake.returnValue) + fake.expectedInvocations++ +end function +function RBS_BTS_AssertMocks() as void + if (m.__mockId = invalid or not RBS_CMN_IsAssociativeArray(m.mocks)) + return + end if + lastId = int(m.__mockId) + for each id in m.mocks + mock = m.mocks[id] + methodName = mock.methodName + if (mock.expectedInvocations <> mock.invocations) + m.MockFail(methodName, "Wrong number of calls. (" + stri(mock.invocations).trim() + " / " + stri(mock.expectedInvocations).trim() + ")") + m.CleanMocks() + return + else if mock.expectedInvocations > 0 and (RBS_CMN_IsArray(mock.expectedArgs) or (type(mock.expectedArgs) = "roAssociativeArray" and RBS_CMN_IsArray(mock.expectedArgs.multiInvoke))) + isMultiArgsSupported = type(mock.expectedArgs) = "roAssociativeArray" and RBS_CMN_IsArray(mock.expectedArgs.multiInvoke) + for invocationIndex = 0 to mock.invocations - 1 + invokedArgs = mock.allInvokedArgs[invocationIndex] + if isMultiArgsSupported + expectedArgs = mock.expectedArgs.multiInvoke[invocationIndex] + else + expectedArgs = mock.expectedArgs + end if + for i = 0 to expectedArgs.count() -1 + value = invokedArgs[i] + expected = expectedArgs[i] + didNotExpectArg = RBS_CMN_IsString(expected) and expected = m.invalidValue + if (didNotExpectArg) + expected = invalid + end if + if (not (RBS_CMN_IsString(expected) and expected = m.ignoreValue) and not m.eqValues(value, expected)) + if (expected = invalid) + expected = "[INVALID]" + end if + m.MockFail(methodName, "on Invocation #" + stri(invocationIndex).trim() + ", expected arg #" + stri(i).trim() + " to be '" + RBS_CMN_AsString(expected) + "' got '" + RBS_CMN_AsString(value) + "')") + m.CleanMocks() + return + end if + end for + end for + end if + end for + m.CleanMocks() +end function +function RBS_BTS_CleanMocks() as void + if m.mocks = invalid return + for each id in m.mocks + mock = m.mocks[id] + mock.target.__mocks = invalid + end for + m.mocks = invalid + end function + function RBS_BTS_CleanStubs() as void + if m.stubs = invalid return + for each id in m.stubs + stub = m.stubs[id] + stub.target.__stubs = invalid + end for + m.stubs = invalid + end function + function RBS_BTS_MockFail(methodName, message) as dynamic + if (m.currentResult.isFail) then return m.GetLegacyCompatibleReturnValue(false) ' skip test we already failed + m.currentResult.AddResult("mock failure on '" + methodName + "' : " + message) + return m.GetLegacyCompatibleReturnValue(false) + end function + function RBS_BTS_StubCallback0(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__Stubs["0"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_StubCallback1(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__Stubs["1"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_StubCallback2(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__Stubs["2"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_StubCallback3(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__Stubs["3"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_StubCallback4(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__Stubs["4"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_StubCallback5(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__Stubs["5"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_MockCallback0(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__mocks["0"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_MockCallback1(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__mocks["1"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_MockCallback2(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__mocks["2"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_MockCallback3(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__mocks["3"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_MockCallback4(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__mocks["4"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_MockCallback5(arg1=invalid, arg2=invalid, arg3=invalid, arg4=invalid, arg5=invalid, arg6=invalid, arg7=invalid, arg8=invalid, arg9 =invalid)as dynamic + fake = m.__mocks["5"] + return fake.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end function + function RBS_BTS_rodash_pathsAsArray_(path) + pathRE = CreateObject("roRegex", "\[([0-9]+)\]", "i") + segments = [] + if type(path) = "String" or type(path) = "roString" + dottedPath = pathRE.replaceAll(path, ".\1") + stringSegments = dottedPath.tokenize(".") + for each s in stringSegments + if (Asc(s) >= 48) and (Asc(s) <= 57) + segments.push(s.toInt()) + else + segments.push(s) + end if + end for + else if type(path) = "roList" or type(path) = "roArray" + stringPath = "" + for each s in path + stringPath = stringPath + "." + Box(s).toStr() + end for + segments = m.pathAsArray_(stringPath) + else + segments = invalid + end if + return segments + end function + function RBS_BTS_rodash_get_(aa, path, default=invalid) + if type(aa) <> "roAssociativeArray" and type(aa) <> "roArray" and type(aa) <> "roSGNode" then return default + segments = m.pathAsArray_(path) + if (Type(path) = "roInt" or Type(path) = "roInteger" or Type(path) = "Integer") + path = stri(path).trim() + end if + if segments = invalid then return default + result = invalid + while segments.count() > 0 + key = segments.shift() + if (type(key) = "roInteger") 'it's a valid index + if (aa <> invalid and GetInterface(aa, "ifArray") <> invalid) + value = aa[key] + else if (aa <> invalid and GetInterface(aa, "ifSGNodeChildren") <> invalid) + value = aa.getChild(key) + else if (aa <> invalid and GetInterface(aa, "ifAssociativeArray") <> invalid) + key = tostr(key) + if not aa.doesExist(key) + exit while + end if + value = aa.lookup(key) + else + value = invalid + end if + else + if not aa.doesExist(key) + exit while + end if + value = aa.lookup(key) + end if + if segments.count() = 0 + result = value + exit while + end if + if type(value) <> "roAssociativeArray" and type(value) <> "roArray" and type(value) <> "roSGNode" + exit while + end if + aa = value + end while + if result = invalid then return default + return result + end function +function RBS_CMN_IsXmlElement(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifXMLElement") <> invalid +end function +function RBS_CMN_IsFunction(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifFunction") <> invalid +end function +function RBS_CMN_GetFunction(filename, functionName) as object + if (not RBS_CMN_IsNotEmptyString(functionName)) then return invalid + if (not RBS_CMN_IsNotEmptyString(filename)) then return invalid + mapFunction = RBSFM_getFunctionsForFile(filename) + if mapFunction <> invalid + map = mapFunction() + if (type(map) ="roAssociativeArray") + functionPointer = map[functionName] + return functionPointer + else + return invalid + end if + end if + return invalid +end function +function RBS_CMN_GetFunctionBruteForce(functionName) as object + if (not RBS_CMN_IsNotEmptyString(functionName)) then return invalid + filenames = RBSFM_getFilenames() + for i = 0 to filenames.count() - 1 + filename = filenames[i] + mapFunction = RBSFM_getFunctionsForFile(filename) + if mapFunction <> invalid + map = mapFunction() + if (type(map) ="roAssociativeArray") + functionPointer = map[functionName] + if functionPointer <> invalid + return functionPointer + end if + end if + end if + end for + return invalid +end function +function RBS_CMN_IsBoolean(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifBoolean") <> invalid +end function +function RBS_CMN_IsInteger(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifInt") <> invalid and (Type(value) = "roInt" or Type(value) = "roInteger" or Type(value) = "Integer") +end function +function RBS_CMN_IsFloat(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifFloat") <> invalid +end function +function RBS_CMN_IsDouble(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifDouble") <> invalid +end function +function RBS_CMN_IsLongInteger(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifLongInt") <> invalid +end function +function RBS_CMN_IsNumber(value ) as boolean + return RBS_CMN_IsLongInteger(value) or RBS_CMN_IsDouble(value) or RBS_CMN_IsInteger(value) or RBS_CMN_IsFloat(value) +end function +function RBS_CMN_IsList(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifList") <> invalid +end function +function RBS_CMN_IsArray(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifArray") <> invalid +end function +function RBS_CMN_IsAssociativeArray(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifAssociativeArray") <> invalid +end function +function RBS_CMN_IsSGNode(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifSGNodeChildren") <> invalid +end function +function RBS_CMN_IsString(value ) as boolean + return RBS_CMN_IsValid(value) and GetInterface(value, "ifString") <> invalid +end function +function RBS_CMN_IsNotEmptyString(value ) as boolean + return RBS_CMN_IsString(value) and len(value) > 0 +end function +function RBS_CMN_IsDateTime(value ) as boolean + return RBS_CMN_IsValid(value) and (GetInterface(value, "ifDateTime") <> invalid or Type(value) = "roDateTime") +end function +function RBS_CMN_IsValid(value ) as boolean + return not RBS_CMN_IsUndefined(value) and value <> invalid +end function +function RBS_CMN_IsUndefined(value ) as boolean + return type(value) = "" or Type(value) = "" +end function +function RBS_CMN_ValidStr(obj ) as string + if obj <> invalid and GetInterface(obj, "ifString") <> invalid + return obj + else + return "" + end if +end function +function RBS_CMN_AsString(input ) as string + if RBS_CMN_IsValid(input) = false + return "" + else if RBS_CMN_IsString(input) + return input + else if RBS_CMN_IsInteger(input) or RBS_CMN_IsLongInteger(input) or RBS_CMN_IsBoolean(input) + return input.ToStr() + else if RBS_CMN_IsFloat(input) or RBS_CMN_IsDouble(input) + return Str(input).Trim() + else if type(input) = "roSGNode" + return "Node(" + input.subType() +")" + else if type(input) = "roAssociativeArray" + isFirst = true + text = "{" + if (not isFirst) + text += "," + isFirst = false + end if + for each key in input + text += key + ":" + RBS_CMN_AsString(input[key]) + end for + text += "}" + return text + else + return "" + end if +end function +function RBS_CMN_AsInteger(input ) as integer + if RBS_CMN_IsValid(input) = false + return 0 + else if RBS_CMN_IsString(input) + return input.ToInt() + else if RBS_CMN_IsInteger(input) + return input + else if RBS_CMN_IsFloat(input) or RBS_CMN_IsDouble(input) or RBS_CMN_IsLongInteger(input) + return Int(input) + else + return 0 + end if +end function +function RBS_CMN_AsLongInteger(input ) as longinteger + if RBS_CMN_IsValid(input) = false + return 0 + else if RBS_CMN_IsString(input) + return RBS_CMN_AsInteger(input) + else if RBS_CMN_IsLongInteger(input) or RBS_CMN_IsFloat(input) or RBS_CMN_IsDouble(input) or RBS_CMN_IsInteger(input) + return input + else + return 0 + end if +end function +function RBS_CMN_AsFloat(input ) as float + if RBS_CMN_IsValid(input) = false + return 0.0 + else if RBS_CMN_IsString(input) + return input.ToFloat() + else if RBS_CMN_IsInteger(input) + return (input / 1) + else if RBS_CMN_IsFloat(input) or RBS_CMN_IsDouble(input) or RBS_CMN_IsLongInteger(input) + return input + else + return 0.0 + end if +end function +function RBS_CMN_AsDouble(input ) as double + if RBS_CMN_IsValid(input) = false + return 0.0 + else if RBS_CMN_IsString(input) + return RBS_CMN_AsFloat(input) + else if RBS_CMN_IsInteger(input) or RBS_CMN_IsLongInteger(input) or RBS_CMN_IsFloat(input) or RBS_CMN_IsDouble(input) + return input + else + return 0.0 + end if +end function +function RBS_CMN_AsBoolean(input ) as boolean + if RBS_CMN_IsValid(input) = false + return false + else if RBS_CMN_IsString(input) + return LCase(input) = "true" + else if RBS_CMN_IsInteger(input) or RBS_CMN_IsFloat(input) + return input <> 0 + else if RBS_CMN_IsBoolean(input) + return input + else + return false + end if +end function +function RBS_CMN_AsArray(value ) as object + if RBS_CMN_IsValid(value) + if not RBS_CMN_IsArray(value) + return [value] + else + return value + end if + end if + return [] +end function +function RBS_CMN_IsNullOrEmpty(value ) as boolean + if RBS_CMN_IsString(value) + return Len(value) = 0 + else + return not RBS_CMN_IsValid(value) + end if +end function +function RBS_CMN_FindElementIndexInArray(array , value , compareAttribute = invalid , caseSensitive = false ) as integer + if RBS_CMN_IsArray(array) + for i = 0 to RBS_CMN_AsArray(array).Count() - 1 + compareValue = array[i] + if compareAttribute <> invalid and RBS_CMN_IsAssociativeArray(compareValue) + compareValue = compareValue.LookupCI(compareAttribute) + end if + if RBS_BTS_EqValues(compareValue, value) + return i + end if + item = array[i] + next + end if + return -1 +end function +function RBS_CMN_ArrayContains(array , value , compareAttribute = invalid ) as boolean + return (RBS_CMN_FindElementIndexInArray(array, value, compareAttribute) > -1) +end function +function RBS_CMN_FindElementIndexInNode(node , value ) as integer + if type(node) = "roSGNode" + for i = 0 to node.getChildCount() - 1 + compareValue = node.getChild(i) + if type(compareValue) = "roSGNode" and compareValue.isSameNode(value) + return i + end if + next + end if + return -1 +end function +function RBS_CMN_NodeContains(node , value ) as boolean + return (RBS_CMN_FindElementIndexInNode(node, value) > -1) +end function +function RBS_ItG_GetTestCases(group) as object + if (group.hasSoloTests = true) + return group.soloTestCases + else + return group.testCases + end if +end function +function RBS_ItG_GetRunnableTestSuite(group) as object + testCases = RBS_ItG_GetTestCases(group) + runnableSuite = BaseTestSuite() + runnableSuite.name = group.name + runnableSuite.isLegacy = group.isLegacy = true + if group.testCaseLookup = invalid + group.testCaseLookup = {} + end if + for each testCase in testCases + name = testCase.name + if (testCase.isSolo = true) + name += " [SOLO] " + end if + testFunction = RBS_CMN_GetFunction(group.filename, testCase.funcName) + runnableSuite.addTest(name, testFunction, testCase.funcName) + group.testCaseLookup[name] = testCase + end for + runnableSuite.SetUp = RBS_CMN_GetFunction(group.filename, group.setupFunctionName) + runnableSuite.TearDown = RBS_CMN_GetFunction(group.filename, group.teardownFunctionName) + runnableSuite.BeforeEach = RBS_CMN_GetFunction(group.filename, group.beforeEachFunctionName) + runnableSuite.AfterEach = RBS_CMN_GetFunction(group.filename, group.afterEachFunctionName) + return runnableSuite +end function +function ItemGenerator(scheme as object) as object + this = {} + this.getItem = RBS_IG_GetItem + this.getAssocArray = RBS_IG_GetAssocArray + this.getArray = RBS_IG_GetArray + this.getSimpleType = RBS_IG_GetSimpleType + this.getInteger = RBS_IG_GetInteger + this.getFloat = RBS_IG_GetFloat + this.getString = RBS_IG_GetString + this.getBoolean = RBS_IG_GetBoolean + if not RBS_CMN_IsValid(scheme) + return invalid + end if + return this.getItem(scheme) +end function +function RBS_IG_GetItem(scheme as object) as object + item = invalid + if RBS_CMN_IsAssociativeArray(scheme) + item = m.getAssocArray(scheme) + else if RBS_CMN_IsArray(scheme) + item = m.getArray(scheme) + else if RBS_CMN_IsString(scheme) + item = m.getSimpleType(lCase(scheme)) + end if + return item +end function +function RBS_IG_GetAssocArray(scheme as object) as object + item = {} + for each key in scheme + if not item.DoesExist(key) + item[key] = m.getItem(scheme[key]) + end if + end for + return item +end function +function RBS_IG_GetArray(scheme as object) as object + item = [] + for each key in scheme + item.Push(m.getItem(key)) + end for + return item +end function +function RBS_IG_GetSimpleType(typeStr as string) as object + item = invalid + if typeStr = "integer" or typeStr = "int" or typeStr = "roint" + item = m.getInteger() + else if typeStr = "float" or typeStr = "rofloat" + item = m.getFloat() + else if typeStr = "string" or typeStr = "rostring" + item = m.getString(10) + else if typeStr = "boolean" or typeStr = "roboolean" + item = m.getBoolean() + end if + return item +end function +function RBS_IG_GetBoolean() as boolean + return RBS_CMN_AsBoolean(Rnd(2) \ Rnd(2)) +end function +function RBS_IG_GetInteger(seed = 100 as integer) as integer + return Rnd(seed) +end function +function RBS_IG_GetFloat() as float + return Rnd(0) +end function +function RBS_IG_GetString(seed as integer) as string + item = "" + if seed > 0 + stringLength = Rnd(seed) + for i = 0 to stringLength + chType = Rnd(3) + if chType = 1 'Chr(48-57) - numbers + chNumber = 47 + Rnd(10) + else if chType = 2 'Chr(65-90) - Uppercase Letters + chNumber = 64 + Rnd(26) + else 'Chr(97-122) - Lowercase Letters + chNumber = 96 + Rnd(26) + end if + item = item + Chr(chNumber) + end for + end if + return item +end function +function UnitTestRuntimeConfig() + this = {} + this.CreateSuites = RBS_CreateSuites + this.hasSoloSuites = false + this.hasSoloGroups = false + this.hasSoloTests = false + this.suites = this.CreateSuites() + return this +end function +function RBS_CreateSuites() + suites = RBSFM_getTestSuitesForProject() + includedSuites = [] + for i = 0 to suites.count() -1 + suite = suites[i] + if (suite.valid) + if (suite.isSolo) + m.hasSoloSuites = true + end if + if (suite.hasSoloTests = true) + m.hasSoloTests = true + end if + if (suite.hasSoloGroups = true) + m.hasSoloGroups = true + end if + includedSuites.Push(suite) + else + ? "ERROR! suite was not valid - ignoring" + end if + end for + return includedSuites +end function +function RBS_STATS_CreateTotalStatistic() as object + statTotalItem = { + Suites : [] + Time : 0 + Total : 0 + Correct : 0 + Fail : 0 + Ignored : 0 + Crash : 0 + IgnoredTestNames: [] + } + return statTotalItem +end function +function RBS_STATS_MergeTotalStatistic(stat1, stat2) as void + for each suite in stat2.Suites + stat1.Suites.push(suite) + end for + stat1.Time += stat2.Time + stat1.Total += stat2.Total + stat1.Correct += stat2.Correct + stat1.Fail += stat2.Fail + stat1.Crash += stat2.Crash + stat1.Ignored += stat2.Ignored + stat1.IgnoredTestNames.append(stat2.IgnoredTestNames) +end function +function RBS_STATS_CreateSuiteStatistic(name as string) as object + statSuiteItem = { + Name : name + Tests : [] + Time : 0 + Total : 0 + Correct : 0 + Fail : 0 + Crash : 0 + Ignored : 0 + IgnoredTestNames:[] + } + return statSuiteItem +end function +function RBS_STATS_CreateTestStatistic(name as string, result = "Success" as string, time = 0 as integer, errorCode = 0 as integer, errorMessage = "" as string) as object + statTestItem = { + Name : name + Result : result + Time : time + Error : { + Code : errorCode + Message : errorMessage + } + } + return statTestItem +end function +sub RBS_STATS_AppendTestStatistic(statSuiteObj as object, statTestObj as object) + if RBS_CMN_IsAssociativeArray(statSuiteObj) and RBS_CMN_IsAssociativeArray(statTestObj) + statSuiteObj.Tests.Push(statTestObj) + if RBS_CMN_IsInteger(statTestObj.time) + statSuiteObj.Time = statSuiteObj.Time + statTestObj.Time + end if + statSuiteObj.Total = statSuiteObj.Total + 1 + if lCase(statTestObj.Result) = "success" + statSuiteObj.Correct = statSuiteObj.Correct + 1 + else if lCase(statTestObj.result) = "fail" + statSuiteObj.Fail = statSuiteObj.Fail + 1 + else + statSuiteObj.crash = statSuiteObj.crash + 1 + end if + end if +end sub +sub RBS_STATS_AppendSuiteStatistic(statTotalObj as object, statSuiteObj as object) + if RBS_CMN_IsAssociativeArray(statTotalObj) and RBS_CMN_IsAssociativeArray(statSuiteObj) + statTotalObj.Suites.Push(statSuiteObj) + statTotalObj.Time = statTotalObj.Time + statSuiteObj.Time + if RBS_CMN_IsInteger(statSuiteObj.Total) + statTotalObj.Total = statTotalObj.Total + statSuiteObj.Total + end if + if RBS_CMN_IsInteger(statSuiteObj.Correct) + statTotalObj.Correct = statTotalObj.Correct + statSuiteObj.Correct + end if + if RBS_CMN_IsInteger(statSuiteObj.Fail) + statTotalObj.Fail = statTotalObj.Fail + statSuiteObj.Fail + end if + if RBS_CMN_IsInteger(statSuiteObj.Crash) + statTotalObj.Crash = statTotalObj.Crash + statSuiteObj.Crash + end if + end if +end sub +function UnitTestCase(name as string, func as dynamic, funcName as string, isSolo as boolean, isIgnored as boolean, lineNumber as integer, params = invalid, paramTestIndex =0, paramLineNumber = 0) + this = {} + this.isSolo = isSolo + this.func = func + this.funcName = funcName + this.isIgnored = isIgnored + this.name = name + this.lineNumber = lineNumber + this.paramLineNumber = paramLineNumber + this.assertIndex = 0 + this.assertLineNumberMap = {} + this.AddAssertLine = RBS_TC_AddAssertLine + this.getTestLineIndex = 0 + this.rawParams = params + this.paramTestIndex = paramTestIndex + this.isParamTest = false + if (params <> invalid) + this.name += stri(this.paramTestIndex) + end if + return this +end function +function RBS_TC_AddAssertLine(lineNumber as integer) + m.assertLineNumberMap[stri(m.assertIndex).trim()] = lineNumber + m.assertIndex++ +end function +function RBS_TC_GetAssertLine(testCase, index) + if (testCase.assertLineNumberMap.doesExist(stri(index).trim())) + return testCase.assertLineNumberMap[stri(index).trim()] + else + return testCase.lineNumber + end if +end function +function Logger(config) as object + this = {} + this.config = config + this.verbosityLevel = { + basic : 0 + normal : 1 + verbose : 2 + } + this.verbosity = this.config.logLevel + this.PrintStatistic = RBS_LOGGER_PrintStatistic + this.PrintMetaSuiteStart = RBS_LOGGER_PrintMetaSuiteStart + this.PrintSuiteStatistic = RBS_LOGGER_PrintSuiteStatistic + this.PrintTestStatistic = RBS_LOGGER_PrintTestStatistic + this.PrintStart = RBS_LOGGER_PrintStart + this.PrintEnd = RBS_LOGGER_PrintEnd + this.PrintSuiteStart = RBS_LOGGER_PrintSuiteStart + return this +end function +sub RBS_LOGGER_PrintStatistic(statObj as object) + m.PrintStart() + previousfile = invalid + for each testSuite in statObj.Suites + if (not statObj.testRunHasFailures or ((not m.config.showOnlyFailures) or testSuite.fail > 0 or testSuite.crash > 0)) + if (testSuite.metaTestSuite.filePath <> previousfile) + m.PrintMetaSuiteStart(testSuite.metaTestSuite) + previousfile = testSuite.metaTestSuite.filePath + end if + m.PrintSuiteStatistic(testSuite, statObj.testRunHasFailures) + end if + end for + ? "" + m.PrintEnd() + ? "Total = "; RBS_CMN_AsString(statObj.Total); " ; Passed = "; statObj.Correct; " ; Failed = "; statObj.Fail; " ; Ignored = "; statObj.Ignored + ? " Time spent: "; statObj.Time; "ms" + ? "" + ? "" + if (statObj.ignored > 0) + ? "IGNORED TESTS:" + for each ignoredItemName in statObj.IgnoredTestNames + print ignoredItemName + end for + end if + if (statObj.Total = statObj.Correct) + overrallResult = "Success" + else + overrallResult = "Fail" + end if + ? "RESULT: "; overrallResult +end sub +sub RBS_LOGGER_PrintSuiteStatistic(statSuiteObj as object, hasFailures) + m.PrintSuiteStart(statSuiteObj.Name) + for each testCase in statSuiteObj.Tests + if (not hasFailures or ((not m.config.showOnlyFailures) or testCase.Result <> "Success")) + m.PrintTestStatistic(testCase) + end if + end for + ? " |" +end sub +sub RBS_LOGGER_PrintTestStatistic(testCase as object) + metaTestCase = testCase.metaTestCase + if (LCase(testCase.Result) <> "success") + testChar = "-" + assertIndex = metaTestCase.testResult.failedAssertIndex + locationLine = StrI(RBS_TC_GetAssertLine(metaTestCase,assertIndex)).trim() + else + testChar = "|" + locationLine = StrI(metaTestCase.lineNumber).trim() + end if + locationText = testCase.filePath.trim() + "(" + locationLine + ")" + insetText = "" + if (metaTestcase.isParamTest <> true) + messageLine = RBS_LOGGER_FillText(" " + testChar + " |--" + metaTestCase.Name + " : ", ".", 80) + ? messageLine ; testCase.Result + else if ( metaTestcase.paramTestIndex = 0) + name = metaTestCase.Name + if (len(name) > 1 and right(name, 1) = "0") + name = left(name, len(name) - 1) + end if + ? " " + testChar + " |--" + name+ " : " + end if + if (metaTestcase.isParamTest = true) + insetText = " " + messageLine = RBS_LOGGER_FillText(" " + testChar + insetText + " |--" + formatJson(metaTestCase.rawParams) + " : ", ".", 80) + ? messageLine ; testCase.Result + end if + if LCase(testCase.Result) <> "success" + ? " | "; insettext ;" |--Location: "; locationText + if (metaTestcase.isParamTest = true) + ? " | "; insettext ;" |--Param Line: "; StrI(metaTestCase.paramlineNumber).trim() + end if + ? " | "; insettext ;" |--Error Message: "; testCase.Error.Message + end if +end sub +function RBS_LOGGER_FillText(text as string, fillChar = " ", numChars = 40) as string + if (len(text) >= numChars) + text = left(text, numChars - 5) + "..." + fillChar + fillChar + else + numToFill= numChars - len(text) -1 + for i = 0 to numToFill + text += fillChar + end for + end if + return text +end function +sub RBS_LOGGER_PrintStart() + ? "" + ? "[START TEST REPORT]" + ? "" +end sub +sub RBS_LOGGER_PrintEnd() + ? "" + ? "[END TEST REPORT]" + ? "" +end sub +sub RBS_LOGGER_PrintSuiteSetUp(sName as string) + if m.verbosity = m.verbosityLevel.verbose + ? "=================================================================" + ? "=== SetUp "; sName; " suite." + ? "=================================================================" + end if +end sub +sub RBS_LOGGER_PrintMetaSuiteStart(metaTestSuite) + ? metaTestSuite.name; " (" ; metaTestSuite.filePath + "(1))" +end sub +sub RBS_LOGGER_PrintSuiteStart(sName as string) + ? " |-" ; sName +end sub +sub RBS_LOGGER_PrintSuiteTearDown(sName as string) + if m.verbosity = m.verbosityLevel.verbose + ? "=================================================================" + ? "=== TearDown "; sName; " suite." + ? "=================================================================" + end if +end sub +sub RBS_LOGGER_PrintTestSetUp(tName as string) + if m.verbosity = m.verbosityLevel.verbose + ? "----------------------------------------------------------------" + ? "--- SetUp "; tName; " test." + ? "----------------------------------------------------------------" + end if +end sub +sub RBS_LOGGER_PrintTestTearDown(tName as string) + if m.verbosity = m.verbosityLevel.verbose + ? "----------------------------------------------------------------" + ? "--- TearDown "; tName; " test." + ? "----------------------------------------------------------------" + end if +end sub +function UnitTestResult() as object + this = {} + this.messages = CreateObject("roArray", 0, true) + this.isFail = false + this.currentAssertIndex = 0 + this.failedAssertIndex = 0 + this.Reset = RBS_TRes_Reset + this.AddResult = RBS_TRes_AddResult + this.GetResult = RBS_TRes_GetResult + return this +end function +function RBS_TRes_Reset() as void + m.isFail = false + m.messages = CreateObject("roArray", 0, true) +end function +function RBS_TRes_AddResult(message as string) as string + if (message <> "") + m.messages.push(message) + if (not m.isFail) + m.failedAssertIndex = m.currentAssertIndex + end if + m.isFail = true + end if + m.currentAssertIndex++ + return message +end function +function RBS_TRes_GetResult() as string + if (m.isFail) + msg = m.messages.peek() + if (msg <> invalid) + return msg + else + return "unknown test failure" + end if + else + return "" + end if +end function +function RBS_TR_TestRunner(args = {}) as object + this = {} + this.testScene = args.testScene + this.nodeContext = args.nodeContext + fs = CreateObject("roFileSystem") + defaultConfig = { + logLevel : 1, + testsDirectory: "pkg:/source/Tests", + testFilePrefix: "Test__", + failFast: false, + showOnlyFailures: false, + maxLinesWithoutSuiteDirective: 100 + } + rawConfig = invalid + config = invalid + if (args.testConfigPath <> invalid and fs.Exists(args.testConfigPath)) + ? "Loading test config from " ; args.testConfigPath + rawConfig = ReadAsciiFile(args.testConfigPath) + else if (fs.Exists("pkg:/source/tests/testconfig.json")) + ? "Loading test config from default location : pkg:/source/tests/testconfig.json" + rawConfig = ReadAsciiFile("pkg:/source/tests/testconfig.json") + else + ? "None of the testConfig.json locations existed" + end if + if (rawConfig <> invalid) + config = ParseJson(rawConfig) + end if + if (config = invalid or not RBS_CMN_IsAssociativeArray(config) or RBS_CMN_IsNotEmptyString(config.rawtestsDirectory)) + ? "WARNING : specified config is invalid - using default" + config = defaultConfig + end if + if (args.showOnlyFailures <> invalid) + config.showOnlyFailures = args.showOnlyFailures = "true" + end if + if (args.failFast <> invalid) + config.failFast = args.failFast = "true" + end if + this.testUtilsDecoratorMethodName = args.testUtilsDecoratorMethodName + this.config = config + this.config.testsDirectory = config.testsDirectory + this.logger = Logger(this.config) + this.global = args.global + this.Run = RBS_TR_Run + return this +end function +sub RBS_TR_Run() + if type(RBSFM_getTestSuitesForProject) <> "Function" + ? " ERROR! RBSFM_getTestSuitesForProject is not found! That looks like you didn't run the preprocessor as part of your test process. Please refer to the docs." + return + end if + totalStatObj = RBS_STATS_CreateTotalStatistic() + m.runtimeConfig = UnitTestRuntimeConfig() + m.runtimeConfig.global = m.global + totalStatObj.testRunHasFailures = false + for each metaTestSuite in m.runtimeConfig.suites + if (m.runtimeConfig.hasSoloTests = true) + if (metaTestSuite.hasSoloTests <> true) + if (m.config.logLevel = 2) + ? "TestSuite " ; metaTestSuite.name ; " Is filtered because it has no solo tests" + end if + goto skipSuite + end if + else if (m.runtimeConfig.hasSoloSuites) + if (metaTestSuite.isSolo <> true) + if (m.config.logLevel = 2) + ? "TestSuite " ; metaTestSuite.name ; " Is filtered due to solo flag" + end if + goto skipSuite + end if + end if + if (metaTestSuite.isIgnored = true) + if (m.config.logLevel = 2) + ? "Ignoring TestSuite " ; metaTestSuite.name ; " Due to Ignore flag" + end if + totalstatobj.ignored ++ + totalStatObj.IgnoredTestNames.push("|-" + metaTestSuite.name + " [WHOLE SUITE]") + goto skipSuite + end if + if (metaTestSuite.isNodeTest = true and metaTestSuite.nodeTestFileName <> "") + ? " +++++RUNNING NODE TEST" + nodeType = metaTestSuite.nodeTestFileName + ? " node type is " ; nodeType + node = m.testScene.CallFunc("Rooibos_CreateTestNode", nodeType) + if (type(node) = "roSGNode" and node.subType() = nodeType) + args = { + "metaTestSuite": metaTestSuite + "testUtilsDecoratorMethodName": m.testUtilsDecoratorMethodName + "config": m.config + "runtimeConfig": m.runtimeConfig + } + nodeStatResults = node.callFunc("Rooibos_RunNodeTests", args) + RBS_STATS_MergeTotalStatistic(totalStatObj, nodeStatResults) + m.testScene.RemoveChild(node) + else + ? " ERROR!! - could not create node required to execute tests for " ; metaTestSuite.name + ? " Node of type " ; nodeType ; " was not found/could not be instantiated" + end if + else + if (metaTestSuite.hasIgnoredTests) + totalStatObj.IgnoredTestNames.push("|-" + metaTestSuite.name) + end if + RBS_RT_RunItGroups(metaTestSuite, totalStatObj, m.testUtilsDecoratorMethodName, m.config, m.runtimeConfig, m.nodeContext) + end if + skipSuite: + end for + m.logger.PrintStatistic(totalStatObj) + RBS_TR_SendHomeKeypress() +end sub +sub RBS_RT_RunItGroups(metaTestSuite, totalStatObj, testUtilsDecoratorMethodName, config, runtimeConfig, nodeContext = invalid) + if (testUtilsDecoratorMethodName <> invalid) + testUtilsDecorator = RBS_CMN_GetFunctionBruteForce(testUtilsDecoratorMethodName) + if (not RBS_CMN_IsFunction(testUtilsDecorator)) + ? "[ERROR] Test utils decorator method `" ; testUtilsDecoratorMethodName ;"` was not in scope! for testSuite: " + metaTestSuite.name + end if + end if + for each itGroup in metaTestSuite.itGroups + testSuite = RBS_ItG_GetRunnableTestSuite(itGroup) + if (nodeContext <> invalid) + testSuite.node = nodeContext + testSuite.global = nodeContext.global + testSuite.top = nodeContext.top + end if + if (RBS_CMN_IsFunction(testUtilsDecorator)) + testUtilsDecorator(testSuite) + end if + totalStatObj.Ignored += itGroup.ignoredTestCases.count() + if (itGroup.isIgnored = true) + if (config.logLevel = 2) + ? "Ignoring itGroup " ; itGroup.name ; " Due to Ignore flag" + end if + totalStatObj.ignored += itGroup.testCases.count() + totalStatObj.IgnoredTestNames.push(" |-" + itGroup.name + " [WHOLE GROUP]") + goto skipItGroup + else + if (itGroup.ignoredTestCases.count() > 0) + totalStatObj.IgnoredTestNames.push(" |-" + itGroup.name) + totalStatObj.ignored += itGroup.ignoredTestCases.count() + for each testCase in itGroup.ignoredTestCases + if (testcase.isParamTest <> true) + totalStatObj.IgnoredTestNames.push(" | |--" + testCase.name) + else if (testcase.paramTestIndex = 0) + testCaseName = testCase.Name + if (len(testCaseName) > 1 and right(testCaseName, 1) = "0") + testCaseName = left(testCaseName, len(testCaseName) - 1) + end if + totalStatObj.IgnoredTestNames.push(" | |--" + testCaseName) + end if + end for + end if + end if + if (runtimeConfig.hasSoloTests) + if (itGroup.hasSoloTests <> true) + if (config.logLevel = 2) + ? "Ignoring itGroup " ; itGroup.name ; " Because it has no solo tests" + end if + goto skipItGroup + end if + else if (runtimeConfig.hasSoloGroups) + if (itGroup.isSolo <> true) + goto skipItGroup + end if + end if + if (testSuite.testCases.Count() = 0) + if (config.logLevel = 2) + ? "Ignoring TestSuite " ; itGroup.name ; " - NO TEST CASES" + end if + goto skipItGroup + end if + if RBS_CMN_IsFunction(testSuite.SetUp) + testSuite.SetUp() + end if + RBS_RT_RunTestCases(metaTestSuite, itGroup, testSuite, totalStatObj, config, runtimeConfig) + if RBS_CMN_IsFunction(testSuite.TearDown) + testSuite.TearDown() + end if + if (totalStatObj.testRunHasFailures = true and config.failFast = true) + exit for + end if + skipItGroup: + end for +end sub +sub RBS_RT_RunTestCases(metaTestSuite, itGroup, testSuite, totalStatObj, config, runtimeConfig) + suiteStatObj = RBS_STATS_CreateSuiteStatistic(itGroup.Name) + testSuite.global = runtimeConfig.global + for each testCase in testSuite.testCases + metaTestCase = itGroup.testCaseLookup[testCase.Name] + if (runtimeConfig.hasSoloTests and not metaTestCase.isSolo) + goto skipTestCase + end if + if RBS_CMN_IsFunction(testSuite.beforeEach) + testSuite.beforeEach() + end if + testTimer = CreateObject("roTimespan") + testStatObj = RBS_STATS_CreateTestStatistic(testCase.Name) + testSuite.testCase = testCase.Func + testStatObj.filePath = metaTestSuite.filePath + testStatObj.metaTestCase = metaTestCase + testSuite.currentResult = UnitTestResult() + testStatObj.metaTestCase.testResult = testSuite.currentResult + if (metaTestCase.isParamsValid) + if (metaTestCase.isParamTest) + testCaseParams = [] + for paramIndex = 0 to metaTestCase.rawParams.count() + paramValue = metaTestCase.rawParams[paramIndex] + if type(paramValue) = "roString" and len(paramValue) >= 8 and left(paramValue, 8) = "#RBSNode" + nodeType = "ContentNode" + paramDirectiveArgs = paramValue.split("|") + if paramDirectiveArgs.count() > 1 + nodeType = paramDirectiveArgs[1] + end if + paramValue = createObject("roSGNode", nodeType) + end if + testCaseParams.push(paramValue) + end for + if (metaTestCase.expectedNumberOfParams = 1) + testSuite.testCase(testCaseParams[0]) + else if (metaTestCase.expectedNumberOfParams = 2) + testSuite.testCase(testCaseParams[0], testCaseParams[1]) + else if (metaTestCase.expectedNumberOfParams = 3) + testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2]) + else if (metaTestCase.expectedNumberOfParams = 4) + testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3]) + else if (metaTestCase.expectedNumberOfParams = 5) + testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3], testCaseParams[4]) + else if (metaTestCase.expectedNumberOfParams = 6) + testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3], testCaseParams[4], testCaseParams[5]) + end if + else + testSuite.testCase() + end if + else + testSuite.Fail("Could not parse args for test ") + end if + if testSuite.isAutoAssertingMocks = true + testSuite.AssertMocks() + testSuite.CleanMocks() + testSuite.CleanStubs() + end if + runResult = testSuite.currentResult.GetResult() + if runResult <> "" + testStatObj.Result = "Fail" + testStatObj.Error.Code = 1 + testStatObj.Error.Message = runResult + else + testStatObj.Result = "Success" + end if + testStatObj.Time = testTimer.TotalMilliseconds() + RBS_STATS_AppendTestStatistic(suiteStatObj, testStatObj) + if RBS_CMN_IsFunction(testSuite.afterEach) + testSuite.afterEach() + end if + if testStatObj.Result <> "Success" + totalStatObj.testRunHasFailures = true + end if + if testStatObj.Result = "Fail" and config.failFast = true + exit for + end if + skipTestCase: + end for + suiteStatObj.metaTestSuite = metaTestSuite + RBS_STATS_AppendSuiteStatistic(totalStatObj, suiteStatObj) +end sub +sub RBS_TR_SendHomeKeypress() + ut = CreateObject("roUrlTransfer") + ut.SetUrl("http://localhost:8060/keypress/Home") + ut.PostFromString("") +end sub +function Rooibos_RunNodeTests(args) as object + ? " RUNNING NODE TESTS" + totalStatObj = RBS_STATS_CreateTotalStatistic() + RBS_RT_RunItGroups(args.metaTestSuite, totalStatObj, args.testUtilsDecoratorMethodName, args.config, args.runtimeConfig, m) + return totalStatObj +end function +function Rooibos_CreateTestNode(nodeType) as object + node = createObject("roSGNode", nodeType) + if (type(node) = "roSGNode" and node.subType() = nodeType) + m.top.AppendChild(node) + return node + else + ? " Error creating test node of type " ; nodeType + return invalid + end if +end function \ No newline at end of file diff --git a/outRun/.roku-deploy-staging/source/tests/testConfig.json b/outRun/.roku-deploy-staging/source/tests/testConfig.json new file mode 100644 index 00000000..8380fe04 --- /dev/null +++ b/outRun/.roku-deploy-staging/source/tests/testConfig.json @@ -0,0 +1,5 @@ +{ + "logLevel": 1, + "failFast": false, + "showOnlyFailures": true +} diff --git a/outRun/.roku-deploy-staging/source/tests/testUtils.brs b/outRun/.roku-deploy-staging/source/tests/testUtils.brs new file mode 100644 index 00000000..54d2476d --- /dev/null +++ b/outRun/.roku-deploy-staging/source/tests/testUtils.brs @@ -0,0 +1,8 @@ + +sub AddTestUtils(testCase) + 'add your own test utils you want access to when testing here + + 'e.g. + 'testCase.testUtils = TestUtils() + 'testCase.r = rodash() +end sub \ No newline at end of file diff --git a/outRun/.roku-deploy-staging/source/tests/verifyBeforeEachAfterEach.brs b/outRun/.roku-deploy-staging/source/tests/verifyBeforeEachAfterEach.brs new file mode 100644 index 00000000..5bfae93e --- /dev/null +++ b/outRun/.roku-deploy-staging/source/tests/verifyBeforeEachAfterEach.brs @@ -0,0 +1,100 @@ +'@TestSuite [BEAER] BeforeEach and AfterEach Running + +'@Setup +function BEAER_Setup() as void + ? "!!! Setup" +end function + + +'@TearDown +function BEAER_TearDown() as void + ? "!!! TearDown" +end function + +'@BeforeEach +function BEAER_BeforeEach() as void + ? "!!! Before" +end function + + +'@AfterEach +function BEAER_AfterEach() as void + ? "!!! After" +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests group 1 - should have global before/after +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test 1 +function BEAER_group1_1() as void + m.AssertTrue(true) +end function + +'@Test 2 +function BEAER_group1_2() as void + m.AssertTrue(true) +end function + +'@Test 3 +'@Params["a"] +'@Params["b"] +'@Params["c"] +function BEAER_group1_3(values) as void + m.AssertTrue(true) +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests group 2 - should have group before after +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@BeforeEach +function BEAER_group2_BeforeEach() as void + ? "!!! Before GROUP 2" +end function + + +'@AfterEach +function BEAER_group2_AfterEach() as void + ? "!!! After GROUP 2" +end function + +'@Test 1 +function BEAER_group2_1() as void + m.AssertTrue(true) +end function + +'@Test 2 +function BEAER_group2_2() as void + m.AssertTrue(true) +end function + +'@Test 3 +'@Params["a"] +'@Params["b"] +'@Params["c"] +function BEAER_group2_3(values) as void + m.AssertTrue(true) +end function + +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +'@It tests group 3 - should have global before/after +'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +'@Test 1 +function BEAER_group3_1() as void + m.AssertTrue(true) +end function + +'@Test 2 +function BEAER_group3_2() as void + m.AssertTrue(true) +end function + +'@Test 3 +'@Params["a"] +'@Params["b"] +'@Params["c"] +function BEAER_group3_3(values) as void + m.AssertTrue(true) +end function \ No newline at end of file diff --git a/package.json b/package.json index 3a3dea81..8073cdcd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rooibos", - "version": "2.1.4", + "version": "2.2.0", "description": "simple, flexible, fun brightscript test framework for roku scenegraph apps", "main": "index.js", "directories": { diff --git a/samples/example/source/tests/rooibos/rooibosDist.brs b/samples/example/source/tests/rooibos/rooibosDist.brs index 25173ce1..9495ed55 100644 --- a/samples/example/source/tests/rooibos/rooibosDist.brs +++ b/samples/example/source/tests/rooibos/rooibosDist.brs @@ -1,19 +1,23 @@ '/** ' * rooibos - simple, flexible, fun brightscript test framework for roku scenegraph apps -' * @version v2.1.4 +' * @version v2.2.0 ' * @link https://github.com/georgejecook/rooibos#readme ' * @license MIT ' */ -function Rooibos__Init(preTestSetup = invalid, testUtilsDecoratorMethodName = invalid, testSceneName = "TestsScene") as void +function Rooibos__Init(preTestSetup = invalid, testUtilsDecoratorMethodName = invalid, testSceneName = invalid, nodeContext = invalid) as void args = {} if createObject("roAPPInfo").IsDev() <> true then ? " not running in dev mode! - rooibos tests only support sideloaded builds - aborting" return end if args.testUtilsDecoratorMethodName = testUtilsDecoratorMethodName + args.nodeContext = nodeContext screen = CreateObject("roSGScreen") m.port = CreateObject("roMessagePort") screen.setMessagePort(m.port) + if testSceneName = invalid + testSceneName = "TestsScene" + end if ? "Starting test using test scene with name TestsScene" ; testSceneName scene = screen.CreateScene(testSceneName) scene.id = "ROOT" @@ -1924,6 +1928,7 @@ end function function RBS_TR_TestRunner(args = {}) as object this = {} this.testScene = args.testScene + this.nodeContext = args.nodeContext fs = CreateObject("roFileSystem") defaultConfig = { logLevel : 1, @@ -2021,7 +2026,7 @@ sub RBS_TR_Run() if (metaTestSuite.hasIgnoredTests) totalStatObj.IgnoredTestNames.push("|-" + metaTestSuite.name) end if - RBS_RT_RunItGroups(metaTestSuite, totalStatObj, m.testUtilsDecoratorMethodName, m.config, m.runtimeConfig) + RBS_RT_RunItGroups(metaTestSuite, totalStatObj, m.testUtilsDecoratorMethodName, m.config, m.runtimeConfig, m.nodeContext) end if skipSuite: end for diff --git a/src/Rooibos.brs b/src/Rooibos.brs index 9b8479f0..fe39fd2b 100644 --- a/src/Rooibos.brs +++ b/src/Rooibos.brs @@ -14,8 +14,9 @@ ' * Use this to add things like, rodash, common test utils, etc ' * @param testsSceneName as string - name of scene to create. All unit tests run in the scene thread ' * and therefore require a screen and scene are created. +' * @param nodeContext as object - this is the global scope of your tests - so where anonymous methods will run from. This should be m ' */ -function Rooibos__Init(preTestSetup = invalid, testUtilsDecoratorMethodName = invalid, testSceneName = "TestsScene") as void +function Rooibos__Init(preTestSetup = invalid, testUtilsDecoratorMethodName = invalid, testSceneName = invalid, nodeContext = invalid) as void args = {} if createObject("roAPPInfo").IsDev() <> true then ? " not running in dev mode! - rooibos tests only support sideloaded builds - aborting" @@ -23,11 +24,14 @@ function Rooibos__Init(preTestSetup = invalid, testUtilsDecoratorMethodName = i end if args.testUtilsDecoratorMethodName = testUtilsDecoratorMethodName + args.nodeContext = nodeContext screen = CreateObject("roSGScreen") m.port = CreateObject("roMessagePort") screen.setMessagePort(m.port) - + if testSceneName = invalid + testSceneName = "TestsScene" + end if ? "Starting test using test scene with name TestsScene" ; testSceneName scene = screen.CreateScene(testSceneName) scene.id = "ROOT" diff --git a/src/Rooibos_TestRunner.brs b/src/Rooibos_TestRunner.brs index 09846613..0958eb1d 100644 --- a/src/Rooibos_TestRunner.brs +++ b/src/Rooibos_TestRunner.brs @@ -14,6 +14,7 @@ function RBS_TR_TestRunner(args = {}) as object this = {} this.testScene = args.testScene + this.nodeContext = args.nodeContext fs = CreateObject("roFileSystem") defaultConfig = { logLevel : 1, @@ -142,7 +143,7 @@ sub RBS_TR_Run() if (metaTestSuite.hasIgnoredTests) totalStatObj.IgnoredTestNames.push("|-" + metaTestSuite.name) end if - RBS_RT_RunItGroups(metaTestSuite, totalStatObj, m.testUtilsDecoratorMethodName, m.config, m.runtimeConfig) + RBS_RT_RunItGroups(metaTestSuite, totalStatObj, m.testUtilsDecoratorMethodName, m.config, m.runtimeConfig, m.nodeContext) end if skipSuite: end for