--- a/testing/profiles/profile
+++ b/testing/profiles/profile
@@ -42,16 +42,28 @@ try:
import jsondiff
except ImportError:
from mozbuild.base import MozbuildObject
build = MozbuildObject.from_environment(cwd=here)
build.virtualenv_manager.install_pip_package("jsondiff")
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.
:param profile: Profile name relative to this `here`.
:returns: A dictionary of preferences set in the profile.
"""
pref_files = pref_files or Profile.preference_file_names
prefs = {}
@@ -96,17 +108,42 @@ def read(key):
a suite as defined in suites.json.
"""
prefs = {}
for profile in get_profiles(key):
prefs.update(read_prefs(profile))
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.
:param b: The second profile or suite name.
"""
prefs_a = read(a)
prefs_b = read(b)
res = jsondiff.diff(prefs_a, prefs_b, syntax='symmetric')
@@ -114,41 +151,30 @@ def diff(a, b):
return 0
if isinstance(res, list) and len(res) == 2:
res = {
jsondiff.Symbol('delete'): res[0],
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):
"""Sort the given pref file alphabetically, preserving preceding comments
that start with '//'.
:param path: Path to the preference file to sort.
"""
@@ -208,16 +234,19 @@ def cli(args=sys.argv[1:]):
parser = ArgumentParser()
subparsers = parser.add_subparsers()
diff_parser = subparsers.add_parser('diff')
diff_parser.add_argument('a', metavar='A',
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')
sort_parser.add_argument('profile', help="Path to profile to sort preferences.")
sort_parser.set_defaults(func=sort)
show_parser = subparsers.add_parser('show')
show_parser.add_argument('suite', help="Name of suite to show arguments for.")