Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(255)

Unified Diff: tools/ui/dialog_audit.py

Issue 2675653002: Chrome dialog audit
Patch Set: Map a few more, and update at r449542 Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/ui/dialog_audit.cache ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/ui/dialog_audit.py
diff --git a/tools/ui/dialog_audit.py b/tools/ui/dialog_audit.py
new file mode 100755
index 0000000000000000000000000000000000000000..7d7fa9cbb61e000b93ad58adf7ef033bc2efa552
--- /dev/null
+++ b/tools/ui/dialog_audit.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+# Copyright 2017 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.
+
+"""
+Systematically search the Chrome codebase for dialog invocations.
+"""
+
+import subprocess
+import sys
+import re
+import mmap
+import os
+import csv
+
+CLASS_UNKNOWN = '<class unknown>'
+CLASS_UNKNOWN = '?'
+VERBOSE = False
+
+BLACKLIST = {
+ 'ViewEventTestBase', # Has lots of uninteresting descendants in test files.
+ 'TestDelegate', # Causes infinite recursion.
+ 'UI' # Used in mash/webtest/webtest.cc - confuses regex.
+}
+
+def InheritsRe(klass):
+ return r'public.*[: ]{}[, ]'.format(klass)
+
+def ChildRe(klass):
+ return r'class ([\w :]+)\s*: [^{]*' + klass + r'[^{]*[{]'
+
+def FindFilesInheritingFromClass(klass):
+ process = subprocess.Popen(['git', 'grep', '-l', InheritsRe(klass), '--',
+ '*.h', '*.mm', '*.cc'],
+ stdout=subprocess.PIPE)
+ return process.communicate()[0].splitlines()
+
+def ToLine(lines, offset):
+ line = 0
+ pos = 0
+ while pos < offset[0]:
+ pos += len(lines[line])
+ line += 1
+ return line
+
+def FindClassesInFilesInheritingFromClass(files, klass):
+ children = []
+ child_re = re.compile(ChildRe(klass), re.MULTILINE)
+ for filename in files:
+ with open(filename, 'r+') as f:
+ lines = f.readlines()
+ text = " ".join([l[:-1] for l in lines])
+ found = child_re.finditer(text)
+ before = len(children)
+ for child in found:
+ child_klass = child.group(1).split()[-1]
+ children.append((child_klass, filename, ToLine(lines, child.span()),
+ re.sub(' +', ' ', child.group(0)), klass))
+ if len(children) == before:
+ sys.stderr.write(
+ "ERROR: Couldn't find a child of {} in {}\n".format(klass,
+ filename))
+ return children
+
+def HuntForDecendants(result, parent, klasses, depth=''):
+ for klass in klasses:
+ if klass[0] in BLACKLIST:
+ sys.stderr.write('{}Skipping (blacklisted): {}\n'.format(depth, klass[0]))
+ continue
+
+ result.append(klass)
+ sys.stderr.write('{}{}'.format(depth, klass[0]))
+ files = FindFilesInheritingFromClass(klass[0])
+ children = FindClassesInFilesInheritingFromClass(files, klass[0])
+ sys.stderr.write( ' has {} children\n'.format(len(children)))
+ HuntForDecendants(result, klass, children, depth + ' ')
+
+def DefaultHunt():
+ seed = 'WidgetDelegate'
+ #$seed = 'DialogDelegate'
+ #seed = 'DialogDelegateView'
+ #seed = 'LocationBarBubbleDelegateView'
+ result = []
+ HuntForDecendants(result, 'Seed', [(seed, '', 0, '', 'Seed')])
+ return result
+
+def ToUrl(filename, klass, line):
+ return 'https://cs.chromium.org/chromium/src/{}?q={}&l={}'.format(
+ filename, klass, line)
+
+def Hyperlink(klass, url):
+ return '=HYPERLINK("{}", "{}")'.format(url, klass)
+
+def Process(data):
+ cells = eval("".join(open('tools/ui/cells.cache').readlines()))
+ newcells = []
+ cellmap = {}
+ for row in range(len(cells)):
+ ux_name = cells[row][1]
+ klass = cells[row][0]
+ if klass == '' and ux_name != '':
+ newcells.append([CLASS_UNKNOWN, ux_name])
+ print "Unmatched: {} {}".format(row, ux_name)
+ else:
+ cellmap.setdefault(klass, []).append(row)
+ newcells.append([klass, ux_name])
+ print ""
+ ignored = []
+ parents = {}
+ missing = {}
+ dupe_check = {}
+ for (klass, filename, line, match, parent) in data:
+ # Ignore test files
+ if filename.find('test.') > 0:
+ ignored.append((klass, filename))
+ continue
+
+ dupe_check.setdefault(klass, []).append(filename)
+ if len(dupe_check[klass]) > 1:
+ print "WARNING: {} found in {}".format(klass, dupe_check[klass])
+
+ # Track classes that have children and ignore them.
+ parents.setdefault(parent, [('f', 'c', 0, 'p'), []])[1].append(klass)
+ if parent in missing:
+ parents[parent][0] = missing[parent]
+ del missing[parent]
+
+ url = ToUrl(filename, klass, line)
+ if klass in cellmap:
+ for row in cellmap[klass]:
+ newcells[row][0] = Hyperlink(klass, url)
+ newcells[row].append(filename)
+ newcells[row].append(parent)
+ if VERBOSE:
+ print 'Matched: {}'.format(klass)
+ continue
+
+ missing[klass] = (filename, klass, line, parent)
+
+ sys.stderr.write('\nIgnored (Tests):')
+ for ignore in ignored:
+ sys.stderr.write(' {}({})'.format(ignore[0], os.path.basename(ignore[1])))
+ sys.stderr.write('\n')
+
+ sys.stderr.write('\nIgnored (Parents):')
+ for pk in parents:
+ parent = parents[pk]
+ filename, klass, line, p = parent[0]
+ children = " ".join(parent[1])
+ basename = os.path.basename(filename)
+ sys.stderr.write('\n\t {}({}): {}'.format(pk, basename, children))
+ sys.stderr.write('\n')
+
+ # Sort by filename.
+ missing_list = missing.values()
+ missing_list.sort()
+
+ # Write out CSV
+ with open('tools/ui/sheet.csv', 'wb') as csvfile:
+ writer = csv.writer(csvfile, dialect='excel-tab')
+ for row in range(len(cells)):
+ if row == 0:
+ writer.writerow(['DialogClass', 'Description', 'File', 'ParentClass',
+ 'Children'])
+ continue
+ writer.writerow(newcells[row])
+
+ for m in missing_list:
+ filename, klass, line, parent = m
+ url = ToUrl(filename, klass, line)
+ missing_text = 'What is this?'
+ writer.writerow([Hyperlink(klass, url), missing_text, filename, parent])
+ print 'Missing: {:40} from {}'.format(klass, url)
+
+ for pk in parents:
+ filename, klass, line, parent = parents[pk][0]
+ url = ToUrl(filename, klass, line)
+ num_children = len(parents[pk][1])
+ parent_text = 'Has {} Child{}'.format(num_children,
+ num_children != 1 and "ren" or "")
+ writer.writerow([Hyperlink(klass, url), parent_text, filename, parent,
+ ",".join(parents[pk][1])])
+ for ignore in ignored:
+ writer.writerow([ignore[0], '<test>', ignore[1]])
+
+def main():
+ try:
+ data = eval(open('tools/ui/dialog_audit.cache').readline())
+ Process(data)
+ except IOError as e:
+ data = DefaultHunt()
+ print data
+ sys.stderr.write("""
+Try `tools/ui/dialog_audit.py > tools/ui/dialog_audit.cache.new && \
+mv tools/ui/dialog_audit.cache.new tools/ui/dialog_audit.cache`
+""")
+
+if __name__ == '__main__':
+ sys.exit(main())
« no previous file with comments | « tools/ui/dialog_audit.cache ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698