Env.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. # Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http:#www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """ Test Env, manages DUT, App and EnvConfig, interface for test cases to access these components """
  15. import functools
  16. import os
  17. import threading
  18. import traceback
  19. import netifaces
  20. from . import EnvConfig
  21. def _synced(func):
  22. @functools.wraps(func)
  23. def decorator(self, *args, **kwargs):
  24. with self.lock:
  25. ret = func(self, *args, **kwargs)
  26. return ret
  27. decorator.__doc__ = func.__doc__
  28. return decorator
  29. class Env(object):
  30. """
  31. test env, manages DUTs and env configs.
  32. :keyword app: class for default application
  33. :keyword dut: class for default DUT
  34. :keyword env_tag: test env tag, used to select configs from env config file
  35. :keyword env_config_file: test env config file path
  36. :keyword test_name: test suite name, used when generate log folder name
  37. """
  38. CURRENT_LOG_FOLDER = ''
  39. def __init__(self,
  40. app=None,
  41. dut=None,
  42. env_tag=None,
  43. env_config_file=None,
  44. test_suite_name=None,
  45. **kwargs):
  46. self.app_cls = app
  47. self.default_dut_cls = dut
  48. self.config = EnvConfig.Config(env_config_file, env_tag)
  49. self.log_path = self.app_cls.get_log_folder(test_suite_name)
  50. if not os.path.exists(self.log_path):
  51. os.makedirs(self.log_path)
  52. Env.CURRENT_LOG_FOLDER = self.log_path
  53. self.allocated_duts = dict()
  54. self.lock = threading.RLock()
  55. @_synced
  56. def get_dut(self, dut_name, app_path, dut_class=None, app_class=None, app_config_name=None, **dut_init_args):
  57. """
  58. get_dut(dut_name, app_path, dut_class=None, app_class=None)
  59. :param dut_name: user defined name for DUT
  60. :param app_path: application path, app instance will use this path to process application info
  61. :param dut_class: dut class, if not specified will use default dut class of env
  62. :param app_class: app class, if not specified will use default app of env
  63. :param app_config_name: app build config
  64. :keyword dut_init_args: extra kwargs used when creating DUT instance
  65. :return: dut instance
  66. """
  67. if dut_name in self.allocated_duts:
  68. dut = self.allocated_duts[dut_name]['dut']
  69. else:
  70. if dut_class is None:
  71. dut_class = self.default_dut_cls
  72. if app_class is None:
  73. app_class = self.app_cls
  74. app_target = dut_class.TARGET
  75. detected_target = None
  76. try:
  77. port = self.config.get_variable(dut_name)
  78. if not app_target:
  79. result, detected_target = dut_class.confirm_dut(port)
  80. except ValueError:
  81. # try to auto detect ports
  82. allocated_ports = [self.allocated_duts[x]['port'] for x in self.allocated_duts]
  83. available_ports = dut_class.list_available_ports()
  84. for port in available_ports:
  85. if port not in allocated_ports:
  86. result, detected_target = dut_class.confirm_dut(port)
  87. if result:
  88. break
  89. else:
  90. port = None
  91. if not app_target:
  92. app_target = detected_target
  93. if not app_target:
  94. raise ValueError("DUT class doesn't specify the target, and autodetection failed")
  95. app_inst = app_class(app_path, app_config_name, app_target)
  96. if port:
  97. try:
  98. dut_config = self.get_variable(dut_name + '_port_config')
  99. except ValueError:
  100. dut_config = dict()
  101. dut_config.update(dut_init_args)
  102. dut = dut_class(dut_name, port,
  103. os.path.join(self.log_path, dut_name + '.log'),
  104. app_inst,
  105. **dut_config)
  106. self.allocated_duts[dut_name] = {'port': port, 'dut': dut}
  107. else:
  108. raise ValueError('Failed to get DUT')
  109. return dut
  110. @_synced
  111. def close_dut(self, dut_name):
  112. """
  113. close_dut(dut_name)
  114. close one DUT by name if DUT name is valid (the name used by ``get_dut``). otherwise will do nothing.
  115. :param dut_name: user defined name for DUT
  116. :return: None
  117. """
  118. try:
  119. dut = self.allocated_duts.pop(dut_name)['dut']
  120. dut.close()
  121. except KeyError:
  122. pass
  123. @_synced
  124. def get_variable(self, variable_name):
  125. """
  126. get_variable(variable_name)
  127. get variable from config file. If failed then try to auto-detected it.
  128. :param variable_name: name of the variable
  129. :return: value of variable if successfully found. otherwise None.
  130. """
  131. return self.config.get_variable(variable_name)
  132. PROTO_MAP = {
  133. 'ipv4': netifaces.AF_INET,
  134. 'ipv6': netifaces.AF_INET6,
  135. 'mac': netifaces.AF_LINK,
  136. }
  137. @_synced
  138. def get_pc_nic_info(self, nic_name='pc_nic', proto='ipv4'):
  139. """
  140. get_pc_nic_info(nic_name="pc_nic")
  141. try to get info of a specified NIC and protocol.
  142. :param nic_name: pc nic name. allows passing variable name, nic name value.
  143. :param proto: "ipv4", "ipv6" or "mac"
  144. :return: a dict of nic info if successfully found. otherwise None.
  145. nic info keys could be different for different protocols.
  146. key "addr" is available for both mac, ipv4 and ipv6 pic info.
  147. """
  148. interfaces = netifaces.interfaces()
  149. if nic_name in interfaces:
  150. # the name is in the interface list, we regard it as NIC name
  151. if_addr = netifaces.ifaddresses(nic_name)
  152. else:
  153. # it's not in interface name list, we assume it's variable name
  154. _nic_name = self.get_variable(nic_name)
  155. if_addr = netifaces.ifaddresses(_nic_name)
  156. return if_addr[self.PROTO_MAP[proto]][0]
  157. @_synced
  158. def close(self, dut_debug=False):
  159. """
  160. close()
  161. close all DUTs of the Env.
  162. :param dut_debug: if dut_debug is True, then print all dut expect failures before close it
  163. :return: exceptions during close DUT
  164. """
  165. dut_close_errors = []
  166. for dut_name in self.allocated_duts:
  167. dut = self.allocated_duts[dut_name]['dut']
  168. try:
  169. if dut_debug:
  170. dut.print_debug_info()
  171. dut.close()
  172. except Exception as e:
  173. traceback.print_exc()
  174. dut_close_errors.append(e)
  175. self.allocated_duts = dict()
  176. return dut_close_errors