CIAssignExampleTest.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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. """
  15. Command line tool to assign example tests to CI test jobs.
  16. """
  17. # TODO: Need to handle running examples on different chips
  18. import os
  19. import sys
  20. import re
  21. import argparse
  22. import yaml
  23. test_fw_path = os.getenv("TEST_FW_PATH")
  24. if test_fw_path:
  25. sys.path.insert(0, test_fw_path)
  26. from Utility import CaseConfig, SearchCases, GitlabCIJob
  27. class Group(object):
  28. MAX_EXECUTION_TIME = 30
  29. MAX_CASE = 15
  30. SORT_KEYS = ["env_tag"]
  31. def __init__(self, case):
  32. self.execution_time = 0
  33. self.case_list = [case]
  34. self.filters = dict(zip(self.SORT_KEYS, [case.case_info[x] for x in self.SORT_KEYS]))
  35. def accept_new_case(self):
  36. """
  37. check if allowed to add any case to this group
  38. :return: True or False
  39. """
  40. max_time = (sum([x.case_info["execution_time"] for x in self.case_list]) < self.MAX_EXECUTION_TIME)
  41. max_case = (len(self.case_list) < self.MAX_CASE)
  42. return max_time and max_case
  43. def add_case(self, case):
  44. """
  45. add case to current group
  46. :param case: test case
  47. :return: True if add succeed, else False
  48. """
  49. added = False
  50. if self.accept_new_case():
  51. for key in self.filters:
  52. if case.case_info[key] != self.filters[key]:
  53. break
  54. else:
  55. self.case_list.append(case)
  56. added = True
  57. return added
  58. def output(self):
  59. """
  60. output data for job configs
  61. :return: {"Filter": case filter, "CaseConfig": list of case configs for cases in this group}
  62. """
  63. output_data = {
  64. "Filter": self.filters,
  65. "CaseConfig": [{"name": x.case_info["name"]} for x in self.case_list],
  66. }
  67. return output_data
  68. class AssignTest(object):
  69. """
  70. Auto assign tests to CI jobs.
  71. :param test_case: path of test case file(s)
  72. :param ci_config_file: path of ``.gitlab-ci.yml``
  73. """
  74. CI_TEST_JOB_PATTERN = re.compile(r"^example_test_.+")
  75. def __init__(self, test_case, ci_config_file):
  76. self.test_cases = self._search_cases(test_case)
  77. self.jobs = self._parse_gitlab_ci_config(ci_config_file)
  78. def _parse_gitlab_ci_config(self, ci_config_file):
  79. with open(ci_config_file, "r") as f:
  80. ci_config = yaml.load(f)
  81. job_list = list()
  82. for job_name in ci_config:
  83. if self.CI_TEST_JOB_PATTERN.search(job_name) is not None:
  84. job_list.append(GitlabCIJob.Job(ci_config[job_name], job_name))
  85. return job_list
  86. @staticmethod
  87. def _search_cases(test_case, case_filter=None):
  88. """
  89. :param test_case: path contains test case folder
  90. :param case_filter: filter for test cases
  91. :return: filtered test case list
  92. """
  93. test_methods = SearchCases.Search.search_test_cases(test_case)
  94. return CaseConfig.filter_test_cases(test_methods, case_filter if case_filter else dict())
  95. def _group_cases(self):
  96. """
  97. separate all cases into groups according group rules. each group will be executed by one CI job.
  98. :return: test case groups.
  99. """
  100. groups = []
  101. for case in self.test_cases:
  102. for group in groups:
  103. # add to current group
  104. if group.add_case(case):
  105. break
  106. else:
  107. # create new group
  108. groups.append(Group(case))
  109. return groups
  110. def assign_cases(self):
  111. """
  112. separate test cases to groups and assign test cases to CI jobs.
  113. :raise AssertError: if failed to assign any case to CI job.
  114. :return: None
  115. """
  116. failed_to_assign = []
  117. test_groups = self._group_cases()
  118. for group in test_groups:
  119. for job in self.jobs:
  120. if job.match_group(group):
  121. job.assign_group(group)
  122. break
  123. else:
  124. failed_to_assign.append(group)
  125. assert not failed_to_assign
  126. def output_configs(self, output_path):
  127. """
  128. :param output_path: path to output config files for each CI job
  129. :return: None
  130. """
  131. if not os.path.exists(output_path):
  132. os.makedirs(output_path)
  133. for job in self.jobs:
  134. job.output_config(output_path)
  135. if __name__ == '__main__':
  136. parser = argparse.ArgumentParser()
  137. parser.add_argument("test_case",
  138. help="test case folder or file")
  139. parser.add_argument("ci_config_file",
  140. help="gitlab ci config file")
  141. parser.add_argument("output_path",
  142. help="output path of config files")
  143. args = parser.parse_args()
  144. assign_test = AssignTest(args.test_case, args.ci_config_file)
  145. assign_test.assign_cases()
  146. assign_test.output_configs(args.output_path)