[cts] Move dep list out of python and allow dirs

Moves the allowed dependencies list(s) out of verify_cts_deps.py and allows
directories (such as //sdk and //third_party/dart-pkg/pub) to be
included in the CTS.

Change-Id: I1b47a7203448d4b12f41a523e1a45be1fdc50f0d
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/441204
Commit-Queue: John Shamoon <johnshamoon@google.com>
Reviewed-by: Jeremy Manson <jeremymanson@google.com>
Testability-Review: Jeremy Manson <jeremymanson@google.com>
diff --git a/sdk/cts/build/allowed_cts_deps.gni b/sdk/cts/build/allowed_cts_deps.gni
new file mode 100644
index 0000000..ef50ef0
--- /dev/null
+++ b/sdk/cts/build/allowed_cts_deps.gni
@@ -0,0 +1,15 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Individual dependencies that are approved for use in CTS.
+ALLOWED_CTS_DEPS = [
+  "//src/sys/pkg/bin/pm:host",
+  "//zircon/public/lib/zxtest:zxtest",
+]
+
+# All targets in these directories and their subdirectories are approved for use in CTS.
+ALLOWED_CTS_DIRS = [
+  "//sdk/*",
+  "//third_party/dart-pkg/pub/*",
+]
diff --git a/sdk/cts/build/cts_element.gni b/sdk/cts/build/cts_element.gni
index 37bda00..1dc4563 100644
--- a/sdk/cts/build/cts_element.gni
+++ b/sdk/cts/build/cts_element.gni
@@ -1,6 +1,7 @@
 # Copyright 2020 The Fuchsia 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("//sdk/cts/build/allowed_cts_deps.gni")
 import("//sdk/cts/build/cts_vars.gni")
 
 # Defines a CTS element.
@@ -33,6 +34,10 @@
              "--invoker_label",
              invoker_label,
              "--deps",
-           ] + fully_qualified_deps
+           ] + fully_qualified_deps + [
+             "--allowed_cts_deps"
+           ] + ALLOWED_CTS_DEPS + [
+             "--allowed_cts_dirs"
+           ] + ALLOWED_CTS_DIRS
   }
 }
diff --git a/sdk/cts/build/verify_cts_deps.py b/sdk/cts/build/verify_cts_deps.py
index f0576b6..4960668 100755
--- a/sdk/cts/build/verify_cts_deps.py
+++ b/sdk/cts/build/verify_cts_deps.py
@@ -16,25 +16,9 @@
 
 import argparse
 import os
+import re
 import sys
 
-# These must be fully qualified labels (without a toolchain).
-ALLOWED_CTS_DEPS = [
-    '//sdk/testing/sl4f/client:client',
-    '//sdk:sdk',
-    '//src/sys/pkg/bin/pm:host',
-    '//sdk/cts/tools/package_manager:pm_test_package_gather_deps',
-    '//third_party/dart-pkg/pub/archive:archive',
-    '//third_party/dart-pkg/pub/async:async',
-    '//third_party/dart-pkg/pub/file:file',
-    '//third_party/dart-pkg/pub/logging:logging',
-    '//third_party/dart-pkg/pub/path:path',
-    '//third_party/dart-pkg/pub/quiver:quiver',
-    '//third_party/dart-pkg/pub/retry:retry',
-    '//third_party/dart-pkg/pub/test:test',
-    '//zircon/public/lib/zxtest:zxtest',
-]
-
 CTS_EXTENSION = '.this_is_cts'
 
 
@@ -50,7 +34,7 @@
     Raises: ValueError if any parameter is empty or if root_build_dir does not exist.
     """
 
-    def __init__(self, root_build_dir, cts_file, invoker_label, deps):
+    def __init__(self, root_build_dir, cts_file, invoker_label, deps, allowed_cts_deps, allowed_cts_dirs):
         if root_build_dir and os.path.exists(root_build_dir):
             self.root_build_dir = root_build_dir
         else:
@@ -71,6 +55,16 @@
         else:
             raise ValueError('deps cannot be empty.')
 
+        if allowed_cts_deps:
+            self.allowed_cts_deps = allowed_cts_deps
+        else:
+            raise ValueError('allowed_cts_deps cannot be empty')
+
+        if allowed_cts_dirs:
+            self.allowed_cts_dirs = allowed_cts_dirs
+        else:
+            raise ValueError('allowed_cts_dirs cannot be empty')
+
     def get_file_path(self, dep):
         """Returns the path to a CTS file.
 
@@ -108,8 +102,19 @@
         """
         unaccepted_deps = []
         for dep in self.deps:
-            if dep not in ALLOWED_CTS_DEPS and not os.path.exists(
-                    self.get_file_path(dep)):
+            dep_found = False
+
+            if dep in self.allowed_cts_deps or os.path.exists(self.get_file_path(dep)):
+                dep_found = True
+            else:
+                # Dep isn't in the allow list and a CTS file doesn't exist. Check if
+                # all targets in dep's directory are allowed (//sdk/*).
+                for allowed_dir in self.allowed_cts_dirs:
+                    pattern = re.compile(allowed_dir)
+                    if pattern.match(dep):
+                        dep_found = True
+
+            if not dep_found:
                 unaccepted_deps.append(dep)
 
         return unaccepted_deps
@@ -149,10 +154,24 @@
         help=
         'A list of at least one GN label representing the target\'s dependencies.'
     )
+    parser.add_argument(
+        '--allowed_cts_deps',
+        nargs='+',
+        required=True,
+        help='The list of allowed CTS dependencies in allowed_cts_deps.gni')
+    parser.add_argument(
+        '--allowed_cts_dirs',
+        nargs='+',
+        required=True,
+        help='The list of allowed CTS dependency directories in allowed_cts_deps.gni')
     args = parser.parse_args()
     try:
-        cts_element = VerifyCtsDeps(
-            args.root_build_dir, args.output, args.invoker_label, args.deps)
+        cts_element = VerifyCtsDeps(args.root_build_dir,
+                                    args.output,
+                                    args.invoker_label,
+                                    args.deps,
+                                    args.allowed_cts_deps,
+                                    args.allowed_cts_dirs)
     except ValueError as e:
         print('ValueError: %s' % e)
         return 1
diff --git a/sdk/cts/build/verify_cts_deps_test.py b/sdk/cts/build/verify_cts_deps_test.py
index f4a558f..2e89c96 100755
--- a/sdk/cts/build/verify_cts_deps_test.py
+++ b/sdk/cts/build/verify_cts_deps_test.py
@@ -16,30 +16,49 @@
         cts_file = 'test.this_is_cts'
         invoker_label = "//sdk/cts/build:verify_cts_deps_test"
         deps = ['//zircon/public/lib/zxtest:zxtest']
+        allowed_cts_dirs = ['//sdk/*']
 
         try:
-            VerifyCtsDeps(root_build_dir, cts_file, invoker_label, deps)
+            VerifyCtsDeps(
+                root_build_dir, cts_file, invoker_label, deps, deps,
+                allowed_cts_dirs)
         except Exception as e:
             self.assertTrue(False, e)
 
         with self.assertRaises(ValueError):
-            VerifyCtsDeps('', cts_file, invoker_label, deps)
+            VerifyCtsDeps(
+                '', cts_file, invoker_label, deps, deps, allowed_cts_dirs)
         with self.assertRaises(ValueError):
-             VerifyCtsDeps('/this/path/doesnt/exist', cts_file, invoker_label, deps)
+            VerifyCtsDeps(
+                '/this/path/doesnt/exist', cts_file, invoker_label, deps, deps,
+                allowed_cts_dirs)
         with self.assertRaises(ValueError):
-             VerifyCtsDeps(root_build_dir, '', invoker_label, deps)
+            VerifyCtsDeps(
+                root_build_dir, '', invoker_label, deps, deps, allowed_cts_dirs)
         with self.assertRaises(ValueError):
-             VerifyCtsDeps(root_build_dir, cts_file, '', deps)
+            VerifyCtsDeps(
+                root_build_dir, cts_file, '', deps, deps, allowed_cts_dirs)
         with self.assertRaises(ValueError):
-             VerifyCtsDeps(root_build_dir, cts_file, invoker_label, [])
+            VerifyCtsDeps(
+                root_build_dir, cts_file, invoker_label, [], deps,
+                allowed_cts_dirs)
+        with self.assertRaises(ValueError):
+            VerifyCtsDeps(
+                root_build_dir, cts_file, invoker_label, deps, [],
+                allowed_cts_dirs)
+        with self.assertRaises(ValueError):
+            VerifyCtsDeps(
+                root_build_dir, cts_file, invoker_label, deps, deps, [])
 
     def test_get_file_path(self):
         root_build_dir = os.getcwd()
         cts_file = 'test.this_is_cts'
         invoker_label = "//sdk/cts/build:verify_cts_deps_test"
         deps = ['//zircon/public/lib/zxtest:zxtest']
+        allowed_cts_dirs = ['//sdk/*']
         cts_element = VerifyCtsDeps(
-            root_build_dir, cts_file, invoker_label, deps)
+            root_build_dir, cts_file, invoker_label, deps, deps,
+            allowed_cts_dirs)
 
         dep = '//zircon/public/lib/zxtest:zxtest'
         self.assertEqual(
@@ -60,47 +79,71 @@
         root_build_dir = os.getcwd()
         cts_file = 'test.this_is_cts'
         invoker_label = "//sdk/cts/build:verify_cts_deps_test"
+        allowed_cts_deps = ['//zircon/public/lib/zxtest:zxtest']
+        allowed_cts_dirs = ['//sdk/*']
 
-        deps = ['//zircon/public/lib/zxtest:zxtest']
         cts_element = VerifyCtsDeps(
-            root_build_dir, cts_file, invoker_label, deps)
+            root_build_dir, cts_file, invoker_label, allowed_cts_deps,
+            allowed_cts_deps, allowed_cts_dirs)
         self.assertListEqual(cts_element.verify_deps(), [])
 
         deps = ['//this/dep/isnt/allowed/in:cts']
         cts_element = VerifyCtsDeps(
-            root_build_dir, cts_file, invoker_label, deps)
+            root_build_dir, cts_file, invoker_label, deps, allowed_cts_deps,
+            allowed_cts_dirs)
         self.assertListEqual(cts_element.verify_deps(), deps)
 
         deps = [
-            '//this/dep/isnt/allowed/in:cts',
-            '//this/dep/isnt/allowed/in:cts2'
+            '//this/dep/isnt/allowed/in:cts', '//this/dep/isnt/allowed/in:cts2'
         ]
         cts_element = VerifyCtsDeps(
-            root_build_dir, cts_file, invoker_label, deps)
+            root_build_dir, cts_file, invoker_label, deps, allowed_cts_deps,
+            allowed_cts_dirs)
         self.assertListEqual(cts_element.verify_deps(), deps)
 
+        deps = ['//sdk/this/is/a/real:target']
+        cts_element = VerifyCtsDeps(
+            root_build_dir, cts_file, invoker_label, deps, allowed_cts_deps,
+            allowed_cts_dirs)
+        self.assertListEqual(cts_element.verify_deps(), [])
+
+        deps = [
+            '//sdk/this/is/a/real:target',
+            '//zircon/public/lib/zxtest:zxtest',
+        ]
+        cts_element = VerifyCtsDeps(
+            root_build_dir, cts_file, invoker_label, deps, allowed_cts_deps,
+            allowed_cts_dirs)
+        self.assertListEqual(cts_element.verify_deps(), [])
+
     def test_create_cts_dep_file(self):
         root_build_dir = os.getcwd()
         invoker_label = "//sdk/cts/build:verify_cts_deps_test"
         deps = ['//sdk:sdk', '//zircon/public/lib/zxtest:zxtest']
+        allowed_cts_deps = ['//zircon/public/lib/zxtest:zxtest']
+        allowed_cts_dirs = ['//sdk/*']
 
         with TemporaryDirectory() as cts_file:
-          cts_file += '/create_cts_dep_file.this_is_cts'
-          cts_element = VerifyCtsDeps(root_build_dir, cts_file, invoker_label, deps)
-          cts_element.create_cts_dep_file()
-          self.assertTrue(os.path.exists(cts_file))
-          with open(cts_file) as f:
-              lines = [line.strip() for line in f.readlines()]
-              self.assertListEqual(deps, lines)
+            cts_file += '/create_cts_dep_file.this_is_cts'
+            cts_element = VerifyCtsDeps(
+                root_build_dir, cts_file, invoker_label, deps, allowed_cts_deps,
+                allowed_cts_dirs)
+            cts_element.create_cts_dep_file()
+            self.assertTrue(os.path.exists(cts_file))
+            with open(cts_file) as f:
+                lines = [line.strip() for line in f.readlines()]
+                self.assertListEqual(deps, lines)
 
         with TemporaryDirectory() as cts_file:
-          cts_file += '/cts/create_cts_dep_file.this_is_cts'
-          cts_element = VerifyCtsDeps(root_build_dir, cts_file, invoker_label, deps)
-          cts_element.create_cts_dep_file()
-          self.assertTrue(os.path.exists(cts_file))
-          with open(cts_file) as f:
-              lines = [line.strip() for line in f.readlines()]
-              self.assertListEqual(deps, lines)
+            cts_file += '/cts/create_cts_dep_file.this_is_cts'
+            cts_element = VerifyCtsDeps(
+                root_build_dir, cts_file, invoker_label, deps, allowed_cts_deps,
+                allowed_cts_dirs)
+            cts_element.create_cts_dep_file()
+            self.assertTrue(os.path.exists(cts_file))
+            with open(cts_file) as f:
+                lines = [line.strip() for line in f.readlines()]
+                self.assertListEqual(deps, lines)
 
 
 if __name__ == '__main__':