callvalidator.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. #!/usr/bin/env python
  2. # usage: txtmerge.py <input_dir> <output_file_name>
  3. # does the exact opposite of txtsplit.py
  4. from os import listdir
  5. from os.path import isfile, join
  6. import sys
  7. import re
  8. import io
  9. import time
  10. import xml.etree.ElementTree as ET
  11. idir = ''
  12. callList = []
  13. locationCallList = []
  14. neverCalledLocations = []
  15. callFileList = []
  16. calledFileList = []
  17. validate = 'all'
  18. def loadValidationList(validationListFile):
  19. result = []
  20. try:
  21. tree = ET.parse(validationListFile)
  22. root = tree.getroot()
  23. for file in root.iter('File'):
  24. result.append(join(file.attrib['folder'], file.attrib['name']))
  25. return result
  26. except:
  27. pass
  28. #endtry
  29. try:
  30. with io.open(validationListFile, 'r', encoding='utf-8') as ifile:
  31. lines = ifile.readlines()
  32. index = 1
  33. for line in lines:
  34. temp = line.strip(" \r\n\t").split(";")
  35. file = temp[0].strip(" \t")
  36. folder = idir
  37. if ".qsrc" not in file:
  38. raise SyntaxError("Incorrect file content: '<filename>[; <folder>] expected, found %s." % (index, line),(validationListFile, index, 1, 'if ".qsrc" not in temp[0].strip(" \t"):', index, len(line)))
  39. if len(temp) == 2:
  40. folder = temp[1].strip(" \t")
  41. result.append(join(folder,file))
  42. index += 1
  43. return result
  44. except SyntaxError as e:
  45. raise SystemExit("Invalid list filed: %s" % e.msg)
  46. # except Exception as e:
  47. # raise SystemExit()
  48. #endtry
  49. #enddef
  50. def functionEmptyOrNamed(callArray):
  51. if len(callArray) < 3:
  52. return ''
  53. else:
  54. return callArray[2].strip(" \t\n")
  55. #endif
  56. #enddef
  57. def processLines(lines, fileName):
  58. findPattern = "(gt|gs|xgt|xgs)\s*('|\")\w+('|\")(,\s*('|\")\w+('|\"))?"
  59. blockEndPattern = "[^}]*}$"
  60. commentBlock = False
  61. lineNo = 0
  62. locationCalls = {"calllocation": lines[0].strip(' #\n'), "fileName": fileName, "calls": []}
  63. for line in lines:
  64. line = line.strip(" \t\n")
  65. if (not commentBlock) and line.strip("!").startswith("{"):
  66. commentBlock = True
  67. if commentBlock and (line == '}' or re.search(blockEndPattern, line) != None):
  68. commentBlock = False
  69. if not commentBlock and not line.startswith("!"):
  70. match = re.search(findPattern, line)
  71. if match != None:
  72. temp = match.group().replace('\t', '').replace(' ', '').replace("','","|").replace('","','|').strip(" '\n").replace("'","|").replace('"','|').split('|')
  73. temp = [t.strip(" '\"") for t in temp]
  74. if temp[1].lower() not in ['boystat', 'exp_gain']:
  75. calltype = temp[0]
  76. loc = temp[1]
  77. fun = functionEmptyOrNamed(temp)
  78. if ("%s.qsrc" % loc) not in calledFileList:
  79. calledFileList.append("%s.qsrc" % loc)
  80. calledLocation = {"location": loc, "function": fun, "valid": 0, "reason": "%s.qsrc doesn't exist." % loc}
  81. callLine = {"callId": 0, "calltype": calltype, "call": "%s '%s', '%s'" % (calltype, loc, fun), "lineNo": lineNo}
  82. if calledLocation not in callList:
  83. callList.append(calledLocation)
  84. callLine['callId'] = callList.index(calledLocation)
  85. locationCalls['calls'].append(callLine)
  86. #endif
  87. #endif
  88. #endif
  89. lineNo += 1
  90. #endfor
  91. locationCallList.append(locationCalls)
  92. #enddedf
  93. def validateCalls(lines):
  94. calls = [c for c in callList if c['location'].lower() == lines[0].strip(' #\n').lower()]
  95. if validate == 'all' and (calls == None or len(calls) == 0):
  96. neverCalledLocations.append(lines[0].strip(' #\n').lower())
  97. for call in calls:
  98. if call['function'] == 'start' or call['function'] == 'Start' or call['function'] == '':
  99. call['valid'] = 1
  100. call['reason'] = ""
  101. else:
  102. call['reason'] = "No $ARGS[0] or ARGS[i] expecting '%s'" % call['function']
  103. findPattern = "\$*(args|ARGS)\[(0|i)\]\s*=\s*('|\")%s('|\")" % call['function']
  104. for line in lines:
  105. match = re.search(findPattern, line)
  106. if match != None:
  107. call['valid'] = 1
  108. call['reason'] = ''
  109. break
  110. #endfor
  111. #endif
  112. #endfor
  113. #enddef
  114. assert (len(sys.argv) == 2 or len(sys.argv) == 3 or len(sys.argv) == 4), "usage:\ncallvalidator.py source=<src_input_dir> [file=<filename> | list=<listfilename>] [folder=<folder>]"
  115. idir = str(sys.argv[1].replace("source=", ""))
  116. if len(sys.argv) == 4:
  117. vdir = str(sys.argv[3].replace("folder=", ""))
  118. else:
  119. vdir = idir
  120. validationTarget = ''
  121. if len(sys.argv) > 2 and "file=" in str(sys.argv[2]):
  122. validationTarget = sys.argv[2].replace("file=", "")
  123. callFileList = [join(vdir, sys.argv[2].replace("file=", ""))]
  124. validate = 'file'
  125. if len(sys.argv) > 2 and "list=" in str(sys.argv[2]):
  126. validationTarget = sys.argv[2].replace("list=", "")
  127. callFileList = loadValidationList(join(sys.argv[3].replace("folder=", ""), sys.argv[2].replace("list=", "")))
  128. validate = 'list'
  129. if validate == 'all':
  130. validationTarget = idir
  131. callFileList = [join(idir,f) for f in listdir(idir) if ".qsrc" in f]
  132. print("Start building call list: %s" % time.strftime("%H:%M:%S", time.localtime()))
  133. # grab the location files
  134. buildlist_start = time.time()
  135. # build a list of all the calls happening
  136. for file in callFileList:
  137. with io.open(file, 'rt', encoding='utf-8') as ifile:
  138. lines = ifile.readlines()
  139. processLines(lines, ifile.name)
  140. #endwith
  141. #endfor
  142. for call in callList:
  143. call['callId'] = callList.index(call)
  144. print("Finished building call list: %s" % time.strftime("%H:%M:%S", time.localtime()))
  145. # validating that all the calls are for valid locations
  146. for file in calledFileList:
  147. if file not in ['boyStat.qsrc', 'exp_gain.qsrc']:
  148. try:
  149. with io.open(join(idir, file), 'rt', encoding='utf-8') as ifile:
  150. lines = ifile.readlines()
  151. validateCalls(lines)
  152. except IOError:
  153. pass
  154. #endwith
  155. #endif
  156. #endfor
  157. print("Finished validation %s" % time.strftime("%H:%M:%S", time.localtime()))
  158. # create the call validity file and a list of files that call invalid locations
  159. timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
  160. txtname = 'call_validity [%s] - %s.txt' % (validationTarget, time.strftime('%Y%m%d%H%M%S', time.localtime()))
  161. htmlname = 'call_validity [%s] - %s.html' % (validationTarget, time.strftime('%Y%m%d%H%M%S', time.localtime()))
  162. callheads = []
  163. callIds = []
  164. invalidCalls = []
  165. noInvLoc = 0
  166. noNonExistingLoc = 0
  167. noInvFun = 0
  168. noInvCall = 0
  169. noLocNeverCalled = len(neverCalledLocations)
  170. currLoc = ''
  171. for call in callList:
  172. if call['valid'] == 0:
  173. if currLoc != call['location']:
  174. noInvLoc += 1
  175. if ".qsrc doesn't exist" in call['reason']:
  176. noNonExistingLoc += 1
  177. callIds.append(call['callId'])
  178. invalidCalls.append(call)
  179. #enfif
  180. #endfor
  181. invalidCalls = sorted(invalidCalls, key=lambda k: (k['location'].lower(), k['function'].lower()))
  182. noInvFun = len(invalidCalls)
  183. for locationCall in locationCallList:
  184. calls = locationCall['calls']
  185. Ids = [call['callId'] for call in calls]
  186. if any(x in Ids for x in callIds):
  187. callheads.append(locationCall)
  188. #endfor
  189. #endfor
  190. callheads = sorted(callheads, key=lambda k: (k['calllocation'].lower()))
  191. noInvCall = len(callheads)
  192. location = ''
  193. txtInvalidLines = []
  194. #htmlInvalidLines = []
  195. for call in invalidCalls:
  196. if location != '' and location.lower() != call['location'].lower():
  197. txtInvalidLines.append('\n')
  198. # if location == '' or location.lower() != call['location'].lower():
  199. # htmlInvalidLines.append('\t\t\t<tr><td span="3"><h3>%s</td></tr>\n' % call['location'])
  200. txtInvalidLines.append(" '%s', '%s' : %s\n" % (call['location'], call['function'], call['reason']))
  201. # htmlInvalidLines.append('\t\t\t<tr><td style="border: 1px solid black; padding: 5px;">\'%s\'</td><td style="border: 1px solid black; padding: 5px;">%s</td></tr>\n' % (call['function'], call['reason']))
  202. location = call['location']
  203. #endfor
  204. txtLocationLines = []
  205. #htmlLocationLines = []
  206. for head in callheads:
  207. txtLocationLines.append(" ---- %s [%s]:\n" % (head["calllocation"], head["fileName"]))
  208. # htmlLocationLines.append("\t\t<h3>%s</h3>\n" % head["calllocation"])
  209. # htmlLocationLines.append('\t<table style="border: 2px solid black;>\n')
  210. # htmlLocationLines.append('\t\t<thead>\n')
  211. ## htmlLocationLines.append('\t\t\t<tr><td style="border: 1px solid black; padding: 5px;">Line</td><td style="border: 1px solid black; padding: 5px;">Call</td><td style="border: 1px solid black; padding: 5px;">Reason</td></tr>')
  212. # htmlLocationLines.append('\t\t</thead>\n')
  213. # htmlLocationLines.append('\t\t<tbody>\n')
  214. for call in head['calls']:
  215. if call['callId'] in callIds:
  216. target = callList[call['callId']]
  217. txtLocationLines.append(" invalid call on line %04d: %s '%s', '%s'\t: %s\n" % (call['lineNo'], call['calltype'], target['location'], target['function'], target['reason']))
  218. # htmlLocationLines.append('\t\t\t<tr><td style="border: 1px solid black; padding: 5px;">%d</td><td style="border: 1px solid black; padding: 5px;">%s \'%s\', \'%s\'</td><td style="border: 1px solid black; padding: 5px;">%s</td></tr>\n' % (call['lineNo'], call['calltype'], target['location'], target['function'], target['reason']) )
  219. #endif
  220. #endfor
  221. txtLocationLines.append('\n')
  222. # htmlLocationLines.append('\t\t</tbody>\n')
  223. # htmlLocationLines.append('\t</table>\n')
  224. # htmlLocationLines.append('<br/>')
  225. #endfor
  226. try:
  227. with io.open(txtname, 'w', encoding='utf-8') as ofile:
  228. ofile.write("----- Summary -----\n")
  229. ofile.write(" Locations called incorrectly : %03d\n" % noInvLoc)
  230. ofile.write(" Non-existing locations called : %03d\n" % noNonExistingLoc)
  231. ofile.write(" Number of incorrect calls : %03d\n" % noInvFun)
  232. ofile.write(" Locations making incorrect calls: %03d\n" % noInvCall)
  233. if validate == 'all':
  234. ofile.write(" Locations never called : %03d\n" % noLocNeverCalled)
  235. ofile.write("\n")
  236. if validate == 'list':
  237. ofile.write("----- Validated files -----\n")
  238. if len(callFileList) > 10:
  239. ofile.write(" The list contains %d files. Please see the list in %s.\n" % (len(callFileList), validationTarget))
  240. else:
  241. for file in callFileList:
  242. ofile.write(" %s\n" % file)
  243. #endif
  244. ofile.write('\n')
  245. #endif
  246. ofile.write("----- List of Invalid calls -----\n")
  247. ofile.write("\n")
  248. for line in txtInvalidLines:
  249. ofile.write(line)
  250. ofile.write('\n')
  251. ofile.write("----- List of Locations and invalid calls they make -----\n")
  252. ofile.write('\n')
  253. for line in txtLocationLines:
  254. ofile.write(line)
  255. if validate == 'all':
  256. ofile.write("----- List of Locations that are never called -----\n")
  257. for line in neverCalledLocations:
  258. ofile.write("%s\n" % line)
  259. #endwith
  260. except IOError as e:
  261. raise SystemExit("ERROR: call validity file was not created! REASON: %s" % e.strerror)
  262. #endtry_except
  263. print("File saved finish: %s" % time.strftime("%H:%M:%S", time.localtime()))
  264. #### Create HTML
  265. """ with io.open(htmlname, 'w', encoding='utf-8') as hfile:
  266. hfile.write('<!DOCTYPE html>\n')
  267. hfile.write('<html lang="en">\n')
  268. hfile.write('\t<head>\n')
  269. hfile.write('\t\t<title>Invalid Call Report</title>\n')
  270. hfile.write('\t</head>\n')
  271. hfile.write('\t<body>\n')
  272. hfile.write("\t\t<h2>List of Invalid calls</h2>\n")
  273. hfile.write('<br/>\n')
  274. hfile.write('\t<table style="border: 2px solid black;>\n')
  275. hfile.write('\t\t<thead>\n')
  276. hfile.write('\t\t\t<tr><td style="border: 1px solid black; padding: 5px;">Function</td><td style="border: 1px solid black; padding: 5px;">Reason</td></tr>')
  277. hfile.write('\t\t</thead>\n')
  278. hfile.write('\t\t<tbody>\n')
  279. for line in htmlInvalidLines:
  280. hfile.write(line)
  281. hfile.write('\t\t</tbody>\n')
  282. hfile.write('\t\t</table>\n')
  283. hfile.write('<br/>\n')
  284. hfile.write("<h2>List of Locations and invalid calls they make</h2>\n")
  285. hfile.write('<br/>\n')
  286. for line in htmlLocationLines:
  287. hfile.write(line)
  288. hfile.write('\t</body>\n')
  289. hfile.write('</html>\n') """
  290. #endwith