def install_sys(target_type, target_ip, sys_name, sys_path="", quiet=False, timeout=60): """Install a system. :param target_type: type of target (wp85, ar759x, ...) :param target_ip: ip address of the target :param sys_name: name of the system definition file :param sys_path: path of the system definition file :param quiet: if True, do not produce logs :param timeout: timeout for the command. :returns: None :raises: AssertionError """ cmd = "app install %s.%s.update %s" % ( os.path.join(sys_path, sys_name), target_type, target_ip, ) if not quiet: swilog.info(cmd) rsp, _exit = pexpect.run(cmd, timeout=timeout, encoding="utf-8", withexitstatus=1) if not quiet: swilog.info(rsp) assert _exit == 0
def L_SecureStorage_0011(target, legato): """Purpose: Verify that the Secure Storage Write and Read APIs. Can be repeatedly called many times reliably. This script will 1. Use the "Write" API to write an item. 2. Use the "Read" API to read the item. 3. Repeat 1 and 2 many times. 4. Verify that for all operations both write and read are successful and the data read is the same as the data written. :param target: fixture to communicate with the target :param legato: fixture to call useful functions regarding legato :param app_leg: fixture regarding to build, install and remove app """ swilog.step("Execute L_SecureStorage_0011") test_title = r"Write\ Read\ Test" test_type = "writeread" test_cycle = "50" if not legato.is_app_running(APP_NAME): legato.start(APP_NAME) secure_storage_test_post(target, legato, test_title, test_type, test_cycle) swilog.info("[PASSED] L_SecureStorage_0011")
def install_and_clean_app(request, legato, clean_test): """Set up the environment, install apps and clean up. :param request: object to access data :param legato: fixture to call useful functions regarding legato :param clean_test: fixture to clean up environment """ assert clean_test test_name = request.node.name if legato.get_current_system_status() != "good": legato.restore_golden_legato() if test_name != "L_UpdateCtrl_UnlockProbation_0001": # Since the test framwork would change the probation period to 1ms, # it is neccessary to change it back to the default (30mins) # because this testcase requires to run under probation legato.reset_probation_timer() # Make install application legato.make_install(APP_NAME_01, APP_PATH_01) swilog.info("[PASSED] Make and install the test app successfully.") yield if legato.is_app_running(APP_NAME_01): legato.stop(APP_NAME_01) legato.clean(APP_NAME_01)
def make_install_sandbox_app(legato, tmpdir, app_name, app_path, expect_tst): """Build and install sandbox test app. :param legato: fixture to call useful functions regarding legato :param tmpdir: fixture to provide a temporary directory unique to the test invocation :param app_name: name of the app :param app_path: path to the adef file :param expect_tst: expected value """ rsp = pexpect.run("cat %s/%s.adef" % (app_path, app_name), encoding="utf-8") swilog.info(rsp) make_should_fail = False # Go to temp directory os.chdir(str(tmpdir)) try: legato.clean(app_name) except: swilog.info("Failed to clean the app.") try: legato.make(app_name, app_path, make_should_fail) legato.clear_target_log() legato.install(app_name) legato.start(app_name) time.sleep(5) except: assert expect_tst == "invalid", ( "Test failed during " "make/install/start but was " "not marked as invalid" ) else: assert expect_tst != "invalid Test should be invalid but was passed" return expect_tst
def create_cfg_xml(self, config_files): """Read the xml configuration file. Args: config_files: list of xml config files used to search for an unknown value. It is possible to include other xml files with the tag "include_xml". It is also possible to use the text value of a xml element in the file path to include with $(). In the following example, $("name") refers to the parameter name ("WP7502") of the xml. .. literalinclude:: ../../../test/config/target.xml In the included file, the tag "include_child_only" allows to include all the child of the root tree, not the root tree. """ # Go to LeTP tests because configuration file path can # be relative to the path of execution swilog.info("Create config from {}".format(config_files)) old_path = os.getcwd() if "LETP_TESTS" in os.environ: os.chdir(os.environ["LETP_TESTS"]) # --config can be called multiple times for config in config_files: # the xml files can be separated by a comma if config in self.xml_file_lists: # Already processed continue self._include_one_cfg_xml(config) # Go to previous path os.chdir(old_path)
def test_target_reinit(target): """To test target.reinit fixture.""" swilog.info("test target.reinit") target.wait_for_device_up(_TIMEOUT) _validate_func_call(target.slink1.wait_for_device_up, _TIMEOUT) _validate_cmd_call(target.close, ()) _validate_cmd_call(target.reinit, ())
def are_apps_installed(target, apps): """Check if apps with the given name exists. The format is either {app_name: version, ...} or [app_name, ...] """ _exit, rsp = target.run("app list", withexitstatus=True) if _exit != 0: swilog.error("Unable to list the apps installed") return None app_list = rsp.strip().split("\r\n") for app in app_list: if app in apps: swilog.info("Application %s exists. Now checking the version" % app) if isinstance(apps, dict): expected_version = apps[app] if expected_version and expected_version != "": ver_check = version_validity(expected_version) if ver_check == 0: swilog.info("%s has no version" % app) else: _exit, rsp = target.run("app version %s" % app, withexitstatus=1) found_version = rsp.strip().split(" ")[1] if found_version == expected_version: apps.pop(app) else: # No version to check apps.pop(app) else: # No version to check apps.remove(app) return len(apps) == 0
def resolve_xml(self): """Resolve the matched xml files. Searching order in the configuration trees: 1. $LETP_TESTS 2. letp-internal 3. LeTP """ if "$LETP_TESTS" in self.config_path: xml_file = os.path.expandvars(self.config_path) return self._find_xml_config(xml_file) else: current_path = pathlib.Path(os.path.abspath(__file__)) letp_test_config_dir = os.path.expandvars("$LETP_TESTS") letp_internal_config_dir = os.path.expandvars( "$LETP_INTERNAL_PATH") letp_config_dir = os.path.join(current_path.parent) for dir_name in [ letp_test_config_dir, letp_internal_config_dir, letp_config_dir, ]: if not (dir_name and os.path.exists(dir_name)): continue xml_file_path = os.path.join(dir_name, self.config_path) resolved_xml_file = self._find_xml_config(xml_file_path) if resolved_xml_file: swilog.info("{} will be used".format(resolved_xml_file)) return resolved_xml_file swilog.error("Cannot resolve {}".format(self.config_path)) return None
def are_apps_running(target, app_names): """Check the applications are running. Args: target: target fixture to communicate with the module app_name: names of the application Returns: True if all applications are running, False instead and None if it was not possible to determine the status. """ _exit, rsp = target.run("app status", withexitstatus=True) if _exit != 0: swilog.error("Unable to get apps status") return None app_list = rsp.strip().split("\r\n") for app in app_list: status, app_name = app.strip().split(" ") if app_name not in app_names: continue if status != "[running]": swilog.info("%s is not running" % app_name) return False app_names.remove(app_name) if len(app_names) != 0: swilog.info("The following apps have not been found: %s" % ",".join(app_names)) return False return True
def wait_for_usb_dev(self, timeout=300): """Wait for usb device to show up in sys fs.""" time_elapsed = time.time() end_time = time.time() + timeout if not self.name: return False while time_elapsed <= end_time: swilog.info("Waiting for USB device {} to be up...".format( self.name)) dev_path = self.get_device_path() if not dev_path: swilog.warning("Don't know how to check if {} is up".format( self.name)) return True if os.path.exists(dev_path): swilog.info("USB device {} is present!".format(self.name)) return True time.sleep(1) time_elapsed = time.time() return False
def scp(file_list, dest, target): """Copy files to target with scp. Args: file_list: file name or python list of files/folder to copy dest: destination folder or file target: fixture of the target to copy files onto LeTP add some flags and the SSH port can be different from 22. """ if isinstance(target, str): # Legacy call, target is target_ip target_ip = target ssh_opts = os.getenv("LETP_SSH_OPTS") ssh_port = os.getenv("LETP_SSH_PORT") else: target_ip = target.target_ip ssh_opts = target.ssh_opts ssh_port = target.ssh_port if isinstance(file_list, str): file_list = [file_list] # To fix manage folders with -r cmd = "scp -r %s -P %s %s root@%s:%s" % ( ssh_opts, ssh_port, " ".join(file_list), target_ip, dest, ) swilog.info(cmd) rsp, _exit = pexpect.run(cmd, withexitstatus=1) swilog.info(rsp) assert _exit == 0, "Impossible to copy"
def permissions_test(target, owner_permissions, group_permissions, others_permissions): """Change the file permissions. Args: target: fixture to communicate with the target owner_permissions: fixture to provide owner permissions group_permissions: fixture to provide group permissions others_permissions: fixture to provide others permissions """ stdout = target.run("cd %s/bin/; ./%s -mc 2 permissions %s/testFile1" % (APP_SANDBOX_PATH, APP_NAME, APP_SANDBOX_PATH)) text = "the owner can %s, group members can %s, and others can %s." % ( owner_permissions, group_permissions, others_permissions, ) assert text in stdout, ( "[FAILED] Did not find correct permissions for " "all groups (owner: %s, group: %s, others: %s)" % (owner_permissions, group_permissions, others_permissions)) swilog.info("[PASSED] Successfully found correct permissions for all " "groups (owner: %s, group: %s, others: %s)" % (owner_permissions, group_permissions, others_permissions))
def test_AT_commands_expect(target): """Send a command and expect/search for regexp patterns. Args: target: fixture to communicate with the target """ # target.run_at_cmd uses low level functions: send + expect. # They can be used to control what to send and what to receive. # Send ATI9, wait for IMEI SV rsp = target.send("/usr/bin/microcom /dev/ttyAT\r\n") target.send("ATI9\r") # expect will find patterns in a list and return # which pattern it has found rsp = target.expect([pexpect.TIMEOUT, "IMEI SV: "], 10) if rsp == 0: # Found the first pattern (pexpect.TIMEOUT) swilog.error("Timeout") elif rsp == 1: # Found the second element: "IMEI SV: " # Now get the IMEI SV rsp = target.expect([pexpect.TIMEOUT, r"\d+\r"], 10) # target.at.before is the content of the received data # target.at.after is the content of the regexp swilog.info("IMEI SV is %s" % target.after.strip()) # get the end of the command rsp = target.expect(["OK"], 10) target.sendcontrol("x")
def L_SampleApps_Gpio_0001(legato): """Validate that the sample app gpioCf3Demo works well. and validate LE-10420. This script will 1. Make and install the test app 2. Run the test app 3. Check if expected messages appears in log :param legato: fixture to call useful functions regarding legato :param init_gpio: fixture to set, check and restore GPIO. :param app_leg: fixture to make, install and remove application """ time.sleep(5) swilog.step("Execute L_SampleApps_Gpio_0001") # Check to see if app started check_log(legato, "Starting process 'gpioCf3Demo'") # Check to see if GPIO 21 is assigned check_log(legato, "Assigning GPIO 21") # Check to see if GPIO 22 is assigned check_log(legato, "Assigning GPIO 22") # Check to see if GPIO 21 is released check_log(legato, "Releasing GPIO 21") # Check to see if GPIO 22 is released check_log(legato, "Releasing GPIO 22") # Check to see if Application 'gpioCf3Demo' has stopped. check_log(legato, "Application 'gpioCf3Demo' has stopped.") swilog.info("[PASSED] L_SampleApps_Gpio_0001")
def check_log(legato, text_log): """Check a text in the target logs. :param legato: fixture to call useful functions regarding legato :param text_log: fixture to provide a necessary text """ error_msg = "[FAILED] Could not find '%s'" % text_log assert legato.find_in_target_log(text_log), error_msg swilog.info("[PASSED] Successfully found '%s'" % text_log)
def reinit(self): """Reinit the link based on the port_type.""" swilog.warning("%s port may be gone!", self.port_type) swilog.info("Try to reconnect the %s port...", self.port_type) dev_tty = self.module.port_detector.get_com_port(self.port_type) self.info.update_name(dev_tty) self.close() self.init() self.update_alias()
def L_UpdateCtrl_Defer_0006(target, legato): """Verify that the deferral will be released after. stopping the client (process) who called le_updateCtrl_Defer() Testing Procedures: 1. Install the app that invokes le_updateCtrl_Defer() onto the target device 2. Run the app 3. Stop the app 4. Install the helloWorld app 5. Check 4. is successful :param target: fixture to communicate with the target :param legato: fixture to call useful functions regarding legato :param install_and_clean_app: fixture to clean up environment and build app """ swilog.step("Test L_UpdateCtrl_Defer_0006") # Begin of the this TC # Set the parameter of the testUpdateCtrl app to # "defer" "4" to run this test case target.run( "config set apps/%s/procs/%s/args/1 defer" % (UPDATE_CTRL_APP, UPDATE_CTRL_APP) ) target.run( "config set apps/%s/procs/%s/args/2 6" % (UPDATE_CTRL_APP, UPDATE_CTRL_APP) ) # Run the testUpdateCtrl app that will invoke le_updateCtrl_Defer() status, rsp = target.run("app start %s" % UPDATE_CTRL_APP, withexitstatus=True) swilog.debug(rsp) assert status == 0, "[FAILED] App could not be started." # Stop the testUPdateCtrl app which is holding a defer lock status, rsp = target.run("app stop %s" % UPDATE_CTRL_APP, withexitstatus=True) swilog.debug(rsp) assert status == 0, "[FAILED] App could not be stopped." # Clean environment legato.clean(UPDATE_CTRL_APP, remove_app=False) # Check whether defer lock is released so that # any system update is allowed by installing # The helloWorld app when when the client (process) # who called le_updateCtrl_Defer() is stopped legato.make_install(UPDATE_CTRL_APP, UPDATE_CTRL_PATH) # If the target device is not shutting down then, # killing the process who holds the defer lock # Won't cause reboot. Marked this test case failed swilog.info( "[PASSED] defer lock was released after a" " process which called Defer() is stopped" ) swilog.info("[PASSED] Test L_UpdateCtrl_Defer_0006")
def test_debug_level(): """Test debug values in different levels.""" print("test debug starts") swilog.debug("debug level message") swilog.info("info level message") swilog.warning("warning level message") swilog.error("error level message") swilog.critical("critical level message") print("test debug ends")
def end_test(is_tc_passed, request): """Verify the result of test case. :param is_tc_passed: status of test case :param request: object to access data """ test_name = request.node.name assert is_tc_passed, "[FAILED] %s" % test_name swilog.info("[PASSED] Test %s" % test_name)
def L_UpdateCtrl_Defer_0002(target, legato): """Verify that le_updateCtrl_Defer() prevents all updates. (install an app) Initial Condition: 1. Current system index is "N" Test Procedures: 1. Install the app that invokes le_updateCtrl_Defer() onto the target device 2. Check the current system index is "N + 1"and run the app 3. Install an app (e.g. helloWorld) onto the target device 4. Check "Updates are currently deferred" is shown from log 5. Check the app is not installed on the target device (verified by the command line "app list") 6. Check the current system index is "N + 1" (Notes: the current system index, the current system state and the current system status can be verified by the command line "legato status") :param target: fixture to communicate with the target :param legato: fixture to call useful functions regarding legato :param install_and_clean_app: fixture to clean up environment and build app """ swilog.step("Test L_UpdateCtrl_Defer_0002") # Set the parameter of the testUpdateCtrl app to # "defer" "2" to run this test case target.run( "config set apps/%s/procs/%s/args/1 defer" % (UPDATE_CTRL_APP, UPDATE_CTRL_APP) ) target.run( "config set apps/%s/procs/%s/args/2 2" % (UPDATE_CTRL_APP, UPDATE_CTRL_APP) ) # Run the testUpdateCtrl app that will invoke le_updateCtrl_Defer() status, rsp = target.run("app start %s" % UPDATE_CTRL_APP, withexitstatus=True) swilog.debug(rsp) assert status == 0, "[FAILED] App could not be started." # Perform an update to the system by installing the new helloWorld app # If the installation was successful then, # the system update attempt is proceed legato.make(HELLO_WORLD_APP, HELLO_WORLD_PATH) legato.install(HELLO_WORLD_APP, HELLO_WORLD_PATH, should_fail=True) # If the system update was successful, # the update was allowed and mark this TC failed swilog.info( "[PASSED] Defer() doesn't allow update to \ happen when attempting to install an app" ) swilog.info("[PASSED] Test L_UpdateCtrl_Defer_0002")
def wait_for_system_to_start(target, indx, time_wait=100): """Wait for System to start.""" swilog.info("Waiting for System to start") time_past = 0 while time_wait > time_past: if get_current_system_index(target) == indx: return 0 time_past += 1 time.sleep(1) return 1
def L_AtomicFile_Stream_0032(target, legato, init_atomicFile): """Purpose: Verify that le_atomFile_CancelStream cancels. all changes and close the file pointer returned by le_atomFile_OpenStream, le_atomFile_TryOpenStream, le_atomFile_CreateStream and le_atomFile_TryCreateStream with a read file lock Initial condition: 1. Test app is unsandboxed Verification: This test case will mark as "failed" when 1. File's contents being modified This script will 1. Make and install the test app 2. Run the test app 3. Check file's contents remain unchanged == 0: 4. Repeat above for different test scenarios :param target: fixture to communicate with the target :param legato: fixture to call useful functions regarding legato :param app_leg: fixture regarding to build, install and remove app :param init_atomicFile: fixture to initialize and clean up environment """ test_app_name = "atomCancelStream" test_app_proc_name = "atomCancelStreamProc" hw_file_path = os.path.join(TEST_TOOLS, "testFile.txt") test_file_path = init_atomicFile test_descriptions = [ "openStreamReadFlockOK", "tryOpenStreamReadFlockOK", "createStreamReadFlockOK", "tryCreateStreamReadFlockOK", ] ref_file_path = "/home/root/refFile.txt" files.scp([hw_file_path], ref_file_path, target.target_ip) for td in test_descriptions: files.scp([hw_file_path], test_file_path, target.target_ip) legato.clear_target_log() legato.runProc(test_app_name, test_app_proc_name, test_file_path, td) assert legato.wait_for_log_msg("le_atomFile_CancelStream is called", 20) is True target.run("diff %s %s" % (test_file_path, ref_file_path)) swilog.info("[PASSED] le_atomFile_CancelStream cancels" " all changes and closes the file pointer) " "for le_atomFile_OpenStream(, " "le_atomFile_TryOpenStream(, le_atomFile_CreateStream( and " "le_atomFile_TryCreateStream( with a read file lock")
def verify_when_build_successful(ipc_type_str, interface_opts_str, build_cmd_str, is_binded_str, api_type_str): """Verify if the building is successful. Args: ipc_type_str: ipc type client-side IPC server-side IPC interface_opts_str: interface optionals [optional] [optional] [manual-start] [optional] [types-only] build_cmd_str: build command mkapp: command to make application mksys: command to make system is_binded_str: binding string bound not bound api_type_str: directory name of API type legato API customized API """ if ipc_type_str in "client-side IPC": if (interface_opts_str in "[optional]") or (interface_opts_str in "[optional] [manual-start]"): # Build succesful when client-side IPC # With interface options [optional]/[optional] [manual-start] swilog.info("%s doesn't complain when the %s API is %s to %s " "with the interface options: %s" % ( build_cmd_str, ipc_type_str, is_binded_str, api_type_str, interface_opts_str, )) else: # Build successful when client-side IPC # With interface options [optional] [types-only] swilog.error("%s doesn't complain when the %s API is %s to %s " "with the interface options: %s" % ( build_cmd_str, ipc_type_str, is_binded_str, api_type_str, interface_opts_str, )) else: # Build successful when server-side IPC # With interface options that contain optional swilog.error( "%s doesn't complain when the %s provides %s " "with interface options: %s" % (build_cmd_str, ipc_type_str, api_type_str, interface_opts_str))
def L_AtomicFile_Operation_0031(target, legato, init_atomicFile): """Purpose: Verify that le_atomFile_Cancel cancels all changes. and closes the file descriptor for le_atomFile_Open(, le_atomFile_TryOpen(,le_atomFile_Create( and le_atomFile_TryCreate( with a write file lock Initial condition: 1. test app is unsandboxed Verification: This test case will mark as "failed" when 1. le_atomFile_Cancel commits all changes This script will 1. Make and install the test app 2. Run the test app 3. Check latest changes weren't committed after \ le_atomFile_Cancel is called == 0: 4. Repeat above for different test scenarios :param target: fixture to communicate with the target :param legato: fixture to call useful functions regarding legato :param app_leg: fixture regarding to build, install and remove app :param init_atomicFile: fixture to initialize and clean up environment """ test_app_name = "atomCancel" test_app_proc_name = "atomCancelProc" hw_file_path = os.path.join(TEST_TOOLS, "testFile.txt") test_file_path = init_atomicFile test_descriptions = [ "openWrtFlockOK", "tryOpenWrtFlockOK", "createWrtFlockOK", "tryCreateWrtFlockOK", ] ref_file_path = "/home/root/refFile.txt" files.scp([hw_file_path], ref_file_path, target.target_ip) for td in test_descriptions: files.scp([hw_file_path], test_file_path, target.target_ip) legato.clear_target_log() legato.runProc(test_app_name, test_app_proc_name, test_file_path, td) assert legato.wait_for_log_msg("le_atomFile_Cancel is called", 20) is True target.run("diff %s %s" % (test_file_path, ref_file_path)) swilog.info( "\n[PASSED] le_atomFile_Cancel" " cancels all changes and closes the file descriptor) for " "le_atomFile_Open(, le_atomFile_TryOpen(, le_atomFile_Create(" " and le_atomFile_TryCreate( with a write file lock" )
def test_config_same_test_with_2_cfg(read_config): """Test should be called twice. The same test has different xml configuration in the json file. """ global COUNT # First time, expected_value = "test" if COUNT == 0 else "test2" val = read_config.findtext("foo/bar") swilog.info("Found value: %s" % val) assert val == expected_value
def max_count_test(target, flag): """Check the 'max count' flag. :param target: fixture to communicate with the target :param flag: fixture to provide option """ stdout = target.run("cd %s/bin/; ./%s %s permissions %s/testFile1" % (APP_SANDBOX_PATH, APP_NAME, flag, APP_SANDBOX_PATH)) error_msg = "[FAILED] Did not find 'Maximum file count " "reached' (%s)" % flag assert "Maximum file count reached." in stdout, error_msg swilog.info("[PASSED] Successfully found 'Maximum file count reached' " "(%s)" % flag)
def test_config_value_with_json_env_vari(request, read_config): """Test environment variable in json file. If this function gets called that means, the system was able to get $LETP_TESTS path and call this function from json file, $LETP_TESTS is the env variable used in json file. """ assert request assert read_config swilog.info("env variable was found")
def login(self, attempts=10): """Login to target cli.""" for _ in range(attempts): swilog.info("send root") self.sendline("root") if setup_linux_login(target=self): break for _ in range(attempts): if self.run("stty columns %d" % TTY_SIZE, withexitstatus=1)[0] == 0: return assert False, "Impossible to send command stty to target"
def sandbox(target, legato, tmpdir, tpl_val, expect_tst, failed_reason, init_sandbox): """General function for all test cases. This function will: 1. Update, build and install app 2. Test is passed if verification OK :param target: fixture to communicate with the target :param legato: fixture to call useful functions regarding legato :param tmpdir: fixture to provide a temporary directory unique to the test invocation :param tpl_val: testing value :param expect_tst: expected value :param failed_reason: failed reason :param init_sandbox: fixture to initation and cleanup the environment """ # list_obj[0] app_name # list_obj[1] app_path # list_obj[2] template_name # list_obj[3] test_title list_obj = init_sandbox swilog.info("test val %s, expect test %s" % (tpl_val, expect_tst)) rsp = os.system( r'sed "s/TEMPLATE_VALUE_1/%s/g" "%s/%s.adef" > %s/%s.adef\ ' % (tpl_val, list_obj[1], list_obj[2], list_obj[1], list_obj[0]) ) swilog.info(rsp) expect_tst = make_install_sandbox_app( legato, tmpdir, list_obj[0], list_obj[1], expect_tst ) if expect_tst != "invalid": logread = target.run("/sbin/logread") with open("temp.out", "w") as f: f.write(logread.replace("\r", "")) status = sandbox_verification(tpl_val, expect_tst, "temp.out", list_obj[3]) # Test is passed if verification OK # Or if status != 0 but there is a reason to fail. # If the reason is a JIRA ticket, the test is FAILED operator1 = status != 0 and failed_reason != "" operator2 = operator1 and "LE-" not in failed_reason err_msg = "Verification failed for %s. value %s, expected value : %s" % ( list_obj[3], tpl_val, expect_tst, ) assert status == 0 or operator2, err_msg
def test_AT_commands_expect_in_order(target): """Wait for a list of regexp patterns in the order of the list. Args: target: fixture to communicate with the target """ # Expect some patterns in a list (in order of the list) # Send ATI9, wait for first Revision, then IMEI, then FSN then OK rsp = target.send("/usr/bin/microcom /dev/ttyAT\r\n") target.send("ATI9\r\n") rsp = target.expect_in_order(["Revision", "IMEI", "FSN", "OK"], 10) swilog.info("Receive: %s" % rsp) target.sendcontrol("x")