From 222f7114fcdde3d7643b3a1c5b30e7fa0c6dd3fb Mon Sep 17 00:00:00 2001 From: Niels Thykier Date: Sun, 26 Apr 2015 18:20:11 +0200 Subject: [PATCH] Compute simple stats installability tester graph Signed-off-by: Niels Thykier --- britney.py | 8 ++++ installability/tester.py | 85 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/britney.py b/britney.py index d530f44..a396ba5 100755 --- a/britney.py +++ b/britney.py @@ -309,6 +309,14 @@ class Britney(object): else: write_nuninst(self.options.noninst_status, nuninst) + stats = self._inst_tester.compute_stats() + self.__log("> Installability tester statistics (per architecture)", type="I") + for arch in self.options.architectures: + arch_stat = stats[arch] + self.__log("> %s" % arch, type="I") + for stat in arch_stat.stat_summary(): + self.__log("> - %s" % stat, type="I") + # read the release-critical bug summaries for testing and unstable self.bugs = {'unstable': self.read_bugs(self.options.unstable), 'testing': self.read_bugs(self.options.testing),} diff --git a/installability/tester.py b/installability/tester.py index 3dd5118..f38034c 100644 --- a/installability/tester.py +++ b/installability/tester.py @@ -12,8 +12,9 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. +from collections import defaultdict from functools import partial -from itertools import filterfalse +from itertools import chain, filterfalse from britney_util import iter_except @@ -548,3 +549,85 @@ class InstallabilityTester(object): return self._cache_ess[arch] + def compute_stats(self): + universe = self._universe + eqv_table = self._eqv_table + graph_stats = defaultdict(ArchStats) + seen_eqv = defaultdict(set) + + for pkg in universe: + (pkg_name, pkg_version, pkg_arch) = pkg + deps, con = universe[pkg] + arch_stats = graph_stats[pkg_arch] + + arch_stats.nodes += 1 + + if pkg in eqv_table and pkg not in seen_eqv[pkg_arch]: + eqv = [e for e in eqv_table[pkg] if e[2] == pkg_arch] + arch_stats.eqv_nodes += len(eqv) + + arch_stats.add_dep_edges(deps) + arch_stats.add_con_edges(con) + + for stat in graph_stats.values(): + stat.compute_all() + + return graph_stats + + +class ArchStats(object): + + def __init__(self): + self.nodes = 0 + self.eqv_nodes = 0 + self.dep_edges = [] + self.con_edges = [] + self.stats = defaultdict(lambda: defaultdict(int)) + + def stat(self, statname): + return self.stats[statname] + + def stat_summary(self): + text = [] + for statname in ['nodes', 'dependency-clauses', 'dependency-clause-alternatives', 'negative-dependency-clauses']: + stat = self.stats[statname] + if statname != 'nodes': + format_str = "%s, max: %d, min: %d, median: %d, average: %f (%d/%d)" + values = [statname, stat['max'], stat['min'], stat['median'], stat['average'], stat['sum'], stat['size']] + if 'average-per-node' in stat: + format_str += ", average-per-node: %f" + values.append(stat['average-per-node']) + else: + format_str = "nodes: %d, eqv-nodes: %d" + values = (self.nodes, self.eqv_nodes) + text.append(format_str % tuple(values)) + return text + + def add_dep_edges(self, edges): + self.dep_edges.append(edges) + + def add_con_edges(self, edges): + self.con_edges.append(edges) + + def _list_stats(self, stat_name, sorted_list, average_per_node=False): + if sorted_list: + stats = self.stats[stat_name] + stats['max'] = sorted_list[-1] + stats['min'] = sorted_list[0] + stats['sum'] = sum(sorted_list) + stats['size'] = len(sorted_list) + stats['average'] = float(stats['sum'])/len(sorted_list) + stats['median'] = sorted_list[len(sorted_list)//2] + if average_per_node: + stats['average-per-node'] = float(stats['sum'])/self.nodes + + def compute_all(self): + dep_edges = self.dep_edges + con_edges = self.con_edges + sorted_no_dep_edges = sorted(len(x) for x in dep_edges) + sorted_size_dep_edges = sorted(len(x) for x in chain.from_iterable(dep_edges)) + sorted_no_con_edges = sorted(len(x) for x in con_edges) + self._list_stats('dependency-clauses', sorted_no_dep_edges) + self._list_stats('dependency-clause-alternatives', sorted_size_dep_edges, average_per_node=True) + self._list_stats('negative-dependency-clauses', sorted_no_con_edges) +