From ece099e7b63d8cfafbc070244bb4164b3cebc91e Mon Sep 17 00:00:00 2001 From: RokuRnD Date: Wed, 13 Dec 2017 17:27:55 +0200 Subject: [PATCH] update v.1.2.2 - Add a way to skip tests in the BrightScript Unit Test framework - fix EqValues method of BaseTestSuite for arrays. https://github.com/rokudev/unit-testing-framework/pull/18 - version update. --- UnitTestFramework.brs | 4145 +++++++++++++++++++++-------------------- 1 file changed, 2091 insertions(+), 2054 deletions(-) diff --git a/UnitTestFramework.brs b/UnitTestFramework.brs index 55a1ec4..6ee344a 100644 --- a/UnitTestFramework.brs +++ b/UnitTestFramework.brs @@ -2,2062 +2,2099 @@ '* Roku Unit Testing Framework (BETA) '* A beta tool for automating test suites for Roku channels. '* -'* Build Version: 1.2.1 -'* Build Date: 04/03/2017 +'* Build Version: 1.2.2 +'* Build Date: 12/13/2017 '* '* Public Documentation is avaliable on GitHub: '* https://github.com/rokudev/unit-testing-framework '* '***************************************************************** -'***************************************************************** -'* Copyright Roku 2011-2017 -'* All Rights Reserved -'***************************************************************** - -' Functions in this file: -' -' BaseTestSuite -' BTS__AddTest -' BTS__CreateTest -' BTS__Fail -' BTS__AssertFalse -' BTS__AssertTrue -' BTS__AssertEqual -' BTS__AssertNotEqual -' BTS__AssertInvalid -' BTS__AssertNotInvalid -' BTS__AssertAAHasKey -' BTS__AssertAANotHasKey -' BTS__AssertAAHasKeys -' BTS__AssertAANotHasKeys -' BTS__AssertArrayContains -' BTS__AssertArrayNotContains -' BTS__AssertArrayContainsSubset -' BTS__AssertArrayNotContainsSubset -' BTS__AssertArrayCount -' BTS__AssertArrayNotCount -' BTS__AssertEmpty -' BTS__AssertNotEmpty -' BTS__AssertArrayContainsOnly -' BTS__EqValues -' BTS__EqAssocArray -' BTS__EqArray -' BTS__BaseComparator - -'---------------------------------------------------------------- -' Main function. Create BaseTestSuite object. -' -' @return A BaseTestSuite object. -'---------------------------------------------------------------- -function BaseTestSuite() - - this = {} - this.Name = "BaseTestSuite" - 'Test Cases methods - this.testCases = [] - this.addTest = BTS__AddTest - this.createTest = BTS__CreateTest - - 'Assertion methods which determine test failure - this.fail = BTS__Fail - this.assertFalse = BTS__AssertFalse - this.assertTrue = BTS__AssertTrue - this.assertEqual = BTS__AssertEqual - this.assertNotEqual = BTS__AssertNotEqual - this.assertInvalid = BTS__AssertInvalid - this.assertNotInvalid = BTS__AssertNotInvalid - this.assertAAHasKey = BTS__AssertAAHasKey - this.assertAANotHasKey = BTS__AssertAANotHasKey - this.assertAAHasKeys = BTS__AssertAAHasKeys - this.assertAANotHasKeys = BTS__AssertAANotHasKeys - this.assertArrayContains = BTS__AssertArrayContains - this.assertArrayNotContains = BTS__AssertArrayNotContains - this.assertArrayContainsSubset = BTS__AssertArrayContainsSubset - this.assertArrayNotContainsSubset = BTS__AssertArrayNotContainsSubset - this.assertArrayCount = BTS__AssertArrayCount - this.assertArrayNotCount = BTS__AssertArrayNotCount - this.assertEmpty = BTS__AssertEmpty - this.assertNotEmpty = BTS__AssertnotEmpty - this.assertArrayContainsOnly = BTS__AssertArrayContainsOnly - - 'Type Comparison Functionality - this.eqValues = BTS__EqValues - this.eqAssocArrays = BTS__EqAssocArray - this.eqArrays = BTS__EqArray - this.baseComparator = BTS__BaseComparator - - return this -End Function - -'---------------------------------------------------------------- -' Add a test to a suite's test cases array. -' -' @param name (string) A test name. -' @param func (string) A test function name. -'---------------------------------------------------------------- -Sub BTS__AddTest(name as String, func as Object, setup = invalid as Object, teardown = invalid as Object) - m.testCases.Push(m.createTest(name, func, setup, teardown)) -End Sub - -'---------------------------------------------------------------- -' Create a test object. -' -' @param name (string) A test name. -' @param func (string) A test function name. -'---------------------------------------------------------------- -Function BTS__CreateTest(name as String, func as Object, setup = invalid as Object, teardown = invalid as Object) as Object - return { - Name: name - Func: func - SetUp: setup - TearDown: teardown - } -End Function - -'---------------------------------------------------------------- -' Assertion methods which determine test failure -'---------------------------------------------------------------- - -'---------------------------------------------------------------- -' Fail immediately, with the given message -' -' @param msg (string) An error message. -' Default value: "Error". -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__Fail(msg = "Error" as string) as string - return msg -End Function - -'---------------------------------------------------------------- -' Fail the test if the expression is true. -' -' @param expr (dynamic) An expression to evaluate. -' @param msg (string) An error message. -' Default value: "Expression evaluates to true" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertFalse(expr as dynamic, msg = "Expression evaluates to true" as string) as string - if not TF_Utils__IsBoolean(expr) or expr - return m.fail(msg) - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail the test unless the expression is true. -' -' @param expr (dynamic) An expression to evaluate. -' @param msg (string) An error message. -' Default value: "Expression evaluates to false" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertTrue(expr as dynamic, msg = "Expression evaluates to false" as string) as string - if not TF_Utils__IsBoolean(expr) or not expr then - return msg - End if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the two objects are unequal as determined by the '<>' operator. -' -' @param first (dynamic) A first object to compare. -' @param second (dynamic) A second object to compare. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertEqual(first as dynamic, second as dynamic, msg = "" as string) as string - if not m.eqValues(first, second) - if msg = "" - first_as_string = TF_Utils__AsString(first) - second_as_string = TF_Utils__AsString(second) - msg = first_as_string + " != " + second_as_string - end if - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the two objects are equal as determined by the '=' operator. -' -' @param first (dynamic) A first object to compare. -' @param second (dynamic) A second object to compare. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertNotEqual(first as dynamic, second as dynamic, msg = "" as string) as string - if m.eqValues(first, second) - if msg = "" - first_as_string = TF_Utils__AsString(first) - second_as_string = TF_Utils__AsString(second) - msg = first_as_string + " == " + second_as_string - end if - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the value is not invalid. -' -' @param value (dynamic) A value to check. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertInvalid(value as dynamic, msg = "" as string) as string - if value <> Invalid - if msg = "" - expr_as_string = TF_Utils__AsString(value) - msg = expr_as_string + " <> Invalid" - end if - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the value is invalid. -' -' @param value (dynamic) A value to check. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertNotInvalid(value as dynamic, msg = "" as string) as string - if value = Invalid - if msg = "" - expr_as_string = TF_Utils__AsString(value) - msg = expr_as_string + " = Invalid" - end if - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the array doesn't have the key. -' -' @param array (dynamic) A target array. -' @param key (string) A key name. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertAAHasKey(array as dynamic, key as string, msg = "" as string) as string - if TF_Utils__IsAssociativeArray(array) - if not array.DoesExist(key) - if msg = "" - msg = "Array doesn't have the '" + key + "' key." - end if - return msg - end if - else - msg = "Input value is not an Associative Array." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the array has the key. -' -' @param array (dynamic) A target array. -' @param key (string) A key name. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertAANotHasKey(array as dynamic, key as string, msg = "" as string) as string - if TF_Utils__IsAssociativeArray(array) - if array.DoesExist(key) - if msg = "" - msg = "Array has the '" + key + "' key." - end if - return msg - end if - else - msg = "Input value is not an Associative Array." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the array doesn't have the keys list. -' -' @param array (dynamic) A target associative array. -' @param keys (object) A key names array. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertAAHasKeys(array as dynamic, keys as object, msg = "" as string) as string - if TF_Utils__IsAssociativeArray(array) and TF_Utils__IsArray(keys) - for each key in keys - if not array.DoesExist(key) - if msg = "" - msg = "Array doesn't have the '" + key + "' key." - end if - return msg - end if - end for - else - msg = "Input value is not an Associative Array." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the array has the keys list. -' -' @param array (dynamic) A target associative array. -' @param keys (object) A key names array. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertAANotHasKeys(array as dynamic, keys as object, msg = "" as string) as string - if TF_Utils__IsAssociativeArray(array) and TF_Utils__IsArray(keys) - for each key in keys - if array.DoesExist(key) - if msg = "" - msg = "Array has the '" + key + "' key." - end if - return msg - end if - end for - else - msg = "Input value is not an Associative Array." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the array doesn't have the item. -' -' @param array (dynamic) A target array. -' @param value (dynamic) A value to check. -' @param key (object) A key name for associative array. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertArrayContains(array as dynamic, value as dynamic, key = invalid as string, msg = "" as string) as string - if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array) - if not TF_Utils__ArrayContains(array, value, key) - msg = "Array doesn't have the '" + TF_Utils__AsString(value) + "' value." - return msg - end if - else - msg = "Input value is not an Array." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the array has the item. -' -' @param array (dynamic) A target array. -' @param value (dynamic) A value to check. -' @param key (object) A key name for associative array. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertArrayNotContains(array as dynamic, value as dynamic, key = invalid as string, msg = "" as string) as string - if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array) - if TF_Utils__ArrayContains(array, value, key) - msg = "Array has the '" + TF_Utils__AsString(value) + "' value." - return msg - end if - else - msg = "Input value is not an Array." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the array doesn't have the item subset. -' -' @param array (dynamic) A target array. -' @param subset (dynamic) An items array to check. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertArrayContainsSubset(array as dynamic, subset as dynamic, msg = "" as string) as string - if (TF_Utils__IsAssociativeArray(array) and TF_Utils__IsAssociativeArray(subset)) or (TF_Utils__IsArray(array) and TF_Utils__IsArray(subset)) - isAA = TF_Utils__IsAssociativeArray(subset) - for each item in subset - key = invalid - value = item - if isAA - key = item - value = item[key] - end if - if not TF_Utils__ArrayContains(array, value, key) - msg = "Array doesn't have the '" + TF_Utils__AsString(value) + "' value." - return msg - end if - end for - else - msg = "Input value is not an Array." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the array have the item from subset. -' -' @param array (dynamic) A target array. -' @param subset (dynamic) A items array to check. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertArrayNotContainsSubset(array as dynamic, subset as dynamic, msg = "" as string) as string - if (TF_Utils__IsAssociativeArray(array) and TF_Utils__IsAssociativeArray(subset)) or (TF_Utils__IsArray(array) and TF_Utils__IsArray(subset)) - isAA = TF_Utils__IsAssociativeArray(subset) - for each item in subset - key = invalid - value = item - if isAA - key = item - value = item[key] - end if - if TF_Utils__ArrayContains(array, value, key) - msg = "Array has the '" + TF_Utils__AsString(value) + "' value." - return msg - end if - end for - else - msg = "Input value is not an Array." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the array items count <> expected count -' -' @param array (dynamic) A target array. -' @param count (integer) An expected array items count. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertArrayCount(array as dynamic, count as integer, msg = "" as string) as string - if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array) - if array.Count() <> count - msg = "Array items count <> " + TF_Utils__AsString(count) + "." - return msg - end if - else - msg = "Input value is not an Array." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the array items count = expected count. -' -' @param array (dynamic) A target array. -' @param count (integer) An expected array items count. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertArrayNotCount(array as dynamic, count as integer, msg = "" as string) as string - if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array) - if array.Count() = count - msg = "Array items count = " + TF_Utils__AsString(count) + "." - return msg - end if - else - msg = "Input value is not an Array." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the item is not empty array or string. -' -' @param item (dynamic) An array or string to check. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertEmpty(item as dynamic, msg = "" as string) as string - if TF_Utils__IsAssociativeArray(item) or TF_Utils__IsArray(item) - if item.Count() > 0 - msg = "Array is not empty." - return msg - end if - else if TF_Utils__AsString(item) <> "" - msg = "Input value is not empty." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the item is empty array or string. -' -' @param item (dynamic) An array or string to check. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertNotEmpty(item as dynamic, msg = "" as string) as string - if TF_Utils__IsAssociativeArray(item) or TF_Utils__IsArray(item) - if item.Count() = 0 - msg = "Array is empty." - return msg - end if - else if TF_Utils__AsString(item) = "" - msg = "Input value is empty." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Fail if the array doesn't contains items of specific type only. -' -' @param array (dynamic) A target array. -' @param typeStr (string) An items type name. -' @param msg (string) An error message. -' Default value: "" -' -' @return An error message. -'---------------------------------------------------------------- -Function BTS__AssertArrayContainsOnly(array as dynamic, typeStr as string, msg = "" as string) as string - if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array) - methodName = "TF_Utils__Is" + typeStr - for each item in array - if not methodName(item) - msg = TF_Utils__AsString(item) + "is not a '" + typeStr + "' type." - return msg - end if - end for - else - msg = "Input value is not an Array." - return msg - end if - return "" -End Function - -'---------------------------------------------------------------- -' Type Comparison Functionality -'---------------------------------------------------------------- - -'---------------------------------------------------------------- -' Compare two arbitrary values to each other. -' -' @param Value1 (dynamic) A first item to compare. -' @param Value2 (dynamic) A second item to compare. -' @param comparator (Function, optional) Function, to compare 2 values. Should take in 2 parameters and return either true or false. -' -' @return True if values are equal or False in other case. -'---------------------------------------------------------------- -Function BTS__EqValues(Value1 as dynamic, Value2 as dynamic, comparator = m.baseComparator as Function) as Boolean - return comparator(value1, value2) -End Function - -'---------------------------------------------------------------- -' Base comparator for comparing two values. -' -' @param Value1 (dynamic) A first item to compare. -' @param Value2 (dynamic) A second item to compare. -' -' @return True if values are equal or False in other case. -function BTS__BaseComparator(value1 as Dynamic, value2 as Dynamic) as Boolean - value1Type = type(value1) - value2Type = type(value2) - - if (value1Type = "roList" or value1Type = "roArray") and (value2Type = "roList" or value2Type = "roArray") - return m.eqArrays(value1, value2) - else if value1Type = "roAssociativeArray" and value2Type = "roAssociativeArray" - return m.eqAssocArrays(value1, value2) - else - return value1 = value2 - end if -end function - -'---------------------------------------------------------------- -' Compare two roAssociativeArray objects for equality. -' -' @param Value1 (object) A first associative array. -' @param Value2 (object) A second associative array. -' -' @return True if arrays are equal or False in other case. -'---------------------------------------------------------------- -Function BTS__EqAssocArray(Value1 as Object, Value2 as Object) as Boolean - 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 m.eqValues(v1, v2) - return False - end if - end if - end for - return True - end if -End Function - -'---------------------------------------------------------------- -' Compare two roArray objects for equality. -' -' @param Value1 (object) A first array. -' @param Value2 (object) A second array. -' -' @return True if arrays are equal or False in other case. -'---------------------------------------------------------------- -Function BTS__EqArray(Value1 as Object, Value2 as Object) as Boolean - 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 m.eqValues(v1, v2) then - return False - end if - end for - return True - end if -End Function'***************************************************************** -'* Copyright Roku 2011-2017 -'* All Rights Reserved -'***************************************************************** - -' Functions in this file: -' ItemGenerator -' IG_GetItem -' IG_GetAssocArray -' IG_GetArray -' IG_GetSimpleType -' IG_GetBoolean -' IG_GetInteger -' IG_GetFloat -' IG_GetString -' - -'---------------------------------------------------------------- -' Main function to generate object according to specified scheme. -' -' @param scheme (object) A scheme with desired object structure. Can be -' any simple type, array of types or associative array in form -' { propertyName1 : "propertyType1" -' propertyName2 : "propertyType2" -' ... -' propertyNameN : "propertyTypeN" } -' -' @return An object according to specified scheme or invalid, -' if scheme is not valid. -'---------------------------------------------------------------- -Function ItemGenerator(scheme as object) as Object - - this = {} - - this.getItem = IG_GetItem - this.getAssocArray = IG_GetAssocArray - this.getArray = IG_GetArray - this.getSimpleType = IG_GetSimpleType - this.getInteger = IG_GetInteger - this.getFloat = IG_GetFloat - this.getString = IG_GetString - this.getBoolean = IG_GetBoolean - - if not TF_Utils__IsValid(scheme) - return invalid - end if - - return this.getItem(scheme) -End Function - -' TODO: Create IG_GetInvalidItem function with random type fields - -'---------------------------------------------------------------- -' Generate object according to specified scheme. -' -' @param scheme (object) A scheme with desired object structure. -' Can be any simple type, array of types or associative array. -' -' @return An object according to specified scheme or invalid, -' if scheme is not one of simple type, array or -' associative array. -'---------------------------------------------------------------- -Function IG_GetItem(scheme as object) as object - - item = invalid - - if TF_Utils__IsAssociativeArray(scheme) - item = m.getAssocArray(scheme) - else if TF_Utils__IsArray(scheme) - item = m.getArray(scheme) - else if TF_Utils__IsString(scheme) - item = m.getSimpleType(lCase(scheme)) - end if - - return item -End Function - -'---------------------------------------------------------------- -' Generates associative array according to specified scheme. -' -' @param scheme (object) An associative array with desired -' object structure in form -' { propertyName1 : "propertyType1" -' propertyName2 : "propertyType2" -' ... -' propertyNameN : "propertyTypeN" } -' -' @return An associative array according to specified scheme. -'---------------------------------------------------------------- -Function 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 - -'---------------------------------------------------------------- -' Generates array according to specified scheme. -' -' @param scheme (object) An array with desired object types. -' -' @return An array according to specified scheme. -'---------------------------------------------------------------- -Function IG_GetArray(scheme as object) as object - - item = [] - - for each key in scheme - item.Push(m.getItem(key)) - end for - - return item -End Function - -'---------------------------------------------------------------- -' Generates random value of specified type. -' -' @param typeStr (string) A name of desired object type. -' -' @return A simple type object or invalid if type is not supported. -'---------------------------------------------------------------- -Function 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 - -'---------------------------------------------------------------- -' Generates random boolean value. -' -' @return A random boolean value. -'---------------------------------------------------------------- -Function IG_GetBoolean() as boolean - return TF_Utils__AsBoolean(Rnd(2) \ Rnd(2)) -End Function - -'---------------------------------------------------------------- -' Generates random integer value from 1 to specified seed value. -' -' @param seed (integer) A seed value for Rnd function. -' Default value: 100. -' -' @return A random integer value. -'---------------------------------------------------------------- -Function IG_GetInteger(seed = 100 as integer) as integer - return Rnd(seed) -End Function - -'---------------------------------------------------------------- -' Generates random float value. -' -' @return A random float value. -'---------------------------------------------------------------- -Function IG_GetFloat() as float - return Rnd(0) -End Function - -'---------------------------------------------------------------- -' Generates random string with specified length. -' -' @param seed (integer) A string length. -' -' @return A random string value or empty string if seed is 0. -'---------------------------------------------------------------- -Function 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'***************************************************************** -'* Copyright Roku 2011-2017 -'* All Rights Reserved -'***************************************************************** - -' Functions in this file: -' Logger -' Logger__SetVerbosity -' Logger__SetEcho -' Logger__SetServerURL -' Logger__PrintStatistic -' Logger__SendToServer -' Logger__CreateTotalStatistic -' Logger__CreateSuiteStatistic -' Logger__CreateTestStatistic -' Logger__AppendSuiteStatistic -' Logger__AppendTestStatistic -' Logger__PrintSuiteStatistic -' Logger__PrintTestStatistic -' Logger__PrintStart -' Logger__PrintEnd -' Logger__PrintSuiteSetUp -' Logger__PrintSuiteStart -' Logger__PrintSuiteEnd -' Logger__PrintSuiteTearDown -' Logger__PrintTestSetUp -' Logger__PrintTestStart -' Logger__PrintTestEnd -' Logger__PrintTestTearDown - -'---------------------------------------------------------------- -' Main function. Create Logger object. -' -' @return A Logger object. -'---------------------------------------------------------------- -function Logger() as Object - this = {} - - this.verbosityLevel = { - basic : 0 - normal : 1 - verboseFailed : 2 - verbose : 3 - } - - ' Internal properties - this.verbosity = this.verbosityLevel.normal - this.echoEnabled = false - this.serverURL = invalid - - ' Interface - this.SetVerbosity = Logger__SetVerbosity - this.SetEcho = Logger__SetEcho - this.SetServerURL = Logger__SetServerURL - this.PrintStatistic = Logger__PrintStatistic - this.SendToServer = Logger__SendToServer - - this.CreateTotalStatistic = Logger__CreateTotalStatistic - this.CreateSuiteStatistic = Logger__CreateSuiteStatistic - this.CreateTestStatistic = Logger__CreateTestStatistic - this.AppendSuiteStatistic = Logger__AppendSuiteStatistic - this.AppendTestStatistic = Logger__AppendTestStatistic - - ' Internal functions - this.PrintSuiteStatistic = Logger__PrintSuiteStatistic - this.PrintTestStatistic = Logger__PrintTestStatistic - this.PrintStart = Logger__PrintStart - this.PrintEnd = Logger__PrintEnd - this.PrintSuiteSetUp = Logger__PrintSuiteSetUp - this.PrintSuiteStart = Logger__PrintSuiteStart - this.PrintSuiteEnd = Logger__PrintSuiteEnd - this.PrintSuiteTearDown = Logger__PrintSuiteTearDown - this.PrintTestSetUp = Logger__PrintTestSetUp - this.PrintTestStart = Logger__PrintTestStart - this.PrintTestEnd = Logger__PrintTestEnd - this.PrintTestTearDown = Logger__PrintTestTearDown - - return this -end function - -'---------------------------------------------------------------- -' Set logging verbosity parameter. -' -' @param verbosity (integer) A verbosity level. -' Posible values: -' 0 - basic -' 1 - normal -' 2 - verbose failed tests -' 3 - verbose -' Default level: 1 -'---------------------------------------------------------------- -sub Logger__SetVerbosity(verbosity = m.verbosityLevel.normal as Integer) - if verbosity >= m.verbosityLevel.basic and verbosity <= m.verbosityLevel.verbose - m.verbosity = verbosity - end if -end sub - -'---------------------------------------------------------------- -' Set logging echo parameter. -' -' @param enable (boolean) A echo trigger. -' Posible values: true or false -' Default value: false -'---------------------------------------------------------------- -sub Logger__SetEcho(enable = false as Boolean) - m.echoEnabled = enable -end sub - -'---------------------------------------------------------------- -' Set storage server URL parameter. -' -' @param url (string) A storage server URL. -' Default level: invalid -'---------------------------------------------------------------- -sub Logger__SetServerURL(url = invalid as String) - if url <> invalid - m.serverURL = url - end if -end sub - -'---------------------------------------------------------------- -' Set storage server URL parameter. -' -' @param url (string) A storage server URL. -' Default level: invalid -'---------------------------------------------------------------- -sub Logger__SendToServer(statObj as Object) - if m.serverURL <> invalid - ' Send log file to server - end if -end sub - -'---------------------------------------------------------------- -' Print statistic object with specified verbosity. -' -' @param statObj (object) A statistic object to print. -'---------------------------------------------------------------- -sub Logger__PrintStatistic(statObj as Object) - if not m.echoEnabled - m.PrintStart() - - if m.verbosity = m.verbosityLevel.normal or m.verbosity = m.verbosityLevel.verboseFailed - for each testSuite in statObj.Suites - for each testCase in testSuite.Tests - if m.verbosity = m.verbosityLevel.verboseFailed and testCase.result = "Fail" - m.printTestStatistic(testCase) - else - ? "*** "; testSuite.Name; ": "; testCase.Name; " - "; testCase.Result - end if - end for - end for - else if m.verbosity = m.verbosityLevel.verbose - for each testSuite in statObj.Suites - m.PrintSuiteStatistic(testSuite) - end for - end if - end if - - ? "***" - ? "*** Total = "; TF_Utils__AsString(statObj.Total); " ; Passed = "; statObj.Correct; " ; Failed = "; statObj.Fail; " ; Crashes = "; statObj.Crash; - ? " Time spent: "; statObj.Time; "ms" - ? "***" - - m.PrintEnd() -end sub - -'---------------------------------------------------------------- -' Create an empty statistic object for totals in output log. -' -' @return An empty statistic object. -'---------------------------------------------------------------- -function Logger__CreateTotalStatistic() as Object - statTotalItem = { - Suites : [] - Time : 0 - Total : 0 - Correct : 0 - Fail : 0 - Crash : 0 - } - - if m.echoEnabled - m.PrintStart() - end if - - return statTotalItem -end function - -'---------------------------------------------------------------- -' Create an empty statistic object for test suite with specified name. -' -' @param name (string) A test suite name for statistic object. -' -' @return An empty statistic object for test suite. -'---------------------------------------------------------------- -function Logger__CreateSuiteStatistic(name as String) as Object - statSuiteItem = { - Name : name - Tests : [] - Time : 0 - Total : 0 - Correct : 0 - Fail : 0 - Crash : 0 - } - - if m.echoEnabled - if m.verbosity = m.verbosityLevel.verbose - m.PrintSuiteStart(name) - end if - end if - - return statSuiteItem -end function - -'---------------------------------------------------------------- -' Create statistic object for test with specified name. -' -' @param name (string) A test name. -' @param result (string) A result of test running. -' Posible values: "Success", "Fail". -' Default value: "Success" -' @param time (integer) A test running time. -' Default value: 0 -' @param errorCode (integer) An error code for failed test. -' Posible values: -' 252 (&hFC) : ERR_NORMAL_END -' 226 (&hE2) : ERR_VALUE_RETURN -' 233 (&hE9) : ERR_USE_OF_UNINIT_VAR -' 020 (&h14) : ERR_DIV_ZERO -' 024 (&h18) : ERR_TM -' 244 (&hF4) : ERR_RO2 -' 236 (&hEC) : ERR_RO4 -' 002 (&h02) : ERR_SYNTAX -' 241 (&hF1) : ERR_WRONG_NUM_PARAM -' Default value: 0 -' @param errorMessage (string) An error message for failed test. -' -' @return A statistic object for test. -'---------------------------------------------------------------- -function Logger__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 - } - } - - if m.echoEnabled - if m.verbosity = m.verbosityLevel.verbose - m.PrintTestStart(name) - end if - end if - - return statTestItem -end function - -'---------------------------------------------------------------- -' Append test statistic to test suite statistic. -' -' @param statSuiteObj (object) A target test suite object. -' @param statTestObj (object) A test statistic to append. -'---------------------------------------------------------------- -sub Logger__AppendTestStatistic(statSuiteObj as Object, statTestObj as Object) - if TF_Utils__IsAssociativeArray(statSuiteObj) and TF_Utils__IsAssociativeArray(statTestObj) - statSuiteObj.Tests.Push(statTestObj) - - if TF_Utils__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 - - if m.echoEnabled - if m.verbosity = m.verbosityLevel.normal - ? "*** "; statSuiteObj.Name; ": "; statTestObj.Name; " - "; statTestObj.Result - else if m.verbosity = m.verbosityLevel.verbose - m.PrintTestStatistic(statTestObj) - end if - end if - end if -end sub - -'---------------------------------------------------------------- -' Append suite statistic to total statistic object. -' -' @param statTotalObj (object) A target total statistic object. -' @param statSuiteObj (object) A test suite statistic object to append. -'---------------------------------------------------------------- -sub Logger__AppendSuiteStatistic(statTotalObj as Object, statSuiteObj as Object) - if TF_Utils__IsAssociativeArray(statTotalObj) and TF_Utils__IsAssociativeArray(statSuiteObj) - statTotalObj.Suites.Push(statSuiteObj) - statTotalObj.Time = statTotalObj.Time + statSuiteObj.Time - - if TF_Utils__IsInteger(statSuiteObj.Total) - statTotalObj.Total = statTotalObj.Total + statSuiteObj.Total - end if - - if TF_Utils__IsInteger(statSuiteObj.Correct) - statTotalObj.Correct = statTotalObj.Correct + statSuiteObj.Correct - end if - - if TF_Utils__IsInteger(statSuiteObj.Fail) - statTotalObj.Fail = statTotalObj.Fail + statSuiteObj.Fail - end if - - if TF_Utils__IsInteger(statSuiteObj.Crash) - statTotalObj.Crash = statTotalObj.Crash + statSuiteObj.Crash - end if - - if m.echoEnabled - if m.verbosity = m.verbosityLevel.verbose - m.PrintSuiteStatistic(statSuiteObj) - end if - end if - end if -end sub - -'---------------------------------------------------------------- -' Print test suite statistic. -' -' @param statSuiteObj (object) A target test suite object to print. -'---------------------------------------------------------------- -sub Logger__PrintSuiteStatistic(statSuiteObj as Object) - if not m.echoEnabled - m.PrintSuiteStart(statSuiteObj.Name) - - for each testCase in statSuiteObj.Tests - m.PrintTestStatistic(testCase) - end for - end if - - ? "===" - ? "=== Total = "; TF_Utils__AsString(statSuiteObj.Total); " ; Passed = "; statSuiteObj.Correct; " ; Failed = "; statSuiteObj.Fail; " ; Crashes = "; statSuiteObj.Crash; - ? " Time spent: "; statSuiteObj.Time; "ms" - ? "===" - - m.PrintSuiteEnd(statSuiteObj.Name) -end sub - -'---------------------------------------------------------------- -' Print test statistic. -' -' @param statTestObj (object) A target test object to print. -'---------------------------------------------------------------- -sub Logger__PrintTestStatistic(statTestObj as Object) - if not m.echoEnabled - m.PrintTestStart(statTestObj.Name) - end if - - ? "--- Result: "; statTestObj.Result - ? "--- Time: "; statTestObj.Time - - if LCase(statTestObj.Result) <> "success" - ? "--- Error Code: "; statTestObj.Error.Code - ? "--- Error Message: "; statTestObj.Error.Message - end if - - m.PrintTestEnd(statTestObj.Name) -end sub - -'---------------------------------------------------------------- -' Print testting start message. -'---------------------------------------------------------------- -sub Logger__PrintStart() - ? "" - ? "******************************************************************" - ? "******************************************************************" - ? "************* Start testing *************" - ? "******************************************************************" -end sub - -'---------------------------------------------------------------- -' Print testing end message. -'---------------------------------------------------------------- -sub Logger__PrintEnd() - ? "******************************************************************" - ? "************* End testing *************" - ? "******************************************************************" - ? "******************************************************************" - ? "" -end sub - -'---------------------------------------------------------------- -' Print test suite SetUp message. -'---------------------------------------------------------------- -sub Logger__PrintSuiteSetUp(sName as String) - if m.verbosity = m.verbosityLevel.verbose - ? "=================================================================" - ? "=== SetUp "; sName; " suite." - ? "=================================================================" - end if -end sub - -'---------------------------------------------------------------- -' Print test suite start message. -'---------------------------------------------------------------- -sub Logger__PrintSuiteStart(sName as String) - ? "=================================================================" - ? "=== Start "; sName; " suite:" - ? "===" -end sub - -'---------------------------------------------------------------- -' Print test suite end message. -'---------------------------------------------------------------- -sub Logger__PrintSuiteEnd(sName as String) - ? "===" - ? "=== End "; sName; " suite." - ? "=================================================================" -End Sub - -'---------------------------------------------------------------- -' Print test suite TearDown message. -'---------------------------------------------------------------- -sub Logger__PrintSuiteTearDown(sName as String) - if m.verbosity = m.verbosityLevel.verbose - ? "=================================================================" - ? "=== TearDown "; sName; " suite." - ? "=================================================================" - end if -end sub - -'---------------------------------------------------------------- -' Print test setUp message. -'---------------------------------------------------------------- -sub Logger__PrintTestSetUp(tName as String) - if m.verbosity = m.verbosityLevel.verbose - ? "----------------------------------------------------------------" - ? "--- SetUp "; tName; " test." - ? "----------------------------------------------------------------" - end if -end sub - -'---------------------------------------------------------------- -' Print test start message. -'---------------------------------------------------------------- -sub Logger__PrintTestStart(tName as String) - ? "----------------------------------------------------------------" - ? "--- Start "; tName; " test:" - ? "---" -end sub - -'---------------------------------------------------------------- -' Print test end message. -'---------------------------------------------------------------- -sub Logger__PrintTestEnd(tName as String) - ? "---" - ? "--- End "; tName; " test." - ? "----------------------------------------------------------------" -end sub - -'---------------------------------------------------------------- -' Print test TearDown message. -'---------------------------------------------------------------- -sub Logger__PrintTestTearDown(tName as String) - if m.verbosity = m.verbosityLevel.verbose - ? "----------------------------------------------------------------" - ? "--- TearDown "; tName; " test." - ? "----------------------------------------------------------------" - end if -end sub -'***************************************************************** -'* Copyright Roku 2011-2017 -'* All Rights Reserved -'***************************************************************** - -' Functions in this file: -' TestRunner -' TestRunner__Run -' TestRunner__SetTestsDirectory -' TestRunner__SetTestFilePrefix -' TestRunner__SetTestSuitePrefix -' TestRunner__SetTestSuiteName -' TestRunner__SetTestCaseName -' TestRunner__SetFailFast -' TestRunner__GetTestSuitesList -' TestRunner__GetTestSuiteNamesList -' TestRunner__GetTestFilesList -' TestRunner__GetTestNodesList -' TestFramework__RunNodeTests - -'---------------------------------------------------------------- -' Main function. Create TestRunner object. -' -' @return A TestRunner object. -'---------------------------------------------------------------- -function TestRunner() as Object - - this = {} - - this.isNodeMode = getGlobalAA().top <> invalid - this.logger = Logger() - - ' Internal properties - this.nodesTestDirectory = "pkg:/components/tests" - if this.isNodeMode - this.testsDirectory = this.nodesTestDirectory - this.testFilePrefix = m.top.subtype() - else - this.testsDirectory = "pkg:/source/tests" - this.testFilePrefix = "Test__" - end if - this.testSuitePrefix = "TestSuite__" - this.testSuiteName = "" - this.testCaseName = "" - this.failFast = false - - ' Interface - this.Run = TestRunner__Run - this.SetTestsDirectory = TestRunner__SetTestsDirectory - this.SetTestFilePrefix = TestRunner__SetTestFilePrefix - this.SetTestSuitePrefix = TestRunner__SetTestSuitePrefix - this.SetTestSuiteName = TestRunner__SetTestSuiteName - this.SetTestCaseName = TestRunner__SetTestCaseName - this.SetFailFast = TestRunner__SetFailFast - - ' Internal functions - this.GetTestFilesList = TestRunner__GetTestFilesList - this.GetTestSuitesList = TestRunner__GetTestSuitesList - this.GetTestNodesList = TestRunner__GetTestNodesList - this.GetTestSuiteNamesList = TestRunner__GetTestSuiteNamesList - - return this -end function - -'---------------------------------------------------------------- -' Run main test loop. -' -' @param statObj (object, optional) statistic object to be used in tests -' @param testSuiteNamesList (array, optional) array of test suite function names to be used in tests -' -' @return Statistic object if run in node mode, invalid otherwise -'---------------------------------------------------------------- -function TestRunner__Run(statObj = m.logger.CreateTotalStatistic() as Object, testSuiteNamesList = [] as Object) as Object - alltestCount = 0 - totalStatObj = statObj - testSuitesList = m.GetTestSuitesList(testSuiteNamesList) - - for each testSuite in testSuitesList - testCases = testSuite.testCases - testCount = testCases.Count() - alltestCount = alltestCount + testCount - - if TF_Utils__IsFunction(testSuite.SetUp) - m.logger.PrintSuiteSetUp(testSuite.Name) - testSuite.SetUp() - end if - - suiteStatObj = m.logger.CreateSuiteStatistic(testSuite.Name) - - for each testCase in testCases - if m.testCaseName = "" or (m.testCaseName <> "" and LCase(testCase.Name) = LCase(m.testCaseName)) - if TF_Utils__IsFunction(testCase.SetUp) - m.logger.PrintTestSetUp(testCase.Name) - testCase.SetUp() - end if - - testTimer = CreateObject("roTimespan") - testStatObj = m.logger.CreateTestStatistic(testCase.Name) - testSuite.testInstance = testCase - testSuite.testCase = testCase.Func - - runResult = testSuite.testCase() - - if runResult <> "" - testStatObj.Result = "Fail" - testStatObj.Error.Code = 1 - testStatObj.Error.Message = runResult - else - testStatObj.Result = "Success" - end if - - testStatObj.Time = testTimer.TotalMilliseconds() - m.logger.AppendTestStatistic(suiteStatObj, testStatObj) - - if TF_Utils__IsFunction(testCase.TearDown) - m.logger.PrintTestTearDown(testCase.Name) - testCase.TearDown() - end if - - if testStatObj.Result = "Fail" and m.failFast - exit for - end if - end if - end for - - m.logger.AppendSuiteStatistic(totalStatObj, suiteStatObj) - - if TF_Utils__IsFunction(testSuite.TearDown) - m.logger.PrintSuiteTearDown(testSuite.Name) - testSuite.TearDown() - end if - - if testStatObj.Result = "Fail" and m.failFast - exit for - end if - end for - - if m.isNodeMode - return totalStatObj - else - testNodes = m.getTestNodesList() - - for each testNodeName in testNodes - testNode = createObject("roSGNode", testNodeName) - if testNode <> invalid - testSuiteNamesList = m.GetTestSuiteNamesList(testNodeName) - totalStatObj = testNode.callFunc("TestFramework__RunNodeTests", [totalStatObj, testSuiteNamesList]) - end if - end for - - m.logger.PrintStatistic(totalStatObj) - end if -end function - -'---------------------------------------------------------------- -' Set testsDirectory property. -'---------------------------------------------------------------- -sub TestRunner__SetTestsDirectory(testsDirectory as String) - if testsDirectory <> invalid - m.testsDirectory = testsDirectory - end if -end sub - -'---------------------------------------------------------------- -' Set setTestFilePrefix property. -'---------------------------------------------------------------- -sub TestRunner__SetTestFilePrefix(testFilePrefix as String) - if testFilePrefix <> invalid - m.testFilePrefix = setTestFilePrefix - end if -end sub - -'---------------------------------------------------------------- -' Set testSuitePrefix property. -'---------------------------------------------------------------- -sub TestRunner__SetTestSuitePrefix(testSuitePrefix as String) - if testSuitePrefix <> invalid - m.testSuitePrefix = testSuitePrefix - end if -end sub - -'---------------------------------------------------------------- -' Set testSuiteName property. -'---------------------------------------------------------------- -sub TestRunner__SetTestSuiteName(testSuiteName as String) - if testSuiteName <> invalid - m.testSuiteName = testSuiteName - end if -end sub - -'---------------------------------------------------------------- -' Set testCaseName property. -'---------------------------------------------------------------- -sub TestRunner__SetTestCaseName(testCaseName as String) - if testCaseName <> invalid - m.testCaseName = testCaseName - end if -end sub - -'---------------------------------------------------------------- -' Set failFast property. -'---------------------------------------------------------------- -sub TestRunner__SetFailFast(failFast = false as Boolean) - m.failFast = failFast -end sub - -'---------------------------------------------------------------- -' Builds an array of test suite objects. -' -' @param testSuiteNamesList (string, optional) array of names of test suite functions. If not passed, scans all test files for test suites -' -' @return An array of test suites. -'---------------------------------------------------------------- -function TestRunner__GetTestSuitesList(testSuiteNamesList = [] as Object) as Object - result = [] - - if testSuiteNamesList.count() > 0 - for each functionName in testSuiteNamesList - eval("testSuite=" + functionName) - testSuite = testSuite() - - if TF_Utils__IsAssociativeArray(testSuite) - result.Push(testSuite) - end if - end for - else - testSuiteRegex = CreateObject("roRegex", "^(function|sub)\s(" + m.testSuitePrefix + m.testSuiteName + "[0-9a-z\_]*)\s*\(", "i") - testFilesList = m.GetTestFilesList() - - for each filePath in testFilesList - code = TF_Utils__AsString(ReadAsciiFile(filePath)) - - if code <> "" - for each line in code.Tokenize(chr(10)) - line.Trim() - - if testSuiteRegex.IsMatch(line) - testSuite = invalid - functionName = testSuiteRegex.Match(line).Peek() - - eval("testSuite=" + functionName) - testSuite = testSuite() - - if TF_Utils__IsAssociativeArray(testSuite) - result.Push(testSuite) - end if - end if - end for - end if - end for - end if - - return result -end function - - -'---------------------------------------------------------------- -' Scans all test files for test suite function names for a given test node. -' -' @param testNodeName (string) name of a test node, test suites for which are needed -' -' @return An array of test suite names. -'---------------------------------------------------------------- -function TestRunner__GetTestSuiteNamesList(testNodeName as String) as Object - result = [] - testSuiteRegex = CreateObject("roRegex", "^(function|sub)\s(" + m.testSuitePrefix + m.testSuiteName + "[0-9a-z\_]*)\s*\(", "i") - testFilesList = m.GetTestFilesList(m.nodesTestDirectory, testNodeName) - - for each filePath in testFilesList - code = TF_Utils__AsString(ReadAsciiFile(filePath)) - - if code <> "" - for each line in code.Tokenize(chr(10)) - line.Trim() - - if testSuiteRegex.IsMatch(line) - functionName = testSuiteRegex.Match(line).Peek() - result.Push(functionName) - end if - end for - end if - end for - - return result -end function - - -'---------------------------------------------------------------- -' Scan testsDirectory and all subdirectories for test files. -' -' @param testsDirectory (string, optional) A target directory with test files. -' @param testFilePrefix (string, optional) prefix, used by test files -' -' @return An array of test files. -'---------------------------------------------------------------- -function TestRunner__GetTestFilesList(testsDirectory = m.testsDirectory as String, testFilePrefix = m.testFilePrefix as String) as Object - result = [] - testsFileRegex = CreateObject("roRegex", "^(" + testFilePrefix + ")[0-9a-z\_]*\.brs$", "i") - - if testsDirectory <> "" - fileSystem = CreateObject("roFileSystem") - listing = fileSystem.GetDirectoryListing(testsDirectory) - - for each item in listing - itemPath = testsDirectory + "/" + item - itemStat = fileSystem.Stat(itemPath) - - if itemStat.type = "directory" then - result.Append(m.getTestFilesList(itemPath, testFilePrefix)) - else if testsFileRegex.IsMatch(item) then - result.Push(itemPath) - end if - end for - end if - - return result -end function - - -'---------------------------------------------------------------- -' Scan nodesTestDirectory and all subdirectories for test nodes. -' -' @param nodesTestDirectory (string, optional) A target directory with test nodes. -' -' @return An array of test node names. -'---------------------------------------------------------------- -function TestRunner__GetTestNodesList(testsDirectory = m.nodesTestDirectory as String) as Object - result = [] - testsFileRegex = CreateObject("roRegex", "^(" + m.testFilePrefix + ")[0-9a-z\_]*\.xml$", "i") - - if testsDirectory <> "" - fileSystem = CreateObject("roFileSystem") - listing = fileSystem.GetDirectoryListing(testsDirectory) - - for each item in listing - itemPath = testsDirectory + "/" + item - itemStat = fileSystem.Stat(itemPath) - - if itemStat.type = "directory" then - result.Append(m.getTestNodesList(itemPath)) - else if testsFileRegex.IsMatch(item) then - result.Push(item.replace(".xml", "")) - end if - end for - end if - - return result -end function - - -'---------------------------------------------------------------- -' Creates and runs test runner. Should be used ONLY within a node. -' -' @param params (array) parameters, passed from main thread, used to setup new test runner -' -' @return statistic object. -'---------------------------------------------------------------- -function TestFramework__RunNodeTests(params as Object) as Object - statObj = params[0] - testSuiteNamesList = params[1] - - Runner = TestRunner() - return Runner.RUN(statObj, testSuiteNamesList) -end function -'***************************************************************** -'* Copyright Roku 2011-2017 -'* All Rights Reserved -'***************************************************************** -' Common framework utility functions -'***************************************************************** - -'************************************************* -' TF_Utils__IsXmlElement - check if value contains XMLElement interface -' @param value As Dynamic -' @return As Boolean - true if value contains XMLElement interface, else return false -'************************************************* -function TF_Utils__IsXmlElement(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifXMLElement") <> invalid -end function - -'************************************************* -' TF_Utils__IsFunction - check if value contains Function interface -' @param value As Dynamic -' @return As Boolean - true if value contains Function interface, else return false -'************************************************* -function TF_Utils__IsFunction(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifFunction") <> invalid -end function - -'************************************************* -' TF_Utils__IsBoolean - check if value contains Boolean interface -' @param value As Dynamic -' @return As Boolean - true if value contains Boolean interface, else return false -'************************************************* -function TF_Utils__IsBoolean(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifBoolean") <> invalid -end function - -'************************************************* -' TF_Utils__IsInteger - check if value type equals Integer -' @param value As Dynamic -' @return As Boolean - true if value type equals Integer, else return false -'************************************************* -function TF_Utils__IsInteger(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifInt") <> invalid and (Type(value) = "roInt" or Type(value) = "roInteger" or Type(value) = "Integer") -end function - -'************************************************* -' TF_Utils__IsFloat - check if value contains Float interface -' @param value As Dynamic -' @return As Boolean - true if value contains Float interface, else return false -'************************************************* -function TF_Utils__IsFloat(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifFloat") <> invalid -end function - -'************************************************* -' TF_Utils__IsDouble - check if value contains Double interface -' @param value As Dynamic -' @return As Boolean - true if value contains Double interface, else return false -'************************************************* -function TF_Utils__IsDouble(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifDouble") <> invalid -end function - -'************************************************* -' TF_Utils__IsLongInteger - check if value contains LongInteger interface -' @param value As Dynamic -' @return As Boolean - true if value contains LongInteger interface, else return false -'************************************************* -function TF_Utils__IsLongInteger(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifLongInt") <> invalid -end function - -'************************************************* -' TF_Utils__IsNumber - check if value contains LongInteger or Integer or Double or Float interface -' @param value As Dynamic -' @return As Boolean - true if value is number, else return false -'************************************************* -function TF_Utils__IsNumber(value as Dynamic) as Boolean - return TF_Utils__IsLongInteger(value) or TF_Utils__IsDouble(value) or TF_Utils__IsInteger(value) or TF_Utils__IsFloat(value) -end function - -'************************************************* -' TF_Utils__IsList - check if value contains List interface -' @param value As Dynamic -' @return As Boolean - true if value contains List interface, else return false -'************************************************* -function TF_Utils__IsList(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifList") <> invalid -end function - -'************************************************* -' TF_Utils__IsArray - check if value contains Array interface -' @param value As Dynamic -' @return As Boolean - true if value contains Array interface, else return false -'************************************************* -function TF_Utils__IsArray(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifArray") <> invalid -end function - -'************************************************* -' TF_Utils__IsAssociativeArray - check if value contains AssociativeArray interface -' @param value As Dynamic -' @return As Boolean - true if value contains AssociativeArray interface, else return false -'************************************************* -function TF_Utils__IsAssociativeArray(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifAssociativeArray") <> invalid -end function - -'************************************************* -' TF_Utils__IsSGNode - check if value contains SGNodeChildren interface -' @param value As Dynamic -' @return As Boolean - true if value contains SGNodeChildren interface, else return false -'************************************************* -function TF_Utils__IsSGNode(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifSGNodeChildren") <> invalid -end function - -'************************************************* -' TF_Utils__IsString - check if value contains String interface -' @param value As Dynamic -' @return As Boolean - true if value contains String interface, else return false -'************************************************* -function TF_Utils__IsString(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and GetInterface(value, "ifString") <> invalid -end function - -'************************************************* -' TF_Utils__IsNotEmptyString - check if value contains String interface and length more 0 -' @param value As Dynamic -' @return As Boolean - true if value contains String interface and length more 0, else return false -'************************************************* -function TF_Utils__IsNotEmptyString(value as Dynamic) as Boolean - return TF_Utils__IsString(value) and len(value) > 0 -end function - -'************************************************* -' TF_Utils__IsDateTime - check if value contains DateTime interface -' @param value As Dynamic -' @return As Boolean - true if value contains DateTime interface, else return false -'************************************************* -function TF_Utils__IsDateTime(value as Dynamic) as Boolean - return TF_Utils__IsValid(value) and (GetInterface(value, "ifDateTime") <> invalid or Type(value) = "roDateTime") -end function - -'************************************************* -' TF_Utils__IsValid - check if value initialized and not equal invalid -' @param value As Dynamic -' @return As Boolean - true if value initialized and not equal invalid, else return false -'************************************************* -function TF_Utils__IsValid(value as Dynamic) as Boolean - return Type(value) <> "" and value <> invalid -end function - -'************************************************* -' TF_Utils__ValidStr - return value if his contains String interface else return empty string -' @param value As Object -' @return As String - value if his contains String interface else return empty string -'************************************************* -function TF_Utils__ValidStr(obj as Object) as String - if obj <> invalid and GetInterface(obj, "ifString") <> invalid - return obj - else - return "" - end if -end function - -'************************************************* -' TF_Utils__AsString - convert input to String if this possible, else return empty string -' @param input As Dynamic -' @return As String - return converted string -'************************************************* -function TF_Utils__AsString(input as Dynamic) as String - if TF_Utils__IsValid(input) = false - return "" - else if TF_Utils__IsString(input) - return input - else if TF_Utils__IsInteger(input) or TF_Utils__IsLongInteger(input) or TF_Utils__IsBoolean(input) - return input.ToStr() - else if TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) - return Str(input).Trim() - else - return "" - end If -end function - -'************************************************* -' TF_Utils__AsInteger - convert input to Integer if this possible, else return 0 -' @param input As Dynamic -' @return As Integer - return converted Integer -'************************************************* -function TF_Utils__AsInteger(input as Dynamic) as Integer - if TF_Utils__IsValid(input) = false - return 0 - else if TF_Utils__IsString(input) - return input.ToInt() - else if TF_Utils__IsInteger(input) - return input - else if TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) or TF_Utils__IsLongInteger(input) - return Int(input) - else - return 0 - end if -end function - -'************************************************* -' TF_Utils__AsLongInteger - convert input to LongInteger if this possible, else return 0 -' @param input As Dynamic -' @return As Integer - return converted LongInteger -'************************************************* -function TF_Utils__AsLongInteger(input as Dynamic) as LongInteger - if TF_Utils__IsValid(input) = false - return 0 - else if TF_Utils__IsString(input) - return TF_Utils__AsInteger(input) - else if TF_Utils__IsLongInteger(input) or TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) or TF_Utils__IsInteger(input) - return input - else - return 0 - end if -end function - -'************************************************* -' TF_Utils__AsFloat - convert input to Float if this possible, else return 0.0 -' @param input As Dynamic -' @return As Float - return converted Float -'************************************************* -function TF_Utils__AsFloat(input as Dynamic) as Float - if TF_Utils__IsValid(input) = false - return 0.0 - else if TF_Utils__IsString(input) - return input.ToFloat() - else if TF_Utils__IsInteger(input) - return (input / 1) - else if TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) or TF_Utils__IsLongInteger(input) - return input - else - return 0.0 - end if -end function - -'************************************************* -' TF_Utils__AsDouble - convert input to Double if this possible, else return 0.0 -' @param input As Dynamic -' @return As Float - return converted Double -'************************************************* -function TF_Utils__AsDouble(input as Dynamic) as Double - if TF_Utils__IsValid(input) = false - return 0.0 - else if TF_Utils__IsString(input) - return TF_Utils__AsFloat(input) - else if TF_Utils__IsInteger(input) or TF_Utils__IsLongInteger(input) or TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) - return input - else - return 0.0 - end if -end function - -'************************************************* -' TF_Utils__AsBoolean - convert input to Boolean if this possible, else return False -' @param input As Dynamic -' @return As Boolean -'************************************************* -function TF_Utils__AsBoolean(input as Dynamic) as Boolean - if TF_Utils__IsValid(input) = false - return false - else if TF_Utils__IsString(input) - return LCase(input) = "true" - else if TF_Utils__IsInteger(input) or TF_Utils__IsFloat(input) - return input <> 0 - else if TF_Utils__IsBoolean(input) - return input - else - return false - end if -end function - -'************************************************* -' TF_Utils__AsArray - if type of value equals array return value, else return array with one element [value] -' @param value As Object -' @return As Object - roArray -'************************************************* -function TF_Utils__AsArray(value as Object) as Object - if TF_Utils__IsValid(value) - if not TF_Utils__IsArray(value) - return [value] - else - return value - end if - end if - return [] -end function - -'===================== -' Strings -'===================== - -'************************************************* -' TF_Utils__IsNullOrEmpty - check if value is invalid or empty -' @param value As Dynamic -' @return As Boolean - true if value is null or empty string, else return false -'************************************************* -function TF_Utils__IsNullOrEmpty(value as Dynamic) as Boolean - if TF_Utils__IsString(value) - return Len(value) = 0 - else - return not TF_Utils__IsValid(value) - end if -end function - -'===================== -' Arrays -'===================== - -'************************************************* -' TF_Utils__FindElementIndexInArray - find an element index in array -' @param array As Object -' @param value As Object -' @param compareAttribute As Dynamic -' @param caseSensitive As Boolean -' @return As Integer - element index if array contains a value, else return -1 -'************************************************* -function TF_Utils__FindElementIndexInArray(array as Object, value as Object, compareAttribute = invalid as Dynamic, caseSensitive = false as Boolean) as Integer - if TF_Utils__IsArray(array) - for i = 0 to TF_Utils__AsArray(array).Count() - 1 - compareValue = array[i] - - if compareAttribute <> invalid and TF_Utils__IsAssociativeArray(compareValue) - compareValue = compareValue.LookupCI(compareAttribute) - end If - - if TF_Utils__IsString(compareValue) and TF_Utils__IsString(value) and not caseSensitive - if LCase(compareValue) = LCase(value) - return i - end If - else if compareValue = value - return i - end if - - item = array[i] - next - end if - return -1 -end function - -'************************************************* -' TF_Utils__ArrayContains - check if array contains specified value -' @param array As Object -' @param value As Object -' @param compareAttribute As Dynamic -' @return As Boolean - true if array contains a value, else return false -'************************************************* -function TF_Utils__ArrayContains(array as Object, value as Object, compareAttribute = invalid as Dynamic) as Boolean - return (TF_Utils__FindElementIndexInArray(array, value, compareAttribute) > -1) -end function +'***************************************************************** +'* Copyright Roku 2011-2017 +'* All Rights Reserved +'***************************************************************** + +' Functions in this file: +' +' BaseTestSuite +' BTS__AddTest +' BTS__CreateTest +' BTS__Fail +' BTS__AssertFalse +' BTS__AssertTrue +' BTS__AssertEqual +' BTS__AssertNotEqual +' BTS__AssertInvalid +' BTS__AssertNotInvalid +' BTS__AssertAAHasKey +' BTS__AssertAANotHasKey +' BTS__AssertAAHasKeys +' BTS__AssertAANotHasKeys +' BTS__AssertArrayContains +' BTS__AssertArrayNotContains +' BTS__AssertArrayContainsSubset +' BTS__AssertArrayNotContainsSubset +' BTS__AssertArrayCount +' BTS__AssertArrayNotCount +' BTS__AssertEmpty +' BTS__AssertNotEmpty +' BTS__AssertArrayContainsOnly +' BTS__EqValues +' BTS__EqAssocArray +' BTS__EqArray +' BTS__BaseComparator + +'---------------------------------------------------------------- +' Main function. Create BaseTestSuite object. +' +' @return A BaseTestSuite object. +'---------------------------------------------------------------- +function BaseTestSuite() + + this = {} + this.Name = "BaseTestSuite" + this.SKIP_TEST_MESSAGE_PREFIX = "SKIP_TEST_MESSAGE_PREFIX__" + 'Test Cases methods + this.testCases = [] + this.addTest = BTS__AddTest + this.createTest = BTS__CreateTest + + 'Assertion methods which determine test failure or skipping + this.skip = BTS__Skip + this.fail = BTS__Fail + this.assertFalse = BTS__AssertFalse + this.assertTrue = BTS__AssertTrue + this.assertEqual = BTS__AssertEqual + this.assertNotEqual = BTS__AssertNotEqual + this.assertInvalid = BTS__AssertInvalid + this.assertNotInvalid = BTS__AssertNotInvalid + this.assertAAHasKey = BTS__AssertAAHasKey + this.assertAANotHasKey = BTS__AssertAANotHasKey + this.assertAAHasKeys = BTS__AssertAAHasKeys + this.assertAANotHasKeys = BTS__AssertAANotHasKeys + this.assertArrayContains = BTS__AssertArrayContains + this.assertArrayNotContains = BTS__AssertArrayNotContains + this.assertArrayContainsSubset = BTS__AssertArrayContainsSubset + this.assertArrayNotContainsSubset = BTS__AssertArrayNotContainsSubset + this.assertArrayCount = BTS__AssertArrayCount + this.assertArrayNotCount = BTS__AssertArrayNotCount + this.assertEmpty = BTS__AssertEmpty + this.assertNotEmpty = BTS__AssertnotEmpty + this.assertArrayContainsOnly = BTS__AssertArrayContainsOnly + + 'Type Comparison Functionality + this.eqValues = BTS__EqValues + this.eqAssocArrays = BTS__EqAssocArray + this.eqArrays = BTS__EqArray + this.baseComparator = BTS__BaseComparator + + return this +End Function + +'---------------------------------------------------------------- +' Add a test to a suite's test cases array. +' +' @param name (string) A test name. +' @param func (string) A test function name. +'---------------------------------------------------------------- +Sub BTS__AddTest(name as String, func as Object, setup = invalid as Object, teardown = invalid as Object) + m.testCases.Push(m.createTest(name, func, setup, teardown)) +End Sub + +'---------------------------------------------------------------- +' Create a test object. +' +' @param name (string) A test name. +' @param func (string) A test function name. +'---------------------------------------------------------------- +Function BTS__CreateTest(name as String, func as Object, setup = invalid as Object, teardown = invalid as Object) as Object + return { + Name: name + Func: func + SetUp: setup + TearDown: teardown + } +End Function + +'---------------------------------------------------------------- +' Assertion methods which determine test failure or skipping +'---------------------------------------------------------------- + +'---------------------------------------------------------------- +' Should be used to skip test cases. To skip test you must return the result of this method invocation. +' +' @param message (string) Optional skip message. +' Default value: "". +' +' @return A skip message, with a specific prefix added, in order to runner know that this test should be skipped. +'---------------------------------------------------------------- +function BTS__Skip(message = "" as String) as String + ' add prefix so we know that this test is skipped, but not failed + return m.SKIP_TEST_MESSAGE_PREFIX + message +end function + +'---------------------------------------------------------------- +' Fail immediately, with the given message +' +' @param msg (string) An error message. +' Default value: "Error". +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__Fail(msg = "Error" as string) as string + return msg +End Function + +'---------------------------------------------------------------- +' Fail the test if the expression is true. +' +' @param expr (dynamic) An expression to evaluate. +' @param msg (string) An error message. +' Default value: "Expression evaluates to true" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertFalse(expr as dynamic, msg = "Expression evaluates to true" as string) as string + if not TF_Utils__IsBoolean(expr) or expr + return m.fail(msg) + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail the test unless the expression is true. +' +' @param expr (dynamic) An expression to evaluate. +' @param msg (string) An error message. +' Default value: "Expression evaluates to false" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertTrue(expr as dynamic, msg = "Expression evaluates to false" as string) as string + if not TF_Utils__IsBoolean(expr) or not expr then + return msg + End if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the two objects are unequal as determined by the '<>' operator. +' +' @param first (dynamic) A first object to compare. +' @param second (dynamic) A second object to compare. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertEqual(first as dynamic, second as dynamic, msg = "" as string) as string + if not m.eqValues(first, second) + if msg = "" + first_as_string = TF_Utils__AsString(first) + second_as_string = TF_Utils__AsString(second) + msg = first_as_string + " != " + second_as_string + end if + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the two objects are equal as determined by the '=' operator. +' +' @param first (dynamic) A first object to compare. +' @param second (dynamic) A second object to compare. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertNotEqual(first as dynamic, second as dynamic, msg = "" as string) as string + if m.eqValues(first, second) + if msg = "" + first_as_string = TF_Utils__AsString(first) + second_as_string = TF_Utils__AsString(second) + msg = first_as_string + " == " + second_as_string + end if + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the value is not invalid. +' +' @param value (dynamic) A value to check. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertInvalid(value as dynamic, msg = "" as string) as string + if value <> Invalid + if msg = "" + expr_as_string = TF_Utils__AsString(value) + msg = expr_as_string + " <> Invalid" + end if + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the value is invalid. +' +' @param value (dynamic) A value to check. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertNotInvalid(value as dynamic, msg = "" as string) as string + if value = Invalid + if msg = "" + expr_as_string = TF_Utils__AsString(value) + msg = expr_as_string + " = Invalid" + end if + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the array doesn't have the key. +' +' @param array (dynamic) A target array. +' @param key (string) A key name. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertAAHasKey(array as dynamic, key as string, msg = "" as string) as string + if TF_Utils__IsAssociativeArray(array) + if not array.DoesExist(key) + if msg = "" + msg = "Array doesn't have the '" + key + "' key." + end if + return msg + end if + else + msg = "Input value is not an Associative Array." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the array has the key. +' +' @param array (dynamic) A target array. +' @param key (string) A key name. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertAANotHasKey(array as dynamic, key as string, msg = "" as string) as string + if TF_Utils__IsAssociativeArray(array) + if array.DoesExist(key) + if msg = "" + msg = "Array has the '" + key + "' key." + end if + return msg + end if + else + msg = "Input value is not an Associative Array." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the array doesn't have the keys list. +' +' @param array (dynamic) A target associative array. +' @param keys (object) A key names array. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertAAHasKeys(array as dynamic, keys as object, msg = "" as string) as string + if TF_Utils__IsAssociativeArray(array) and TF_Utils__IsArray(keys) + for each key in keys + if not array.DoesExist(key) + if msg = "" + msg = "Array doesn't have the '" + key + "' key." + end if + return msg + end if + end for + else + msg = "Input value is not an Associative Array." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the array has the keys list. +' +' @param array (dynamic) A target associative array. +' @param keys (object) A key names array. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertAANotHasKeys(array as dynamic, keys as object, msg = "" as string) as string + if TF_Utils__IsAssociativeArray(array) and TF_Utils__IsArray(keys) + for each key in keys + if array.DoesExist(key) + if msg = "" + msg = "Array has the '" + key + "' key." + end if + return msg + end if + end for + else + msg = "Input value is not an Associative Array." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the array doesn't have the item. +' +' @param array (dynamic) A target array. +' @param value (dynamic) A value to check. +' @param key (object) A key name for associative array. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertArrayContains(array as dynamic, value as dynamic, key = invalid as string, msg = "" as string) as string + if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array) + if not TF_Utils__ArrayContains(array, value, key) + msg = "Array doesn't have the '" + TF_Utils__AsString(value) + "' value." + return msg + end if + else + msg = "Input value is not an Array." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the array has the item. +' +' @param array (dynamic) A target array. +' @param value (dynamic) A value to check. +' @param key (object) A key name for associative array. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertArrayNotContains(array as dynamic, value as dynamic, key = invalid as string, msg = "" as string) as string + if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array) + if TF_Utils__ArrayContains(array, value, key) + msg = "Array has the '" + TF_Utils__AsString(value) + "' value." + return msg + end if + else + msg = "Input value is not an Array." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the array doesn't have the item subset. +' +' @param array (dynamic) A target array. +' @param subset (dynamic) An items array to check. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertArrayContainsSubset(array as dynamic, subset as dynamic, msg = "" as string) as string + if (TF_Utils__IsAssociativeArray(array) and TF_Utils__IsAssociativeArray(subset)) or (TF_Utils__IsArray(array) and TF_Utils__IsArray(subset)) + isAA = TF_Utils__IsAssociativeArray(subset) + for each item in subset + key = invalid + value = item + if isAA + key = item + value = item[key] + end if + if not TF_Utils__ArrayContains(array, value, key) + msg = "Array doesn't have the '" + TF_Utils__AsString(value) + "' value." + return msg + end if + end for + else + msg = "Input value is not an Array." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the array have the item from subset. +' +' @param array (dynamic) A target array. +' @param subset (dynamic) A items array to check. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertArrayNotContainsSubset(array as dynamic, subset as dynamic, msg = "" as string) as string + if (TF_Utils__IsAssociativeArray(array) and TF_Utils__IsAssociativeArray(subset)) or (TF_Utils__IsArray(array) and TF_Utils__IsArray(subset)) + isAA = TF_Utils__IsAssociativeArray(subset) + for each item in subset + key = invalid + value = item + if isAA + key = item + value = item[key] + end if + if TF_Utils__ArrayContains(array, value, key) + msg = "Array has the '" + TF_Utils__AsString(value) + "' value." + return msg + end if + end for + else + msg = "Input value is not an Array." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the array items count <> expected count +' +' @param array (dynamic) A target array. +' @param count (integer) An expected array items count. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertArrayCount(array as dynamic, count as integer, msg = "" as string) as string + if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array) + if array.Count() <> count + msg = "Array items count <> " + TF_Utils__AsString(count) + "." + return msg + end if + else + msg = "Input value is not an Array." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the array items count = expected count. +' +' @param array (dynamic) A target array. +' @param count (integer) An expected array items count. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertArrayNotCount(array as dynamic, count as integer, msg = "" as string) as string + if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array) + if array.Count() = count + msg = "Array items count = " + TF_Utils__AsString(count) + "." + return msg + end if + else + msg = "Input value is not an Array." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the item is not empty array or string. +' +' @param item (dynamic) An array or string to check. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertEmpty(item as dynamic, msg = "" as string) as string + if TF_Utils__IsAssociativeArray(item) or TF_Utils__IsArray(item) + if item.Count() > 0 + msg = "Array is not empty." + return msg + end if + else if TF_Utils__AsString(item) <> "" + msg = "Input value is not empty." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the item is empty array or string. +' +' @param item (dynamic) An array or string to check. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertNotEmpty(item as dynamic, msg = "" as string) as string + if TF_Utils__IsAssociativeArray(item) or TF_Utils__IsArray(item) + if item.Count() = 0 + msg = "Array is empty." + return msg + end if + else if TF_Utils__AsString(item) = "" + msg = "Input value is empty." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Fail if the array doesn't contains items of specific type only. +' +' @param array (dynamic) A target array. +' @param typeStr (string) An items type name. +' @param msg (string) An error message. +' Default value: "" +' +' @return An error message. +'---------------------------------------------------------------- +Function BTS__AssertArrayContainsOnly(array as dynamic, typeStr as string, msg = "" as string) as string + if TF_Utils__IsAssociativeArray(array) or TF_Utils__IsArray(array) + methodName = "TF_Utils__Is" + typeStr + for each item in array + if not methodName(item) + msg = TF_Utils__AsString(item) + "is not a '" + typeStr + "' type." + return msg + end if + end for + else + msg = "Input value is not an Array." + return msg + end if + return "" +End Function + +'---------------------------------------------------------------- +' Type Comparison Functionality +'---------------------------------------------------------------- + +'---------------------------------------------------------------- +' Compare two arbitrary values to each other. +' +' @param Value1 (dynamic) A first item to compare. +' @param Value2 (dynamic) A second item to compare. +' @param comparator (Function, optional) Function, to compare 2 values. Should take in 2 parameters and return either true or false. +' +' @return True if values are equal or False in other case. +'---------------------------------------------------------------- +Function BTS__EqValues(Value1 as dynamic, Value2 as dynamic, comparator = invalid as Object) as Boolean + if comparator = invalid + return m.baseComparator(value1, value2) + else + return comparator(value1, value2) + end if +End Function + +'---------------------------------------------------------------- +' Base comparator for comparing two values. +' +' @param Value1 (dynamic) A first item to compare. +' @param Value2 (dynamic) A second item to compare. +' +' @return True if values are equal or False in other case. +function BTS__BaseComparator(value1 as Dynamic, value2 as Dynamic) as Boolean + value1Type = type(value1) + value2Type = type(value2) + + if (value1Type = "roList" or value1Type = "roArray") and (value2Type = "roList" or value2Type = "roArray") + return m.eqArrays(value1, value2) + else if value1Type = "roAssociativeArray" and value2Type = "roAssociativeArray" + return m.eqAssocArrays(value1, value2) + else + return value1 = value2 + end if +end function + +'---------------------------------------------------------------- +' Compare two roAssociativeArray objects for equality. +' +' @param Value1 (object) A first associative array. +' @param Value2 (object) A second associative array. +' +' @return True if arrays are equal or False in other case. +'---------------------------------------------------------------- +Function BTS__EqAssocArray(Value1 as Object, Value2 as Object) as Boolean + 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 m.eqValues(v1, v2) + return False + end if + end if + end for + return True + end if +End Function + +'---------------------------------------------------------------- +' Compare two roArray objects for equality. +' +' @param Value1 (object) A first array. +' @param Value2 (object) A second array. +' +' @return True if arrays are equal or False in other case. +'---------------------------------------------------------------- +Function BTS__EqArray(Value1 as Object, Value2 as Object) as Boolean + 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 m.eqValues(v1, v2) then + return False + end if + end for + return True + end if +End Function'***************************************************************** +'* Copyright Roku 2011-2017 +'* All Rights Reserved +'***************************************************************** + +' Functions in this file: +' ItemGenerator +' IG_GetItem +' IG_GetAssocArray +' IG_GetArray +' IG_GetSimpleType +' IG_GetBoolean +' IG_GetInteger +' IG_GetFloat +' IG_GetString +' + +'---------------------------------------------------------------- +' Main function to generate object according to specified scheme. +' +' @param scheme (object) A scheme with desired object structure. Can be +' any simple type, array of types or associative array in form +' { propertyName1 : "propertyType1" +' propertyName2 : "propertyType2" +' ... +' propertyNameN : "propertyTypeN" } +' +' @return An object according to specified scheme or invalid, +' if scheme is not valid. +'---------------------------------------------------------------- +Function ItemGenerator(scheme as object) as Object + + this = {} + + this.getItem = IG_GetItem + this.getAssocArray = IG_GetAssocArray + this.getArray = IG_GetArray + this.getSimpleType = IG_GetSimpleType + this.getInteger = IG_GetInteger + this.getFloat = IG_GetFloat + this.getString = IG_GetString + this.getBoolean = IG_GetBoolean + + if not TF_Utils__IsValid(scheme) + return invalid + end if + + return this.getItem(scheme) +End Function + +' TODO: Create IG_GetInvalidItem function with random type fields + +'---------------------------------------------------------------- +' Generate object according to specified scheme. +' +' @param scheme (object) A scheme with desired object structure. +' Can be any simple type, array of types or associative array. +' +' @return An object according to specified scheme or invalid, +' if scheme is not one of simple type, array or +' associative array. +'---------------------------------------------------------------- +Function IG_GetItem(scheme as object) as object + + item = invalid + + if TF_Utils__IsAssociativeArray(scheme) + item = m.getAssocArray(scheme) + else if TF_Utils__IsArray(scheme) + item = m.getArray(scheme) + else if TF_Utils__IsString(scheme) + item = m.getSimpleType(lCase(scheme)) + end if + + return item +End Function + +'---------------------------------------------------------------- +' Generates associative array according to specified scheme. +' +' @param scheme (object) An associative array with desired +' object structure in form +' { propertyName1 : "propertyType1" +' propertyName2 : "propertyType2" +' ... +' propertyNameN : "propertyTypeN" } +' +' @return An associative array according to specified scheme. +'---------------------------------------------------------------- +Function 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 + +'---------------------------------------------------------------- +' Generates array according to specified scheme. +' +' @param scheme (object) An array with desired object types. +' +' @return An array according to specified scheme. +'---------------------------------------------------------------- +Function IG_GetArray(scheme as object) as object + + item = [] + + for each key in scheme + item.Push(m.getItem(key)) + end for + + return item +End Function + +'---------------------------------------------------------------- +' Generates random value of specified type. +' +' @param typeStr (string) A name of desired object type. +' +' @return A simple type object or invalid if type is not supported. +'---------------------------------------------------------------- +Function 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 + +'---------------------------------------------------------------- +' Generates random boolean value. +' +' @return A random boolean value. +'---------------------------------------------------------------- +Function IG_GetBoolean() as boolean + return TF_Utils__AsBoolean(Rnd(2) \ Rnd(2)) +End Function + +'---------------------------------------------------------------- +' Generates random integer value from 1 to specified seed value. +' +' @param seed (integer) A seed value for Rnd function. +' Default value: 100. +' +' @return A random integer value. +'---------------------------------------------------------------- +Function IG_GetInteger(seed = 100 as integer) as integer + return Rnd(seed) +End Function + +'---------------------------------------------------------------- +' Generates random float value. +' +' @return A random float value. +'---------------------------------------------------------------- +Function IG_GetFloat() as float + return Rnd(0) +End Function + +'---------------------------------------------------------------- +' Generates random string with specified length. +' +' @param seed (integer) A string length. +' +' @return A random string value or empty string if seed is 0. +'---------------------------------------------------------------- +Function 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'***************************************************************** +'* Copyright Roku 2011-2017 +'* All Rights Reserved +'***************************************************************** + +' Functions in this file: +' Logger +' Logger__SetVerbosity +' Logger__SetEcho +' Logger__SetServerURL +' Logger__PrintStatistic +' Logger__SendToServer +' Logger__CreateTotalStatistic +' Logger__CreateSuiteStatistic +' Logger__CreateTestStatistic +' Logger__AppendSuiteStatistic +' Logger__AppendTestStatistic +' Logger__PrintSuiteStatistic +' Logger__PrintTestStatistic +' Logger__PrintStart +' Logger__PrintEnd +' Logger__PrintSuiteSetUp +' Logger__PrintSuiteStart +' Logger__PrintSuiteEnd +' Logger__PrintSuiteTearDown +' Logger__PrintTestSetUp +' Logger__PrintTestStart +' Logger__PrintTestEnd +' Logger__PrintTestTearDown + +'---------------------------------------------------------------- +' Main function. Create Logger object. +' +' @return A Logger object. +'---------------------------------------------------------------- +function Logger() as Object + this = {} + + this.verbosityLevel = { + basic : 0 + normal : 1 + verboseFailed : 2 + verbose : 3 + } + + ' Internal properties + this.verbosity = this.verbosityLevel.normal + this.echoEnabled = false + this.serverURL = invalid + + ' Interface + this.SetVerbosity = Logger__SetVerbosity + this.SetEcho = Logger__SetEcho + this.SetServerURL = Logger__SetServerURL + this.PrintStatistic = Logger__PrintStatistic + this.SendToServer = Logger__SendToServer + + this.CreateTotalStatistic = Logger__CreateTotalStatistic + this.CreateSuiteStatistic = Logger__CreateSuiteStatistic + this.CreateTestStatistic = Logger__CreateTestStatistic + this.AppendSuiteStatistic = Logger__AppendSuiteStatistic + this.AppendTestStatistic = Logger__AppendTestStatistic + + ' Internal functions + this.PrintSuiteStatistic = Logger__PrintSuiteStatistic + this.PrintTestStatistic = Logger__PrintTestStatistic + this.PrintStart = Logger__PrintStart + this.PrintEnd = Logger__PrintEnd + this.PrintSuiteSetUp = Logger__PrintSuiteSetUp + this.PrintSuiteStart = Logger__PrintSuiteStart + this.PrintSuiteEnd = Logger__PrintSuiteEnd + this.PrintSuiteTearDown = Logger__PrintSuiteTearDown + this.PrintTestSetUp = Logger__PrintTestSetUp + this.PrintTestStart = Logger__PrintTestStart + this.PrintTestEnd = Logger__PrintTestEnd + this.PrintTestTearDown = Logger__PrintTestTearDown + + return this +end function + +'---------------------------------------------------------------- +' Set logging verbosity parameter. +' +' @param verbosity (integer) A verbosity level. +' Posible values: +' 0 - basic +' 1 - normal +' 2 - verbose failed tests +' 3 - verbose +' Default level: 1 +'---------------------------------------------------------------- +sub Logger__SetVerbosity(verbosity = m.verbosityLevel.normal as Integer) + if verbosity >= m.verbosityLevel.basic and verbosity <= m.verbosityLevel.verbose + m.verbosity = verbosity + end if +end sub + +'---------------------------------------------------------------- +' Set logging echo parameter. +' +' @param enable (boolean) A echo trigger. +' Posible values: true or false +' Default value: false +'---------------------------------------------------------------- +sub Logger__SetEcho(enable = false as Boolean) + m.echoEnabled = enable +end sub + +'---------------------------------------------------------------- +' Set storage server URL parameter. +' +' @param url (string) A storage server URL. +' Default level: invalid +'---------------------------------------------------------------- +sub Logger__SetServerURL(url = invalid as String) + if url <> invalid + m.serverURL = url + end if +end sub + +'---------------------------------------------------------------- +' Set storage server URL parameter. +' +' @param url (string) A storage server URL. +' Default level: invalid +'---------------------------------------------------------------- +sub Logger__SendToServer(statObj as Object) + if m.serverURL <> invalid + ' Send log file to server + end if +end sub + +'---------------------------------------------------------------- +' Print statistic object with specified verbosity. +' +' @param statObj (object) A statistic object to print. +'---------------------------------------------------------------- +sub Logger__PrintStatistic(statObj as Object) + if not m.echoEnabled + m.PrintStart() + + if m.verbosity = m.verbosityLevel.normal or m.verbosity = m.verbosityLevel.verboseFailed + for each testSuite in statObj.Suites + for each testCase in testSuite.Tests + if m.verbosity = m.verbosityLevel.verboseFailed and testCase.result = "Fail" + m.printTestStatistic(testCase) + else + ? "*** "; testSuite.Name; ": "; testCase.Name; " - "; testCase.Result + end if + end for + end for + else if m.verbosity = m.verbosityLevel.verbose + for each testSuite in statObj.Suites + m.PrintSuiteStatistic(testSuite) + end for + end if + end if + + ? "***" + ? "*** Total = "; TF_Utils__AsString(statObj.Total); " ; Passed = "; statObj.Correct; " ; Failed = "; statObj.Fail; " ; Skipped = "; statObj.skipped; " ; Crashes = "; statObj.Crash; + ? " Time spent: "; statObj.Time; "ms" + ? "***" + + m.PrintEnd() +end sub + +'---------------------------------------------------------------- +' Create an empty statistic object for totals in output log. +' +' @return An empty statistic object. +'---------------------------------------------------------------- +function Logger__CreateTotalStatistic() as Object + statTotalItem = { + Suites : [] + Time : 0 + Total : 0 + Correct : 0 + Fail : 0 + Skipped : 0 + Crash : 0 + } + + if m.echoEnabled + m.PrintStart() + end if + + return statTotalItem +end function + +'---------------------------------------------------------------- +' Create an empty statistic object for test suite with specified name. +' +' @param name (string) A test suite name for statistic object. +' +' @return An empty statistic object for test suite. +'---------------------------------------------------------------- +function Logger__CreateSuiteStatistic(name as String) as Object + statSuiteItem = { + Name : name + Tests : [] + Time : 0 + Total : 0 + Correct : 0 + Fail : 0 + Skipped : 0 + Crash : 0 + } + + if m.echoEnabled + if m.verbosity = m.verbosityLevel.verbose + m.PrintSuiteStart(name) + end if + end if + + return statSuiteItem +end function + +'---------------------------------------------------------------- +' Create statistic object for test with specified name. +' +' @param name (string) A test name. +' @param result (string) A result of test running. +' Posible values: "Success", "Fail". +' Default value: "Success" +' @param time (integer) A test running time. +' Default value: 0 +' @param errorCode (integer) An error code for failed test. +' Posible values: +' 252 (&hFC) : ERR_NORMAL_END +' 226 (&hE2) : ERR_VALUE_RETURN +' 233 (&hE9) : ERR_USE_OF_UNINIT_VAR +' 020 (&h14) : ERR_DIV_ZERO +' 024 (&h18) : ERR_TM +' 244 (&hF4) : ERR_RO2 +' 236 (&hEC) : ERR_RO4 +' 002 (&h02) : ERR_SYNTAX +' 241 (&hF1) : ERR_WRONG_NUM_PARAM +' Default value: 0 +' @param errorMessage (string) An error message for failed test. +' +' @return A statistic object for test. +'---------------------------------------------------------------- +function Logger__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 + } + } + + if m.echoEnabled + if m.verbosity = m.verbosityLevel.verbose + m.PrintTestStart(name) + end if + end if + + return statTestItem +end function + +'---------------------------------------------------------------- +' Append test statistic to test suite statistic. +' +' @param statSuiteObj (object) A target test suite object. +' @param statTestObj (object) A test statistic to append. +'---------------------------------------------------------------- +sub Logger__AppendTestStatistic(statSuiteObj as Object, statTestObj as Object) + if TF_Utils__IsAssociativeArray(statSuiteObj) and TF_Utils__IsAssociativeArray(statTestObj) + statSuiteObj.Tests.Push(statTestObj) + + if TF_Utils__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 if lCase(statTestObj.result) = "skipped" + statSuiteObj.skipped++ + else + statSuiteObj.crash = statSuiteObj.crash + 1 + end if + + if m.echoEnabled + if m.verbosity = m.verbosityLevel.normal + ? "*** "; statSuiteObj.Name; ": "; statTestObj.Name; " - "; statTestObj.Result + else if m.verbosity = m.verbosityLevel.verbose + m.PrintTestStatistic(statTestObj) + end if + end if + end if +end sub + +'---------------------------------------------------------------- +' Append suite statistic to total statistic object. +' +' @param statTotalObj (object) A target total statistic object. +' @param statSuiteObj (object) A test suite statistic object to append. +'---------------------------------------------------------------- +sub Logger__AppendSuiteStatistic(statTotalObj as Object, statSuiteObj as Object) + if TF_Utils__IsAssociativeArray(statTotalObj) and TF_Utils__IsAssociativeArray(statSuiteObj) + statTotalObj.Suites.Push(statSuiteObj) + statTotalObj.Time = statTotalObj.Time + statSuiteObj.Time + + if TF_Utils__IsInteger(statSuiteObj.Total) + statTotalObj.Total = statTotalObj.Total + statSuiteObj.Total + end if + + if TF_Utils__IsInteger(statSuiteObj.Correct) + statTotalObj.Correct = statTotalObj.Correct + statSuiteObj.Correct + end if + + if TF_Utils__IsInteger(statSuiteObj.Fail) + statTotalObj.Fail = statTotalObj.Fail + statSuiteObj.Fail + end if + + if TF_Utils__IsInteger(statSuiteObj.skipped) + statTotalObj.skipped += statSuiteObj.skipped + end if + + if TF_Utils__IsInteger(statSuiteObj.Crash) + statTotalObj.Crash = statTotalObj.Crash + statSuiteObj.Crash + end if + + if m.echoEnabled + if m.verbosity = m.verbosityLevel.verbose + m.PrintSuiteStatistic(statSuiteObj) + end if + end if + end if +end sub + +'---------------------------------------------------------------- +' Print test suite statistic. +' +' @param statSuiteObj (object) A target test suite object to print. +'---------------------------------------------------------------- +sub Logger__PrintSuiteStatistic(statSuiteObj as Object) + if not m.echoEnabled + m.PrintSuiteStart(statSuiteObj.Name) + + for each testCase in statSuiteObj.Tests + m.PrintTestStatistic(testCase) + end for + end if + + ? "===" + ? "=== Total = "; TF_Utils__AsString(statSuiteObj.Total); " ; Passed = "; statSuiteObj.Correct; " ; Failed = "; statSuiteObj.Fail; " ; Skipped = "; statSuiteObj.skipped; " ; Crashes = "; statSuiteObj.Crash; + ? " Time spent: "; statSuiteObj.Time; "ms" + ? "===" + + m.PrintSuiteEnd(statSuiteObj.Name) +end sub + +'---------------------------------------------------------------- +' Print test statistic. +' +' @param statTestObj (object) A target test object to print. +'---------------------------------------------------------------- +sub Logger__PrintTestStatistic(statTestObj as Object) + if not m.echoEnabled + m.PrintTestStart(statTestObj.Name) + end if + + ? "--- Result: "; statTestObj.Result + ? "--- Time: "; statTestObj.Time + + if lCase(statTestObj.result) = "skipped" + if len(statTestObj.message) > 0 + ? "--- Message: "; statTestObj.message + end if + else if LCase(statTestObj.Result) <> "success" + ? "--- Error Code: "; statTestObj.Error.Code + ? "--- Error Message: "; statTestObj.Error.Message + end if + + m.PrintTestEnd(statTestObj.Name) +end sub + +'---------------------------------------------------------------- +' Print testting start message. +'---------------------------------------------------------------- +sub Logger__PrintStart() + ? "" + ? "******************************************************************" + ? "******************************************************************" + ? "************* Start testing *************" + ? "******************************************************************" +end sub + +'---------------------------------------------------------------- +' Print testing end message. +'---------------------------------------------------------------- +sub Logger__PrintEnd() + ? "******************************************************************" + ? "************* End testing *************" + ? "******************************************************************" + ? "******************************************************************" + ? "" +end sub + +'---------------------------------------------------------------- +' Print test suite SetUp message. +'---------------------------------------------------------------- +sub Logger__PrintSuiteSetUp(sName as String) + if m.verbosity = m.verbosityLevel.verbose + ? "=================================================================" + ? "=== SetUp "; sName; " suite." + ? "=================================================================" + end if +end sub + +'---------------------------------------------------------------- +' Print test suite start message. +'---------------------------------------------------------------- +sub Logger__PrintSuiteStart(sName as String) + ? "=================================================================" + ? "=== Start "; sName; " suite:" + ? "===" +end sub + +'---------------------------------------------------------------- +' Print test suite end message. +'---------------------------------------------------------------- +sub Logger__PrintSuiteEnd(sName as String) + ? "===" + ? "=== End "; sName; " suite." + ? "=================================================================" +End Sub + +'---------------------------------------------------------------- +' Print test suite TearDown message. +'---------------------------------------------------------------- +sub Logger__PrintSuiteTearDown(sName as String) + if m.verbosity = m.verbosityLevel.verbose + ? "=================================================================" + ? "=== TearDown "; sName; " suite." + ? "=================================================================" + end if +end sub + +'---------------------------------------------------------------- +' Print test setUp message. +'---------------------------------------------------------------- +sub Logger__PrintTestSetUp(tName as String) + if m.verbosity = m.verbosityLevel.verbose + ? "----------------------------------------------------------------" + ? "--- SetUp "; tName; " test." + ? "----------------------------------------------------------------" + end if +end sub + +'---------------------------------------------------------------- +' Print test start message. +'---------------------------------------------------------------- +sub Logger__PrintTestStart(tName as String) + ? "----------------------------------------------------------------" + ? "--- Start "; tName; " test:" + ? "---" +end sub + +'---------------------------------------------------------------- +' Print test end message. +'---------------------------------------------------------------- +sub Logger__PrintTestEnd(tName as String) + ? "---" + ? "--- End "; tName; " test." + ? "----------------------------------------------------------------" +end sub + +'---------------------------------------------------------------- +' Print test TearDown message. +'---------------------------------------------------------------- +sub Logger__PrintTestTearDown(tName as String) + if m.verbosity = m.verbosityLevel.verbose + ? "----------------------------------------------------------------" + ? "--- TearDown "; tName; " test." + ? "----------------------------------------------------------------" + end if +end sub +'***************************************************************** +'* Copyright Roku 2011-2017 +'* All Rights Reserved +'***************************************************************** + +' Functions in this file: +' TestRunner +' TestRunner__Run +' TestRunner__SetTestsDirectory +' TestRunner__SetTestFilePrefix +' TestRunner__SetTestSuitePrefix +' TestRunner__SetTestSuiteName +' TestRunner__SetTestCaseName +' TestRunner__SetFailFast +' TestRunner__GetTestSuitesList +' TestRunner__GetTestSuiteNamesList +' TestRunner__GetTestFilesList +' TestRunner__GetTestNodesList +' TestFramework__RunNodeTests + +'---------------------------------------------------------------- +' Main function. Create TestRunner object. +' +' @return A TestRunner object. +'---------------------------------------------------------------- +function TestRunner() as Object + + this = {} + + this.isNodeMode = getGlobalAA().top <> invalid + this.logger = Logger() + + ' Internal properties + this.SKIP_TEST_MESSAGE_PREFIX = "SKIP_TEST_MESSAGE_PREFIX__" + this.nodesTestDirectory = "pkg:/components/tests" + if this.isNodeMode + this.testsDirectory = this.nodesTestDirectory + this.testFilePrefix = m.top.subtype() + else + this.testsDirectory = "pkg:/source/tests" + this.testFilePrefix = "Test__" + end if + this.testSuitePrefix = "TestSuite__" + this.testSuiteName = "" + this.testCaseName = "" + this.failFast = false + + ' Interface + this.Run = TestRunner__Run + this.SetTestsDirectory = TestRunner__SetTestsDirectory + this.SetTestFilePrefix = TestRunner__SetTestFilePrefix + this.SetTestSuitePrefix = TestRunner__SetTestSuitePrefix + this.SetTestSuiteName = TestRunner__SetTestSuiteName + this.SetTestCaseName = TestRunner__SetTestCaseName + this.SetFailFast = TestRunner__SetFailFast + + ' Internal functions + this.GetTestFilesList = TestRunner__GetTestFilesList + this.GetTestSuitesList = TestRunner__GetTestSuitesList + this.GetTestNodesList = TestRunner__GetTestNodesList + this.GetTestSuiteNamesList = TestRunner__GetTestSuiteNamesList + + return this +end function + +'---------------------------------------------------------------- +' Run main test loop. +' +' @param statObj (object, optional) statistic object to be used in tests +' @param testSuiteNamesList (array, optional) array of test suite function names to be used in tests +' +' @return Statistic object if run in node mode, invalid otherwise +'---------------------------------------------------------------- +function TestRunner__Run(statObj = m.logger.CreateTotalStatistic() as Object, testSuiteNamesList = [] as Object) as Object + alltestCount = 0 + totalStatObj = statObj + testSuitesList = m.GetTestSuitesList(testSuiteNamesList) + + for each testSuite in testSuitesList + testCases = testSuite.testCases + testCount = testCases.Count() + alltestCount = alltestCount + testCount + + if TF_Utils__IsFunction(testSuite.SetUp) + m.logger.PrintSuiteSetUp(testSuite.Name) + testSuite.SetUp() + end if + + suiteStatObj = m.logger.CreateSuiteStatistic(testSuite.Name) + + for each testCase in testCases + if m.testCaseName = "" or (m.testCaseName <> "" and LCase(testCase.Name) = LCase(m.testCaseName)) + if TF_Utils__IsFunction(testCase.SetUp) + m.logger.PrintTestSetUp(testCase.Name) + testCase.SetUp() + end if + + testTimer = CreateObject("roTimespan") + testStatObj = m.logger.CreateTestStatistic(testCase.Name) + testSuite.testInstance = testCase + testSuite.testCase = testCase.Func + + runResult = testSuite.testCase() + + if runResult <> "" + if instr(0, runResult, m.SKIP_TEST_MESSAGE_PREFIX) = 1 + testStatObj.result = "Skipped" + testStatObj.message = runResult.mid(len(m.SKIP_TEST_MESSAGE_PREFIX)) ' remove prefix from the message + else + testStatObj.Result = "Fail" + testStatObj.Error.Code = 1 + testStatObj.Error.Message = runResult + end if + else + testStatObj.Result = "Success" + end if + + testStatObj.Time = testTimer.TotalMilliseconds() + m.logger.AppendTestStatistic(suiteStatObj, testStatObj) + + if TF_Utils__IsFunction(testCase.TearDown) + m.logger.PrintTestTearDown(testCase.Name) + testCase.TearDown() + end if + + if testStatObj.Result = "Fail" and m.failFast + exit for + end if + end if + end for + + m.logger.AppendSuiteStatistic(totalStatObj, suiteStatObj) + + if TF_Utils__IsFunction(testSuite.TearDown) + m.logger.PrintSuiteTearDown(testSuite.Name) + testSuite.TearDown() + end if + + if testStatObj.Result = "Fail" and m.failFast + exit for + end if + end for + + if m.isNodeMode + return totalStatObj + else + testNodes = m.getTestNodesList() + + for each testNodeName in testNodes + testNode = createObject("roSGNode", testNodeName) + if testNode <> invalid + testSuiteNamesList = m.GetTestSuiteNamesList(testNodeName) + totalStatObj = testNode.callFunc("TestFramework__RunNodeTests", [totalStatObj, testSuiteNamesList]) + end if + end for + + m.logger.PrintStatistic(totalStatObj) + end if +end function + +'---------------------------------------------------------------- +' Set testsDirectory property. +'---------------------------------------------------------------- +sub TestRunner__SetTestsDirectory(testsDirectory as String) + if testsDirectory <> invalid + m.testsDirectory = testsDirectory + end if +end sub + +'---------------------------------------------------------------- +' Set setTestFilePrefix property. +'---------------------------------------------------------------- +sub TestRunner__SetTestFilePrefix(testFilePrefix as String) + if testFilePrefix <> invalid + m.testFilePrefix = setTestFilePrefix + end if +end sub + +'---------------------------------------------------------------- +' Set testSuitePrefix property. +'---------------------------------------------------------------- +sub TestRunner__SetTestSuitePrefix(testSuitePrefix as String) + if testSuitePrefix <> invalid + m.testSuitePrefix = testSuitePrefix + end if +end sub + +'---------------------------------------------------------------- +' Set testSuiteName property. +'---------------------------------------------------------------- +sub TestRunner__SetTestSuiteName(testSuiteName as String) + if testSuiteName <> invalid + m.testSuiteName = testSuiteName + end if +end sub + +'---------------------------------------------------------------- +' Set testCaseName property. +'---------------------------------------------------------------- +sub TestRunner__SetTestCaseName(testCaseName as String) + if testCaseName <> invalid + m.testCaseName = testCaseName + end if +end sub + +'---------------------------------------------------------------- +' Set failFast property. +'---------------------------------------------------------------- +sub TestRunner__SetFailFast(failFast = false as Boolean) + m.failFast = failFast +end sub + +'---------------------------------------------------------------- +' Builds an array of test suite objects. +' +' @param testSuiteNamesList (string, optional) array of names of test suite functions. If not passed, scans all test files for test suites +' +' @return An array of test suites. +'---------------------------------------------------------------- +function TestRunner__GetTestSuitesList(testSuiteNamesList = [] as Object) as Object + result = [] + + if testSuiteNamesList.count() > 0 + for each functionName in testSuiteNamesList + eval("testSuite=" + functionName) + testSuite = testSuite() + + if TF_Utils__IsAssociativeArray(testSuite) + result.Push(testSuite) + end if + end for + else + testSuiteRegex = CreateObject("roRegex", "^(function|sub)\s(" + m.testSuitePrefix + m.testSuiteName + "[0-9a-z\_]*)\s*\(", "i") + testFilesList = m.GetTestFilesList() + + for each filePath in testFilesList + code = TF_Utils__AsString(ReadAsciiFile(filePath)) + + if code <> "" + for each line in code.Tokenize(chr(10)) + line.Trim() + + if testSuiteRegex.IsMatch(line) + testSuite = invalid + functionName = testSuiteRegex.Match(line).Peek() + + eval("testSuite=" + functionName) + testSuite = testSuite() + + if TF_Utils__IsAssociativeArray(testSuite) + result.Push(testSuite) + end if + end if + end for + end if + end for + end if + + return result +end function + + +'---------------------------------------------------------------- +' Scans all test files for test suite function names for a given test node. +' +' @param testNodeName (string) name of a test node, test suites for which are needed +' +' @return An array of test suite names. +'---------------------------------------------------------------- +function TestRunner__GetTestSuiteNamesList(testNodeName as String) as Object + result = [] + testSuiteRegex = CreateObject("roRegex", "^(function|sub)\s(" + m.testSuitePrefix + m.testSuiteName + "[0-9a-z\_]*)\s*\(", "i") + testFilesList = m.GetTestFilesList(m.nodesTestDirectory, testNodeName) + + for each filePath in testFilesList + code = TF_Utils__AsString(ReadAsciiFile(filePath)) + + if code <> "" + for each line in code.Tokenize(chr(10)) + line.Trim() + + if testSuiteRegex.IsMatch(line) + functionName = testSuiteRegex.Match(line).Peek() + result.Push(functionName) + end if + end for + end if + end for + + return result +end function + + +'---------------------------------------------------------------- +' Scan testsDirectory and all subdirectories for test files. +' +' @param testsDirectory (string, optional) A target directory with test files. +' @param testFilePrefix (string, optional) prefix, used by test files +' +' @return An array of test files. +'---------------------------------------------------------------- +function TestRunner__GetTestFilesList(testsDirectory = m.testsDirectory as String, testFilePrefix = m.testFilePrefix as String) as Object + result = [] + testsFileRegex = CreateObject("roRegex", "^(" + testFilePrefix + ")[0-9a-z\_]*\.brs$", "i") + + if testsDirectory <> "" + fileSystem = CreateObject("roFileSystem") + listing = fileSystem.GetDirectoryListing(testsDirectory) + + for each item in listing + itemPath = testsDirectory + "/" + item + itemStat = fileSystem.Stat(itemPath) + + if itemStat.type = "directory" then + result.Append(m.getTestFilesList(itemPath, testFilePrefix)) + else if testsFileRegex.IsMatch(item) then + result.Push(itemPath) + end if + end for + end if + + return result +end function + + +'---------------------------------------------------------------- +' Scan nodesTestDirectory and all subdirectories for test nodes. +' +' @param nodesTestDirectory (string, optional) A target directory with test nodes. +' +' @return An array of test node names. +'---------------------------------------------------------------- +function TestRunner__GetTestNodesList(testsDirectory = m.nodesTestDirectory as String) as Object + result = [] + testsFileRegex = CreateObject("roRegex", "^(" + m.testFilePrefix + ")[0-9a-z\_]*\.xml$", "i") + + if testsDirectory <> "" + fileSystem = CreateObject("roFileSystem") + listing = fileSystem.GetDirectoryListing(testsDirectory) + + for each item in listing + itemPath = testsDirectory + "/" + item + itemStat = fileSystem.Stat(itemPath) + + if itemStat.type = "directory" then + result.Append(m.getTestNodesList(itemPath)) + else if testsFileRegex.IsMatch(item) then + result.Push(item.replace(".xml", "")) + end if + end for + end if + + return result +end function + + +'---------------------------------------------------------------- +' Creates and runs test runner. Should be used ONLY within a node. +' +' @param params (array) parameters, passed from main thread, used to setup new test runner +' +' @return statistic object. +'---------------------------------------------------------------- +function TestFramework__RunNodeTests(params as Object) as Object + statObj = params[0] + testSuiteNamesList = params[1] + + Runner = TestRunner() + return Runner.RUN(statObj, testSuiteNamesList) +end function +'***************************************************************** +'* Copyright Roku 2011-2017 +'* All Rights Reserved +'***************************************************************** +' Common framework utility functions +'***************************************************************** + +'************************************************* +' TF_Utils__IsXmlElement - check if value contains XMLElement interface +' @param value As Dynamic +' @return As Boolean - true if value contains XMLElement interface, else return false +'************************************************* +function TF_Utils__IsXmlElement(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifXMLElement") <> invalid +end function + +'************************************************* +' TF_Utils__IsFunction - check if value contains Function interface +' @param value As Dynamic +' @return As Boolean - true if value contains Function interface, else return false +'************************************************* +function TF_Utils__IsFunction(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifFunction") <> invalid +end function + +'************************************************* +' TF_Utils__IsBoolean - check if value contains Boolean interface +' @param value As Dynamic +' @return As Boolean - true if value contains Boolean interface, else return false +'************************************************* +function TF_Utils__IsBoolean(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifBoolean") <> invalid +end function + +'************************************************* +' TF_Utils__IsInteger - check if value type equals Integer +' @param value As Dynamic +' @return As Boolean - true if value type equals Integer, else return false +'************************************************* +function TF_Utils__IsInteger(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifInt") <> invalid and (Type(value) = "roInt" or Type(value) = "roInteger" or Type(value) = "Integer") +end function + +'************************************************* +' TF_Utils__IsFloat - check if value contains Float interface +' @param value As Dynamic +' @return As Boolean - true if value contains Float interface, else return false +'************************************************* +function TF_Utils__IsFloat(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifFloat") <> invalid +end function + +'************************************************* +' TF_Utils__IsDouble - check if value contains Double interface +' @param value As Dynamic +' @return As Boolean - true if value contains Double interface, else return false +'************************************************* +function TF_Utils__IsDouble(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifDouble") <> invalid +end function + +'************************************************* +' TF_Utils__IsLongInteger - check if value contains LongInteger interface +' @param value As Dynamic +' @return As Boolean - true if value contains LongInteger interface, else return false +'************************************************* +function TF_Utils__IsLongInteger(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifLongInt") <> invalid +end function + +'************************************************* +' TF_Utils__IsNumber - check if value contains LongInteger or Integer or Double or Float interface +' @param value As Dynamic +' @return As Boolean - true if value is number, else return false +'************************************************* +function TF_Utils__IsNumber(value as Dynamic) as Boolean + return TF_Utils__IsLongInteger(value) or TF_Utils__IsDouble(value) or TF_Utils__IsInteger(value) or TF_Utils__IsFloat(value) +end function + +'************************************************* +' TF_Utils__IsList - check if value contains List interface +' @param value As Dynamic +' @return As Boolean - true if value contains List interface, else return false +'************************************************* +function TF_Utils__IsList(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifList") <> invalid +end function + +'************************************************* +' TF_Utils__IsArray - check if value contains Array interface +' @param value As Dynamic +' @return As Boolean - true if value contains Array interface, else return false +'************************************************* +function TF_Utils__IsArray(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifArray") <> invalid +end function + +'************************************************* +' TF_Utils__IsAssociativeArray - check if value contains AssociativeArray interface +' @param value As Dynamic +' @return As Boolean - true if value contains AssociativeArray interface, else return false +'************************************************* +function TF_Utils__IsAssociativeArray(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifAssociativeArray") <> invalid +end function + +'************************************************* +' TF_Utils__IsSGNode - check if value contains SGNodeChildren interface +' @param value As Dynamic +' @return As Boolean - true if value contains SGNodeChildren interface, else return false +'************************************************* +function TF_Utils__IsSGNode(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifSGNodeChildren") <> invalid +end function + +'************************************************* +' TF_Utils__IsString - check if value contains String interface +' @param value As Dynamic +' @return As Boolean - true if value contains String interface, else return false +'************************************************* +function TF_Utils__IsString(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and GetInterface(value, "ifString") <> invalid +end function + +'************************************************* +' TF_Utils__IsNotEmptyString - check if value contains String interface and length more 0 +' @param value As Dynamic +' @return As Boolean - true if value contains String interface and length more 0, else return false +'************************************************* +function TF_Utils__IsNotEmptyString(value as Dynamic) as Boolean + return TF_Utils__IsString(value) and len(value) > 0 +end function + +'************************************************* +' TF_Utils__IsDateTime - check if value contains DateTime interface +' @param value As Dynamic +' @return As Boolean - true if value contains DateTime interface, else return false +'************************************************* +function TF_Utils__IsDateTime(value as Dynamic) as Boolean + return TF_Utils__IsValid(value) and (GetInterface(value, "ifDateTime") <> invalid or Type(value) = "roDateTime") +end function + +'************************************************* +' TF_Utils__IsValid - check if value initialized and not equal invalid +' @param value As Dynamic +' @return As Boolean - true if value initialized and not equal invalid, else return false +'************************************************* +function TF_Utils__IsValid(value as Dynamic) as Boolean + return Type(value) <> "" and value <> invalid +end function + +'************************************************* +' TF_Utils__ValidStr - return value if his contains String interface else return empty string +' @param value As Object +' @return As String - value if his contains String interface else return empty string +'************************************************* +function TF_Utils__ValidStr(obj as Object) as String + if obj <> invalid and GetInterface(obj, "ifString") <> invalid + return obj + else + return "" + end if +end function + +'************************************************* +' TF_Utils__AsString - convert input to String if this possible, else return empty string +' @param input As Dynamic +' @return As String - return converted string +'************************************************* +function TF_Utils__AsString(input as Dynamic) as String + if TF_Utils__IsValid(input) = false + return "" + else if TF_Utils__IsString(input) + return input + else if TF_Utils__IsInteger(input) or TF_Utils__IsLongInteger(input) or TF_Utils__IsBoolean(input) + return input.ToStr() + else if TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) + return Str(input).Trim() + else + return "" + end If +end function + +'************************************************* +' TF_Utils__AsInteger - convert input to Integer if this possible, else return 0 +' @param input As Dynamic +' @return As Integer - return converted Integer +'************************************************* +function TF_Utils__AsInteger(input as Dynamic) as Integer + if TF_Utils__IsValid(input) = false + return 0 + else if TF_Utils__IsString(input) + return input.ToInt() + else if TF_Utils__IsInteger(input) + return input + else if TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) or TF_Utils__IsLongInteger(input) + return Int(input) + else + return 0 + end if +end function + +'************************************************* +' TF_Utils__AsLongInteger - convert input to LongInteger if this possible, else return 0 +' @param input As Dynamic +' @return As Integer - return converted LongInteger +'************************************************* +function TF_Utils__AsLongInteger(input as Dynamic) as LongInteger + if TF_Utils__IsValid(input) = false + return 0 + else if TF_Utils__IsString(input) + return TF_Utils__AsInteger(input) + else if TF_Utils__IsLongInteger(input) or TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) or TF_Utils__IsInteger(input) + return input + else + return 0 + end if +end function + +'************************************************* +' TF_Utils__AsFloat - convert input to Float if this possible, else return 0.0 +' @param input As Dynamic +' @return As Float - return converted Float +'************************************************* +function TF_Utils__AsFloat(input as Dynamic) as Float + if TF_Utils__IsValid(input) = false + return 0.0 + else if TF_Utils__IsString(input) + return input.ToFloat() + else if TF_Utils__IsInteger(input) + return (input / 1) + else if TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) or TF_Utils__IsLongInteger(input) + return input + else + return 0.0 + end if +end function + +'************************************************* +' TF_Utils__AsDouble - convert input to Double if this possible, else return 0.0 +' @param input As Dynamic +' @return As Float - return converted Double +'************************************************* +function TF_Utils__AsDouble(input as Dynamic) as Double + if TF_Utils__IsValid(input) = false + return 0.0 + else if TF_Utils__IsString(input) + return TF_Utils__AsFloat(input) + else if TF_Utils__IsInteger(input) or TF_Utils__IsLongInteger(input) or TF_Utils__IsFloat(input) or TF_Utils__IsDouble(input) + return input + else + return 0.0 + end if +end function + +'************************************************* +' TF_Utils__AsBoolean - convert input to Boolean if this possible, else return False +' @param input As Dynamic +' @return As Boolean +'************************************************* +function TF_Utils__AsBoolean(input as Dynamic) as Boolean + if TF_Utils__IsValid(input) = false + return false + else if TF_Utils__IsString(input) + return LCase(input) = "true" + else if TF_Utils__IsInteger(input) or TF_Utils__IsFloat(input) + return input <> 0 + else if TF_Utils__IsBoolean(input) + return input + else + return false + end if +end function + +'************************************************* +' TF_Utils__AsArray - if type of value equals array return value, else return array with one element [value] +' @param value As Object +' @return As Object - roArray +'************************************************* +function TF_Utils__AsArray(value as Object) as Object + if TF_Utils__IsValid(value) + if not TF_Utils__IsArray(value) + return [value] + else + return value + end if + end if + return [] +end function + +'===================== +' Strings +'===================== + +'************************************************* +' TF_Utils__IsNullOrEmpty - check if value is invalid or empty +' @param value As Dynamic +' @return As Boolean - true if value is null or empty string, else return false +'************************************************* +function TF_Utils__IsNullOrEmpty(value as Dynamic) as Boolean + if TF_Utils__IsString(value) + return Len(value) = 0 + else + return not TF_Utils__IsValid(value) + end if +end function + +'===================== +' Arrays +'===================== + +'************************************************* +' TF_Utils__FindElementIndexInArray - find an element index in array +' @param array As Object +' @param value As Object +' @param compareAttribute As Dynamic +' @param caseSensitive As Boolean +' @return As Integer - element index if array contains a value, else return -1 +'************************************************* +function TF_Utils__FindElementIndexInArray(array as Object, value as Object, compareAttribute = invalid as Dynamic, caseSensitive = false as Boolean) as Integer + if TF_Utils__IsArray(array) + for i = 0 to TF_Utils__AsArray(array).Count() - 1 + compareValue = array[i] + + if compareAttribute <> invalid and TF_Utils__IsAssociativeArray(compareValue) + compareValue = compareValue.LookupCI(compareAttribute) + end If + + if TF_Utils__IsString(compareValue) and TF_Utils__IsString(value) and not caseSensitive + if LCase(compareValue) = LCase(value) + return i + end If + else if compareValue = value + return i + end if + + item = array[i] + next + end if + return -1 +end function + +'************************************************* +' TF_Utils__ArrayContains - check if array contains specified value +' @param array As Object +' @param value As Object +' @param compareAttribute As Dynamic +' @return As Boolean - true if array contains a value, else return false +'************************************************* +function TF_Utils__ArrayContains(array as Object, value as Object, compareAttribute = invalid as Dynamic) as Boolean + return (TF_Utils__FindElementIndexInArray(array, value, compareAttribute) > -1) +end function