Skip to content

Commit

Permalink
tests: cleanup unit test loop launcher and add some doc (#1365)
Browse files Browse the repository at this point in the history
  • Loading branch information
canepat authored Jul 26, 2023
1 parent eaf08df commit 4948871
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 22 deletions.
39 changes: 39 additions & 0 deletions tests/unit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Unit Test Loop
This is a simple script to stress the unit test suite, useful when you need to debug some flaky unit test.
It works with multiple compilers and build configurations.

## Usage

```
$cd tests/unit
$./run_unit_test_loop.py
Usage: ./run_unit_test_loop.py [-h] [-i iterations] [-m modules] [-t test] builddir
Launch an automated unit test sequence on target build configuration
builddir
the path of the target build folder
-h print this help
-i iterations
the number of iterations for each configuration (default: 1000)
-m modules
the list of unit test modules to launch (default: ['core_test', 'node_test', 'rpcdaemon_test', 'sentry_test', 'sync_test'])
-o options
the Catch2 options to pass to the launcher enclosed in string (default: "" i.e. none)
-t test
the name of the unique Catch2 TEST_CASE to execute (default: run all tests)
```

## Examples

```
$cd tests/unit
$./run_unit_test_loop.py -i 100 -m node_test ../../cmake-build-clang-release
$./run_unit_test_loop.py -i 100 -m node_test -o "-d yes" ../../cmake-build-clang-release
$./run_unit_test_loop.py -i 100 -m node_test -t "MemoryMutationCursor: to_next" ../../cmake-build-clang-release
```
48 changes: 26 additions & 22 deletions tests/unit/run_unit_test_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@
import getopt
import os
import sys
from typing import Dict, List
from typing import List


class UnitTestModule(str, Enum):
""" The unit test modules"""
CORE_TEST = 'CORE_TEST'
NODE_TEST = 'NODE_TEST'
RPCDAEMON_TEST = 'RPCDAEMON_TEST'
SENTRY_TEST = 'SENTRY_TEST'
SYNC_TEST = 'SYNC_TEST'
core_test = 'core_test'
node_test = 'node_test'
rpcdaemon_test = 'rpcdaemon_test'
sentry_test = 'sentry_test'
sync_test = 'sync_test'

@classmethod
def item_names(cls) -> List[str]:
""" Return the list of enumeration item names """
return [member_name for member_name in UnitTestModule.__members__.keys()]

@classmethod
def has_item(cls, name: str) -> bool:
""" Return true if name is a valid enumeration item, false otherwise """
return name in UnitTestModule._member_names_ # pylint: disable=no-member
return name in cls.item_names()


class UnitTest:
Expand All @@ -31,12 +36,12 @@ def __init__(self, module: UnitTestModule, build_dir: str):
self.module = module
self.build_dir = build_dir

def execute(self, num_iterations: int, test_name: str = None) -> None:
def execute(self, num_iterations: int, test_name: str = None, test_options: str = "") -> None:
""" Execute the unit tests `num_iterations` times """
cmd = self.build_dir + "/cmd/test/" + self.module.name.lower()
if test_name is not None and test_name != '':
cmd = cmd + " \"" + test_name + "\""
cmd = cmd + " -d yes"
cmd = cmd + " " + test_options
print("Unit test runner: " + cmd + "\n")

print("Unit test stress for " + self.module.name.lower() + " STARTED")
Expand All @@ -49,15 +54,8 @@ def execute(self, num_iterations: int, test_name: str = None) -> None:
print("Unit test stress for " + self.module.name.lower() + " COMPLETED [" + str(num_iterations) + "]")


DEFAULT_TEST_NAME: str = None
DEFAULT_NUM_ITERATIONS: int = 1000
DEFAULT_MODULES: List[str] = [
UnitTestModule.CORE_TEST,
UnitTestModule.NODE_TEST,
UnitTestModule.RPCDAEMON_TEST,
UnitTestModule.SENTRY_TEST,
UnitTestModule.SYNC_TEST
]
DEFAULT_MODULES: List[str] = UnitTestModule.item_names()


def usage(argv):
Expand All @@ -74,21 +72,24 @@ def usage(argv):
print(" \tthe number of iterations for each configuration (default: " + str(DEFAULT_NUM_ITERATIONS) + ")")
print("-m\tmodules")
print(" \tthe list of unit test modules to launch (default: " + str(DEFAULT_MODULES) + ")")
print("-o\toptions")
print(" \tthe Catch2 options to pass to the launcher enclosed in string (default: \"\" i.e. none)")
print("-t\ttest")
print(" \tthe name of the unique TEST to execute (default: run all tests)")
print(" \tthe name of the unique Catch2 TEST_CASE to execute (default: run all tests)")
sys.exit(0)


def main(argv) -> int:
""" Main entry point """
opts, args = getopt.getopt(argv[1:], "hi:m:t:")
opts, args = getopt.getopt(argv[1:], "hi:m:o:t:")

if len(args) == 0:
usage(argv)
return 1

build_dir = args[0]
test_name = DEFAULT_TEST_NAME
test_name = None
test_options = ""
iterations = DEFAULT_NUM_ITERATIONS
modules = DEFAULT_MODULES

Expand All @@ -99,17 +100,20 @@ def main(argv) -> int:
iterations = int(option_arg)
elif option == "-m":
modules = str(option_arg).split(",")
elif option == "-o":
test_options = option_arg
print("test_options=" + test_options)
elif option == "-t":
test_name = option_arg

for module_name in modules:
module_name = module_name.upper()
module_name = module_name.lower()
if not UnitTestModule.has_item(module_name):
print("Invalid test module name [" + module_name + "], ignored")
continue
unit_test_module = UnitTestModule(module_name)
unit_test = UnitTest(unit_test_module, build_dir)
unit_test.execute(iterations, test_name)
unit_test.execute(iterations, test_name, test_options)

return 0

Expand Down

0 comments on commit 4948871

Please sign in to comment.