Files
renpy/renpycoverage.py
T
2016-10-15 01:19:27 -04:00

263 lines
6.1 KiB
Python

from __future__ import print_function, unicode_literals, division, absolute_import
import Cython.Coverage
import os
import coverage # @UnresolvedImport
import cPickle
import ast
ROOT = os.path.dirname(os.path.abspath(__file__))
def _find_c_source(base_path):
base_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "module", "gen.coverage", os.path.basename(base_path))
if os.path.exists(base_path + '.c'):
c_file = base_path + '.c'
elif os.path.exists(base_path + '.cpp'):
c_file = base_path + '.cpp'
else:
c_file = None
return c_file
Cython.Coverage._find_c_source = _find_c_source
class FixedCythonReporter(coverage.FileReporter):
def __init__(self, old, fn):
# fn = old.filename
#
# if not os.path.exists(fn):
# new_fn = os.path.join(os.path.dirname(fn), "module", os.path.basename(fn))
# if os.path.exists(new_fn):
# fn = new_fn
super(FixedCythonReporter, self).__init__(fn)
if old._code is None:
self._lines = set()
else:
self._lines = set(old._code)
def lines(self):
return self._lines
class CythonCoverage(Cython.Coverage.Plugin):
def _find_source_files(self, filename):
if not filename.endswith(".pyx"):
return None, None
pyx_base = os.path.basename(filename)
pyx_search = [
"module",
"module/gen.coverage",
"module/pysdlsound",
"renpy",
"renpy/display",
"renpy/gl",
"renpy/styledata",
"renpy/text",
"renpy/audio",
]
for i in pyx_search:
pyx_fn = os.path.join(ROOT, i, pyx_base)
if os.path.exists(pyx_fn):
break
else:
return None, None
c_base = os.path.basename(filename)[:-4] + ".c"
modules = [
"",
"renpy.",
"renpy.display.",
"renpy.gl.",
"renpy.styledata.",
"renpy.text.",
"renpy.audio.",
"pysdlsound."
]
for i in modules:
c_fn = os.path.join(ROOT, "module", "gen.coverage", i + c_base)
if os.path.exists(c_fn):
break
else:
print("Could not find C source for", filename)
return None, None
return c_fn, pyx_fn
def file_tracer(self, filename):
if not filename.endswith(".pyx"):
return None
_, pyx_fn = self._find_source_files(filename)
rv = super(CythonCoverage, self).file_tracer(pyx_fn)
def source_filename():
return pyx_fn
rv.source_filename = source_filename
return rv
def file_reporter(self, filename):
_, pyx_fn = self._find_source_files(filename)
r = super(CythonCoverage, self).file_reporter(filename)
return FixedCythonReporter(r, pyx_fn)
class RenpyTracer(coverage.FileTracer):
def __init__(self, filename):
self.filename = filename
def source_filename(self):
return self.filename
import renpy
renpy_import_all = False
class PycodeVisitor(ast.NodeVisitor):
def __init__(self, lines):
self.lines = lines
def statement(self, n):
self.lines.add(n.lineno)
self.generic_visit(n)
visit_FunctionDef = statement
visit_ClassDef = statement
visit_Return = statement
visit_Delete = statement
visit_Assign = statement
visit_AugAssign = statement
visit_Print = statement
visit_For = statement
visit_While = statement
visit_If = statement
visit_With = statement
visit_Raise = statement
visit_TryExcept = statement
visit_TryFinally = statement
visit_Assert = statement
visit_Import = statement
visit_ImportFrom = statement
# Global
visit_Exec = statement
# pass
visit_Break = statement
visit_Continue = statement
def visit_Expr(self, n):
if not isinstance(n.value, ast.Str):
self.lines.add(n.lineno)
self.generic_visit(n)
class RenpyReporter(coverage.FileReporter):
def __init__(self, filename):
super(RenpyReporter, self).__init__(filename)
global renpy_import_all
if not renpy_import_all:
import pygame_sdl2
pygame_sdl2.import_as_pygame()
renpy.import_all()
renpy_import_all = True
renpy.config.basedir = '/'
renpy.config.renpy_base = '/'
# renpy.game.contexts = [ renpy.execution.Context(False) ]
renpy.game.script = renpy.script.Script()
# stmts = renpy.parser.parse(filename)
with open(filename + "c", "rb") as f:
data = renpy.game.script.read_rpyc_data(f, 1)
try:
stmts = cPickle.loads(data)[1]
except:
stmts = [ ]
print(filename + "c", "failed")
all_stmts = [ ]
for i in stmts:
i.get_children(all_stmts.append)
self._lines = set()
for i in all_stmts:
if not isinstance(i, renpy.ast.Init):
self._lines.add(i.linenumber)
for i in renpy.game.script.all_pycode:
self.pycode_lines(i)
renpy.game.script.all_pycode = [ ]
def pycode_lines(self, pycode):
if pycode.mode != 'exec':
return
nodes = renpy.python.py_compile(pycode.source, pycode.mode, pycode.location[0], pycode.location[1], ast_node=True)
v = PycodeVisitor(self._lines)
for i in nodes:
v.visit(i)
def lines(self):
return self._lines
class RenpyCoverage(coverage.CoveragePlugin):
def file_tracer(self, filename):
for i in [ ".rpy", ".rpym" ]:
if filename.endswith(i):
break
else:
return None
return RenpyTracer(filename)
def file_reporter(self, filename):
return RenpyReporter(filename)
def coverage_init(reg, options):
reg.add_file_tracer(RenpyCoverage())
reg.add_file_tracer(CythonCoverage())