Mojo: Add a utility to compare old mojom_bindings_generator.py output to the current one.

Maybe one day I'll consider checking in golden files and making it a
proper (automated) test ... but in the meanwhile it's still useful for
refactoring the bindings generator code.

R=darin@chromium.org

Review URL: https://codereview.chromium.org/288943005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@270845 0039d316-1c4b-4281-b951-d872f2087c98


CrOS-Libchrome-Original-Commit: d9491433e39ce89fe8206371d9df821ad9a8610e
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py
new file mode 100644
index 0000000..00524a2
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py
@@ -0,0 +1,32 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from fnmatch import filter
+from os import walk
+from os.path import join
+import sys
+
+
+def FindFiles(top, pattern, **kwargs):
+  """Finds files under |top| matching the glob pattern |pattern|, returning a
+  list of paths."""
+  matches = []
+  for dirpath, _, filenames in walk(top, **kwargs):
+    for filename in filter(filenames, pattern):
+      matches.append(join(dirpath, filename))
+  return matches
+
+
+def main(argv):
+  if len(argv) != 3:
+    print "usage: %s path pattern" % argv[0]
+    return 1
+
+  for filename in FindFiles(argv[1], argv[2]):
+    print filename
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py
new file mode 100644
index 0000000..20ef461
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py
@@ -0,0 +1,47 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os.path
+from subprocess import check_call
+import sys
+
+
+def RunBindingsGenerator(out_dir, root_dir, mojom_file, extra_flags=None):
+  out_dir = os.path.abspath(out_dir)
+  root_dir = os.path.abspath(root_dir)
+  mojom_file = os.path.abspath(mojom_file)
+
+  # The mojom file should be under the root directory somewhere.
+  assert mojom_file.startswith(root_dir)
+  mojom_reldir = os.path.dirname(os.path.relpath(mojom_file, root_dir))
+
+  # TODO(vtl): Abstract out the "main" functions, so that we can just import
+  # the bindings generator (which would be more portable and easier to use in
+  # tests).
+  this_dir = os.path.dirname(os.path.abspath(__file__))
+  # We're in src/mojo/public/tools/bindings/pylib/mojom_tests/support;
+  # mojom_bindings_generator.py is in .../bindings.
+  bindings_generator = os.path.join(this_dir, os.pardir, os.pardir, os.pardir,
+                                    "mojom_bindings_generator.py")
+
+  args = ["python", bindings_generator,
+          "-o", os.path.join(out_dir, mojom_reldir)]
+  if extra_flags:
+    args.extend(extra_flags)
+  args.append(mojom_file)
+
+  check_call(args)
+
+
+def main(argv):
+  if len(argv) < 4:
+    print "usage: %s out_dir root_dir mojom_file [extra_flags]" % argv[0]
+    return 1
+
+  RunBindingsGenerator(argv[1], argv[2], argv[3], extra_flags=argv[4:])
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/mojo/tools/check_mojom_golden_files.py b/mojo/tools/check_mojom_golden_files.py
new file mode 100755
index 0000000..6c2e187
--- /dev/null
+++ b/mojo/tools/check_mojom_golden_files.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import os.path
+import sys
+from filecmp import dircmp
+from shutil import rmtree
+from tempfile import mkdtemp
+
+_script_dir = os.path.dirname(os.path.abspath(__file__))
+_mojo_dir = os.path.join(_script_dir, os.pardir)
+sys.path.insert(0, os.path.join(_mojo_dir, "public", "tools", "bindings",
+                                "pylib"))
+from mojom_tests.support.find_files import FindFiles
+from mojom_tests.support.run_bindings_generator import RunBindingsGenerator
+
+
+def _ProcessDircmpResults(results, verbose=False):
+  """Prints results of directory comparison and returns true if they are
+  identical (note: the "left" directory should be the golden directory)."""
+  rv = not (bool(results.left_only) or bool(results.right_only) or \
+            bool(results.common_funny) or bool(results.funny_files) or \
+            bool(results.diff_files))
+  if verbose:
+    for f in results.left_only:
+      print "%s exists in golden directory but not in current output" % f
+    for f in results.right_only:
+      print "%s exists in current output but not in golden directory" % f
+    for f in results.common_funny + results.funny_files:
+      print "Unable to compare %s between golden directory and current output" \
+          % f
+    for f in results.diff_files:
+      print "%s differs between golden directory and current output" % f
+  for r in results.subdirs.values():
+    # If we're being verbose, check subdirectories even if we know that there
+    # are differences. Note that it's "... and rv" to avoid the short-circuit.
+    if rv or verbose:
+      rv = _ProcessDircmpResults(r, verbose=verbose) and rv
+  return rv
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument("--generate_golden_files", action="store_true",
+                      help=("generate golden files (does not obliterate "
+                            "directory"))
+  parser.add_argument("--keep_temp_dir", action="store_true",
+                      help="don't delete the temporary directory")
+  parser.add_argument("--verbose", action="store_true",
+                      help="spew excess verbiage")
+  parser.add_argument("golden_dir", metavar="GOLDEN_DIR",
+                      help="directory with the golden files")
+  args = parser.parse_args()
+
+  if args.generate_golden_files:
+    if os.path.exists(args.golden_dir):
+      print "WARNING: golden directory %s already exists" % args.golden_dir
+    out_dir = args.golden_dir
+  else:
+    if not os.path.exists(args.golden_dir):
+      print "ERROR: golden directory %s does not exist" % args.golden_dir
+      return 1
+    out_dir = mkdtemp()
+  if args.verbose:
+    print "Generating files to %s ..." % out_dir
+
+  mojom_files = FindFiles(_mojo_dir, "*.mojom")
+  for mojom_file in mojom_files:
+    if args.verbose:
+      print "  Processing %s ..." % os.path.relpath(mojom_file, _mojo_dir)
+    RunBindingsGenerator(out_dir, _mojo_dir, mojom_file)
+
+  if args.generate_golden_files:
+    return 0
+
+  identical = _ProcessDircmpResults(dircmp(args.golden_dir, out_dir, ignore=[]),
+                                    verbose=args.verbose)
+
+  if args.keep_temp_dir:
+    if args.verbose:
+      print "Not removing %s ..." % out_dir
+  else:
+    if args.verbose:
+      print "Removing %s ..." % out_dir
+    rmtree(out_dir)
+
+  if not identical:
+    print "FAILURE: current output differs from golden files"
+    return 1
+
+  print "SUCCESS: current output identical to golden files"
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/mojo/tools/test_runner.py b/mojo/tools/test_runner.py
index 4455235..c36caea 100755
--- a/mojo/tools/test_runner.py
+++ b/mojo/tools/test_runner.py
@@ -12,7 +12,7 @@
 
 _logging = logging.getLogger()
 
-_script_dir = os.path.dirname(os.path.realpath(__file__))
+_script_dir = os.path.dirname(os.path.abspath(__file__))
 sys.path.insert(0, os.path.join(_script_dir, "pylib"))
 
 from transitive_hash import transitive_hash