Projet

Général

Profil

0006-add-merge-coverage-script-8425.patch

Benjamin Dauvergne, 05 octobre 2015 21:19

Télécharger (8,49 ko)

Voir les différences:

Subject: [PATCH 6/8] add merge-coverage script (#8425)

 merge-coverage.py | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 260 insertions(+)
 create mode 100755 merge-coverage.py
merge-coverage.py
1
#!/usr/bin/python
2
import sys
3
import os
4
import xml.etree.ElementTree as ET
5
import logging
6
import re
7
from shutil import copyfile
8
from optparse import OptionParser
9

  
10
### This file came from the https://github.com/flow123d/flow123d repo they were nice enough to spend time to write this. 
11
### It is copied here for other people to use on its own.
12

  
13
# parse arguments
14
newline = 10*'\t';
15
parser = OptionParser(usage="%prog [options] [file1 file2 ... filen]", version="%prog 1.0",
16
    epilog = "If no files are specified all xml files in current directory will be selected. \n" +
17
             "Useful when there is not known precise file name only location")
18

  
19
parser.add_option("-o", "--output",     dest="filename",    default="coverage-merged.xml",
20
    help="output file xml name", metavar="FILE")
21
parser.add_option("-p", "--path",       dest="path",        default="./",
22
    help="xml location, default current directory", metavar="FILE")
23
parser.add_option("-l", "--log",        dest="loglevel",    default="DEBUG",
24
    help="Log level DEBUG, INFO, WARNING, ERROR, CRITICAL")
25
parser.add_option("-f", "--filteronly", dest="filteronly",  default=False, action='store_true',
26
    help="If set all files will be filtered by keep rules otherwise "+
27
		 "all given files will be merged and filtered.")
28
parser.add_option("-s", "--suffix",     dest="suffix",      default='',
29
    help="Additional suffix which will be added to filtered files so they original files can be preserved")
30
parser.add_option("-k", "--keep",       dest="packagefilters", default=None,  metavar="NAME", action="append",
31
    help="preserves only specific packages. e.g.: " + newline + 
32
         "'python merge.py -k src.la.*'" + newline + 
33
         "will keep all packgages in folder " +
34
         "src/la/ and all subfolders of this folders. " + newline +
35
         "There can be mutiple rules e.g.:" + newline + 
36
         "'python merge.py -k src.la.* -k unit_tests.la.'" + newline +
37
         "Format of the rule is simple dot (.) separated names with wildcard (*) allowed, e.g: " + newline +
38
         "package.subpackage.*")
39
(options, args) = parser.parse_args()
40

  
41

  
42
# get arguments
43
path = options.path
44
xmlfiles = args
45
loglevel = getattr(logging, options.loglevel.upper())
46
finalxml = os.path.join (path, options.filename)
47
filteronly = options.filteronly
48
filtersuffix = options.suffix
49
packagefilters = options.packagefilters
50
logging.basicConfig (level=loglevel, format='%(levelname)s %(asctime)s: %(message)s', datefmt='%x %X')
51

  
52

  
53

  
54
if not xmlfiles:
55
	for filename in os.listdir (path):
56
	    if not filename.endswith ('.xml'): continue
57
	    fullname = os.path.join (path, filename)
58
	    if fullname == finalxml: continue
59
	    xmlfiles.append (fullname)
60

  
61
	if not xmlfiles:
62
		print 'No xml files found!'
63
		sys.exit (1)
64

  
65
else:
66
	xmlfiles=[path+filename for filename in xmlfiles]
67

  
68

  
69

  
70
# constants
71
PACKAGES_LIST = 'packages/package';
72
PACKAGES_ROOT = 'packages'
73
CLASSES_LIST = 'classes/class';
74
CLASSES_ROOT = 'classes'
75
METHODS_LIST = 'methods/method';
76
METHODS_ROOT = 'methods'
77
LINES_LIST = 'lines/line';
78
LINES_ROOT = 'lines'
79

  
80

  
81

  
82
def merge_xml (xmlfile1, xmlfile2, outputfile):
83
	# parse
84
	xml1 = ET.parse(xmlfile1)
85
	xml2 = ET.parse(xmlfile2)
86

  
87
	# get packages
88
	packages1 = filter_xml(xml1)
89
	packages2 = filter_xml(xml2)
90

  
91
	# find root
92
	packages1root = xml1.find(PACKAGES_ROOT)
93

  
94

  
95
	# merge packages
96
	merge (packages1root, packages1, packages2, 'name', merge_packages);
97

  
98
	# write result to output file
99
	xml1.write (outputfile,  encoding="UTF-8", xml_declaration=True)
100

  
101

  
102
def filter_xml (xmlfile):
103
	xmlroot = xmlfile.getroot()
104
	packageroot = xmlfile.find(PACKAGES_ROOT)
105
	packages = xmlroot.findall (PACKAGES_LIST)
106

  
107
	# delete nodes from tree AND from list
108
	included = []
109
	if packagefilters: logging.debug ('excluding packages:')
110
	for pckg in packages:
111
		name = pckg.get('name')
112
		if not include_package (name):
113
			logging.debug ('excluding package "{0}"'.format(name))
114
			packageroot.remove (pckg)
115
		else:
116
			included.append (pckg)
117
	return included
118

  
119

  
120
def prepare_packagefilters ():
121
	if not packagefilters:
122
		return None
123

  
124
	# create simple regexp from given filter
125
	for i in range (len (packagefilters)):
126
		packagefilters[i] = '^' + packagefilters[i].replace ('.', '\.').replace ('*', '.*') + '$'
127

  
128

  
129

  
130
def include_package (name):
131
	if not packagefilters:
132
		return True
133

  
134
	for packagefilter in packagefilters:
135
		if re.search(packagefilter, name):
136
			return True
137
	return False
138

  
139
def get_attributes_chain (obj, attrs):
140
	"""Return a joined arguments of object based on given arguments"""
141

  
142
	if type(attrs) is list:
143
		result = ''
144
		for attr in attrs:
145
			result += obj.attrib[attr]
146
		return result
147
	else:
148
		return 	obj.attrib[attrs]
149

  
150

  
151
def merge (root, list1, list2, attr, merge_function):
152
	""" Groups given lists based on group attributes. Process of merging items with same key is handled by
153
		passed merge_function. Returns list1. """
154
	for item2 in list2:
155
		found = False
156
		for item1 in list1:
157
			if get_attributes_chain(item1, attr) == get_attributes_chain(item2, attr):
158
				item1 = merge_function (item1, item2)
159
				found = True
160
				break
161
		if found:
162
			continue
163
		else:
164
			root.append(item2)
165

  
166

  
167
def merge_packages (package1, package2):
168
	"""Merges two packages. Returns package1."""
169
	classes1 = package1.findall (CLASSES_LIST);
170
	classes2 = package2.findall (CLASSES_LIST);
171
	if classes1 or classes2:
172
		merge (package1.find (CLASSES_ROOT), classes1, classes2, ['filename','name'], merge_classes);
173

  
174
	return package1
175

  
176

  
177
def merge_classes (class1, class2):
178
	"""Merges two classes. Returns class1."""
179

  
180
	lines1 = class1.findall (LINES_LIST);
181
	lines2 = class2.findall (LINES_LIST);
182
	if lines1 or lines2:
183
		merge (class1.find (LINES_ROOT), lines1, lines2, 'number', merge_lines);
184

  
185
	methods1 = class1.findall (METHODS_LIST)
186
	methods2 = class2.findall (METHODS_LIST)
187
	if methods1 or methods2:
188
		merge (class1.find (METHODS_ROOT), methods1, methods2, 'name', merge_methods);
189

  
190
	return class1
191

  
192

  
193
def merge_methods (method1, method2):
194
	"""Merges two methods. Returns method1."""
195

  
196
	lines1 = method1.findall (LINES_LIST);
197
	lines2 = method2.findall (LINES_LIST);
198
	merge (method1.find (LINES_ROOT), lines1, lines2, 'number', merge_lines);
199

  
200

  
201
def merge_lines (line1, line2):
202
	"""Merges two lines by summing their hits. Returns line1."""
203

  
204
	# merge hits
205
	value = int (line1.get('hits')) + int (line2.get('hits'))
206
	line1.set ('hits', str(value))
207

  
208
	# merge conditionals
209
	con1 = line1.get('condition-coverage')
210
	con2 = line2.get('condition-coverage')
211
	if (con1 is not None and con2 is not None):
212
		con1value = int(con1.split('%')[0])
213
		con2value = int(con2.split('%')[0])
214
		# bigger coverage on second line, swap their conditionals
215
		if (con2value > con1value):
216
			line1.set ('condition-coverage', str(con2))
217
			line1.__setitem__(0, line2.__getitem__(0))
218

  
219
	return line1
220

  
221
# prepare filters
222
prepare_packagefilters ()
223

  
224

  
225
if filteronly:
226
	# filter all given files
227
	currfile = 1
228
	totalfiles = len (xmlfiles)
229
	for xmlfile in xmlfiles:
230
		xml = ET.parse(xmlfile)
231
		filter_xml(xml)
232
		logging.debug ('{1}/{2} filtering: {0}'.format (xmlfile, currfile, totalfiles))
233
		xml.write (xmlfile + filtersuffix,  encoding="UTF-8", xml_declaration=True)
234
		currfile += 1
235
else:
236
	# merge all given files
237
	totalfiles = len (xmlfiles)
238

  
239
	# special case if only one file was given
240
	# filter given file and save it
241
	if (totalfiles == 1):
242
		logging.warning ('Only one file given!')
243
		xmlfile = xmlfiles.pop(0)
244
		xml = ET.parse(xmlfile)
245
		filter_xml(xml)
246
		xml.write (finalxml,  encoding="UTF-8", xml_declaration=True)
247
		sys.exit (0)
248

  
249

  
250
	currfile = 1
251
	logging.debug ('{2}/{3} merging: {0} & {1}'.format (xmlfiles[0], xmlfiles[1], currfile, totalfiles-1))
252
	merge_xml (xmlfiles[0], xmlfiles[1], finalxml)
253

  
254

  
255
	currfile = 2
256
	for i in range (totalfiles-2):
257
		xmlfile = xmlfiles[i+2]
258
		logging.debug ('{2}/{3} merging: {0} & {1}'.format (finalxml, xmlfile, currfile, totalfiles-1))
259
		merge_xml (finalxml, xmlfile, finalxml)
260
		currfile += 1
0
-