From 7b2645741a248b563e8329849a732cb5c936207d Mon Sep 17 00:00:00 2001 From: Eran Date: Sun, 21 Feb 2021 18:27:00 +0200 Subject: [PATCH 1/3] run-unit-tests.py no longer requires pyrealsense2.pyd --- unit-tests/py/rspy/devices.py | 26 ++++++++++--- unit-tests/py/rspy/log.py | 4 +- unit-tests/py/rspy/test.py | 19 ++++++---- unit-tests/run-unit-tests.py | 71 ++++++++++++++++++++++------------- 4 files changed, 80 insertions(+), 40 deletions(-) diff --git a/unit-tests/py/rspy/devices.py b/unit-tests/py/rspy/devices.py index b567ca9a96..2674f9bed0 100644 --- a/unit-tests/py/rspy/devices.py +++ b/unit-tests/py/rspy/devices.py @@ -1,17 +1,29 @@ # License: Apache 2.0. See LICENSE file in root directory. # Copyright(c) 2021 Intel Corporation. All Rights Reserved. +from rspy import log + +# We need both pyrealsense2 and acroname. We can work without acroname, but +# without rs no devices at all will be returned. try: - from rspy import acroname + import pyrealsense2 as rs + log.d( rs ) + try: + from rspy import acroname + except ModuleNotFoundError: + # Error should have already been printed + # We assume there's no brainstem library, meaning no acroname either + acroname = None except ModuleNotFoundError: - # Error should have already been printed - # We assume there's no brainstem library, meaning no acroname either + log.w( 'No pyrealsense2 library is available! Running as if no cameras available...' ) + import sys + log.d( 'sys.path=', sys.path ) + rs = None acroname = None -import pyrealsense2 as rs + import time -from rspy import log -_device_by_sn = None +_device_by_sn = dict() _context = None @@ -19,6 +31,8 @@ def query(): """ Start a new LRS context, and collect all devices """ + if not rs: + return # # Before we can start a context and query devices, we need to enable all the ports # on the acroname, if any: diff --git a/unit-tests/py/rspy/log.py b/unit-tests/py/rspy/log.py index 83c76406d4..b3a0084ab0 100644 --- a/unit-tests/py/rspy/log.py +++ b/unit-tests/py/rspy/log.py @@ -54,7 +54,6 @@ def is_color_on(): def quiet_on(): - print( "QUIET ON" ) global out def out(*args): pass @@ -86,6 +85,9 @@ def d(*args): def is_debug_on(): global _debug_on return _debug_on +if '--debug' in sys.argv: + sys.argv.remove( '--debug' ) + debug_on() def i(*args): diff --git a/unit-tests/py/rspy/test.py b/unit-tests/py/rspy/test.py index 5a6681aa4b..7b78d92277 100644 --- a/unit-tests/py/rspy/test.py +++ b/unit-tests/py/rspy/test.py @@ -15,7 +15,6 @@ """ import os, sys, subprocess, traceback, platform -import pyrealsense2 as rs n_assertions = 0 n_failed_assertions = 0 @@ -46,12 +45,16 @@ def set_env_vars(env_vars): for env_var, val in env_vars.items(): os.environ[env_var] = val cmd = [sys.executable] + if 'site' not in sys.modules: + # -S : don't imply 'import site' on initialization + cmd += ["-S"] if sys.flags.verbose: + # -v : verbose (trace import statements) cmd += ["-v"] cmd += sys.argv cmd += ["rerun"] p = subprocess.run( cmd, stderr=subprocess.PIPE, universal_newlines=True ) - exit(p.returncode) + sys.exit( p.returncode ) sys.argv = sys.argv[:-1] # Remove the rerun def find_first_device_or_exit(): @@ -59,10 +62,11 @@ def find_first_device_or_exit(): :return: the first device that was found, if no device is found the test is skipped. That way we can still run the unit-tests when no device is connected and not fail the tests that check a connected device """ + import pyrealsense2 as rs c = rs.context() if not c.devices.size(): # if no device is connected we skip the test print("No device found, skipping test") - exit(0) + sys.exit( 0 ) return c.devices[0] def find_devices_by_product_line_or_exit(product_line): @@ -72,11 +76,12 @@ def find_devices_by_product_line_or_exit(product_line): That way we can still run the unit-tests when no device is connected and not fail the tests that check a connected device """ + import pyrealsense2 as rs c = rs.context() devices_list = c.query_devices(product_line) if devices_list.size() == 0: print("No device of the" , product_line ,"product line was found; skipping test") - exit(0) + sys.exit( 0 ) return devices_list def print_stack(): @@ -112,7 +117,7 @@ def check_failed(): def abort(): print("Abort was specified in a failed check. Aborting test") - exit(1) + sys.exit( 1 ) def check(exp, abort_if_failed = False): """ @@ -365,6 +370,6 @@ def print_results_and_exit(): passed = n_assertions - n_failed_assertions print("test cases:", n_tests, "|" , n_failed_tests, "failed") print("assertions:", n_assertions, "|", passed, "passed |", n_failed_assertions, "failed") - exit(1) + sys.exit(1) print("All tests passed (" + str(n_assertions) + " assertions in " + str(n_tests) + " test cases)") - exit(0) + sys.exit(0) diff --git a/unit-tests/run-unit-tests.py b/unit-tests/run-unit-tests.py index 2f8d645d62..bb93db1837 100644 --- a/unit-tests/run-unit-tests.py +++ b/unit-tests/run-unit-tests.py @@ -1,19 +1,23 @@ #!python3 # License: Apache 2.0. See LICENSE file in root directory. -# Copyright(c) 2020 Intel Corporation. All Rights Reserved. +# Copyright(c) 2021 Intel Corporation. All Rights Reserved. import sys, os, subprocess, locale, re, platform, getopt from abc import ABC, abstractmethod -# Add our py/ module directory to Python's list so we can use them +# Remove Python's default list of places to look for modules! +# We want only modules in the directories we specifically provide to be found, +# otherwise pyrs other than what we compiled might be found... +sys.path = list() +sys.path.append( '' ) +# Add our py/ module directory current_dir = os.path.dirname( os.path.abspath( __file__ )) -sys.path.insert( 1, current_dir + os.sep + "py" ) +sys.path.append( current_dir + os.sep + "py" ) from rspy import log def usage(): - ourname = os.path.basename(sys.argv[0]) ourname = os.path.basename(sys.argv[0]) print( 'Syntax: ' + ourname + ' [options] [dir]' ) print( ' dir: the directory holding the executable tests to run (default to the build directory') @@ -71,8 +75,6 @@ def is_executable(path_to_test): for opt,arg in opts: if opt in ('-h','--help'): usage() - elif opt in ('--debug'): - log.debug_on() elif opt in ('-v','--verbose'): log.verbose_on() elif opt in ('-q','--quiet'): @@ -111,7 +113,7 @@ def is_executable(path_to_test): # wrapper function for subprocess.run def subprocess_run(cmd, stdout = None): - log.d( 'running:', cmd ) + log.d( ' running:', cmd ) handle = None try: if stdout and stdout != subprocess.PIPE: @@ -150,11 +152,16 @@ def subprocess_run(cmd, stdout = None): # We need to add the directory not the file itself pyrs_path = os.path.dirname(pyrs_path) log.d( 'found pyrealsense pyd in:', pyrs_path ) - # Add the necessary path to the PYTHONPATH environment variable so python will look for modules there - os.environ["PYTHONPATH"] = pyrs_path - # We also need to add the path to the python packages that the tests use - os.environ["PYTHONPATH"] += os.pathsep + (current_dir + os.sep + "py") - # We can simply change `sys.path` but any child python scripts won't see it. We change the environment instead. + +# Figure out which sys.path we want the tests to see, assuming we have Python tests +# PYTHONPATH is what Python will ADD to sys.path for the child processes +# (We can simply change `sys.path` but any child python scripts won't see it; we change the environment instead) +# +# We also need to add the path to the python packages that the tests use +os.environ["PYTHONPATH"] = current_dir + os.sep + "py" +# +if pyrs: + os.environ["PYTHONPATH"] += os.pathsep + pyrs_path def remove_newlines (lines): for line in lines: @@ -329,6 +336,11 @@ def __init__(self, testname, path_to_test): @property def command(self): cmd = [sys.executable] + # The unit-tests should only find module we've specifically added -- but Python may have site packages + # that are automatically made available. We want to avoid those: + # -S : don't imply 'import site' on initialization + # NOTE: exit() is defined in site.py and works only if the site module is imported! + cmd += ['-S'] if sys.flags.verbose: cmd += ["-v"] cmd += [self.path_to_script] @@ -390,9 +402,11 @@ def get_tests(): manifestfile = target + '/CMakeFiles/TargetDirectories.txt' else: manifestfile = target + '/../CMakeFiles/TargetDirectories.txt' + #log.d( manifestfile ) for manifest_ctx in grep(r'(?<=unit-tests/build/)\S+(?=/CMakeFiles/test-\S+.dir$)', manifestfile): # We need to first create the test name so we can see if it fits the regex testdir = manifest_ctx['match'].group(0) # "log/internal/test-all" + #log.d( testdir ) testparent = os.path.dirname(testdir) # "log/internal" if testparent: testname = 'test-' + testparent.replace('/', '-') + '-' + os.path.basename(testdir)[5:] # "test-log-internal-all" @@ -409,20 +423,19 @@ def get_tests(): yield ExeTest(testname, exe) - # If we run python tests with no .pyd/.so file they will crash. Therefore we only run them if such a file was found - if pyrs: - # unit-test scripts are in the same directory as this script - for py_test in find(current_dir, '(^|/)test-.*\.py'): - testparent = os.path.dirname(py_test) # "log/internal" <- "log/internal/test-all.py" - if testparent: - testname = 'test-' + testparent.replace('/', '-') + '-' + os.path.basename(py_test)[5:-3] # remove .py - else: - testname = os.path.basename(py_test)[:-3] + # Python unit-test scripts are in the same directory as us... we want to consider running them + # (we may not if they're live and we have no pyrealsense2.pyd): + for py_test in find(current_dir, '(^|/)test-.*\.py'): + testparent = os.path.dirname(py_test) # "log/internal" <- "log/internal/test-all.py" + if testparent: + testname = 'test-' + testparent.replace('/', '-') + '-' + os.path.basename(py_test)[5:-3] # remove .py + else: + testname = os.path.basename(py_test)[:-3] - if regex and not pattern.search( testname ): - continue + if regex and not pattern.search( testname ): + continue - yield PyTest(testname, py_test) + yield PyTest(testname, py_test) def devices_by_test_config( test ): @@ -456,7 +469,8 @@ def test_wrapper( test, configuration = None ): # Run all tests -sys.path.insert( 1, pyrs_path ) +if pyrs: + sys.path.append( pyrs_path ) from rspy import devices devices.query() # @@ -471,7 +485,7 @@ def test_wrapper( test, configuration = None ): continue # if skip_live_tests: - log.d( test.name + ':', 'is live and there are no cameras; skipping' ) + log.w( test.name + ':', 'is live and there are no cameras; skipping' ) continue # for configuration, serial_numbers in devices_by_test_config( test ): @@ -483,6 +497,11 @@ def test_wrapper( test, configuration = None ): test_wrapper( test, configuration ) log.progress() +# +if not n_tests: + log.e( 'No unit-tests found!' ) + sys.exit(1) +# n_errors = log.n_errors() if n_errors: log.out( log.red + str(n_errors) + log.reset, 'of', n_tests, 'test(s)', log.red + 'failed!' + log.reset + log.clear_eos ) From 8c7affbb8c8739df7cd081c575e642de985262f7 Mon Sep 17 00:00:00 2001 From: Eran Date: Mon, 22 Feb 2021 08:31:51 +0200 Subject: [PATCH 2/3] CR fix --- unit-tests/py/rspy/devices.py | 1 + 1 file changed, 1 insertion(+) diff --git a/unit-tests/py/rspy/devices.py b/unit-tests/py/rspy/devices.py index 2674f9bed0..8e2a5e26a9 100644 --- a/unit-tests/py/rspy/devices.py +++ b/unit-tests/py/rspy/devices.py @@ -31,6 +31,7 @@ def query(): """ Start a new LRS context, and collect all devices """ + global rs if not rs: return # From dbcd90cb4407f40e51ce1789f4b34dd43a0f92e3 Mon Sep 17 00:00:00 2001 From: Eran Date: Mon, 22 Feb 2021 16:57:31 +0200 Subject: [PATCH 3/3] CR fix --- unit-tests/run-unit-tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit-tests/run-unit-tests.py b/unit-tests/run-unit-tests.py index bb93db1837..7eacbf079b 100644 --- a/unit-tests/run-unit-tests.py +++ b/unit-tests/run-unit-tests.py @@ -10,7 +10,7 @@ # We want only modules in the directories we specifically provide to be found, # otherwise pyrs other than what we compiled might be found... sys.path = list() -sys.path.append( '' ) +sys.path.append( '' ) # directs Python to search modules in the current directory first # Add our py/ module directory current_dir = os.path.dirname( os.path.abspath( __file__ )) sys.path.append( current_dir + os.sep + "py" )