def main(connection_handle, connection_type, guard_time_seconds, inactivity_time_seconds, terminator, instance, printer, reporter, test_report_handle, send_string=None, keep_going_flag=None): '''Main as a function''' # Dictionary in which results are stored results = { "finished": False, "reboots": 0, "items_run": 0, "items_failed": 0, "items_ignored": 0, "overall_start_time": 0, "last_start_time": 0, "outcomes": [] } results["last_start_time"] = time() return_value = -1 if instance: prompt = PROMPT + "_" + u_utils.get_instance_text(instance) + ": " else: prompt = PROMPT + ": " # If we have a serial interface we have can chose which tests to run # (at least, on the ESP32 platform) else the lot will just run if (send_string is None or esp32_send_first( send_string, connection_handle, connection_type, printer, prompt)): results["overall_start_time"] = time() return_value = watch_items(connection_handle, connection_type, results, guard_time_seconds, inactivity_time_seconds, terminator, printer, reporter, prompt, keep_going_flag) # Write the report if test_report_handle and instance: printer.string("{}writing report file...".format(prompt)) test_report_handle.write("<testsuite name=\"{}\" tests=\"{}\" failures=\"{}\">\n". \ format("instance " + u_utils.get_instance_text(instance), results["items_run"], results["items_failed"])) for outcome in results["outcomes"]: test_report_handle.write(" <testcase classname=\"{}\" name=\"{}\" " \ "time=\"{}\" status=\"{}\"></testcase>\n". \ format("ubxlib_tests", outcome[0], outcome[1], outcome[2])) test_report_handle.write("</testsuite>\n") printer.string("{}end with return value {}.".format(prompt, return_value)) return return_value
def lock(connection, connection_lock, guard_time_seconds, printer, prompt): '''Lock the given connection''' timeout_seconds = guard_time_seconds success = False if connection: instance_text = u_utils.get_instance_text(get_instance(connection)) if connection_lock: # Wait on the lock printer.string("{}instance {} waiting up to {} second(s)" \ " to lock connection...". \ format(prompt, instance_text, guard_time_seconds)) count = 0 while not connection_lock.acquire(False) and \ ((guard_time_seconds == 0) or (timeout_seconds > 0)): sleep(1) timeout_seconds -= 1 count += 1 if count == 30: printer.string("{}instance {} still waiting {} second(s)" \ " for a connection lock (locker is" \ " currently {}).". \ format(prompt, instance_text, timeout_seconds, connection_lock)) count = 0 if (guard_time_seconds == 0) or (timeout_seconds > 0): success = True printer.string("{}instance {} has locked a connection ({}).". \ format(prompt, instance_text, connection_lock)) else: success = True printer.string("{}note: instance {} lock is empty.". \ format(prompt, instance_text)) return success
def unlock(connection, connection_lock, printer, prompt): '''Unlock the given connection''' if connection: instance_text = u_utils.get_instance_text(get_instance(connection)) if connection_lock: connection_lock.release() printer.string("{}instance {} has unlocked a connection.". \ format(prompt, instance_text))
def lock(connection, connection_lock, guard_time_seconds, printer, prompt, keep_going_flag=None, hw_reset=True): '''Lock the given connection''' timeout_seconds = guard_time_seconds success = False if connection: instance_text = u_utils.get_instance_text(get_instance(connection)) if connection_lock: # Wait on the lock printer.string("{}instance {} waiting up to {} second(s)" \ " to lock connection...". \ format(prompt, instance_text, guard_time_seconds)) count = 0 while not connection_lock.acquire(False) and \ ((guard_time_seconds == 0) or (timeout_seconds > 0)) and \ u_utils.keep_going(keep_going_flag, printer, prompt): sleep(1) timeout_seconds -= 1 count += 1 if count == 30: printer.string("{}instance {} still waiting {} second(s)" \ " for a connection lock (locker is" \ " currently {}).". \ format(prompt, instance_text, timeout_seconds, connection_lock)) count = 0 if (guard_time_seconds == 0) or (timeout_seconds > 0): printer.string("{}instance {} has locked a connection ({}).". \ format(prompt, instance_text, connection_lock)) if hw_reset: kmtronic = get_kmtronic(connection) usb_cutter_id_str = get_usb_cutter_id_str(connection) if kmtronic: printer.string("{}using KMTronic to reset {}...". \ format(prompt, instance_text)) u_utils.kmtronic_reset(kmtronic["ip_address"], kmtronic["hex_bitmap"], printer, prompt) if usb_cutter_id_str: printer.string("{}using USB cutter to reset {}...". \ format(prompt, instance_text)) u_utils.usb_cutter_reset([usb_cutter_id_str], printer, prompt) success = True else: success = True printer.string("{}note: instance {} lock is empty.". \ format(prompt, instance_text)) return success
def __init__(self, queue, instance, file_handle, printer): self._queue = queue self._instance = instance self._file_handle = file_handle self._printer = printer self._prompt = PROMPT if instance: self._prompt += "_" + u_utils.get_instance_text(instance) + ": " else: self._prompt += ": "
def _write_events(self, instance): '''Write the events for an instance to file''' remove_list = [] for event in self._events: if event["instance"] == instance: remove_list.append(event) if self._file_handle: string = strftime(u_utils.TIME_FORMAT, event["timestamp"]) + \ " instance " + u_utils.get_instance_text(instance) + \ " " + event_as_string(event) + ".\n" self._file_handle.write(string) if self._file_handle: self._file_handle.flush() # Remove the list items we've written for item in remove_list: self._events.remove(item)
def run(instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle): '''Build/run on nRF5''' return_value = -1 hex_file_path = None instance_text = u_utils.get_instance_text(instance) swo_port = u_utils.JLINK_SWO_PORT downloaded = False # Don't need the platform lock del platform_lock prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running nRF5 for " + mcu + " under " + toolchain if connection and "debugger" in connection and connection["debugger"]: text += ", on JLink debugger serial number " + connection["debugger"] if clean: text += ", clean build" if defines: text += ", with #define(s)" for idx, define in enumerate(defines): if idx == 0: text += " \"" + define + "\"" else: text += ", \"" + define + "\"" if ubxlib_dir: text += ", ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_START, "nRF5SDK/" + toolchain) # Switch to the working directory with u_utils.ChangeDir(working_dir): # Check that everything we need is installed if check_installation(toolchain, TOOLS_LIST, printer, prompt): # Fetch Unity if u_utils.fetch_repo(u_utils.UNITY_URL, u_utils.UNITY_SUBDIR, None, printer, prompt): # Do the build build_start_time = time() if toolchain.lower() == "gcc": build_subdir_gcc = BUILD_SUBDIR_PREFIX_GCC + instance_text.replace( ".", "_") hex_file_path = build_gcc(clean, build_subdir_gcc, ubxlib_dir, defines, printer, prompt, reporter) elif toolchain.lower() == "ses": hex_file_path = build_ses(clean, ubxlib_dir, defines, printer, prompt, reporter) if hex_file_path: # Build succeeded, need to lock a connection to do the download reporter.event( u_report.EVENT_TYPE_BUILD, u_report.EVENT_PASSED, "build took {:.0f} second(s)".format(time() - build_start_time)) # Do the download # On nRF5 doing a download or starting RTT logging # on more than one platform at a time seems to cause # problems, even though it should be tied to the # serial number of the given debugger on that board, # so lock JLink for this. jlink_lock = None if "jlink_lock" in misc_locks: jlink_lock = misc_locks["jlink_lock"] with u_utils.Lock(jlink_lock, INSTALL_LOCK_WAIT_SECONDS, "JLink", printer, prompt) as locked_jlink: if locked_jlink: with u_connection.Lock( connection, connection_lock, CONNECTION_LOCK_GUARD_TIME_SECONDS, printer, prompt) as locked_connection: if locked_connection: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_START) # I have seen the download fail on occasion # so give this two bites of the cherry retries = 2 while not downloaded and (retries > 0): downloaded = download( connection, DOWNLOAD_GUARD_TIME_SECONDS, hex_file_path, printer, prompt) retries -= 1 if not downloaded and (retries > 0): reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_WARNING, "unable to download, will retry..." ) sleep(5) if downloaded: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_COMPLETE) # Now the target can be reset u_utils.reset_nrf_target( connection, printer, prompt) reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_START) if connection and "swo_port" in connection: swo_port = connection["swo_port"] # With JLink started RUN_JLINK.append("-RTTTelnetPort") RUN_JLINK.append(str(swo_port)) if connection and "debugger" in connection and \ connection["debugger"]: RUN_JLINK.append("-USB") RUN_JLINK.append( connection["debugger"]) with u_utils.ExeRun(RUN_JLINK, printer, prompt, with_stdin=True ) as process_jlink: # Open the Telnet port to JLink # to get the debug output telnet_handle = u_utils.open_telnet( swo_port, printer, prompt) if telnet_handle is not None: # Monitor progress return_value = u_monitor. \ main(telnet_handle, u_monitor.CONNECTION_TELNET, RUN_GUARD_TIME_SECONDS, RUN_INACTIVITY_TIME_SECONDS, instance, printer, reporter, test_report_handle) telnet_handle.close() else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to open RTT port " + \ str(swo_port)) # JLink is VERY touchy as to how it exits, # need to send it "exit\n" over stdin # for it to exit cleanly process_jlink.stdin.write( "exit\n".encode()) sleep(5) if return_value == 0: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_COMPLETE) else: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_FAILED) else: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_FAILED, "check debug log for details") # Wait for a short while before giving # the connection lock away to make sure # that everything really has shut down # in the debugger sleep(5) else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to lock a connection") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to lock JLink") else: return_value = 1 reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_FAILED, "check debug log for details") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to fetch Unity") else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "there is a problem with the tools installation for nRF5 SDK") return return_value
def run(instance, mcu, board, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle, keep_going_flag=None, unity_dir=None): '''Build/run on Arduino''' return_value = -1 monitor_dtr_rts_on = None installed = False sketch_paths = [] build_paths = [] return_values = [] instance_text = u_utils.get_instance_text(instance) # None of the misc locks are required del misc_locks # Since we only currently support ESP-IDF we don't need # unity as ESP-IDF already has a copy built in del unity_dir prompt = PROMPT + instance_text + ": " toolchain = toolchain.lower() # Print out what we've been told to do and at the # same time check for the DTR/RTS off marker text = "running Arduino for " + mcu + " with SDK " + toolchain + \ " on board with FQBN \"" + board + "\"" if connection and connection["serial_port"]: text += ", serial port " + connection["serial_port"] if clean: text += ", clean build" if defines: text += ", with #define(s)" for idx, define in enumerate(defines): if define == MONITOR_DTR_RTS_OFF_MARKER: monitor_dtr_rts_on = False if idx == 0: text += " \"" + define + "\"" else: text += ", \"" + define + "\"" if ubxlib_dir: text += ", ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_START, "Arduino") printer.string("{}CD to {}...".format(prompt, working_dir)) with u_utils.ChangeDir(working_dir): # Lock the Arduino platform while we install the tools with u_utils.Lock(platform_lock, PLATFORM_LOCK_GUARD_TIME_SECONDS, "platform", printer, prompt, keep_going_flag) as locked_platform: if locked_platform: installed = install(board, ARDUINO_BOARDS_URLS, printer, prompt, keep_going_flag) if installed: arduino_dir = os.path.join(ubxlib_dir, ARDUINO_SUB_DIR) library_path = os.path.join(working_dir, LIBRARIES_SUB_DIR) # Clear out any pre-built ubxlib library or rebuilds # won't pick up changes clear_prebuilt_library(os.path.join(library_path, LIBRARY_NAME), mcu, printer, prompt) # Create the ubxlib Arduino library if create_library(ubxlib_dir, arduino_dir, toolchain, os.path.join(library_path, LIBRARY_NAME), LIBRARY_NAME_LIB_POSTFIX, clean, printer, prompt, keep_going_flag): # Create the ubxlib Arduino test library if create_library(ubxlib_dir, arduino_dir, toolchain, os.path.join(library_path, LIBRARY_NAME), LIBRARY_NAME_TEST_POSTFIX, clean, printer, prompt, keep_going_flag): # We now build both libraries with the test sketch and we also # build the examples with the just the ubxlib Arduino library. # Make a list of the sketches to build sketch_paths.append( os.path.join(arduino_dir, TEST_SKETCH_SUB_PATH)) for root, _directories, files in os.walk(library_path): for file in files: if os.sep + "examples" + os.sep in root and file.endswith( ".ino"): sketch_paths.append(os.path.join(root, file)) printer.string("{}{} thing(s) to build.".format( prompt, len(sketch_paths))) # Build the sketches: note that the first build of the ubxlib # Arduino library to a .a file will be copied back into the library # directory for use in the following builds build_dir = os.path.join(working_dir, BUILD_SUBDIR) for sketch_path in sketch_paths: build_start_time = time() build_path = build(build_dir, sketch_path, library_path, mcu, board, defines, clean, printer, prompt, reporter, keep_going_flag) if not u_utils.keep_going(keep_going_flag, printer, prompt) or not build_path: break build_paths.append(build_path) reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_PASSED, "build {} of {} took {:.0f} second(s)". \ format(len(build_paths), len(sketch_paths), time() - build_start_time)) if len(build_paths) == len(sketch_paths): # Download and run the builds with u_connection.Lock( connection, connection_lock, CONNECTION_LOCK_GUARD_TIME_SECONDS, printer, prompt, keep_going_flag) as locked: if locked: for build_path in build_paths: # I have seen download failures occur if two # are initiated at the same time so lock the # platform for this downloaded = False with u_utils.Lock( platform_lock, PLATFORM_LOCK_GUARD_TIME_SECONDS, "platform", printer, prompt, keep_going_flag ) as locked_platform: if locked_platform: # Have seen this fail, so give it a few goes reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_START) retries = 0 while u_utils.keep_going(keep_going_flag, printer, prompt) and \ not downloaded and (retries < 3): downloaded = download( build_path, board, connection["serial_port"], printer, prompt, keep_going_flag) if not downloaded: reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_WARNING, "unable to download, will" \ " retry...") retries += 1 sleep(5) if downloaded: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_COMPLETE) reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_START) # Open the COM port to get debug output serial_handle = u_utils.open_serial( connection["serial_port"], 115200, printer, prompt, dtr_set_on=monitor_dtr_rts_on, rts_set_on=monitor_dtr_rts_on) if serial_handle is not None: # Monitor progress return_values.append( u_monitor.main( serial_handle, u_monitor. CONNECTION_SERIAL, RUN_GUARD_TIME_SECONDS, RUN_INACTIVITY_TIME_SECONDS, "\r", instance, printer, reporter, test_report_handle, keep_going_flag= keep_going_flag)) # Delays and flushes here to make sure # that the serial port actually closes # since we might need to re-open it for # another download going around the loop serial_handle.cancel_read() sleep(1) serial_handle.reset_input_buffer() serial_handle.reset_output_buffer() sleep(1) serial_handle.close() reporter.event( u_report. EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_COMPLETE, "serial port closed") sleep(5) else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to open serial port " + \ connection["serial_port"]) if return_values and return_values[ -1] == 0: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_COMPLETE) else: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_FAILED) else: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_FAILED, "unable to download to the target") if return_values: return_value = 0 for item in return_values: # If a return value goes negative then # only count the negative values, i.e. the # number of infrastructure failures if (item < 0) and (return_value >= 0): return_value = item else: if (((item > 0) and (return_value >= 0)) or \ ((item < 0) and (return_value < 0))): return_value += item else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to lock a connection") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "failed a build") else: reporter.event( u_report.EVENT_TYPE_BUILD, u_report.EVENT_FAILED, "unable to build library, check debug log for details") else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to create library, check debug log for details") else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to install tools, check debug log for details") return return_value
def run(instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle): '''Build/run on Zephyr''' return_value = -1 build_dir = None instance_text = u_utils.get_instance_text(instance) # Don't need the platform lock del platform_lock # Only one toolchain for Zephyr del toolchain prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running Zephyr for " + mcu if connection and "debugger" in connection and connection["debugger"]: text += ", on JLink debugger serial number " + connection["debugger"] if clean: text += ", clean build" if defines: text += ", with #define(s)" for idx, define in enumerate(defines): if idx == 0: text += " \"" + define + "\"" else: text += ", \"" + define + "\"" if ubxlib_dir: text += ", ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_START, "Zephyr") # Switch to the working directory with u_utils.ChangeDir(working_dir): # Check that everything we need is installed # and configured if check_installation(TOOLS_LIST, printer, prompt): # Set up the environment variables for Zephyr returned_env = set_env(printer, prompt) if returned_env: # The west tools need to use the environment # configured above. print_env(returned_env, printer, prompt) # Note that Zephyr brings in its own # copy of Unity so there is no need to # fetch it here. # For Zephyr we need to obtain the full board name board = find_board(ubxlib_dir, mcu) if board: # Do the build build_start_time = time() build_dir = build(board, clean, ubxlib_dir, defines, returned_env, printer, prompt, reporter) if build_dir: # Build succeeded, need to lock some things to do the download reporter.event( u_report.EVENT_TYPE_BUILD, u_report.EVENT_PASSED, "build took {:.0f} second(s)".format( time() - build_start_time)) # On NRF52 (NRF5)/Zephyr (NRF53/NRF52) doing a download # on more than one platform at a time or doing a download # while RTT logging is in progress seems to cause problems, # even though it should be tied to the serial number of the # given debugger on that board, so lock JLink for this. jlink_lock = None if "jlink_lock" in misc_locks: jlink_lock = misc_locks["jlink_lock"] with u_utils.Lock(jlink_lock, INSTALL_LOCK_WAIT_SECONDS, "JLink", printer, prompt) as locked_jlink: if locked_jlink: with u_connection.Lock( connection, connection_lock, CONNECTION_LOCK_GUARD_TIME_SECONDS, printer, prompt) as locked_connection: if locked_connection: # Get the device name for JLink jlink_device_name = jlink_device(mcu) if not jlink_device_name: reporter.event( u_report. EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_WARNING, "MCU not found in JLink devices" ) # Do the download reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_START) if download( connection, jlink_device_name, DOWNLOAD_GUARD_TIME_SECONDS, build_dir, returned_env, printer, prompt): reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_COMPLETE) # Now the target can be reset u_utils.reset_nrf_target( connection, printer, prompt) reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_START) if connection and "swo_port" in connection: swo_port = connection[ "swo_port"] # With JLink started if jlink_device_name: RUN_JLINK.append("-Device") RUN_JLINK.append( jlink_device(mcu)) RUN_JLINK.append("-RTTTelnetPort") RUN_JLINK.append(str(swo_port)) if connection and "debugger" in connection and \ connection["debugger"]: RUN_JLINK.append("-USB") RUN_JLINK.append( connection["debugger"]) with u_utils.ExeRun( RUN_JLINK, printer, prompt, with_stdin=True ) as process_jlink: # Open the Telnet port to JLink # to get the debug output telnet_handle = u_utils.open_telnet( swo_port, printer, prompt) if telnet_handle is not None: # Monitor progress return_value = u_monitor. \ main(telnet_handle, u_monitor.CONNECTION_TELNET, RUN_GUARD_TIME_SECONDS, RUN_INACTIVITY_TIME_SECONDS, instance, printer, reporter, test_report_handle) telnet_handle.close() else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to open RTT port " + \ str(swo_port)) # JLink is VERY touchy as to how it exits, # need to send it "exit\n" over stdin # for it to exit cleanly process_jlink.stdin.write( "exit\n".encode()) sleep(5) if return_value == 0: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_COMPLETE) else: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_FAILED) else: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_FAILED, "check debug log for details") else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to lock a connection") else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to lock JLink") else: return_value = 1 reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_FAILED, "check debug log for details") else: return_value = 1 reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to find overlay file") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "environment setup failed") else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "there is a problem with the tools installation for Zephyr") return return_value
def run(instance, defines, ubxlib_dir, working_dir, printer, reporter): '''Run Lint''' return_value = 1 call_list = [] instance_text = u_utils.get_instance_text(instance) prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running Lint from ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_START, "Lint") # Switch to the working directory with u_utils.ChangeDir(working_dir): # Check that everything we need is installed if check_installation(TOOLS_LIST, COMPILER_INCLUDE_DIRS, printer, prompt): # Fetch Unity if u_utils.fetch_repo(u_utils.UNITY_URL, u_utils.UNITY_SUBDIR, None, printer, prompt): # Create the local Lint configuration files if create_lint_config(ubxlib_dir + os.sep + LINT_PLATFORM_PATH, defines, printer, prompt): # Get the file list file_list = get_file_list(ubxlib_dir, LINT_DIRS) # Assemble the call list call_list.append("flexelint") if defines: for item in defines: call_list.append("-d" + item) for item in COMPILER_INCLUDE_DIRS: call_list.append("-i\"" + item + "\"") call_list.append("-i\"" + u_utils.UNITY_SUBDIR + os.sep + "src\"") for item in UBXLIB_INCLUDE_DIRS: call_list.append("-i\"" + ubxlib_dir + os.sep + item + "\"") for item in LINT_PLATFORM_CONFIG_FILES: call_list.append(ubxlib_dir + os.sep + LINT_PLATFORM_PATH + os.sep + item) call_list.extend(file_list) # Print what we're gonna do tmp = "" for item in call_list: tmp += " " + item printer.string("{}in directory {} calling{}". \ format(prompt, os.getcwd(), tmp)) try: text = subprocess.check_output(call_list, stderr=subprocess.STDOUT, shell=True) # Jenkins hangs without this reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_PASSED) for line in text.splitlines(): printer.string("{}{}".format(prompt, line.decode())) return_value = 0 except subprocess.CalledProcessError as error: reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_FAILED) printer.string("{}Lint returned error {}:". format(prompt, error.returncode)) for line in error.output.splitlines(): line = line.strip().decode() if line: reporter.event_extra_information(line) printer.string("{}{}".format(prompt, line)) else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "could not create Lint config") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to fetch Unity") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "there is a problem with the Lint installation") return return_value
def run(instance, ubxlib_dir, working_dir, printer, reporter): '''Run Doxygen''' return_value = 1 got_doxygen = False instance_text = u_utils.get_instance_text(instance) prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running Doxygen from ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_START, "Doxygen") got_doxygen = u_utils.exe_where("doxygen", \ "ERROR: can't find Doxygen, please make" \ " sure that it is installed and on the path.", \ printer, prompt) if got_doxygen: # Sort out any subst printer.string("{}Doxygen finds no files if run from a" \ " subst drive so convert {} to a real" \ " path".format(prompt, ubxlib_dir)) actual_ubxlib_dir = u_utils.get_actual_path(ubxlib_dir) printer.string("{}Actual ubxlib directory is {}". \ format(prompt, actual_ubxlib_dir)) # Run Doxygen config_path = actual_ubxlib_dir + os.sep + DOXYFILE printer.string("{}CD to {}...".format(prompt, actual_ubxlib_dir)) with u_utils.ChangeDir(actual_ubxlib_dir): printer.string("{}in directory {} calling doxygen {}.". \ format(prompt, os.getcwd(), config_path)) try: my_env = os.environ.copy() my_env['UBX_WORKDIR'] = working_dir text = subprocess.check_output(["doxygen", config_path], stderr=subprocess.STDOUT, env=my_env, shell=True) # Jenkins hangs without this reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_PASSED) for line in text.splitlines(): printer.string("{}{}".format(prompt, line.decode())) return_value = 0 except subprocess.CalledProcessError as error: reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_FAILED) printer.string("{}Doxygen returned error {}:". format(prompt, error.returncode)) for line in error.output.splitlines(): line = line.strip().decode() if line: reporter.event_extra_information(line) printer.string("{}{}".format(prompt, line)) else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "there is a problem with the Doxygen installation") return return_value
def run(instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle): '''Build/run on STM32Cube''' return_value = -1 mcu_dir = ubxlib_dir + os.sep + SDK_DIR + os.sep + "mcu" + os.sep + mcu instance_text = u_utils.get_instance_text(instance) # Create a unique project name prefix in case more than # one process is running this updated_project_name_prefix = UPDATED_PROJECT_NAME_PREFIX + str( os.getpid()) + "_" workspace_subdir = STM32CUBE_IDE_WORKSPACE_SUBDIR + "_" + str(os.getpid()) elf_path = None downloaded = False running = False download_list = None # Only one toolchain for STM32Cube del toolchain prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running STM32Cube for " + mcu if connection and "debugger" in connection and connection["debugger"]: text += ", on STLink debugger serial number " + connection["debugger"] if clean: text += ", clean build" if defines: text += ", with #define(s)" for idx, define in enumerate(defines): if idx == 0: text += " \"" + define + "\"" else: text += ", \"" + define + "\"" if ubxlib_dir: text += ", ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) # On STM32F4 we can get USB errors if we try to do a download # on one platform while another is performing SWO logging. # Since each board only runs a single instance of stuff we # can work around this be ensuring that all downloads are # completed before SWO logging begins. # Add us to the list of pending downloads if misc_locks and ("stm32f4_downloads_list" in misc_locks): download_list = misc_locks["stm32f4_downloads_list"] download_list.append(instance_text) reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_START, "STM32Cube") # Switch to the working directory with u_utils.ChangeDir(working_dir): # Check that everything we need is installed if check_installation(PATHS_LIST, printer, prompt): # Fetch Unity if u_utils.fetch_repo(u_utils.UNITY_URL, u_utils.UNITY_SUBDIR, None, printer, prompt): # I've no idea why but on every other # build STM32Cube loses track of where # most of the files are: you'll see it # say that it can't find u_cfg_sw.h and # fail. Until we find out why just # give it two goes, deleting the project # we created before trying again. retries = 2 while (elf_path is None) and (retries > 0): # The STM32Cube IDE, based on Eclipse # has no mechanism for overriding the locations # of things so here we read the .project # file and replace the locations of the # STM32Cube SDK and Unity files as # appropriate if create_project( mcu_dir, PROJECT_NAME, updated_project_name_prefix + PROJECT_NAME, STM32CUBE_FW_PATH, working_dir + os.sep + u_utils.UNITY_SUBDIR, printer, prompt): # Do the build build_start_time = time() elf_path = build_binary( mcu_dir, workspace_subdir, updated_project_name_prefix + PROJECT_NAME, clean, defines, printer, prompt) if elf_path is None: reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_INFORMATION, "unable to build, will retry") printer.string("{}if the compilation." \ " failure was because" \ " the build couldn't" \ " even find u_cfg_sw.h" \ " then ignore it, Eclipse" \ " lost its head, happens" \ " a lot, we will try again.". \ format(prompt)) else: reporter.event( u_report.EVENT_TYPE_BUILD, u_report.EVENT_WARNING, "unable to create STM32Cube project, will retry") retries -= 1 if elf_path: reporter.event( u_report.EVENT_TYPE_BUILD, u_report.EVENT_PASSED, "build took {:.0f} second(s)".format(time() - build_start_time)) # Lock the connection. with u_connection.Lock(connection, connection_lock, CONNECTION_LOCK_GUARD_TIME_SECONDS, printer, prompt) as locked_connection: if locked_connection: # I have seen download failures occur if two # ST-Link connections are initiated at the same time. with u_utils.Lock( platform_lock, PLATFORM_LOCK_GUARD_TIME_SECONDS, "platform", printer, prompt) as locked_platform: if locked_platform: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_START) # Do the download. I have seen the STM32F4 debugger # barf on occasions so give this two bites of # the cherry retries = 2 while not downloaded and (retries > 0): downloaded = download( connection, DOWNLOAD_GUARD_TIME_SECONDS, elf_path, printer, prompt) retries -= 1 if not downloaded: if connection and "serial_port" in connection \ and connection["serial_port"]: # Before retrying, reset the USB port u_utils.usb_reset("STMicroelectronics STLink" \ "Virtual COM Port (" + connection["serial_port"] + ")", printer, prompt) sleep(5) if platform_lock: # Once the download has been done (or not) the platform lock # can be released, after a little safety sleep sleep(1) platform_lock.release() if downloaded: # Remove us from the list of pending downloads if download_list: download_list.remove(instance_text) reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_COMPLETE) # Wait for all the other downloads to complete before # starting SWO logging u_utils.wait_for_completion( download_list, "download", DOWNLOADS_COMPLETE_GUARD_TIME_SECONDS, printer, prompt) # So that all STM32Cube instances don't start up at # once, which can also cause problems, wait the # instance-number number of seconds. hold_off = instance[0] if hold_off > 30: hold_off = 30 sleep(hold_off) # Create and empty the SWO data file and decoded text file file_handle = open(SWO_DATA_FILE, "w").close() file_handle = open( SWO_DECODED_TEXT_FILE, "w").close() reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_START) try: # Start a process which reads the # SWO output from a file, decodes it and # writes it back to a file process = Process( target=swo_decode_process, args=(SWO_DATA_FILE, SWO_DECODED_TEXT_FILE)) process.start() # Two bites at the cherry again retries = 2 while not running and (retries > 0): # Now start Open OCD to reset the target # and capture SWO output sleep(1) with u_utils.ExeRun( open_ocd( OPENOCD_COMMANDS, connection), printer, prompt): running = True # Open the SWO decoded text file for # reading, binary to prevent the line # endings being munged. file_handle = open( SWO_DECODED_TEXT_FILE, "rb") # Monitor progress based on the decoded # SWO text return_value = u_monitor. \ main(file_handle, u_monitor.CONNECTION_PIPE, RUN_GUARD_TIME_SECONDS, RUN_INACTIVITY_TIME_SECONDS, instance, printer, reporter, test_report_handle) file_handle.close() retries -= 1 if not running: sleep(5) process.terminate() except KeyboardInterrupt: # Tidy up process on SIGINT printer.string( "{}caught CTRL-C, terminating..." .format(prompt)) process.terminate() return_value = -1 if return_value == 0: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_COMPLETE) else: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_FAILED) else: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_FAILED) else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to lock a connection") else: reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_FAILED, "check debug log for details") # To avoid a build up of stuff, delete the temporary build and # workspace on exit tmp = mcu_dir + os.sep + updated_project_name_prefix + PROJECT_NAME if os.path.exists(tmp): printer.string("{}deleting temporary build directory {}...". \ format(prompt, tmp)) u_utils.deltree(tmp, printer, prompt) if os.path.exists(workspace_subdir): printer.string("{}deleting temporary workspace directory {}...". \ format(prompt, workspace_subdir)) u_utils.deltree(workspace_subdir, printer, prompt) else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to fetch Unity") else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "there is a problem with the tools installation for STM32F4") # Remove us from the list of pending downloads for safety try: misc_locks["stm32f4_downloads_list"].remove(instance_text) except (AttributeError, ValueError, TypeError): pass return return_value
def run(instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle, keep_going_flag=None, unity_dir=None): '''Build/run on nRF5''' return_value = -1 hex_file_path = None instance_text = u_utils.get_instance_text(instance) downloaded = False _ = (misc_locks) # Suppress unused variable # Don't need the platform lock del platform_lock prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running nRF5 for " + mcu + " under " + toolchain if connection and "debugger" in connection and connection["debugger"]: text += ", on JLink debugger serial number " + connection["debugger"] if clean: text += ", clean build" if defines: text += ", with #define(s)" for idx, define in enumerate(defines): if idx == 0: text += " \"" + define + "\"" else: text += ", \"" + define + "\"" if ubxlib_dir: text += ", ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" if unity_dir: text += ", using Unity from \"" + unity_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_START, "nRF5SDK/" + toolchain) # Switch to the working directory with u_utils.ChangeDir(working_dir): # Check that everything we need is installed if u_utils.keep_going(keep_going_flag, printer, prompt) and \ check_installation(toolchain, TOOLS_LIST, printer, prompt): # Fetch Unity, if necessary if u_utils.keep_going(keep_going_flag, printer, prompt) and \ not unity_dir: if u_utils.fetch_repo(u_utils.UNITY_URL, u_utils.UNITY_SUBDIR, None, printer, prompt, submodule_init=False): unity_dir = os.getcwd() + os.sep + u_utils.UNITY_SUBDIR if unity_dir: # Do the build build_start_time = time() if u_utils.keep_going(keep_going_flag, printer, prompt): if toolchain.lower() == "gcc": build_subdir_gcc = BUILD_SUBDIR_PREFIX_GCC + instance_text.replace( ".", "_") hex_file_path = build_gcc(clean, build_subdir_gcc, ubxlib_dir, unity_dir, defines, printer, prompt, reporter, keep_going_flag) elif toolchain.lower() == "ses": hex_file_path = build_ses(clean, ubxlib_dir, unity_dir, defines, printer, prompt, reporter, keep_going_flag) if hex_file_path: # Build succeeded, need to lock a connection to do the download reporter.event( u_report.EVENT_TYPE_BUILD, u_report.EVENT_PASSED, "build took {:.0f} second(s)".format(time() - build_start_time)) # Do the download with u_connection.Lock( connection, connection_lock, CONNECTION_LOCK_GUARD_TIME_SECONDS, printer, prompt, keep_going_flag) as locked_connection: if locked_connection: reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_START) # I have seen the download fail on occasion # so give this two bites of the cherry retries = 2 while not downloaded and (retries > 0): downloaded = download( connection, DOWNLOAD_GUARD_TIME_SECONDS, hex_file_path, printer, prompt) retries -= 1 if not downloaded and (retries > 0): reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_WARNING, "unable to download, will retry...") sleep(5) if downloaded: reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_COMPLETE) # Now the target can be reset u_utils.reset_nrf_target( connection, printer, prompt) reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_START) with URttReader( "NRF52840_XXAA", jlink_serial=connection["debugger"], printer=printer, prompt=prompt) as rtt_reader: return_value = u_monitor.main( rtt_reader, u_monitor.CONNECTION_RTT, RUN_GUARD_TIME_SECONDS, RUN_INACTIVITY_TIME_SECONDS, "\n", instance, printer, reporter, test_report_handle) if return_value == 0: reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_COMPLETE) else: reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_FAILED) else: reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_FAILED, "check debug log for details") # Wait for a short while before giving # the connection lock away to make sure # that everything really has shut down # in the debugger sleep(5) else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to lock a connection") else: return_value = 1 reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_FAILED, "check debug log for details") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to fetch Unity") else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "there is a problem with the tools installation for nRF5 SDK") return return_value
def run_instances(database, instances, filter_string, ubxlib_dir, working_dir, clean, summary_report_file, test_report_file, debug_file): '''Run the given instances''' return_value = 0 processes = [] platform_locks = [] misc_locks = {} alive_count = 0 report_thread = None report_queue = None reporter = None summary_report_file_path = None test_report_file_path = None debug_file_path = None summary_report_handle = None manager = Manager() # Create a lock to cover things that cross # platforms or that any process of u_run.main() # may need to perform outside of its working # directory misc_locks["system_lock"] = manager.RLock() # Create a lock which can be used on Nordic # platforms (nRF5 and Zephyer): performing a # JLink download to a board while JLink RTT logging # is active on any other board will often stop # the RTT logging even though the sessions are # aimed at debuggers with entirely different # serial numbers. misc_locks["jlink_lock"] = manager.RLock() # Create a "lock" that can be used on STM32F4 # platforms to ensure that all downloads are # completed before logging commences. We # can do this, rather than locking a tool for the # whole time as we have to do with Nordic, because # each STM32F4 board only runs a single instance misc_locks["stm32f4_downloads_list"] = manager.list() # It is possible for some platforms to be a bit # pants at running in multiple instances # hence here we create a lock per platform and pass it # into the instance for it to be able to manage # multiplicity if required create_platform_locks(database, instances, manager, platform_locks) # Launch a thread that prints stuff out # nicely from multiple sources print_queue = manager.Queue() print_thread = u_utils.PrintThread(print_queue) print_thread.start() # Set up a printer for this thread to print to the queue printer = u_utils.PrintToQueue(print_queue, None, True) if summary_report_file: # Launch a thread that manages reporting # from multiple sources summary_report_file_path = working_dir + os.sep + summary_report_file summary_report_handle = open(summary_report_file_path, "w") if summary_report_handle: printer.string("{}writing overall summary report to \"{}\".". \ format(PROMPT, summary_report_file_path)) else: printer.string("{}unable to open file \"{}\" for overall summary report.". \ format(PROMPT, summary_report_file_path)) report_queue = manager.Queue() report_thread = u_report.ReportThread(report_queue, summary_report_handle) report_thread.start() reporter = u_report.ReportToQueue(report_queue, None, None, printer) reporter.open() # From this post: # https://stackoverflow.com/questions/11312525/catch-ctrlc-sigint-and-exit-multiprocesses-gracefully-in-python # ...create a pool of worker processes to run our # instances, then they will handle sigint correctly # and tidy up after themselves. # SIGINT is ignored while the pool is created original_sigint_handler = signal(SIGINT, SIG_IGN) pool = NoDaemonPool(len(instances)) signal(SIGINT, original_sigint_handler) # Create locks for connections u_connection.init_locks(manager) try: # Set up all the instances for instance in instances: # Provide a working directory that is unique # for each instance and make sure it exists if working_dir: this_working_dir = working_dir + os.sep + \ INSTANCE_DIR_PREFIX + \ u_utils.get_instance_text(instance).replace(".", "_") else: this_working_dir = os.getcwd() + os.sep + \ INSTANCE_DIR_PREFIX + \ u_utils.get_instance_text(instance).replace(".", "_") if not os.path.isdir(this_working_dir): os.makedirs(this_working_dir) # Only clean the working directory if requested if clean: u_utils.deltree(this_working_dir, printer, PROMPT) os.makedirs(this_working_dir) # Create the file paths for this instance if summary_report_file: summary_report_file_path = this_working_dir + os.sep + summary_report_file if test_report_file: test_report_file_path = this_working_dir + os.sep + test_report_file if debug_file: debug_file_path = this_working_dir + os.sep + debug_file # Start u_run.main in each worker thread process = {} process["platform"] = u_data.get_platform_for_instance( database, instance) process["instance"] = instance process["platform_lock"] = None process["connection_lock"] = u_connection.get_lock(instance) for platform_lock in platform_locks: if process["platform"] == platform_lock["platform"]: process["platform_lock"] = platform_lock["lock"] break process["handle"] = pool.apply_async( u_run.main, (database, instance, filter_string, True, ubxlib_dir, this_working_dir, process["connection_lock"], process["platform_lock"], misc_locks, print_queue, report_queue, summary_report_file_path, test_report_file_path, debug_file_path)) alive_count += 1 processes.append(process.copy()) # Wait for all the launched processes to complete printer.string("{}all instances now launched.".format(PROMPT)) loop_count = 0 while alive_count > 0: for process in processes: instance_text = u_utils.get_instance_text(process["instance"]) if not "dealt_with" in process and process["handle"].ready(): try: # If the return value has gone negative, i.e. # an infrastructure failure, leave it there, # else add the number of test failures to it if (return_value >= 0 and process["handle"].get() > 0) or \ (return_value <= 0 and process["handle"].get() < 0): return_value += process["handle"].get() except KeyboardInterrupt as ex: raise KeyboardInterrupt from ex except Exception as ex: # If an instance threw an exception then flag an # infrastructure error return_value = -1 printer.string("{}instance {} threw exception \"{}:" \ " {}\" but I can't tell you where" \ " I'm afraid.". \ format(PROMPT, instance_text, type(ex).__name__, str(ex))) if reporter: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "instance {} threw exception \"{}: {}\"". \ format(instance_text, type(ex).__name__, str(ex))) alive_count -= 1 process["dealt_with"] = True if not process["handle"].ready() and \ (loop_count == STILL_RUNNING_REPORT_SECONDS): printer.string("{}instance {} still running.". \ format(PROMPT, instance_text)) loop_count += 1 if loop_count > STILL_RUNNING_REPORT_SECONDS: loop_count = 0 sleep(1) except KeyboardInterrupt: # Pools can tidy themselves up on SIGINT printer.string( "{}caught CTRL-C, terminating instances...".format(PROMPT)) if reporter: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "CTRL-C received, terminating") pool.terminate() return_value = -1 # Tidy up pool.close() pool.join() if reporter: reporter.event_extra_information("return value overall {} (0 = success, negative =" \ " probable infrastructure failure, positive =" \ " failure(s) (may still be due to infrastructure))". \ format(return_value)) reporter.close() # Wait for the print and report queues to empty # and stop the print process printer.string("{}all runs complete, return value {}.".format( PROMPT, return_value)) sleep(1) print_thread.stop_thread() print_thread.join() # Stop the reporting process if report_thread: report_thread.stop_thread() report_thread.join() if summary_report_handle: summary_report_handle.close() return return_value
def run(instance, ubxlib_dir, working_dir, printer, reporter): '''Run AStyle''' return_value = 1 got_astyle = False call_list = [] instance_text = u_utils.get_instance_text(instance) prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running AStyle from ubxlib directory \"" + ubxlib_dir + \ "\" using configuration file \"" + ubxlib_dir + os.sep + \ CONFIG_FILE + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_START, "AStyle") got_astyle = u_utils.exe_where("astyle", \ "ERROR: can't find AStyle, please make" \ " sure that it is installed and on the path.", \ printer, prompt) if got_astyle: # Run AStyle printer.string("{}CD to {}...".format(prompt, ubxlib_dir)) with u_utils.ChangeDir(ubxlib_dir): # Assemble the call list call_list.append("astyle") call_list.append("--options=" + CONFIG_FILE) # Options file call_list.append("--dry-run") # Don't make changes call_list.append("--formatted") # Only list changed files call_list.append( "--suffix=none") # Don't leave .orig files everywhere call_list.append("--verbose") # Print out stats for exclude_dir in EXCLUDE_DIRS: # Exclude these directories call_list.append("--exclude=" + exclude_dir) call_list.append( "--ignore-exclude-errors-x") # Ignore unfound excludes call_list.append("--recursive") # Recurse through... for include_dir in ASTYLE_DIRS: # ...these files call_list.append(include_dir + os.sep + ASTYLE_FILE_EXTENSIONS) # Print what we're gonna do tmp = "" for item in call_list: tmp += " " + item printer.string("{}in directory {} calling{}". \ format(prompt, os.getcwd(), tmp)) try: text = subprocess.check_output( call_list, stderr=subprocess.STDOUT, shell=True) # Jenkins hangs without this formatted = [] for line in text.splitlines(): line = line.decode(encoding="utf-8", errors="ignore") printer.string("{}{}".format(prompt, line)) # AStyle doesn't return anything other than 0, # need to look for the word "Formatted" to find # a file it has fiddled with if line.startswith("Formatted"): formatted.append(line) if not formatted: reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_PASSED) else: reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_WARNING) for line in formatted: reporter.event_extra_information(line) # We don't return any errors about formatting return_value = 0 except subprocess.CalledProcessError as error: reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_FAILED) printer.string("{}AStyle returned error {}:".format( prompt, error.returncode)) for line in error.output.splitlines(): line = line.strip() if line: reporter.event_extra_information(line) printer.string("{}{}".format(prompt, line)) else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "there is a problem with the AStyle installation") return return_value
def run(instance, defines, ubxlib_dir, working_dir, printer, reporter, keep_going_flag=None): '''Build to check static sizes''' return_value = -1 instance_text = u_utils.get_instance_text(instance) map_file_path = BUILD_SUBDIR + os.sep + MAP_FILE_NAME cflags = C_FLAGS prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running static size check from ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_START, "NoFloat") # Switch to the working directory with u_utils.ChangeDir(working_dir): # Add the #defines to C_FLAGS if defines: for define in defines: cflags += " -D" + define # Assemble the call list call_list = ["python"] call_list.append(ubxlib_dir + os.sep + SUB_DIR + os.sep + "static_size.py") call_list.append("-p") call_list.append(GNU_INSTALL_ROOT) call_list.append("-u") call_list.append(ubxlib_dir) call_list.append("-c") call_list.append(cflags) call_list.append("-l") call_list.append(LD_FLAGS) call_list.append(ubxlib_dir + os.sep + SUB_DIR + os.sep + "source.txt") call_list.append(ubxlib_dir + os.sep + SUB_DIR + os.sep + "include.txt") # Print what we're gonna do tmp = "" for item in call_list: tmp += " " + item printer.string("{}in directory {} calling{}". \ format(prompt, os.getcwd(), tmp)) # Set shell to keep Jenkins happy if u_utils.exe_run(call_list, 0, printer, prompt, shell_cmd=True, keep_going_flag=keep_going_flag): reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_COMPLETE) reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_START) # Having performed the build, open the .map file printer.string("{} opening map file {}...".format( prompt, MAP_FILE_NAME)) if os.path.exists(map_file_path): map_file = open(map_file_path, "r") if map_file: # Parse the cross-reference section to seek # if any of the functions that indicate the # floating point has been introduced turn up got_xref = False got_fp = False for line in map_file.read().splitlines(): if got_xref: for function in FLOAT_FUNCTIONS: if line.startswith(function): printer.string("{} found {} in map file which" \ " indicates floating point is" \ " in use: {}".format(prompt, \ function, line)) got_fp = True else: if line.startswith("Cross Reference Table"): got_xref = True if not got_xref: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_FAILED, "map file has no cross-reference section") else: if got_fp: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_FAILED, "floating point seems to be in use") else: return_value = 0 reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_COMPLETE) map_file.close() else: reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_FAILED, "unable to open map file") else: reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_FAILED, "check debug log for details") return return_value
def run(instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle, keep_going_flag=None): '''Build/run on ESP-IDF''' return_value = -1 monitor_dtr_rts_on = None instance_text = u_utils.get_instance_text(instance) # No issues with running in parallel on ESP-IDF del platform_lock # Only one toolchain for ESP-IDF del toolchain prompt = PROMPT + instance_text + ": " # Print out what we've been told to do and at the # same time check for the DTR/RTS off marker text = "running ESP-IDF for " + mcu if connection and connection["serial_port"]: text += ", on serial port " + connection["serial_port"] if clean: text += ", clean build" if defines: text += ", with #define(s)" for idx, define in enumerate(defines): if define == MONITOR_DTR_RTS_OFF_MARKER: monitor_dtr_rts_on = False if idx == 0: text += " \"" + define + "\"" else: text += ", \"" + define + "\"" if ubxlib_dir: text += ", ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_START, "ESP-IDF") printer.string("{}CD to {}...".format(prompt, working_dir)) with u_utils.ChangeDir(working_dir): # Fetch ESP-IDF into the right sub directory # and install the tools esp_idf_dir = ESP_IDF_ROOT + os.sep + ESP_IDF_LOCATION["subdir"] system_lock = None if u_utils.keep_going(keep_going_flag, printer, prompt) and \ misc_locks and ("system_lock" in misc_locks): system_lock = misc_locks["system_lock"] returned_env = install(ESP_IDF_LOCATION["url"], esp_idf_dir, ESP_IDF_LOCATION["branch"], system_lock, keep_going_flag, printer, prompt, reporter) if u_utils.keep_going(keep_going_flag, printer, prompt) and \ returned_env: # From here on the ESP-IDF tools need to set up # and use the set of environment variables # returned above. print_env(returned_env, printer, prompt) # Now do the build build_dir = working_dir + os.sep + BUILD_SUBDIR build_start_time = time() if u_utils.keep_going(keep_going_flag, printer, prompt) and \ build(esp_idf_dir, ubxlib_dir, build_dir, defines, returned_env, clean, printer, prompt, reporter, keep_going_flag): reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_PASSED, "build took {:.0f} second(s)". \ format(time() - build_start_time)) with u_connection.Lock(connection, connection_lock, CONNECTION_LOCK_GUARD_TIME_SECONDS, printer, prompt, keep_going_flag) as locked: if locked: # Have seen this fail, only with Python 3 for # some reason, so give it a few goes reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_START) retries = 0 downloaded = False while u_utils.keep_going(keep_going_flag, printer, prompt) and \ not downloaded and (retries < 3): downloaded = download(esp_idf_dir, ubxlib_dir, build_dir, connection["serial_port"], returned_env, printer, prompt) if not downloaded: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_WARNING, "unable to download, will retry...") retries += 1 sleep(5) if downloaded: reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_COMPLETE) reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_START) # Open the COM port to get debug output serial_handle = u_utils.open_serial( connection["serial_port"], 115200, printer, prompt, dtr_set_on=monitor_dtr_rts_on, rts_set_on=monitor_dtr_rts_on) if serial_handle is not None: # Monitor progress return_value = u_monitor.main( serial_handle, u_monitor.CONNECTION_SERIAL, RUN_GUARD_TIME_SECONDS, RUN_INACTIVITY_TIME_SECONDS, "\r", instance, printer, reporter, test_report_handle, keep_going_flag=keep_going_flag) serial_handle.close() else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to open serial port " + \ connection["serial_port"]) if return_value == 0: reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_COMPLETE) else: reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_FAILED) else: reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_FAILED, "unable to download to the target") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to lock a connection") else: reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_FAILED, "unable to build, check debug log for details") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "tools installation failed") return return_value
def run(instance, mcu, board, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle, keep_going_flag=None): '''Build/run on Zephyr''' return_value = -1 build_dir = None instance_text = u_utils.get_instance_text(instance) # Don't need the platform or misc locks del platform_lock del misc_locks # Only one toolchain for Zephyr del toolchain prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running Zephyr for " + mcu + " (on a \"" + board + "\" board)" if connection and "debugger" in connection and connection["debugger"]: text += ", on JLink debugger serial number " + connection["debugger"] if clean: text += ", clean build" if defines: text += ", with #define(s)" for idx, define in enumerate(defines): if idx == 0: text += " \"" + define + "\"" else: text += ", \"" + define + "\"" if ubxlib_dir: text += ", ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_START, "Zephyr") # Switch to the working directory with u_utils.ChangeDir(working_dir): # Check that everything we need is installed # and configured if u_utils.keep_going(keep_going_flag, printer, prompt) and \ check_installation(TOOLS_LIST, printer, prompt): # Set up the environment variables for Zephyr returned_env = set_env(printer, prompt) if u_utils.keep_going(keep_going_flag, printer, prompt) and \ returned_env: # The west tools need to use the environment # configured above. print_env(returned_env, printer, prompt) # Note that Zephyr brings in its own # copy of Unity so there is no need to # fetch it here. if board: # Do the build build_start_time = time() build_dir = build(board, clean, ubxlib_dir, defines, returned_env, printer, prompt, reporter, keep_going_flag) if u_utils.keep_going(keep_going_flag, printer, prompt) and \ build_dir: # Build succeeded, need to lock some things to do the download reporter.event( u_report.EVENT_TYPE_BUILD, u_report.EVENT_PASSED, "build took {:.0f} second(s)".format( time() - build_start_time)) # Do the download with u_connection.Lock( connection, connection_lock, CONNECTION_LOCK_GUARD_TIME_SECONDS, printer, prompt, keep_going_flag) as locked_connection: if locked_connection: # Get the device name for JLink jlink_device_name = jlink_device(mcu) if not jlink_device_name: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_WARNING, "MCU not found in JLink devices") # Do the download reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_START) retries = 0 downloaded = False while u_utils.keep_going(keep_going_flag, printer, prompt) and \ not downloaded and (retries < 3): downloaded = download( connection, jlink_device_name, DOWNLOAD_GUARD_TIME_SECONDS, build_dir, returned_env, printer, prompt) if not downloaded: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_WARNING, "unable to download, will retry..." ) retries += 1 sleep(5) if downloaded: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_COMPLETE) # Now the target can be reset u_utils.reset_nrf_target( connection, printer, prompt) reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_START) with URttReader( jlink_device(mcu), jlink_serial=connection[ "debugger"], printer=printer, prompt=prompt) as rtt_reader: return_value = u_monitor.main( rtt_reader, u_monitor.CONNECTION_RTT, RUN_GUARD_TIME_SECONDS, RUN_INACTIVITY_TIME_SECONDS, "\n", instance, printer, reporter, test_report_handle, keep_going_flag=keep_going_flag) if return_value == 0: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_COMPLETE) else: reporter.event( u_report.EVENT_TYPE_TEST, u_report.EVENT_FAILED) else: reporter.event( u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_FAILED, "check debug log for details") else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to lock a connection") else: return_value = 1 reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_FAILED, "check debug log for details") else: return_value = 1 reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to find overlay file") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "environment setup failed") else: reporter.event( u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "there is a problem with the tools installation for Zephyr") return return_value
def run(instance, defines, ubxlib_dir, working_dir, printer, reporter, keep_going_flag=None): '''Build to check static sizes''' return_value = -1 instance_text = u_utils.get_instance_text(instance) cflags = C_FLAGS prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running static size check from ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_START, "StaticSize") # Switch to the working directory with u_utils.ChangeDir(working_dir): # Add the #defines to C_FLAGS if defines: for define in defines: cflags += " -D" + define # Assemble the call list call_list = ["python"] call_list.append(ubxlib_dir + os.sep + SUB_DIR + os.sep + "static_size.py") call_list.append("-p") call_list.append(GNU_INSTALL_ROOT) call_list.append("-u") call_list.append(ubxlib_dir) call_list.append("-c") call_list.append(cflags) call_list.append("-l") call_list.append(LD_FLAGS) call_list.append(ubxlib_dir + os.sep + SUB_DIR + os.sep + "source.txt") call_list.append(ubxlib_dir + os.sep + SUB_DIR + os.sep + "include.txt") # Print what we're gonna do tmp = "" for item in call_list: tmp += " " + item printer.string("{}in directory {} calling{}". \ format(prompt, os.getcwd(), tmp)) # Set shell to keep Jenkins happy if u_utils.exe_run(call_list, 0, printer, prompt, shell_cmd=True, keep_going_flag=keep_going_flag): return_value = 0 reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_COMPLETE) else: reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_FAILED, "check debug log for details") return return_value
def run(instance, ubxlib_dir, working_dir, printer, reporter): '''Run Pylint''' return_value = 1 got_pylint = False min_rating = 10 instance_text = u_utils.get_instance_text(instance) prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running Pylint from ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" text += ", checking for minimum rating " + str(MIN_RATING) printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_START, "Pylint") got_pylint = u_utils.exe_where("pylint", \ "ERROR: can't find pylint, please make" \ " sure that it is installed and on the path.", \ printer, prompt) if got_pylint: # Run Pylint on all the .py files in PYTHON_PATHS return_value = 0 for py_path in PYTHON_PATHS: abs_py_path = ubxlib_dir + os.sep + py_path if os.path.exists(abs_py_path): printer.string("{}CD to {}...".format(prompt, abs_py_path)) with u_utils.ChangeDir(abs_py_path): for py_file in os.listdir(abs_py_path): if py_file.endswith(".py"): printer.string("{}running Pylint on {}...".format(prompt, py_file)) got_rating = False try: # ignore u_settings module as it sets members programatically and # will thus generate a bunch of lint warnings text = subprocess.check_output(["pylint", "--exit-zero", "--ignored-modules=u_settings", py_file], stderr=subprocess.STDOUT, shell=True) # Stop Jenkins hanging rating = 0 for line in text.splitlines(): line = line.decode() printer.string("{}{}".format(prompt, line)) # See if there is a rating in this line outcome = line.rpartition("Your code has been rated at ") if len(outcome) == 3: outcome = outcome[2].split("/") # Get the bit before the "/" in the "x/y" rating try: rating = float(outcome[0]) got_rating = True if rating < min_rating: min_rating = rating if rating < MIN_RATING: return_value += 1 except ValueError: # Can't have been a rating line pass if got_rating: if rating < MIN_RATING: reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_ERROR, "rating {} < minimum ({})". \ format(rating, MIN_RATING)) for line in text.splitlines(): line = line.strip().decode() if line: reporter.event_extra_information(line) else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "Pylint returned no rating for file \"{}\"". \ format(py_file)) printer.string("{}Pylint returned no rating.". \ format(prompt)) # No rating returned, flag an error return_value += 1 except subprocess.CalledProcessError as error: reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_FAILED) printer.string("{}Pylint returned error {}:". format(prompt, error.returncode)) for line in error.output.splitlines(): line = line.strip().decode() if line: reporter.event_extra_information(line) printer.string("{}{}".format(prompt, line)) else: # Flag an error if a path doesn't exist reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "path {} does not exit".format(abs_py_path)) printer.string("{}path \"{}\" does not exist.". \ format(prompt, abs_py_path)) return_value += 1 printer.string("{}Pylint check complete, minimum rating {}.". format(prompt, min_rating)) if min_rating < MIN_RATING: reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_FAILED) else: reporter.event(u_report.EVENT_TYPE_CHECK, u_report.EVENT_PASSED) else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "there is a problem with the Pylint installation") return return_value
def session_run(database, instances, filter_string, ubxlib_dir, working_dir, clean, summary_report_file, test_report_file, debug_file, process_pool, session_name=None, print_queue=None, print_queue_prompt=None, abort_on_first_failure=False, unity_dir=None): '''Start a session running the given instances''' session = {} summary_report_file_path = None test_report_file_path = None debug_file_path = None return_value = 0 local_agent = False agent_context = None with CONTEXT_LOCK: # Start the agent if not already running agent_context = get() if agent_context: if print_queue: agent_context["print_thread"].add_forward_queue(print_queue, print_queue_prompt) else: return_value = -1 # HW reset is false when the agent is started implicitly: # it is up to the caller to call agent.start() explicitly # if it wants a HW reset if start(print_queue, hw_reset=False): return_value = 0 agent_context = get() local_agent = True if agent_context: printer = agent_context["printer"] # Name the session and add it to the session list session["id"] = agent_context["next_session_id"] agent_context["next_session_id"] += 1 session["name"] = "session " + str(session["id"]) if session_name: session["name"] = session_name # Set a flag to indicate that the session is # running: processes can watch this and, if it is # cleared, they must exit at the next opportunity session["running_flag"] = CONTEXT_MANAGER.Event() session["running_flag"].set() session["process_running_count"] = 0 session["processes"] = [] agent_context["sessions"].append(session) agent_context["session_running_count"] += 1 # Launch a thread that manages reporting # from multiple sources session["report_queue"] = None session["reporter"] = None session["report_thread"] = None session["summary_report_handle"] = None if summary_report_file: summary_report_file_path = working_dir + os.sep + summary_report_file session["summary_report_handle"] = open(summary_report_file_path, "w") if session["summary_report_handle"]: printer.string("{}writing summary report to \"{}\".". \ format(PROMPT, summary_report_file_path)) else: printer.string("{}unable to open file \"{}\" for summary report.". \ format(PROMPT, summary_report_file_path)) session["report_queue"] = agent_context["manager"].Queue() session["report_thread"] = u_report.ReportThread(session["report_queue"], session["summary_report_handle"]) session["report_thread"].start() session["reporter"] = u_report.ReportToQueue(session["report_queue"], None, None, agent_context["printer"]) session["reporter"].open() # Add any new platform locks required for these instances create_platform_locks(database, instances, agent_context["manager"], agent_context["platform_locks"]) # Set up all the instances for instance in instances: # Provide a working directory that is unique # for each instance and make sure it exists if working_dir: this_working_dir = working_dir + os.sep + \ INSTANCE_DIR_PREFIX + \ u_utils.get_instance_text(instance) else: this_working_dir = os.getcwd() + os.sep + \ INSTANCE_DIR_PREFIX + \ u_utils.get_instance_text(instance) if not os.path.isdir(this_working_dir): os.makedirs(this_working_dir) # Only clean the working directory if requested if clean: u_utils.deltree(this_working_dir, printer, PROMPT) os.makedirs(this_working_dir) # Create the file paths for this instance if summary_report_file: summary_report_file_path = this_working_dir + os.sep + summary_report_file if test_report_file: test_report_file_path = this_working_dir + os.sep + test_report_file if debug_file: debug_file_path = this_working_dir + os.sep + debug_file # Start u_run.main in each worker thread process = {} process["platform"] = u_data.get_platform_for_instance(database, instance) process["instance"] = instance # Create a flag to be set by u_run. while the process is running process["running_flag"] = CONTEXT_MANAGER.Event() process["platform_lock"] = None process["connection_lock"] = u_connection.get_lock(instance) for platform_lock in agent_context["platform_locks"]: if process["platform"] == platform_lock["platform"]: process["platform_lock"] = platform_lock["lock"] break process["handle"] = process_pool.apply_async(u_run.main, (database, instance, filter_string, True, ubxlib_dir, this_working_dir, process["connection_lock"], process["platform_lock"], agent_context["misc_locks"], agent_context["print_queue"], session["report_queue"], summary_report_file_path, test_report_file_path, debug_file_path, session["running_flag"], process["running_flag"], unity_dir)) session["process_running_count"] += 1 session["processes"].append(process) # The lock is released while we're running so that others can get in if agent_context: try: # IMPORTANT: need to be careful here with the bits of context # referenced while the context lock is released. Stick to things # within a session (or a process of a session) and don't remove # sessions. That way it won't conflict with other calls into this # agent. # Wait for all the launched processes to complete printer.string("{}all instances now launched.".format(PROMPT)) loop_count = 0 while agent_context.is_alive() and (session["process_running_count"] > 0): for process in session["processes"]: instance_text = u_utils.get_instance_text(process["instance"]) if not "stopped" in process and process["handle"].ready(): try: # If the return value has gone negative, i.e. # an infrastructure failure, leave it there, # else add the number of test failures to it if (return_value >= 0 and process["handle"].get() > 0) or \ (return_value <= 0 and process["handle"].get() < 0): return_value += process["handle"].get() if (return_value != 0) and abort_on_first_failure: session["running_flag"].clear() printer.string("{}an instance has failed, aborting" \ " (gracefully, might take a while)" \ " as requested...". \ format(PROMPT)) abort_on_first_failure = False except Exception as ex: # If an instance threw an exception then flag an # infrastructure error return_value = -1 printer.string("{}instance {} threw exception \"{}:" \ " {}\" but I can't tell you where" \ " I'm afraid.". \ format(PROMPT, instance_text, type(ex).__name__, str(ex))) if session["reporter"]: session["reporter"].event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "instance {} threw exception \"{}: {}\"". \ format(instance_text, type(ex).__name__, str(ex))) process["stopped"] = True session["process_running_count"] -= 1 if session["process_running_count"] <= 0: session["stopped"] = True if not process["handle"].ready() and \ (loop_count == STILL_RUNNING_REPORT_SECONDS): printer.string("{}instance {} still running.". \ format(PROMPT, instance_text)) loop_count += 1 if loop_count > STILL_RUNNING_REPORT_SECONDS: loop_count = 0 sleep(1) except KeyboardInterrupt: # Start things cleaning up session["running_flag"].clear() raise KeyboardInterrupt from ex # Now need to lock again while we're manipulating stuff with CONTEXT_LOCK: if agent_context: # Remove the session from the list idx_to_remove = None for idx, item in enumerate(agent_context["sessions"]): if item["id"] == session["id"]: idx_to_remove = idx break if idx_to_remove is not None: agent_context["session_running_count"] -= 1 agent_context["sessions"].pop(idx_to_remove) # Tidy up if session["reporter"]: session["reporter"].event_extra_information("return value overall {} (0 = success," \ " negative = probable infrastructure" \ " failure, positive = failure(s) (may" \ " still be due to infrastructure))". \ format(return_value)) session["reporter"].close() if session["report_thread"]: session["report_thread"].stop_thread() session["report_thread"].join() session["report_thread"] = None if session["summary_report_handle"]: session["summary_report_handle"].close() session["summary_report_handle"] = None printer.string("{}run(s) complete, return value {}.". format(PROMPT, return_value)) if local_agent: stop() else: if print_queue: agent_context["print_thread"].remove_forward_queue(print_queue) return return_value
def run(instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle): '''Build/run on ESP-IDF''' return_value = -1 instance_text = u_utils.get_instance_text(instance) filter_string = "*\r\n" # No issues with running in parallel on ESP-IDF del platform_lock # Only one toolchain for ESP-IDF del toolchain prompt = PROMPT + instance_text + ": " # Print out what we've been told to do text = "running ESP-IDF for " + mcu if connection and connection["serial_port"]: text += ", on serial port " + connection["serial_port"] if clean: text += ", clean build" if defines: text += ", with #define(s)" for idx, define in enumerate(defines): if idx == 0: text += " \"" + define + "\"" else: text += ", \"" + define + "\"" if ubxlib_dir: text += ", ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(prompt, text)) reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_START, "ESP-IDF") printer.string("{}CD to {}...".format(prompt, working_dir)) with u_utils.ChangeDir(working_dir): # Fetch ESP-IDF into the right sub directory # and install the tools esp_idf_location = get_esp_idf_location(instance) esp_idf_dir = ESP_IDF_ROOT + os.sep + esp_idf_location["subdir"] if esp_idf_location: system_lock = None if misc_locks and ("system_lock" in misc_locks): system_lock = misc_locks["system_lock"] returned_env = install(esp_idf_location["url"], esp_idf_dir, esp_idf_location["branch"], system_lock, printer, prompt, reporter) if returned_env: # From here on the ESP-IDF tools need to set up # and use the set of environment variables # returned above. print_env(returned_env, printer, prompt) # Now do the build build_dir = working_dir + os.sep + BUILD_SUBDIR build_start_time = time() if build(esp_idf_dir, ubxlib_dir, build_dir, defines, returned_env, clean, printer, prompt, reporter): reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_PASSED, "build took {:.0f} second(s)". \ format(time() - build_start_time)) with u_connection.Lock(connection, connection_lock, CONNECTION_LOCK_GUARD_TIME_SECONDS, printer, prompt) as locked: if locked: # Have seen this fail, only with Python 3 for # some reason, so give it a few goes reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_START) retries = 0 while not download(esp_idf_dir, ubxlib_dir, build_dir, connection["serial_port"], returned_env, printer, prompt) and (retries < 3): reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_WARNING, "unable to download, will retry...") retries += 1 sleep(5) if retries < 3: reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_COMPLETE) reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_START) # Search the defines list to see if it includes a # "U_CFG_APP_FILTER=blah" item. On ESP32 the tests # that are run are not selected at compile time, # they are selected by sending the "blah" string # over the COM port where it must match a "module name", # a thing in [square brackets] which our naming convention # dictates will be an API name (e.g. "port") or "example". for define in defines: tmp = u_utils.FILTER_MACRO_NAME + "=" if define.startswith(tmp): filter_string = define[len(tmp):] reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_INFORMATION, "only running module \"" + filter_string + "\"") printer.string("{} will use filter [{}].". \ format(prompt, filter_string)) # Add the top and tail it needs for sending filter_string = "[" + filter_string + "]\r\n" break # Open the COM port to get debug output serial_handle = u_utils.open_serial(connection["serial_port"], 115200, printer, prompt) if serial_handle is not None: # Monitor progress return_value = u_monitor.main(serial_handle, u_monitor.CONNECTION_SERIAL, RUN_GUARD_TIME_SECONDS, RUN_INACTIVITY_TIME_SECONDS, instance, printer, reporter, test_report_handle, send_string=filter_string) serial_handle.close() else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to open serial port " + \ connection["serial_port"]) if return_value == 0: reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_COMPLETE) else: reporter.event(u_report.EVENT_TYPE_TEST, u_report.EVENT_FAILED) else: reporter.event(u_report.EVENT_TYPE_DOWNLOAD, u_report.EVENT_FAILED, "unable to download to the target") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "unable to lock a connection") else: reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_FAILED, "unable to build, check debug log for details") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "tools installation failed") else: reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE, u_report.EVENT_FAILED, "don't have ESP-IDF URL for this instance") printer.string("{}error: don't have ESP-IDF URL instance {}.". format(prompt, instance_text)) return return_value
def main(database, instance, filter_string, clean, ubxlib_dir, working_dir, connection_lock, platform_lock, misc_locks, print_queue, report_queue, summary_report_file_path, test_report_file_path, debug_file_path, keep_going_flag, running_flag, unity_dir): '''Main as a function''' return_value = 1 connection = None platform = None summary_report_handle = None test_report_handle = None debug_handle = None instance_text = u_utils.get_instance_text(instance) printer_text = [] if running_flag: # We're off running_flag.set() # Create the files if summary_report_file_path: summary_report_handle = open(summary_report_file_path, "w") if summary_report_handle: printer_text.append("{}writing summary report to \"{}\".". \ format(PROMPT, summary_report_file_path)) else: printer_text.append("{}unable to open file \"{}\" for summary report.". \ format(PROMPT, summary_report_file_path)) if test_report_file_path: test_report_handle = open(test_report_file_path, "w") if test_report_handle: printer_text.append("{}writing test report to \"{}\".". \ format(PROMPT, test_report_file_path)) else: printer_text.append("{}unable to open file \"{}\" for test report.". \ format(PROMPT, test_report_file_path)) if debug_file_path: debug_handle = open(debug_file_path, "w") if debug_handle: printer_text.append("{}writing log output to \"{}\".". \ format(PROMPT, debug_file_path)) else: printer_text.append("{}unable to open file \"{}\" for log" \ " output.".format(PROMPT, debug_file_path)) # Create a printer and send the initial printer text there printer = u_utils.PrintToQueue(print_queue, debug_handle, True) for line in printer_text: printer.string(line) # Print out what we've been told to do text = "running instance " + instance_text if filter_string: text += " with filter_string \"" + filter_string + "\"" if clean: text += ", clean build" if ubxlib_dir: text += ", ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(PROMPT, text)) # Get the connection for this instance connection = u_connection.get_connection(instance) # Get the #defines for this instance defines = u_data.get_defines_for_instance(database, instance) if not defines: defines = [] # If there is a cellular module on this instance, add its # name to the defines list cellular_module_name = u_data.get_cellular_module_for_instance( database, instance) if cellular_module_name: defines.append("U_CFG_TEST_CELL_MODULE_TYPE=" + cellular_module_name) # If there is a short-range module on this instance, add its # name to the defines list short_range_module_name = u_data.get_short_range_module_for_instance( database, instance) if short_range_module_name: defines.append("U_CFG_TEST_SHORT_RANGE_MODULE_TYPE=" + short_range_module_name) # If there is a GNSS module on this instance, add its # name to the defines list gnss_module_name = u_data.get_gnss_module_for_instance(database, instance) if gnss_module_name: defines.append("U_CFG_TEST_GNSS_MODULE_TYPE=" + gnss_module_name) # Also, when running testing it is best to run the # the "port" tests first as, if there's a problem with the # port, you want to notice it first. # This also acts as a flag to indicate that we're running # under u_runner automation defines.append("U_RUNNER_TOP_STR=port") # When running tests on cellular LTE modules, so # SARA-R4 or SARA-R5, we need to set the RF band we # are running in to NOT include the public network, # since otherwise the modules can sometimes wander off # onto it. defines.append("U_CELL_TEST_CFG_BANDMASK1=0x000010ULL") # Defines may be provided via an environment # variable, in a list separated with semicolons, e.g.: # set U_UBXLIB_DEFINES=THING_1;ANOTHER_THING=123;ONE_MORE=boo # Add these in. if UBXLIB_DEFINES_VAR in environ and environ[UBXLIB_DEFINES_VAR].strip(): defines.extend(environ[UBXLIB_DEFINES_VAR].strip().split(";")) # Merge in any filter string we might have defines = merge_filter(defines, filter_string) # It is sometimes useful for the platform tools to be able # to detect that they are running under automation (e.g. this # is used to switch ESP-IDF to using u_runner rather than the # usual ESP-IDF unit test menu system). # For this purpose we add ENV_UBXLIB_AUTO to the environment environ[ENV_UBXLIB_AUTO] = "1" # With a reporter with u_report.ReportToQueue(report_queue, instance, summary_report_handle, printer) as reporter: if connection: # Run the type of build/test specified platform = u_data.get_platform_for_instance(database, instance) if platform: # Since there will be many different platforms, add # the description from the database to the report description = u_data.get_description_for_instance( database, instance) mcu = u_data.get_mcu_for_instance(database, instance) # Zephyr requires a board name also board = u_data.get_board_for_instance(database, instance) toolchain = u_data.get_toolchain_for_instance( database, instance) if description: reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_NAME, description) # A NOTE ABOUT keep_going_flag: the keep_going_flag is passed # into any instance that will take more than a few seconds # to run. Each instance receiving it should ensure that # anything that can be safely stopped and is likely to run # for more than about 10ish seconds should be stopped # if the flag is cleared, in case the user decides to abort # a test run. if platform.lower() == "esp-idf": return_value = u_run_esp_idf.run( instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle, keep_going_flag) elif platform.lower() == "nrf5sdk": return_value = u_run_nrf5sdk.run( instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle, keep_going_flag, unity_dir) elif platform.lower() == "zephyr": return_value = u_run_zephyr.run( instance, mcu, board, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle, keep_going_flag) elif platform.lower() == "stm32cube": return_value = u_run_stm32cube.run( instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle, keep_going_flag, unity_dir) elif platform.lower() == "arduino": return_value = u_run_arduino.run( instance, mcu, board, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle, keep_going_flag, unity_dir) else: printer.string("{}don't know how to handle platform \"{}\".". \ format(PROMPT, platform)) else: printer.string( "{}this instance has no platform.".format(PROMPT)) else: # No connection, must be a local thing if instance[0] == 0: return_value = u_run_lint.run(instance, defines, ubxlib_dir, working_dir, printer, reporter, keep_going_flag, unity_dir) elif instance[0] == 1: return_value = u_run_doxygen.run(instance, ubxlib_dir, working_dir, printer, reporter) elif instance[0] == 2: return_value = u_run_astyle.run(instance, ubxlib_dir, working_dir, printer, reporter) elif instance[0] == 3: return_value = u_run_pylint.run(instance, ubxlib_dir, working_dir, printer, reporter, keep_going_flag) elif instance[0] == 4: return_value = u_run_static_size.run(instance, defines, ubxlib_dir, working_dir, printer, reporter, keep_going_flag) elif instance[0] == 5: return_value = u_run_no_floating_point.run( instance, defines, ubxlib_dir, working_dir, printer, reporter, keep_going_flag) elif instance[0] == 6: printer.string("{}reserved, nothing to do.".format(PROMPT)) return_value = 0 elif instance[0] == 7: printer.string("{}reserved, nothing to do.".format(PROMPT)) return_value = 0 elif instance[0] == 8: printer.string("{}reserved, nothing to do.".format(PROMPT)) return_value = 0 elif instance[0] == 9: printer.string("{}reserved, nothing to do.".format(PROMPT)) return_value = 0 else: printer.string("{}instance {} has no connection and isn't a" \ " local thing.".format(PROMPT, instance_text)) if platform: printer.string("{}instance {}, platform {} EXITING with" \ " return value {}.".format(PROMPT, instance_text, platform, return_value)) elif connection: printer.string("{}instance {}, EXITING with return value {}.". \ format(PROMPT, instance_text, return_value)) else: printer.string("{}instance {} EXITING with return value {}.". \ format(PROMPT, instance_text, return_value)) if summary_report_handle: summary_report_handle.close() if test_report_handle: test_report_handle.close() if debug_handle: debug_handle.close() if running_flag: # We're done running_flag.clear() return return_value
def main(database, instance, filter_string, clean, ubxlib_dir, working_dir, connection_lock, platform_lock, misc_locks, print_queue, report_queue, summary_report_file_path, test_report_file_path, debug_file_path): '''Main as a function''' return_value = 1 connection = None platform = None summary_report_handle = None test_report_handle = None debug_handle = None instance_text = u_utils.get_instance_text(instance) printer_text = [] signal(SIGINT, signal_handler) # Create the files if summary_report_file_path: summary_report_handle = open(summary_report_file_path, "w") if summary_report_handle: printer_text.append("{}writing summary report to \"{}\".". \ format(PROMPT, summary_report_file_path)) else: printer_text.append("{}unable to open file \"{}\" for summary report.". \ format(PROMPT, summary_report_file_path)) if test_report_file_path: test_report_handle = open(test_report_file_path, "w") if test_report_handle: printer_text.append("{}writing test report to \"{}\".". \ format(PROMPT, test_report_file_path)) else: printer_text.append("{}unable to open file \"{}\" for test report.". \ format(PROMPT, test_report_file_path)) if debug_file_path: debug_handle = open(debug_file_path, "w") if debug_handle: printer_text.append("{}writing log output to \"{}\".". \ format(PROMPT, debug_file_path)) else: printer_text.append("{}unable to open file \"{}\" for log" \ " output.".format(PROMPT, debug_file_path)) # Create a printer and send the initial printer text there printer = u_utils.PrintToQueue(print_queue, debug_handle, True) for line in printer_text: printer.string(line) # Print out what we've been told to do text = "running instance " + instance_text if filter_string: text += " with filter_string \"" + filter_string + "\"" if clean: text += ", clean build" if ubxlib_dir: text += ", ubxlib directory \"" + ubxlib_dir + "\"" if working_dir: text += ", working directory \"" + working_dir + "\"" printer.string("{}{}.".format(PROMPT, text)) # Get the connection for this instance connection = u_connection.get_connection(instance) # Get the #defines for this instance defines = u_data.get_defines_for_instance(database, instance) if not defines: defines = [] if filter_string: # The filter_string is just another #define so # add it to the list defines.append(u_utils.FILTER_MACRO_NAME + "=" + \ filter_string) # If there is a cellular module on this instance, add its # name to the defines list cellular_module_name = u_data.get_cellular_module_for_instance( database, instance) if cellular_module_name: defines.append("U_CFG_TEST_CELL_MODULE_TYPE=" + cellular_module_name) # If there is a short-range module on this instance, add its # name to the defines list short_range_module_name = u_data.get_short_range_module_for_instance( database, instance) if short_range_module_name: defines.append("U_CFG_TEST_SHORT_RANGE_MODULE_TYPE=" + short_range_module_name) # Also, when running testing it is best to run the # the "port" tests first as, if there's a problem with the # port, you want to notice it first. defines.append("U_RUNNER_TOP_STR=port") # With a reporter with u_report.ReportToQueue(report_queue, instance, summary_report_handle, printer) as reporter: if connection: # Run the type of build/test specified platform = u_data.get_platform_for_instance(database, instance) if platform: # Since there will be many different platforms, add # the description from the database to the report description = u_data.get_description_for_instance( database, instance) mcu = u_data.get_mcu_for_instance(database, instance) toolchain = u_data.get_toolchain_for_instance( database, instance) if description: reporter.event(u_report.EVENT_TYPE_BUILD, u_report.EVENT_NAME, description) if platform.lower() == "esp-idf": return_value = u_run_esp_idf.run( instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle) elif platform.lower() == "nrf5sdk": return_value = u_run_nrf5sdk.run( instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle) elif platform.lower() == "zephyr": return_value = u_run_zephyr.run( instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle) elif platform.lower() == "stm32cube": return_value = u_run_stm32cube.run( instance, mcu, toolchain, connection, connection_lock, platform_lock, misc_locks, clean, defines, ubxlib_dir, working_dir, printer, reporter, test_report_handle) else: printer.string("{}don't know how to handle platform \"{}\".". \ format(PROMPT, platform)) else: printer.string( "{}this instance has no platform.".format(PROMPT)) else: # No connection, must be a local thing if instance[0] == 0: return_value = u_run_lint.run(instance, defines, ubxlib_dir, working_dir, printer, reporter) elif instance[0] == 1: return_value = u_run_doxygen.run(instance, ubxlib_dir, working_dir, printer, reporter) elif instance[0] == 2: return_value = u_run_astyle.run(instance, ubxlib_dir, working_dir, printer, reporter) elif instance[0] == 3: return_value = u_run_pylint.run(instance, ubxlib_dir, working_dir, printer, reporter) elif instance[0] == 4: return_value = u_run_lint.run(instance, defines, ubxlib_dir, working_dir, printer, reporter) elif instance[0] == 5: printer.string("{}reserved, nothing to do.".format(PROMPT)) return_value = 0 elif instance[0] == 6: printer.string("{}reserved, nothing to do.".format(PROMPT)) return_value = 0 elif instance[0] == 7: printer.string("{}reserved, nothing to do.".format(PROMPT)) return_value = 0 elif instance[0] == 8: printer.string("{}reserved, nothing to do.".format(PROMPT)) return_value = 0 elif instance[0] == 9: printer.string("{}reserved, nothing to do.".format(PROMPT)) return_value = 0 else: printer.string("{}instance {} has no connection and isn't a" \ " local thing.".format(PROMPT, instance_text)) if platform: printer.string("{}instance {}, platform {} EXITING with" \ " return value {}.".format(PROMPT, instance_text, platform, return_value)) elif connection: printer.string("{}instance {}, EXITING with return value {}.". \ format(PROMPT, instance_text, return_value)) else: printer.string("{}instance {} EXITING with return value {}.". \ format(PROMPT, instance_text, return_value)) if summary_report_handle: summary_report_handle.close() if test_report_handle: test_report_handle.close() if debug_handle: debug_handle.close() return return_value