Bug 1460912 - [testing/profiles] Add --format options to ./profile diff r=rwood

The main purpose of this change is to add some structured formats to the
diff. In future commits, the output of |./profile diff ...| will be used
as inputs to other ./profile commands.

The intent of all this work is to make it easier to programmatically
manipulate the pref files when adding in new suites. For example, I want
to say "Automatically remove all prefs from the reftest profile that are
shared with the common profile".

MozReview-Commit-ID: nf8xOjmd1u

--HG--
extra : rebase_source : 3ef8f55e9222aadefa5565f14ff9a8d671f88467
This commit is contained in:
Andrew Halberstadt 2018-05-16 17:10:20 -04:00
Родитель 4c09b651d7
Коммит 848da85cb2
1 изменённых файлов: 52 добавлений и 23 удалений

Просмотреть файл

@ -47,6 +47,18 @@ except ImportError:
import jsondiff
FORMAT_STRINGS = {
'names': (
'{pref}',
'{pref}',
),
'pretty': (
'{pref}: {value}',
'{pref}: {value_a} => {value_b}'
),
}
def read_prefs(profile, pref_files=None):
"""Read and return all preferences set in the given profile.
@ -101,7 +113,32 @@ def read(key):
return prefs
def diff(a, b):
def format_diff(diff, fmt):
"""Format a diff."""
if fmt == 'json':
print(json.dumps(diff, sort_keys=True, indent=2))
return 0
lines = []
for key, prefs in sorted(diff.items()):
lines.append("{}:".format(key))
for pref, value in sorted(prefs.items()):
context = {'pref': pref, 'value': repr(value)}
if isinstance(value, list):
context['value_a'] = repr(value[0])
context['value_b'] = repr(value[1])
text = FORMAT_STRINGS[fmt][1].format(**context)
else:
text = FORMAT_STRINGS[fmt][0].format(**context)
lines.append(' {}'.format(text))
lines.append('')
print('\n'.join(lines).strip())
def diff(a, b, fmt):
"""Diff two profiles or suites.
:param a: The first profile or suite name.
@ -119,31 +156,20 @@ def diff(a, b):
jsondiff.Symbol('insert'): res[1],
}
modified = [(k, v) for k, v in res.items() if not isinstance(k, jsondiff.Symbol)]
if modified:
print("modified ({} => {}):".format(a, b))
for k, v in sorted(modified):
del prefs_a[k]
print(" {}: {} => {}".format(k, repr(v[0]), repr(v[1])))
label_map = {
'insert': 'missing in {}'.format(a),
'delete': 'missing in {}'.format(b),
}
# Post process results to make them JSON compatible and a
# bit more clear. Also calculate identical prefs.
results = {}
results['change'] = {k: v for k, v in res.items() if not isinstance(k, jsondiff.Symbol)}
symbols = [(k, v) for k, v in res.items() if isinstance(k, jsondiff.Symbol)]
for sym, value in symbols:
prefs = []
for k, v in value.items():
if k in prefs_a:
del prefs_a[k]
prefs.append(" {}: {}".format(k, repr(v)))
print("\n{}:\n{}".format(label_map.get(sym.label, sym.label), "\n".join(sorted(prefs))))
results['insert'] = {k: v for sym, pref in symbols for k, v in pref.items()
if sym.label == 'insert'}
results['delete'] = {k: v for sym, pref in symbols for k, v in pref.items()
if sym.label == 'delete'}
if prefs_a:
print("\nidentical:")
for k, v in sorted(prefs_a.items()):
print(" {}: {}".format(k, repr(v)))
same = set(prefs_a.keys()) - set(chain(*results.values()))
results['same'] = {k: v for k, v in prefs_a.items() if k in same}
return format_diff(results, fmt)
def sort_file(path):
@ -213,6 +239,9 @@ def cli(args=sys.argv[1:]):
help="Path to the first profile or suite name to diff.")
diff_parser.add_argument('b', metavar='B',
help="Path to the second profile or suite name to diff.")
diff_parser.add_argument('-f', '--format', dest='fmt', default='pretty',
choices=['pretty', 'json', 'names'],
help="Format to dump diff in (default: pretty)")
diff_parser.set_defaults(func=diff)
sort_parser = subparsers.add_parser('sort')