blob: 97fee2b4b7e1efb103a302d6c981085331c00286 [file] [log] [blame]
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +01001import os
2import subprocess
3import yaml
4from argparse import ArgumentParser
Milosz Wasilewskib5045412016-12-07 11:27:10 +00005from csv import DictWriter
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +01006from jinja2 import Environment, FileSystemLoader
7
8
9def render(obj, template="testplan.html", name=None):
10 if name is None:
11 name = template
Milosz Wasilewskif761ab92018-02-13 11:21:18 +000012 templates_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")
13 _env = Environment(loader=FileSystemLoader(templates_dir))
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +010014 _template = _env.get_template(template)
15 _obj = _template.render(obj=obj)
16 with open("{}".format(name), "wb") as _file:
17 _file.write(_obj.encode('utf-8'))
18
19
20# get list of repositories and cache them
21def repository_list(testplan):
22 repositories = set()
Milosz Wasilewskid5e1dd92018-02-12 12:08:44 +000023 tp_version = testplan['metadata']['format']
24 if tp_version == "Linaro Test Plan v2":
25 if 'manual' in testplan['tests'].keys() and testplan['tests']['manual'] is not None:
26 for test in testplan['tests']['manual']:
27 repositories.add(test['repository'])
28
29 if 'automated' in testplan['tests'].keys() and testplan['tests']['automated'] is not None:
30 for test in testplan['tests']['automated']:
31 repositories.add(test['repository'])
32 if tp_version == "Linaro Test Plan v1":
33 for req in testplan['requirements']:
34 if 'tests' in req.keys() and req['tests'] is not None:
35 if 'manual' in req['tests'].keys() and req['tests']['manual'] is not None:
36 for test in req['tests']['manual']:
37 repositories.add(test['repository'])
38 if 'automated' in req['tests'].keys() and req['tests']['automated'] is not None:
39 for test in req['tests']['automated']:
40 repositories.add(test['repository'])
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +010041 return repositories
42
43
44def clone_repository(repository_url, base_path, ignore=False):
45 path_suffix = repository_url.rsplit("/", 1)[1]
46 if path_suffix.endswith(".git"):
47 path_suffix = path_suffix[:-4]
48
Milosz Wasilewskib5045412016-12-07 11:27:10 +000049 path = os.path.abspath(os.path.join(base_path, path_suffix))
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +010050 if os.path.exists(path) and ignore:
51 return(repository_url, path)
52 # git clone repository_url
53 subprocess.call(['git', 'clone', repository_url, path])
54 # return tuple (repository_url, system_path)
55 return (repository_url, path)
56
57
Milosz Wasilewskib5045412016-12-07 11:27:10 +000058def test_exists(test, repositories, args):
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +010059 test_file_path = os.path.join(
60 repositories[test['repository']],
61 test['path']
62 )
63 current_dir = os.getcwd()
64 print current_dir
65 os.chdir(repositories[test['repository']])
66 if 'revision' in test.keys():
67 subprocess.call(['git', 'checkout', test['revision']])
68 else:
69 # if no revision is specified, use current HEAD
70 output = subprocess.check_output(['git', 'rev-parse', 'HEAD'])
71 test['revision'] = output
72
73 if not os.path.exists(test_file_path) or not os.path.isfile(test_file_path):
74 test['missing'] = True
75 os.chdir(current_dir)
76 return not test['missing']
77 test['missing'] = False
78 # open the file and render the test
79 subprocess.call(['git', 'checkout', 'master'])
80 print current_dir
81 os.chdir(current_dir)
82 print os.getcwd()
83 test_file = open(test_file_path, "r")
84 test_yaml = yaml.load(test_file.read())
85 params_string = ""
86 if 'parameters' in test.keys():
87 params_string = "_".join(["{0}-{1}".format(param_name, param_value).replace("/", "").replace(" ", "") for param_name, param_value in test['parameters'].iteritems()])
88 test_yaml['params'].update(test['parameters'])
Milosz Wasilewski807c0ea2018-02-13 19:14:01 +000089 if args.single_output:
90 # update parameters in test
91 if 'params' in test_yaml.keys():
92 for param_name, param_value in test_yaml['params'].iteritems():
93 if param_name not in test['parameters'].keys():
94 test['parameters'].update({param_name: param_value})
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +010095 print params_string
96 test_name = "{0}_{1}.html".format(test_yaml['metadata']['name'], params_string)
Milosz Wasilewski807c0ea2018-02-13 19:14:01 +000097 if not args.single_output:
98 test['filename'] = test_name
Milosz Wasilewskib5045412016-12-07 11:27:10 +000099 test_path = os.path.join(os.path.abspath(args.output), test_name)
Milosz Wasilewski807c0ea2018-02-13 19:14:01 +0000100 if args.single_output:
101 # update test plan object
102 test.update(test_yaml['run'])
103 else:
104 render(test_yaml, template="test.html", name=test_path)
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100105 return not test['missing']
106
107
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000108def add_csv_row(requirement, test, args, manual=False):
109 fieldnames = [
110 "req_name",
111 "req_owner",
112 "req_category",
113 "path",
114 "repository",
115 "revision",
116 "parameters",
117 "mandatory",
118 "kind",
119 ]
120 csv_file_path = os.path.join(os.path.abspath(args.output), args.csv_name)
121 has_header = False
122 if os.path.isfile(csv_file_path):
123 has_header = True
124 with open(csv_file_path, "ab+") as csv_file:
125 csvdict = DictWriter(csv_file, fieldnames=fieldnames)
126 if not has_header:
127 csvdict.writeheader()
128 csvdict.writerow(
129 {
130 "req_name": requirement.get('name'),
131 "req_owner": requirement.get('owner'),
132 "req_category": requirement.get('category'),
133 "path": test.get('path'),
134 "repository": test.get('repository'),
135 "revision": test.get('revision'),
136 "parameters": test.get('parameters'),
137 "mandatory": test.get('mandatory'),
138 "kind": "manual" if manual else "automated",
139 }
140 )
141
142
143def check_coverage(requirement, repositories, args):
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100144 requirement['covered'] = False
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000145 if 'tests' not in requirement.keys() or requirement['tests'] is None:
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100146 return
147 if 'manual' in requirement['tests'].keys() and requirement['tests']['manual'] is not None:
148 for test in requirement['tests']['manual']:
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000149 if test_exists(test, repositories, args):
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100150 requirement['covered'] = True
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000151 if args.csv_name:
152 add_csv_row(requirement, test, args, True)
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100153 if 'automated' in requirement['tests'].keys() and requirement['tests']['automated'] is not None:
154 for test in requirement['tests']['automated']:
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000155 if test_exists(test, repositories, args):
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100156 requirement['covered'] = True
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000157 if args.csv_name:
158 add_csv_row(requirement, test, args)
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100159
160
161def main():
162 parser = ArgumentParser()
163 parser.add_argument("-f",
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000164 "--file",
165 dest="testplan_list",
166 required=True,
167 nargs="+",
168 help="Test plan file to be used")
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100169 parser.add_argument("-r",
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000170 "--repositories",
171 dest="repository_path",
172 default="repositories",
173 help="Test plan file to be used")
174 parser.add_argument("-o",
175 "--output",
176 dest="output",
177 default="output",
178 help="Destination directory for generated files")
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100179 parser.add_argument("-i",
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000180 "--ignore-clone",
181 dest="ignore_clone",
182 action="store_true",
183 default=False,
184 help="Ignore cloning repositories and use previously cloned")
Milosz Wasilewski807c0ea2018-02-13 19:14:01 +0000185 parser.add_argument("-s",
186 "--single-file-output",
187 dest="single_output",
188 action="store_true",
189 default=False,
190 help="""Render test plan into single HTML file. This option ignores
191 any metadata that is available in test cases""")
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000192 parser.add_argument("-c",
193 "--csv",
194 dest="csv_name",
195 required=False,
196 help="Name of CSV to store overall list of requirements and test. If name is absent, the file will not be generated")
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100197
198 args = parser.parse_args()
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000199 if not os.path.exists(os.path.abspath(args.output)):
200 os.makedirs(os.path.abspath(args.output), 0755)
201 for testplan in args.testplan_list:
202 if os.path.exists(testplan) and os.path.isfile(testplan):
203 testplan_file = open(testplan, "r")
204 tp_obj = yaml.load(testplan_file.read())
205 repo_list = repository_list(tp_obj)
206 repositories = {}
207 for repo in repo_list:
208 repo_url, repo_path = clone_repository(repo, args.repository_path, args.ignore_clone)
209 repositories.update({repo_url: repo_path})
210 # ToDo: check test plan structure
Milosz Wasilewskid5e1dd92018-02-12 12:08:44 +0000211
212 tp_version = tp_obj['metadata']['format']
213 if tp_version == "Linaro Test Plan v1":
214 for requirement in tp_obj['requirements']:
215 check_coverage(requirement, repositories, args)
216 if tp_version == "Linaro Test Plan v2":
217 if 'manual' in tp_obj['tests'].keys() and tp_obj['tests']['manual'] is not None:
218 for test in tp_obj['tests']['manual']:
219 test_exists(test, repositories, args)
220 if 'automated' in tp_obj['tests'].keys() and tp_obj['tests']['automated'] is not None:
221 for test in tp_obj['tests']['automated']:
222 test_exists(test, repositories, args)
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000223 tp_name = tp_obj['metadata']['name'] + ".html"
224 tp_file_name = os.path.join(os.path.abspath(args.output), tp_name)
Milosz Wasilewskid5e1dd92018-02-12 12:08:44 +0000225 if tp_version == "Linaro Test Plan v1":
226 render(tp_obj, name=tp_file_name)
227 if tp_version == "Linaro Test Plan v2":
228 render(tp_obj, name=tp_file_name, template="testplan_v2.html")
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000229 testplan_file.close()
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100230# go through requiremets and for each test:
231# - if file exists render test as separate html file
232# - if file is missing, indicate missing test (red)
233# render test plan with links to test files
234# add option to render as single file (for pdf generation)
235
236if __name__ == "__main__":
237 main()