PageRenderTime 56ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/Scripts/coverage.py

https://gitlab.com/jslee1/WordPress-iOS
Python | 326 lines | 270 code | 46 blank | 10 comment | 10 complexity | 38eaa94b0ece1a0624ade8408af21e89 MD5 | raw file
  1. import glob
  2. import os
  3. import shutil
  4. import subprocess
  5. import StringIO
  6. import sys
  7. # Directories
  8. dataDirectory = "./CoverageData"
  9. cacheDirectory = dataDirectory + "/Cache"
  10. derivedDataDirectory = dataDirectory + "/DerivedData"
  11. buildObjectsDirectory = derivedDataDirectory + "/Build/Intermediates/WordPress.build/Debug-iphonesimulator/WordPress.build/Objects-normal/x86_64"
  12. gcovOutputDirectory = dataDirectory + "/GCOVOutput"
  13. finalReport = dataDirectory + "/FinalReport"
  14. # Files
  15. gcovOutputFileName = gcovOutputDirectory + "/gcov.output"
  16. # File Patterns
  17. allGcdaFiles = "/*.gcda"
  18. allGcnoFiles = "/*.gcno"
  19. # Data conversion methods
  20. def IsInt(i):
  21. try:
  22. int(i)
  23. return True
  24. except ValueError:
  25. return False
  26. # Directory methods
  27. def copyFiles(sourcePattern, destination):
  28. assert sourcePattern
  29. for file in glob.glob(sourcePattern):
  30. shutil.copy(file, destination)
  31. return
  32. def createDirectoryIfNecessary(directory):
  33. if not os.path.exists(directory):
  34. os.makedirs(directory)
  35. return
  36. def removeDirectory(directory):
  37. assert directory
  38. assert directory.startswith(dataDirectory)
  39. subprocess.call(["rm",
  40. "-rf",
  41. directory])
  42. return
  43. def removeFileIfNecessary(file):
  44. if os.path.isfile(gcovOutputFileName):
  45. os.remove(gcovOutputFileName)
  46. return
  47. # Xcode interaction methods
  48. def xcodeBuildOperation(operation, simulator):
  49. assert operation
  50. return subprocess.call(["xcodebuild",
  51. operation,
  52. "-workspace",
  53. "../WordPress.xcworkspace",
  54. "-scheme",
  55. "WordPress",
  56. "-configuration",
  57. "Debug",
  58. "-destination",
  59. "platform=" + simulator,
  60. "-derivedDataPath",
  61. derivedDataDirectory,
  62. "GCC_GENERATE_TEST_COVERAGE_FILES=YES",
  63. "GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES"])
  64. def xcodeClean(simulator):
  65. return xcodeBuildOperation("clean", simulator)
  66. def xcodeBuild(simulator):
  67. return xcodeBuildOperation("build", simulator)
  68. def xcodeTest(simulator):
  69. return xcodeBuildOperation("test", simulator)
  70. # Simulator interaction methods
  71. def simulatorEraseContentAndSettings(simulator):
  72. deviceID = simulator[2]
  73. command = ["xcrun",
  74. "simctl",
  75. "erase",
  76. deviceID]
  77. result = subprocess.call(command)
  78. if (result != 0):
  79. exit("Error: subprocess xcrun failed to erase content and settings for device ID: " + deviceID + ".")
  80. return
  81. # Caching methods
  82. def cacheAllGcdaFiles():
  83. allGcdaFilesPath = buildObjectsDirectory + allGcdaFiles
  84. copyFiles(allGcdaFilesPath, cacheDirectory)
  85. return
  86. def cacheAllGcnoFiles():
  87. allGcnoFilesPath = buildObjectsDirectory + allGcnoFiles
  88. copyFiles(allGcnoFilesPath, cacheDirectory)
  89. return
  90. # Core procedures
  91. def createInitialDirectories():
  92. createDirectoryIfNecessary(dataDirectory)
  93. createDirectoryIfNecessary(cacheDirectory)
  94. createDirectoryIfNecessary(derivedDataDirectory)
  95. createDirectoryIfNecessary(gcovOutputDirectory)
  96. createDirectoryIfNecessary(finalReport)
  97. return
  98. def generateGcdaAndGcnoFiles(simulator):
  99. if xcodeClean(simulator) != 0:
  100. sys.exit("Exit: the clean procedure failed.")
  101. if xcodeBuild(simulator) != 0:
  102. sys.exit("Exit: the build procedure failed.")
  103. if xcodeTest(simulator) != 0:
  104. sys.exit("Exit: the test procedure failed.")
  105. cacheAllGcdaFiles()
  106. cacheAllGcnoFiles()
  107. return
  108. def processGcdaAndGcnoFiles():
  109. removeFileIfNecessary(gcovOutputFileName)
  110. gcovOutputFile = open(gcovOutputFileName, "wb")
  111. sourceFilesPattern = cacheDirectory + allGcnoFiles
  112. for file in glob.glob(sourceFilesPattern):
  113. fileWithPath = "../../" + file
  114. command = ["gcov", fileWithPath]
  115. subprocess.call(command,
  116. cwd = gcovOutputDirectory,
  117. stdout = gcovOutputFile)
  118. return
  119. # Selecting a Simulator
  120. def availableSimulators():
  121. command = ["xcrun",
  122. "simctl",
  123. "list",
  124. "devices"]
  125. process = subprocess.Popen(command,
  126. stdout = subprocess.PIPE)
  127. out, err = process.communicate()
  128. simulators = availableSimulatorsFromXcrunOutput(out)
  129. return simulators
  130. def availableSimulatorsFromXcrunOutput(output):
  131. outStringIO = StringIO.StringIO(output)
  132. iOSVersion = ""
  133. simulators = []
  134. line = outStringIO.readline()
  135. line = line.strip("\r").strip("\n")
  136. assert line == "== Devices =="
  137. while True:
  138. line = outStringIO.readline()
  139. line = line.strip("\r").strip("\n")
  140. if line.startswith("-- "):
  141. iOSVersion = line.strip("-- iOS ").strip(" --")
  142. elif line:
  143. name = line[4:line.rfind(" (", 0, line.rfind(" ("))]
  144. id = line[line.rfind("(", 0, line.rfind("(")) + 1:line.rfind(")", 0, line.rfind(")"))]
  145. simulators.append([iOSVersion, name, id])
  146. else:
  147. break
  148. return simulators
  149. def askUserToSelectSimulator(simulators):
  150. option = ""
  151. while True:
  152. print "\r\nPlease select a simulator:\r\n"
  153. for idx, simulator in enumerate(simulators):
  154. print str(idx) + " - iOS Version: " + simulator[0] + " - Name: " + simulator[1] + " - ID: " + simulator[2]
  155. print "x - Exit\r\n"
  156. option = raw_input(": ")
  157. if option == "x":
  158. exit(0)
  159. elif IsInt(option):
  160. intOption = int(option)
  161. if intOption >= 0 and intOption < len(simulators):
  162. break
  163. print "Invalid option!"
  164. return int(option)
  165. def selectSimulator():
  166. result = None
  167. simulators = availableSimulators()
  168. if (len(simulators) > 0):
  169. option = askUserToSelectSimulator(simulators)
  170. assert option >= 0 and option < len(simulators)
  171. simulatorEraseContentAndSettings(simulators[option])
  172. result = "iOS Simulator,name=" + simulators[option][1] + ",OS=" + simulators[option][0]
  173. print "Selected simulator: " + result
  174. return result
  175. # Parsing the data
  176. def parseCoverageData(line):
  177. header = "Lines executed:"
  178. assert line.startswith(header)
  179. line = line[len(header):]
  180. lineComponents = line.split(" of ")
  181. percentage = float(lineComponents[0].strip("%")) / 100
  182. totalLines = int(lineComponents[1])
  183. linesExecuted = int(round(percentage * totalLines))
  184. return str(percentage), str(totalLines), str(linesExecuted)
  185. def parseFilePath(line):
  186. assert line.startswith("File '")
  187. splitStrings = line.split("'")
  188. path = splitStrings[1]
  189. parentDir = os.path.dirname(os.getcwd())
  190. if path.startswith(parentDir):
  191. path = path[len(parentDir):]
  192. else:
  193. path = None
  194. return path
  195. def parseGcovFiles():
  196. gcovFile = open(gcovOutputFileName, "r")
  197. csvFile = open(finalReport + "/report.csv", "w")
  198. lineNumber = 0
  199. skipNext = False
  200. csvFile.write("File, Covered Lines, Total Lines, Coverage Percentage\r\n")
  201. for line in gcovFile:
  202. lineOffset = lineNumber % 4
  203. if lineOffset == 0:
  204. filePath = parseFilePath(line)
  205. if filePath:
  206. csvFile.write(filePath + ",")
  207. else:
  208. skipNext = True
  209. elif lineOffset == 1:
  210. if not skipNext:
  211. percentage, totalLines, linesExecuted = parseCoverageData(line)
  212. csvFile.write(linesExecuted + "," + totalLines + "," + percentage + "\r\n")
  213. else:
  214. skipNext = False
  215. lineNumber += 1
  216. return
  217. # Main
  218. def main(arguments):
  219. createInitialDirectories()
  220. simulator = selectSimulator()
  221. generateGcdaAndGcnoFiles(simulator)
  222. processGcdaAndGcnoFiles()
  223. parseGcovFiles()
  224. removeDirectory(derivedDataDirectory)
  225. return
  226. main(sys.argv)
  227. print("Done.")