blob: bfd427f85f7c28a21b90910367511a2300780434 [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
12 _env = Environment(loader=FileSystemLoader('templates'))
13 _template = _env.get_template(template)
14 _obj = _template.render(obj=obj)
15 with open("{}".format(name), "wb") as _file:
16 _file.write(_obj.encode('utf-8'))
17
18
19# get list of repositories and cache them
20def repository_list(testplan):
21 repositories = set()
22 for req in testplan['requirements']:
23 if 'tests' in req.keys() and req['tests'] is not None:
24 if 'manual' in req['tests'].keys() and req['tests']['manual'] is not None:
25 for test in req['tests']['manual']:
26 repositories.add(test['repository'])
27 if 'automated' in req['tests'].keys() and req['tests']['automated'] is not None:
28 for test in req['tests']['automated']:
29 repositories.add(test['repository'])
30 return repositories
31
32
33def clone_repository(repository_url, base_path, ignore=False):
34 path_suffix = repository_url.rsplit("/", 1)[1]
35 if path_suffix.endswith(".git"):
36 path_suffix = path_suffix[:-4]
37
Milosz Wasilewskib5045412016-12-07 11:27:10 +000038 path = os.path.abspath(os.path.join(base_path, path_suffix))
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +010039 if os.path.exists(path) and ignore:
40 return(repository_url, path)
41 # git clone repository_url
42 subprocess.call(['git', 'clone', repository_url, path])
43 # return tuple (repository_url, system_path)
44 return (repository_url, path)
45
46
Milosz Wasilewskib5045412016-12-07 11:27:10 +000047def test_exists(test, repositories, args):
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +010048 test_file_path = os.path.join(
49 repositories[test['repository']],
50 test['path']
51 )
52 current_dir = os.getcwd()
53 print current_dir
54 os.chdir(repositories[test['repository']])
55 if 'revision' in test.keys():
56 subprocess.call(['git', 'checkout', test['revision']])
57 else:
58 # if no revision is specified, use current HEAD
59 output = subprocess.check_output(['git', 'rev-parse', 'HEAD'])
60 test['revision'] = output
61
62 if not os.path.exists(test_file_path) or not os.path.isfile(test_file_path):
63 test['missing'] = True
64 os.chdir(current_dir)
65 return not test['missing']
66 test['missing'] = False
67 # open the file and render the test
68 subprocess.call(['git', 'checkout', 'master'])
69 print current_dir
70 os.chdir(current_dir)
71 print os.getcwd()
72 test_file = open(test_file_path, "r")
73 test_yaml = yaml.load(test_file.read())
74 params_string = ""
75 if 'parameters' in test.keys():
76 params_string = "_".join(["{0}-{1}".format(param_name, param_value).replace("/", "").replace(" ", "") for param_name, param_value in test['parameters'].iteritems()])
77 test_yaml['params'].update(test['parameters'])
78 print params_string
79 test_name = "{0}_{1}.html".format(test_yaml['metadata']['name'], params_string)
80 test['filename'] = test_name
Milosz Wasilewskib5045412016-12-07 11:27:10 +000081 test_path = os.path.join(os.path.abspath(args.output), test_name)
82 render(test_yaml, template="test.html", name=test_path)
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +010083 return not test['missing']
84
85
Milosz Wasilewskib5045412016-12-07 11:27:10 +000086def add_csv_row(requirement, test, args, manual=False):
87 fieldnames = [
88 "req_name",
89 "req_owner",
90 "req_category",
91 "path",
92 "repository",
93 "revision",
94 "parameters",
95 "mandatory",
96 "kind",
97 ]
98 csv_file_path = os.path.join(os.path.abspath(args.output), args.csv_name)
99 has_header = False
100 if os.path.isfile(csv_file_path):
101 has_header = True
102 with open(csv_file_path, "ab+") as csv_file:
103 csvdict = DictWriter(csv_file, fieldnames=fieldnames)
104 if not has_header:
105 csvdict.writeheader()
106 csvdict.writerow(
107 {
108 "req_name": requirement.get('name'),
109 "req_owner": requirement.get('owner'),
110 "req_category": requirement.get('category'),
111 "path": test.get('path'),
112 "repository": test.get('repository'),
113 "revision": test.get('revision'),
114 "parameters": test.get('parameters'),
115 "mandatory": test.get('mandatory'),
116 "kind": "manual" if manual else "automated",
117 }
118 )
119
120
121def check_coverage(requirement, repositories, args):
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100122 requirement['covered'] = False
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000123 if 'tests' not in requirement.keys() or requirement['tests'] is None:
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100124 return
125 if 'manual' in requirement['tests'].keys() and requirement['tests']['manual'] is not None:
126 for test in requirement['tests']['manual']:
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000127 if test_exists(test, repositories, args):
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100128 requirement['covered'] = True
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000129 if args.csv_name:
130 add_csv_row(requirement, test, args, True)
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100131 if 'automated' in requirement['tests'].keys() and requirement['tests']['automated'] is not None:
132 for test in requirement['tests']['automated']:
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000133 if test_exists(test, repositories, args):
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100134 requirement['covered'] = True
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000135 if args.csv_name:
136 add_csv_row(requirement, test, args)
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100137
138
139def main():
140 parser = ArgumentParser()
141 parser.add_argument("-f",
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000142 "--file",
143 dest="testplan_list",
144 required=True,
145 nargs="+",
146 help="Test plan file to be used")
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100147 parser.add_argument("-r",
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000148 "--repositories",
149 dest="repository_path",
150 default="repositories",
151 help="Test plan file to be used")
152 parser.add_argument("-o",
153 "--output",
154 dest="output",
155 default="output",
156 help="Destination directory for generated files")
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100157 parser.add_argument("-i",
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000158 "--ignore-clone",
159 dest="ignore_clone",
160 action="store_true",
161 default=False,
162 help="Ignore cloning repositories and use previously cloned")
163 parser.add_argument("-c",
164 "--csv",
165 dest="csv_name",
166 required=False,
167 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 +0100168
169 args = parser.parse_args()
Milosz Wasilewskib5045412016-12-07 11:27:10 +0000170 if not os.path.exists(os.path.abspath(args.output)):
171 os.makedirs(os.path.abspath(args.output), 0755)
172 for testplan in args.testplan_list:
173 if os.path.exists(testplan) and os.path.isfile(testplan):
174 testplan_file = open(testplan, "r")
175 tp_obj = yaml.load(testplan_file.read())
176 repo_list = repository_list(tp_obj)
177 repositories = {}
178 for repo in repo_list:
179 repo_url, repo_path = clone_repository(repo, args.repository_path, args.ignore_clone)
180 repositories.update({repo_url: repo_path})
181 # ToDo: check test plan structure
182 for requirement in tp_obj['requirements']:
183 check_coverage(requirement, repositories, args)
184 tp_name = tp_obj['metadata']['name'] + ".html"
185 tp_file_name = os.path.join(os.path.abspath(args.output), tp_name)
186 render(tp_obj, name=tp_file_name)
187 testplan_file.close()
Milosz Wasilewskif5ccdbd2016-10-25 18:49:20 +0100188# go through requiremets and for each test:
189# - if file exists render test as separate html file
190# - if file is missing, indicate missing test (red)
191# render test plan with links to test files
192# add option to render as single file (for pdf generation)
193
194if __name__ == "__main__":
195 main()