Beispiel #1
0
    def add_lines(self, line_data):
        """Add measured line data.

        `line_data` is a dictionary mapping file names to dictionaries::

            { filename: { lineno: None, ... }, ...}

        """
        if self._debug.should('dataop'):
            self._debug.write("Adding lines: %d files, %d lines total" % (
                len(line_data), sum(len(lines) for lines in line_data.values())
            ))
        self._start_using()
        self._choose_lines_or_arcs(lines=True)
        if not line_data:
            return
        with self._connect() as con:
            self._set_context_id()
            for filename, linenos in iitems(line_data):
                linemap = nums_to_numbits(linenos)
                file_id = self._file_id(filename, add=True)
                query = "select numbits from line_map where file_id = ? and context_id = ?"
                existing = list(con.execute(query, (file_id, self._current_context_id)))
                if existing:
                    linemap = merge_numbits(linemap, from_blob(existing[0][0]))

                con.execute(
                    "insert or replace into line_map "
                    " (file_id, context_id, numbits) values (?, ?, ?)",
                    (file_id, self._current_context_id, to_blob(linemap)),
                )
Beispiel #2
0
    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_map.numbits '
                'from line_map '
                'inner join file on file.id = line_map.file_id '
                'inner join context on context.id = line_map.context_id'
                )
            lines = {
                (files[path], context): from_blob(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_map.numbits '
                'from line_map '
                'inner join file on file.id = line_map.file_id '
                'inner join context on context.id = line_map.context_id'
                )
            for path, context, numbits in cur:
                key = (aliases.map(path), context)
                numbits = from_blob(numbits)
                if key in lines:
                    numbits = merge_numbits(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_map")
            conn.executemany(
                "insert into line_map "
                "(file_id, context_id, numbits) values (?, ?, ?)",
                [
                    (file_ids[file], context_ids[context], to_blob(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()
Beispiel #3
0
 def test_merging(self, nums1, nums2):
     merged = numbits_to_nums(
         merge_numbits(nums_to_numbits(nums1), nums_to_numbits(nums2)))
     self.assertEqual(nums1 | nums2, set(merged))