def test_nomatch(self): aliases = PathAliases() aliases.add('/home/*/src', './mysrc') self.assert_unchanged(aliases, '/home/foo/a.py')
def update(self, other_data, aliases=None): """Update this data with data from several other `CoverageData` instances. If `aliases` is provided, it's a `PathAliases` object that is used to re-map paths to match the local machine's. """ if self._has_lines and other_data._has_arcs: raise CoverageException("Can't combine arc data with line data") if self._has_arcs and other_data._has_lines: raise CoverageException("Can't combine line data with arc data") aliases = aliases or PathAliases() # Force the database we're writing to to exist before we start nesting # contexts. self._start_using() # Collector for all arcs, lines and tracers other_data.read() with other_data._connect() as conn: # Get files data. cur = conn.execute('select path from file') files = {path: aliases.map(path) for (path, ) in cur} cur.close() # Get contexts data. cur = conn.execute('select context from context') contexts = [context for (context, ) in cur] cur.close() # Get arc data. cur = conn.execute( 'select file.path, context.context, arc.fromno, arc.tono ' 'from arc ' 'inner join file on file.id = arc.file_id ' 'inner join context on context.id = arc.context_id') arcs = [(files[path], context, fromno, tono) for (path, context, fromno, tono) in cur] cur.close() # Get line data. cur = conn.execute( 'select file.path, context.context, line_bits.numbits ' 'from line_bits ' 'inner join file on file.id = line_bits.file_id ' 'inner join context on context.id = line_bits.context_id') lines = {(files[path], context): numbits for (path, context, numbits) in cur} cur.close() # Get tracer data. cur = conn.execute('select file.path, tracer ' 'from tracer ' 'inner join file on file.id = tracer.file_id') tracers = {files[path]: tracer for (path, tracer) in cur} cur.close() with self._connect() as conn: conn.isolation_level = 'IMMEDIATE' # Get all tracers in the DB. Files not in the tracers are assumed # to have an empty string tracer. Since Sqlite does not support # full outer joins, we have to make two queries to fill the # dictionary. this_tracers = { path: '' for path, in conn.execute('select path from file') } this_tracers.update({ aliases.map(path): tracer for path, tracer in conn.execute( 'select file.path, tracer from tracer ' 'inner join file on file.id = tracer.file_id') }) # Create all file and context rows in the DB. conn.executemany('insert or ignore into file (path) values (?)', ((file, ) for file in files.values())) file_ids = { path: id for id, path in conn.execute('select id, path from file') } conn.executemany( 'insert or ignore into context (context) values (?)', ((context, ) for context in contexts)) context_ids = { context: id for id, context in conn.execute( 'select id, context from context') } # Prepare tracers and fail, if a conflict is found. # tracer_paths is used to ensure consistency over the tracer data # and tracer_map tracks the tracers to be inserted. tracer_map = {} for path in files.values(): this_tracer = this_tracers.get(path) other_tracer = tracers.get(path, '') # If there is no tracer, there is always the None tracer. if this_tracer is not None and this_tracer != other_tracer: raise CoverageException( "Conflicting file tracer name for '%s': %r vs %r" % (path, this_tracer, other_tracer)) tracer_map[path] = other_tracer # Prepare arc and line rows to be inserted by converting the file # and context strings with integer ids. Then use the efficient # `executemany()` to insert all rows at once. arc_rows = ((file_ids[file], context_ids[context], fromno, tono) for file, context, fromno, tono in arcs) # Get line data. cur = conn.execute( 'select file.path, context.context, line_bits.numbits ' 'from line_bits ' 'inner join file on file.id = line_bits.file_id ' 'inner join context on context.id = line_bits.context_id') for path, context, numbits in cur: key = (aliases.map(path), context) if key in lines: numbits = numbits_union(lines[key], numbits) lines[key] = numbits cur.close() self._choose_lines_or_arcs(arcs=bool(arcs), lines=bool(lines)) # Write the combined data. conn.executemany( 'insert or ignore into arc ' '(file_id, context_id, fromno, tono) values (?, ?, ?, ?)', arc_rows) conn.execute("delete from line_bits") conn.executemany( "insert into line_bits " "(file_id, context_id, numbits) values (?, ?, ?)", [(file_ids[file], context_ids[context], numbits) for (file, context), numbits in lines.items()]) conn.executemany( 'insert or ignore into tracer (file_id, tracer) values (?, ?)', ((file_ids[filename], tracer) for filename, tracer in tracer_map.items())) # Update all internal cache data. self._reset() self.read()
def test_noop(self): aliases = PathAliases() self.assert_unchanged(aliases, '/ned/home/a.py')
def test_windows_root_paths(self): aliases = PathAliases() aliases.add('X:\\', '/tmp/src') self.assert_mapped(aliases, "X:\\a\\file.py", "/tmp/src/a/file.py") self.assert_mapped(aliases, "X:\\file.py", "/tmp/src/file.py")
def test_leading_wildcard(self): aliases = PathAliases() aliases.add('*/d1', './mysrc1') aliases.add('*/d2', './mysrc2') self.assert_mapped(aliases, '/foo/bar/d1/x.py', './mysrc1/x.py') self.assert_mapped(aliases, '/foo/bar/d2/y.py', './mysrc2/y.py')
def test_no_accidental_munging(self): aliases = PathAliases() aliases.add(r'c:\Zoo\boo', 'src/') aliases.add('/home/ned$', 'src/') self.assert_mapped(aliases, r'c:\Zoo\boo\foo.py', 'src/foo.py') self.assert_mapped(aliases, r'/home/ned$/foo.py', 'src/foo.py')
def test_multiple_wildcard(self): aliases = PathAliases() aliases.add('/home/jenkins/*/a/*/b/*/django', './django') self.assert_mapped(aliases, '/home/jenkins/xx/a/yy/b/zz/django/foo/bar.py', './django/foo/bar.py')
def test_no_accidental_match(self): aliases = PathAliases() aliases.add('/home/*/src', './mysrc') self.assert_unchanged(aliases, '/home/foo/srcetc')
def test_multiple_patterns(self): aliases = PathAliases() aliases.add('/home/*/src', './mysrc') aliases.add('/lib/*/libsrc', './mylib') self.assert_mapped(aliases, '/home/foo/src/a.py', './mysrc/a.py') self.assert_mapped(aliases, '/lib/foo/libsrc/a.py', './mylib/a.py')
def update(self, other_data, aliases=None): if self._has_lines and other_data._has_arcs: raise CoverageException("Can't combine arc data with line data") if self._has_arcs and other_data._has_lines: raise CoverageException("Can't combine line data with arc data") aliases = aliases or PathAliases() # See what we had already measured, for accurate conflict reporting. this_measured = self.measured_files() other_files = set() # Force the database we're writing to to exist before we start nesting # contexts. self._start_using() # Start a single transaction in each file. with self._connect(), other_data._connect(): # lines if other_data._has_lines: for context in other_data.measured_contexts(): self.set_context(context) for filename in other_data.measured_files(): lines = set(other_data.lines(filename, context=context)) if lines: other_files.add(filename) filename = aliases.map(filename) lines.update( self.lines(filename, context=context) or ()) self.add_lines({filename: lines}) # arcs if other_data._has_arcs: for context in other_data.measured_contexts(): self.set_context(context) for filename in other_data.measured_files(): arcs = set(other_data.arcs(filename, context=context)) if arcs: other_files.add(filename) filename = aliases.map(filename) arcs.update( self.arcs(filename, context=context) or ()) self.add_arcs({filename: arcs}) # file_tracers for filename in other_files: other_plugin = other_data.file_tracer(filename) filename = aliases.map(filename) if filename in this_measured: this_plugin = self.file_tracer(filename) else: this_plugin = None if this_plugin is None: self.add_file_tracers({filename: other_plugin}) elif this_plugin != other_plugin: raise CoverageException( "Conflicting file tracer name for '%s': %r vs %r" % ( filename, this_plugin, other_plugin, ))
def test_no_accidental_match(self): aliases = PathAliases() aliases.add('/home/*/src', './mysrc') self.assertEqual(aliases.map('/home/foo/srcetc'), '/home/foo/srcetc')
def test_nomatch(self): aliases = PathAliases() aliases.add('/home/*/src', './mysrc') self.assertEqual(aliases.map('/home/foo/a.py'), '/home/foo/a.py')
def test_noop(self): aliases = PathAliases() self.assertEqual(aliases.map('/ned/home/a.py'), '/ned/home/a.py')
def test_noop(self): aliases = PathAliases() self.assert_not_mapped(aliases, '/ned/home/a.py')