Add presubmit for translation expectations

Background (taken from crbug/583195):
translation_expectations.pyl contains the list of translatable grd
files and the languages they're expected to be translated into. It's
used internally during the translation process to determine which grd
files should be copied to google3 for translation, etc. Each
repository with grd files (desktop, Android, iOS) has a
translation_expectations.pyl file, which must list every grd file that
contains strings.

translation_expectations.pyl isn't used at all when building (neither
locally nor on the bots), so there's no indication to developers if
translation_expectations.pyl needs to be updated (e.g. because a grd
file was added) or is malformed. Instead, an error will occur much
later when the weekly translation run happens, causing unnecessary
back-and-forth between TPMs and developers and introducing delays.
This happens every few weeks.

This CL adds a presubmit that checks if the contents of the
translation_expectations.pyl matches the list of .grd files in the
repository. The presubmit only runs if a .grd or .grdp file is
modified, so in most cases it's a no-op. It lists the names of missing
and unlisted files in the warning.

Bug: 583195
Change-Id: I5013311dac5db1f0578f59022832dc8e33c1ee37
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1464078
Reviewed-by: Lei Zhang <[email protected]>
Reviewed-by: anthonyvd <[email protected]>
Reviewed-by: Jochen Eisinger <[email protected]>
Commit-Queue: Mustafa Emre Acer <[email protected]>
Cr-Commit-Position: refs/heads/master@{#748339}
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index af86eec..9be12773 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -4245,6 +4245,7 @@
   results.extend(input_api.RunTests(
     input_api.canned_checks.CheckVPythonSpec(input_api, output_api)))
   results.extend(_CheckTranslationScreenshots(input_api, output_api))
+  results.extend(_CheckTranslationExpectations(input_api, output_api))
   results.extend(_CheckCorrectProductNameInMessages(input_api, output_api))
   results.extend(_CheckBuildtoolsRevisionsAreInSync(input_api, output_api))
   results.extend(_CheckForTooLargeFiles(input_api, output_api))
@@ -4829,3 +4830,48 @@
       sorted(unnecessary_sha1_files)))
 
   return results
+
+
+def _CheckTranslationExpectations(input_api, output_api,
+                                  repo_root=None,
+                                  translation_expectations_path=None,
+                                  grd_files=None):
+  import sys
+  affected_grds = [f for f in input_api.AffectedFiles()
+      if (f.LocalPath().endswith('.grd') or
+          f.LocalPath().endswith('.grdp'))]
+  if not affected_grds:
+    return []
+
+  try:
+    old_sys_path = sys.path
+    sys.path = sys.path + [
+        input_api.os_path.join(
+            input_api.PresubmitLocalPath(), 'tools', 'translation')]
+    from helper import git_helper
+    from helper import translation_helper
+  finally:
+    sys.path = old_sys_path
+
+  # Check that translation expectations can be parsed and we can get a list of
+  # translatable grd files. |repo_root| and |translation_expectations_path| are
+  # only passed by tests.
+  if not repo_root:
+    repo_root = input_api.PresubmitLocalPath()
+  if not translation_expectations_path:
+    translation_expectations_path =  input_api.os_path.join(
+        repo_root, 'tools', 'gritsettings',
+        'translation_expectations.pyl')
+  if not grd_files:
+    grd_files = git_helper.list_grds_in_repository(repo_root)
+
+  try:
+    translation_helper.get_translatable_grds(repo_root, grd_files,
+                                             translation_expectations_path)
+  except Exception as e:
+    return [output_api.PresubmitNotifyResult(
+      'Failed to get a list of translatable grd files. This happens when:\n'
+      ' - One of the modified grd or grdp files cannot be parsed or\n'
+      ' - %s is not updated.\n'
+      'Stack:\n%s' % (translation_expectations_path, str(e)))]
+  return []