diff --git a/workspace_tools/host_tests/host_test.py b/workspace_tools/host_tests/host_test.py index 5e9ce50f0e..24dcd98829 100644 --- a/workspace_tools/host_tests/host_test.py +++ b/workspace_tools/host_tests/host_test.py @@ -22,18 +22,17 @@ except ImportError, e: print "Error: Can't import 'serial' module: %s"% e exit(-1) - import os from sys import stdout from time import sleep, time from optparse import OptionParser +import host_tests_plugins # This is a little tricky. We need to add upper directory to path so # we can find packages we want from the same level as other files do import sys sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) -from workspace_tools.settings import EACOMMANDER_CMD class Mbed: @@ -158,27 +157,6 @@ class Mbed: result = None return result - def safe_sendBreak(self, serial): - """ Wraps serial.sendBreak() to avoid serial::serialposix.py exception on Linux - Traceback (most recent call last): - File "make.py", line 189, in - serial.sendBreak() - File "/usr/lib/python2.7/dist-packages/serial/serialposix.py", line 511, in sendBreak - termios.tcsendbreak(self.fd, int(duration/0.25)) - error: (32, 'Broken pipe') - """ - result = True - try: - serial.sendBreak() - except: - # In linux a termios.error is raised in sendBreak and in setBreak. - # The following setBreak() is needed to release the reset signal on the target mcu. - try: - serial.setBreak(False) - except: - result = False - return result - def touch_file(self, path): """ Touch file and set timestamp to items """ @@ -192,56 +170,15 @@ class Mbed: sleep(1) def reset(self): - """ Reset function. - Supports: - - 'standard' send break command via Mbed's CDC, - - also handles other reset modes: - - E.g. reset by touching file with specific file name: - reboot.txt - startup from standby state, reboots when in run mode. - shutdown.txt - shutdown from run mode - reset.txt - reset FPGA during run mode - - eACommander for reset of SiLabs Gecko baords. + """ Calls proper reset plugin to do the job. + Please refer to host_test_plugins functionality """ - if self.options.forced_reset_type: - if self.options.forced_reset_type == 'eACommander': - # For this copy method 'disk' will be 'serialno' for eACommander command line parameters - # Note: Commands are executed in the order they are specified on the command line - cmd = [EACOMMANDER_CMD, - '--serialno', self.disk.rstrip('/\\'), - '--resettype', '2', '--reset',] - try: - self.flush() - ret = call(cmd, shell=True) - if ret: - resutl_msg = "Return code: %d. Command: "% ret + " ".join(cmd) - result = False - except Exception, e: - resutl_msg = e - result = False - elif self.options.forced_reset_type == 'eACommander-usb': - # For this copy method 'disk' will be 'usb address' for eACommander command line parameters - # Note: Commands are executed in the order they are specified on the command line - cmd = [EACOMMANDER_CMD, - '--usb', self.disk.rstrip('/\\'), - '--resettype', '2', '--reset',] - try: - self.flush() - ret = call(cmd, shell=True) - if ret: - resutl_msg = "Return code: %d. Command: "% ret + " ".join(cmd) - result = False - except Exception, e: - resutl_msg = e - result = False - elif self.options.forced_reset_type.endswith('.txt'): - reset_file_path = os.path.join(self.disk, self.options.forced_reset_type.lower()) - self.touch_file(reset_file_path) - self.flush() - else: - self.safe_sendBreak(self.serial) # Instead of serial.sendBreak() - self.flush() # Flush serials to get only input after reset - #self.flush() + self.flush() + if self.options.forced_reset_type: + host_tests_plugins.call_plugin('ResetMethod', self.options.forced_reset_type, disk=self.disk) + else: + host_tests_plugins.call_plugin('ResetMethod', 'default', serial=self.serial) # Give time to wait for the image loading reset_tout_s = self.options.forced_reset_timeout if self.options.forced_reset_timeout is not None else self.DEFAULT_RESET_TOUT self.reset_timeout(reset_tout_s) @@ -275,20 +212,17 @@ class Test(TestResults): def run(self): """ Test runner for host test. This function will start executing - test() function and forward test result via serial port to test suite + test and forward test result via serial port to test suite """ try: - # We expect here output from test in one of possible statuses - # E.g. self.RESULT_SUCCESS, self.RESULT_FAILURE, self.RESULT_ERROR - result_status = self.test() - self.print_result(result_status) + result = self.test() + self.print_result(self.RESULT_SUCCESS if result else self.RESULT_FAILURE) except Exception, e: - self.notify(str(e)) + print str(e) self.print_result(self.RESULT_ERROR) def setup(self): - """ Setup and check if configuration for test is correct. - E.g. if serial port can be opened + """ Setup and check if configuration for test is correct. E.g. if serial port can be opened """ result = True if not self.mbed.serial: @@ -325,19 +259,17 @@ class Simple(DefaultTest): output from MUT, no supervision over test running in MUT is executed. Just waiting for result """ - def test(self): + def run(self): try: while True: c = self.mbed.serial_read(512) if c is None: - return self.RESULT_IO_SERIAL + self.print_result(self.RESULT_IO_SERIAL) + break stdout.write(c) stdout.flush() except KeyboardInterrupt, _: self.notify("\r\n[CTRL+C] exit") - # If this function ends we assume user break or exception - # occured and error should be issued to test suite - return self.RESULT_ERROR if __name__ == '__main__': diff --git a/workspace_tools/host_tests/host_tests_plugins/__init__.py b/workspace_tools/host_tests/host_tests_plugins/__init__.py new file mode 100644 index 0000000000..e3cf27508e --- /dev/null +++ b/workspace_tools/host_tests/host_tests_plugins/__init__.py @@ -0,0 +1,59 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import host_test_registry + +# This plugins provide 'flashing' methods to host test scripts +import module_copy_mbed +import module_copy_shell +import module_copy_firefox +import module_copy_silabs + +# Plugins used to reset certain platform +import module_reset_mbed +import module_reset_mps2 +import module_reset_silabs + + +# Plugin registry instance +HOST_TEST_PLUGIN_REGISTRY = host_test_registry.HostTestRegistry() + +# Static plugin registration +HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_copy_mbed.load_plugin()) +HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_copy_shell.load_plugin()) +HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_copy_firefox.load_plugin()) +HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_copy_silabs.load_plugin()) +HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_reset_mbed.load_plugin()) + +# Extra supported by default platforms +HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_reset_mps2.load_plugin()) +HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_reset_silabs.load_plugin()) + +# TODO: extend plugin loading to files with name module_*.py loaded ad-hoc + +############################################################################### +# Functional interface for host test plugin registry +############################################################################### +def call_plugin(type, capability, *args, **kwargs): + """ Interface to call plugin registry functional way + """ + return HOST_TEST_PLUGIN_REGISTRY.call_plugin(type, capability, *args, **kwargs) + +def print_plugin_info(): + """ Prints plugins' information in user friendly way + """ + print HOST_TEST_PLUGIN_REGISTRY diff --git a/workspace_tools/host_tests/host_tests_plugins/host_test_plugins.py b/workspace_tools/host_tests/host_tests_plugins/host_test_plugins.py new file mode 100644 index 0000000000..3119b49405 --- /dev/null +++ b/workspace_tools/host_tests/host_tests_plugins/host_test_plugins.py @@ -0,0 +1,86 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from subprocess import call + + +class HostTestPluginBase: + """ Base class for all plug-ins used with host tests. + """ + ########################################################################### + # Interface: + ########################################################################### + + ########################################################################### + # Interface attributes defining plugin name, type etc. + ########################################################################### + name = "HostTestPluginBase" # Plugin name, can be plugin class name + type = "BasePlugin" # Plugin type: ResetMethod, Copymethod etc. + capabilities = [] # Capabilities names: what plugin can achieve + # (e.g. reset using some external command line tool) + stable = False # Determine if plugin is stable and can be used + + ########################################################################### + # Interface methods + ########################################################################### + def setup(self, *args, **kwargs): + """ Configure plugin, this function should be called before plugin execute() method is used. + """ + return False + + def execute(self, capabilitity, *args, **kwargs): + """ Executes capability by name. + Each capability e.g. may directly just call some command line + program or execute building pythonic function + """ + return False + + ########################################################################### + # Interface helper methods - overload only if you need to have custom behaviour + ########################################################################### + def print_plugin_error(self, text): + """ Function prints error in console and exits always with False + """ + print "Plugin error: %s::%s: %s"% (self.name, self.type, text) + return False + + def check_parameters(self, capabilitity, *args, **kwargs): + """ This function should be ran each time we call execute() + to check if none of the required parameters is missing. + """ + missing_parameters = [] + for parameter in self.required_parameters: + if parameter not in kwargs: + missing_parameters.append(parameter) + if len(missing_parameters) > 0: + self.print_plugin_error("execute parameter(s) '%s' missing!"% (', '.join(parameter))) + return False + return True + + def run_command(self, cmd, shell=True): + """ Runs command from command line. + """ + result = True + try: + ret = call(cmd, shell=shell) + if ret: + self.print_plugin_error("[ret=%d] Command: %s"% (self.name, self.type, ret, ' '.join(cmd))) + except Exception, e: + result = False + self.print_plugin_error("[ret=%d] Command: %s"% (self.name, self.type, ret, " ".join(cmd))) + self.print_plugin_error("%s::%s: " + str(e)) + return result diff --git a/workspace_tools/host_tests/host_tests_plugins/host_test_registry.py b/workspace_tools/host_tests/host_tests_plugins/host_test_registry.py new file mode 100644 index 0000000000..39e1e420e1 --- /dev/null +++ b/workspace_tools/host_tests/host_tests_plugins/host_test_registry.py @@ -0,0 +1,77 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +class HostTestRegistry: + """ Simple class used to register and store + host test plugins for further usage + """ + # Here we actually store all the plugins + PLUGINS = {} # 'Plugin Name' : Plugin Object + + def print_error(self, text): + print "Plugin load failed. Reason: %s"% text + + def register_plugin(self, plugin): + """ Registers and stores plugin inside registry for further use. + Method also calls plugin's setup() function to configure plugin if needed. + + Note: Different groups of plugins may demand different extra parameter. Plugins + should be at least for one type of plugin configured with the same parameters + because we do not know which of them will actually use particular parameter. + """ + # TODO: + # - check for unique caps for specified type + if plugin.name not in self.PLUGINS: + if plugin.setup(): # Setup plugin can be completed without errors + self.PLUGINS[plugin.name] = plugin + return True + else: + self.print_error("%s setup failed"% plugin.name) + self.print_error("%s already loaded"% plugin.name) + return False + + def call_plugin(self, type, capability, *args, **kwargs): + """ Execute plugin functionality respectively to its purpose + """ + for plugin_name in self.PLUGINS: + plugin = self.PLUGINS[plugin_name] + if plugin.type == type and capability in plugin.capabilities: + return plugin.execute(capability, *args, **kwargs) + return False + + def load_plugin(self, name): + """ Used to load module from + """ + mod = __import__("module_%s"% name) + return mod + + def __str__(self): + """ User friendly printing method to show hooked plugins + """ + from prettytable import PrettyTable + column_names = ['name', 'type', 'capabilities', 'stable'] + pt = PrettyTable(column_names) + for column in column_names: + pt.align[column] = 'l' + for plugin_name in sorted(self.PLUGINS.keys()): + name = self.PLUGINS[plugin_name].name + type = self.PLUGINS[plugin_name].type + stable = self.PLUGINS[plugin_name].stable + capabilities = ', '.join(self.PLUGINS[plugin_name].capabilities) + row = [name, type, capabilities, stable] + pt.add_row(row) + return pt.get_string() diff --git a/workspace_tools/host_tests/host_tests_plugins/module_copy_firefox.py b/workspace_tools/host_tests/host_tests_plugins/module_copy_firefox.py new file mode 100644 index 0000000000..360835e498 --- /dev/null +++ b/workspace_tools/host_tests/host_tests_plugins/module_copy_firefox.py @@ -0,0 +1,76 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from os.path import join, basename +from host_test_plugins import HostTestPluginBase + + +class HostTestPluginCopyMethod_Firefox(HostTestPluginBase): + + def file_store_firefox(self, file_path, dest_disk): + try: + from selenium import webdriver + profile = webdriver.FirefoxProfile() + profile.set_preference('browser.download.folderList', 2) # custom location + profile.set_preference('browser.download.manager.showWhenStarting', False) + profile.set_preference('browser.download.dir', dest_disk) + profile.set_preference('browser.helperApps.neverAsk.saveToDisk', 'application/octet-stream') + # Launch browser with profile and get file + browser = webdriver.Firefox(profile) + browser.get(file_path) + browser.close() + except: + return False + return True + + # Plugin interface + name = 'HostTestPluginCopyMethod_Firefox' + type = 'CopyMethod' + capabilities = ['firefox'] + required_parameters = ['image_path', 'destination_disk'] + + def setup(self, *args, **kwargs): + """ Configure plugin, this function should be called before plugin execute() method is used. + """ + try: + from selenium import webdriver + except ImportError, e: + self.print_plugin_error("Error: firefox copy method requires selenium library. %s"% e) + return False + return True + + def execute(self, capabilitity, *args, **kwargs): + """ Executes capability by name. + Each capability may directly just call some command line + program or execute building pythonic function + """ + result = False + if self.check_parameters(capabilitity, *args, **kwargs) is True: + image_path = kwargs['image_path'] + destination_disk = kwargs['destination_disk'] + # Prepare correct command line parameter values + image_base_name = basename(image_path) + destination_path = join(destination_disk, image_base_name) + if capabilitity == 'firefox': + self.file_store_firefox(image_path, destination_path) + return result + + +def load_plugin(): + """ Returns plugin available in this module + """ + return HostTestPluginCopyMethod_Firefox() diff --git a/workspace_tools/host_tests/host_tests_plugins/module_copy_mbed.py b/workspace_tools/host_tests/host_tests_plugins/module_copy_mbed.py new file mode 100644 index 0000000000..f0d050b6a0 --- /dev/null +++ b/workspace_tools/host_tests/host_tests_plugins/module_copy_mbed.py @@ -0,0 +1,68 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from shutil import copy +from host_test_plugins import HostTestPluginBase + + +class HostTestPluginCopyMethod_Mbed(HostTestPluginBase): + + def generic_mbed_copy(self, image_path, destination_disk): + """ Generic mbed copy method for "mbed enabled" devices. + It uses standard python shuitl function to copy + image_file (target specific binary) to device's disk. + """ + result = True + if not destination_disk.endswith('/') and not destination_disk.endswith('\\'): + destination_disk += '/' + try: + copy(image_path, destination_disk) + except Exception, e: + self.print_plugin_error("shutil.copy(%s, %s) failed: %s"% (image_path, destination_disk, str(e))) + result = False + return result + + # Plugin interface + name = 'HostTestPluginCopyMethod_Mbed' + type = 'CopyMethod' + stable = True + capabilities = ['default'] + required_parameters = ['image_path', 'destination_disk'] + + def setup(self, *args, **kwargs): + """ Configure plugin, this function should be called before plugin execute() method is used. + """ + return True + + def execute(self, capabilitity, *args, **kwargs): + """ Executes capability by name. + Each capability may directly just call some command line + program or execute building pythonic function + """ + result = False + if self.check_parameters(capabilitity, *args, **kwargs) is True: + if capabilitity == 'default': + image_path = kwargs['image_path'] + destination_disk = kwargs['destination_disk'] + result = self.generic_mbed_copy(image_path, destination_disk) + return result + + +def load_plugin(): + """ Returns plugin available in this module + """ + return HostTestPluginCopyMethod_Mbed() diff --git a/workspace_tools/host_tests/host_tests_plugins/module_copy_mps2.py b/workspace_tools/host_tests/host_tests_plugins/module_copy_mps2.py new file mode 100644 index 0000000000..6db90f540e --- /dev/null +++ b/workspace_tools/host_tests/host_tests_plugins/module_copy_mps2.py @@ -0,0 +1,107 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from shutil import copy +from os.path import join +from host_test_plugins import HostTestPluginBase + + +class HostTestPluginCopyMethod_MPS2(HostTestPluginBase): + + # MPS2 specific flashing / binary setup funcitons + def mps2_set_board_image_file(self, disk, images_cfg_path, image0file_path, image_name='images.txt'): + """ This function will alter image cfg file. + Main goal of this function is to change number of images to 1, comment all + existing image entries and append at the end of file new entry with test path. + @return True when all steps succeed. + """ + MBED_SDK_TEST_STAMP = 'test suite entry' + image_path = join(disk, images_cfg_path, image_name) + new_file_lines = [] # New configuration file lines (entries) + + # Check each line of the image configuration file + try: + with open(image_path, 'r') as file: + for line in file: + if re.search('^TOTALIMAGES', line): + # Check number of total images, should be 1 + new_file_lines.append(re.sub('^TOTALIMAGES:[\t ]*[\d]+', 'TOTALIMAGES: 1', line)) + elif re.search('; - %s[\n\r]*$'% MBED_SDK_TEST_STAMP, line): + # Look for test suite entries and remove them + pass # Omit all test suite entries + elif re.search('^IMAGE[\d]+FILE', line): + # Check all image entries and mark the ';' + new_file_lines.append(';' + line) # Comment non test suite lines + else: + # Append line to new file + new_file_lines.append(line) + except IOError as e: + return False + + # Add new image entry with proper commented stamp + new_file_lines.append('IMAGE0FILE: %s ; - %s\r\n'% (image0file_path, MBED_SDK_TEST_STAMP)) + + # Write all lines to file + try: + with open(image_path, 'w') as file: + for line in new_file_lines: + file.write(line), + except IOError: + return False + + return True + + def mps2_select_core(self, disk, mobo_config_name=""): + """ Function selects actual core + """ + # TODO: implement core selection + pass + + def mps2_switch_usb_auto_mounting_after_restart(self, disk, usb_config_name=""): + """ Function alters configuration to allow USB MSD to be mounted after restarts + """ + # TODO: implement USB MSD restart detection + pass + + # Plugin interface + name = 'HostTestPluginCopyMethod_MPS2' + type = 'CopyMethod' + capabilities = ['mps2'] + required_parameters = ['image_path', 'destination_disk'] + + def setup(self, *args, **kwargs): + """ Configure plugin, this function should be called before plugin execute() method is used. + """ + return True + + def execute(self, capabilitity, *args, **kwargs): + """ Executes capability by name. + Each capability may directly just call some command line + program or execute building pythonic function + """ + result = False + if self.check_parameters(capabilitity, *args, **kwargs) is True: + if capabilitity == 'mps2': + # TODO: Implement MPS2 firmware setup here + pass + return result + + +def load_plugin(): + """ Returns plugin available in this module + """ + return HostTestPluginCopyMethod_MPS2() diff --git a/workspace_tools/host_tests/host_tests_plugins/module_copy_shell.py b/workspace_tools/host_tests/host_tests_plugins/module_copy_shell.py new file mode 100644 index 0000000000..9d57a56d30 --- /dev/null +++ b/workspace_tools/host_tests/host_tests_plugins/module_copy_shell.py @@ -0,0 +1,58 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from os.path import join, basename +from host_test_plugins import HostTestPluginBase + + +class HostTestPluginCopyMethod_Shell(HostTestPluginBase): + + # Plugin interface + name = 'HostTestPluginCopyMethod_Shell' + type = 'CopyMethod' + stable = True + capabilities = ['cp', 'copy', 'xcopy'] + required_parameters = ['image_path', 'destination_disk'] + + def setup(self, *args, **kwargs): + """ Configure plugin, this function should be called before plugin execute() method is used. + """ + return True + + def execute(self, capabilitity, *args, **kwargs): + """ Executes capability by name. + Each capability may directly just call some command line + program or execute building pythonic function + """ + result = False + if self.check_parameters(capabilitity, *args, **kwargs) is True: + image_path = kwargs['image_path'] + destination_disk = kwargs['destination_disk'] + # Prepare correct command line parameter values + image_base_name = basename(image_path) + destination_path = join(destination_disk, image_base_name) + if capabilitity == 'cp' or capabilitity == 'copy' or capabilitity == 'copy': + copy_method = capabilitity + cmd = [copy_method, image_path, destination_path] + result = self.run_command(cmd) + return result + + +def load_plugin(): + """ Returns plugin available in this module + """ + return HostTestPluginCopyMethod_Shell() diff --git a/workspace_tools/host_tests/host_tests_plugins/module_copy_silabs.py b/workspace_tools/host_tests/host_tests_plugins/module_copy_silabs.py new file mode 100644 index 0000000000..1572bbc6ee --- /dev/null +++ b/workspace_tools/host_tests/host_tests_plugins/module_copy_silabs.py @@ -0,0 +1,61 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from host_test_plugins import HostTestPluginBase + + +class HostTestPluginCopyMethod_Silabs(HostTestPluginBase): + + # Plugin interface + name = 'HostTestPluginCopyMethod_Silabs' + type = 'CopyMethod' + capabilities = ['eACommander', 'eACommander-usb'] + required_parameters = ['image_path', 'destination_disk'] + + def setup(self, *args, **kwargs): + """ Configure plugin, this function should be called before plugin execute() method is used. + """ + self.EACOMMANDER_CMD = 'eACommander.exe' + return True + + def execute(self, capabilitity, *args, **kwargs): + """ Executes capability by name. + Each capability may directly just call some command line + program or execute building pythonic function + """ + result = False + if self.check_parameters(capabilitity, *args, **kwargs) is True: + image_path = kwargs['image_path'] + destination_disk = kwargs['destination_disk'] + if capabilitity == 'eACommander': + cmd = [self.EACOMMANDER_CMD, + '--serialno', destination_disk, + '--flash', image_path, + '--resettype', '2', '--reset'] + result = self.run_command(cmd) + elif capabilitity == 'eACommander-usb': + cmd = [self.EACOMMANDER_CMD, + '--usb', destination_disk, + '--flash', image_path] + result = self.run_command(cmd) + return result + + +def load_plugin(): + """ Returns plugin available in this module + """ + return HostTestPluginCopyMethod_Silabs() diff --git a/workspace_tools/host_tests/host_tests_plugins/module_reset_mbed.py b/workspace_tools/host_tests/host_tests_plugins/module_reset_mbed.py new file mode 100644 index 0000000000..0390d84ba6 --- /dev/null +++ b/workspace_tools/host_tests/host_tests_plugins/module_reset_mbed.py @@ -0,0 +1,72 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from host_test_plugins import HostTestPluginBase + + +class HostTestPluginResetMethod_Mbed(HostTestPluginBase): + + def safe_sendBreak(self, serial): + """ Wraps serial.sendBreak() to avoid serial::serialposix.py exception on Linux + Traceback (most recent call last): + File "make.py", line 189, in + serial.sendBreak() + File "/usr/lib/python2.7/dist-packages/serial/serialposix.py", line 511, in sendBreak + termios.tcsendbreak(self.fd, int(duration/0.25)) + error: (32, 'Broken pipe') + """ + result = True + try: + serial.sendBreak() + except: + # In linux a termios.error is raised in sendBreak and in setBreak. + # The following setBreak() is needed to release the reset signal on the target mcu. + try: + serial.setBreak(False) + except: + result = False + return result + + # Plugin interface + name = 'HostTestPluginResetMethod_Mbed' + type = 'ResetMethod' + stable = True + capabilities = ['default'] + required_parameters = ['serial'] + + def setup(self, *args, **kwargs): + """ Configure plugin, this function should be called before plugin execute() method is used. + """ + return True + + def execute(self, capabilitity, *args, **kwargs): + """ Executes capability by name. + Each capability may directly just call some command line + program or execute building pythonic function + """ + result = False + if self.check_parameters(capabilitity, *args, **kwargs) is True: + if capabilitity == 'default': + serial = kwargs['serial'] + result = self.safe_sendBreak(serial) + return result + + +def load_plugin(): + """ Returns plugin available in this module + """ + return HostTestPluginResetMethod_Mbed() diff --git a/workspace_tools/host_tests/host_tests_plugins/module_reset_mps2.py b/workspace_tools/host_tests/host_tests_plugins/module_reset_mps2.py new file mode 100644 index 0000000000..22938090bb --- /dev/null +++ b/workspace_tools/host_tests/host_tests_plugins/module_reset_mps2.py @@ -0,0 +1,74 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import os +from host_test_plugins import HostTestPluginBase + +# Note: This plugin is not fully functional, needs improvements + +class HostTestPluginResetMethod_MPS2(HostTestPluginBase): + """ Plugin used to reset ARM_MPS2 platform + Supports: + reboot.txt - startup from standby state, reboots when in run mode. + shutdown.txt - shutdown from run mode. + reset.txt - reset FPGA during run mode. + """ + def touch_file(self, path): + """ Touch file and set timestamp to items + """ + with open(path, 'a'): + os.utime(path, None) + + # Plugin interface + name = 'HostTestPluginResetMethod_MPS2' + type = 'ResetMethod' + capabilities = ['reboot.txt', 'shutdown.txt', 'reset.txt'] + required_parameters = ['disk'] + + def setup(self, *args, **kwargs): + """ Prepare / configure plugin to work. + This method can receive plugin specific parameters by kwargs and + ignore other parameters which may affect other plugins. + """ + return True + + def execute(self, capabilitity, *args, **kwargs): + """ Executes capability by name. + Each capability may directly just call some command line + program or execute building pythonic function + """ + result = False + if self.check_parameters(capabilitity, *args, **kwargs) is True: + + if capabilitity == 'reboot.txt': + # TODO: Implement touch file for reboot + pass + + elif capabilitity == 'shutdown.txt': + # TODO: Implement touch file for shutdown + pass + + elif capabilitity == 'reset.txt': + # TODO: Implement touch file for reset + pass + + return result + +def load_plugin(): + """ Returns plugin available in this module + """ + return HostTestPluginResetMethod_MPS2() diff --git a/workspace_tools/host_tests/host_tests_plugins/module_reset_silabs.py b/workspace_tools/host_tests/host_tests_plugins/module_reset_silabs.py new file mode 100644 index 0000000000..2c05cb21c3 --- /dev/null +++ b/workspace_tools/host_tests/host_tests_plugins/module_reset_silabs.py @@ -0,0 +1,66 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from host_test_plugins import HostTestPluginBase + + +class HostTestPluginResetMethod_SiLabs(HostTestPluginBase): + + # Plugin interface + name = 'HostTestPluginResetMethod_SiLabs' + type = 'ResetMethod' + stable = True + capabilities = ['eACommander', 'eACommander-usb'] + required_parameters = ['disk'] + + def setup(self, *args, **kwargs): + """ Configure plugin, this function should be called before plugin execute() method is used. + """ + # Note you need to have eACommander.exe on your system path! + self.EACOMMANDER_CMD = 'eACommander.exe' + return True + + def execute(self, capabilitity, *args, **kwargs): + """ Executes capability by name. + Each capability may directly just call some command line + program or execute building pythonic function + """ + result = False + if self.check_parameters(capabilitity, *args, **kwargs) is True: + disk = kwargs['disk'].rstrip('/\\') + + if capabilitity == 'eACommander': + # For this copy method 'disk' will be 'serialno' for eACommander command line parameters + # Note: Commands are executed in the order they are specified on the command line + cmd = [self.EACOMMANDER_CMD, + '--serialno', disk, + '--resettype', '2', '--reset',] + result = self.run_command(cmd) + elif capabilitity == 'eACommander-usb': + # For this copy method 'disk' will be 'usb address' for eACommander command line parameters + # Note: Commands are executed in the order they are specified on the command line + cmd = [self.EACOMMANDER_CMD, + '--usb', disk, + '--resettype', '2', '--reset',] + result = self.run_command(cmd) + return result + + +def load_plugin(): + """ Returns plugin available in this module + """ + return HostTestPluginResetMethod_SiLabs() diff --git a/workspace_tools/settings.py b/workspace_tools/settings.py index 1195e9c26a..4a3266e058 100644 --- a/workspace_tools/settings.py +++ b/workspace_tools/settings.py @@ -76,9 +76,6 @@ GOANNA_PATH = "c:/Program Files (x86)/RedLizards/Goanna Central 3.2.3/bin" CPPCHECK_CMD = ["cppcheck", "--enable=all"] CPPCHECK_MSG_FORMAT = ["--template=[{severity}] {file}@{line}: {id}:{message}"] -# SiliconLabs energyAware Commander 2.84 path -EACOMMANDER_CMD = 'c:/SiliconLabs/SimplicityStudio/v2/commander/eACommander.exe' - BUILD_OPTIONS = [] # mbed.org username diff --git a/workspace_tools/test_api.py b/workspace_tools/test_api.py index 573d6148b2..73e41db8a0 100644 --- a/workspace_tools/test_api.py +++ b/workspace_tools/test_api.py @@ -32,10 +32,9 @@ from prettytable import PrettyTable from time import sleep, time from Queue import Queue, Empty -from shutil import copy from os.path import join, exists, basename from threading import Thread -from subprocess import Popen, PIPE, call +from subprocess import Popen, PIPE # Imports related to mbed build api from workspace_tools.tests import TESTS @@ -46,7 +45,6 @@ from workspace_tools.utils import ToolException from workspace_tools.utils import construct_enum from workspace_tools.targets import TARGET_MAP from workspace_tools.test_db import BaseDBAccess -from workspace_tools.settings import EACOMMANDER_CMD from workspace_tools.build_api import build_project, build_mbed_libs, build_lib from workspace_tools.build_api import get_target_supported_toolchains from workspace_tools.libraries import LIBRARIES, LIBRARY_MAP @@ -54,6 +52,9 @@ from workspace_tools.toolchains import TOOLCHAIN_BIN_PATH from workspace_tools.test_exporters import ReportExporter, ResultExporterType +import workspace_tools.host_tests.host_tests_plugins as host_tests_plugins + + class ProcessObserver(Thread): def __init__(self, proc): Thread.__init__(self) @@ -591,22 +592,6 @@ class SingleTestRunner(object): result = self.TEST_LOOPS_DICT[test_id] return result - def file_store_firefox(self, file_path, dest_disk): - try: - from selenium import webdriver - except ImportError, e: - print "Error: firefox copy method requires selenium library. %s"% e - exit(-1) - profile = webdriver.FirefoxProfile() - profile.set_preference('browser.download.folderList', 2) # custom location - profile.set_preference('browser.download.manager.showWhenStarting', False) - profile.set_preference('browser.download.dir', dest_disk) - profile.set_preference('browser.helperApps.neverAsk.saveToDisk', 'application/octet-stream') - # Launch browser with profile and get file - browser = webdriver.Firefox(profile) - browser.get(file_path) - browser.close() - def image_copy_method_selector(self, target_name, image_path, disk, copy_method, images_config=None, image_dest=None, verbose=False): """ Function copied image file and fiddles with image configuration files in needed. @@ -615,82 +600,20 @@ class SingleTestRunner(object): """ image_dest = image_dest if image_dest is not None else '' _copy_res, _err_msg, _copy_method = self.file_copy_method_selector(image_path, disk, copy_method, image_dest=image_dest, verbose=verbose) - - if images_config is not None: - # For different targets additional configuration file has to be changed - # Here we select target and proper function to handle configuration change - if target_name == 'ARM_MPS2': - images_cfg_path = images_config - image0file_path = os.path.join(disk, image_dest, basename(image_path)) - mps2_set_board_image_file(disk, images_cfg_path, image0file_path) return _copy_res, _err_msg, _copy_method def file_copy_method_selector(self, image_path, disk, copy_method, image_dest='', verbose=False): """ Copy file depending on method you want to use. Handles exception and return code from shell copy commands. """ - result = True - resutl_msg = "" - if copy_method == 'cp' or copy_method == 'copy' or copy_method == 'xcopy': - source_path = image_path.encode('ascii', 'ignore') - image_base_name = basename(image_path).encode('ascii', 'ignore') - destination_path = os.path.join(disk.encode('ascii', 'ignore'), image_dest, image_base_name) - cmd = [copy_method, source_path, destination_path] - try: - ret = call(cmd, shell=True) - if ret: - resutl_msg = "Return code: %d. Command: "% (ret + " ".join(cmd)) - result = False - except Exception, e: - resutl_msg = e - result = False - elif copy_method == 'firefox': - try: - source_path = image_path.encode('ascii', 'ignore') - destination_path = os.path.join(disk.encode('ascii', 'ignore'), image_dest) - self.file_store_firefox(source_path, destination_path) - except Exception, e: - resutl_msg = e - result = False - elif copy_method == 'eACommander': - # For this copy method 'disk' will be 'serialno' for eACommander command line parameters - # Note: Commands are executed in the order they are specified on the command line - cmd = [EACOMMANDER_CMD, - '--serialno', disk.rstrip('/\\'), - '--flash', image_path.encode('ascii', 'ignore'), - '--resettype', '2', '--reset'] - try: - ret = call(cmd, shell=True) - if ret: - resutl_msg = "Return code: %d. Command: "% ret + " ".join(cmd) - result = False - except Exception, e: - resutl_msg = e - result = False - elif copy_method == 'eACommander-usb': - # For this copy method 'disk' will be 'usb address' for eACommander command line parameters - # Note: Commands are executed in the order they are specified on the command line - cmd = [EACOMMANDER_CMD, - '--usb', disk.rstrip('/\\'), - '--flash', image_path.encode('ascii', 'ignore')] - try: - ret = call(cmd, shell=True) - if ret: - resutl_msg = "Return code: %d. Command: "% ret + " ".join(cmd) - result = False - except Exception, e: - resutl_msg = e - result = False + result = False + resutl_msg = '' # TODO: pass result_msg from plugin to test suite + if copy_method is not None: + # image_path - Where is binary with target's firmware + result = host_tests_plugins.call_plugin('CopyMethod', copy_method, image_path=image_path, destination_disk=disk) else: - copy_method = "shutils.copy()" - # Default python method - try: - if not disk.endswith('/') and not disk.endswith('\\'): - disk += '/' - copy(image_path, disk) - except Exception, e: - resutl_msg = e - result = False + copy_method = 'default' + result = host_tests_plugins.call_plugin('CopyMethod', copy_method, image_path=image_path, destination_disk=disk) return result, resutl_msg, copy_method def delete_file(self, file_path): @@ -1324,64 +1247,6 @@ def singletest_in_cli_mode(single_test): report_exporter.report_to_file(test_summary_ext, single_test.opts_report_junit_file_name, test_suite_properties=test_suite_properties_ext) -def mps2_set_board_image_file(disk, images_cfg_path, image0file_path, image_name='images.txt'): - """ This function will alter image cfg file. - Main goal of this function is to change number of images to 1, comment all - existing image entries and append at the end of file new entry with test path. - @return True when all steps succeed. - """ - MBED_SDK_TEST_STAMP = 'test suite entry' - image_path = os.path.join(disk, images_cfg_path, image_name) - new_file_lines = [] # New configuration file lines (entries) - - # Check each line of the image configuration file - try: - with open(image_path, 'r') as file: - for line in file: - if re.search('^TOTALIMAGES', line): - # Check number of total images, should be 1 - new_file_lines.append(re.sub('^TOTALIMAGES:[\t ]*[\d]+', 'TOTALIMAGES: 1', line)) - pass - elif re.search('; - %s[\n\r]*$'% MBED_SDK_TEST_STAMP, line): - # Look for test suite entries and remove them - pass # Omit all test suite entries - elif re.search('^IMAGE[\d]+FILE', line): - # Check all image entries and mark the ';' - new_file_lines.append(';' + line) # Comment non test suite lines - else: - # Append line to new file - new_file_lines.append(line) - except IOError as e: - return False - - # Add new image entry with proper commented stamp - new_file_lines.append('IMAGE0FILE: %s ; - %s\r\n'% (image0file_path, MBED_SDK_TEST_STAMP)) - - # Write all lines to file - try: - with open(image_path, 'w') as file: - for line in new_file_lines: - file.write(line), - except IOError: - return False - - return True - - -def mps2_select_core(disk, mobo_config_name=""): - """ Function selects actual core - """ - # TODO: implement core selection - pass - - -def mps2_switch_usb_auto_mounting_after_restart(disk, usb_config_name=""): - """ Function alters configuration to allow USB MSD to be mounted after restarts - """ - # TODO: implement USB MSD restart detection - pass - - class TestLogger(): """ Super-class for logging and printing ongoing events for test suite pass """