bisect-kit: output candidates information in json
diagnose_cros_autotest.py and bisectors can use 'view --json' subcommand
to dump candidates information in machine readable format.
BUG=b:117860228
TEST=unittest; run diagnose_cros_autotest.py end-to-end test manually
Change-Id: Iafcfc79f6331575b5cbca2fb0e98ac1d577a1b46
Reviewed-on: https://chromium-review.googlesource.com/1337352
Commit-Ready: Kuang-che Wu <kcwu@chromium.org>
Tested-by: Kuang-che Wu <kcwu@chromium.org>
Reviewed-by: Chung-yih Wang <cywang@chromium.org>
diff --git a/bisect_kit/cli.py b/bisect_kit/cli.py
index 89799a0..d847577 100644
--- a/bisect_kit/cli.py
+++ b/bisect_kit/cli.py
@@ -491,14 +491,81 @@
self.states.save()
def cmd_view(self, opts):
- """Shows current progress and candidates."""
+ """Shows remaining candidates."""
self.strategy.rebuild()
# Rebuild twice in order to re-estimate noise.
self.strategy.rebuild()
- self.strategy.show_summary(more=opts.more)
- left, right = self.strategy.get_range()
- self.domain.view(self.states.data['revlist'], self.states.idx2rev(left),
- self.states.idx2rev(right))
+
+ old_idx, new_idx = self.strategy.get_range()
+ old, new = map(self.states.idx2rev, [old_idx, new_idx])
+ highlight_old_idx, highlight_new_idx = self.strategy.get_range(
+ self.strategy.confidence / 10.0)
+ summary = {
+ 'rev_info': [vars(info).copy() for info in self.states.rev_info],
+ 'current_range': (old, new),
+ 'highlight_range':
+ map(self.states.idx2rev, [highlight_old_idx, highlight_new_idx]),
+ 'prob':
+ self.strategy.prob,
+ 'remaining_steps':
+ self.strategy.remaining_steps(),
+ }
+
+ if opts.verbose or opts.json:
+ interesting_indexes = set(range(len(summary['rev_info'])))
+ else:
+ interesting_indexes = set([old_idx, new_idx])
+ for i, p in enumerate(self.strategy.prob):
+ if p > 0.05:
+ interesting_indexes.add(i)
+
+ self.domain.fill_candidate_summary(summary, interesting_indexes)
+
+ if opts.json:
+ print(json.dumps(summary, indent=2, sort_keys=True))
+ else:
+ self.show_summary(summary, interesting_indexes, verbose=opts.verbose)
+
+ def show_summary(self, summary, interesting_indexes, verbose=False):
+ old, new = summary['current_range']
+ old_idx, new_idx = map(self.states.data['revlist'].index, [old, new])
+
+ if 'links_note' in summary:
+ print(summary['links_note'])
+ for key, link in summary.get('links', {}).items():
+ print('%s: %s' % (key, link))
+
+ print('Range: (%s, %s], %s revs left' % (old, new, (new_idx - old_idx)))
+ if 'remaining_steps' in summary:
+ print('(roughly %d steps)' % summary['remaining_steps'])
+
+ for i, rev_info in enumerate(summary['rev_info']):
+ if (not verbose and not old_idx <= i <= new_idx and
+ not rev_info['result_counter']):
+ continue
+
+ detail = []
+ if self.strategy.is_noisy():
+ detail.append('%.4f%%' % summary['prob'][i] * 100)
+ if rev_info['result_counter']:
+ detail.append(str(rev_info['result_counter']))
+ values = sorted(rev_info['values'])
+ if len(values) == 1:
+ detail.append('%.3f' % values[0])
+ elif len(values) > 1:
+ detail.append('n=%d,avg=%.3f,median=%.3f,min=%.3f,max=%.3f' %
+ (len(values), sum(values) / len(values),
+ values[len(values) // 2], values[0], values[-1]))
+
+ print('[%d] %s\t%s' % (i, rev_info['rev'], ' '.join(detail)))
+ if i in interesting_indexes:
+ if 'comment' in rev_info:
+ print('\t%s' % rev_info['comment'])
+ for action in rev_info.get('actions', []):
+ if 'text' in action:
+ print('\t%s' % action['text'])
+ if 'link' in action:
+ print('\t%s' % action['link'])
def current_status(self, session=None, session_base=None):
"""Gets current bisect status.
@@ -813,7 +880,8 @@
parser_view = subparsers.add_parser(
'view', help='Shows current progress and candidates')
- parser_view.add_argument('--more', action='store_true')
+ parser_view.add_argument('--verbose', '-v', action='store_true')
+ parser_view.add_argument('--json', action='store_true')
parser_view.set_defaults(func=self.cmd_view)
parser_log = subparsers.add_parser(