We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Remove code paths related to mobly.test_runner.TestRunner.import_test_modules
In the new invocation model, the import_test_modules function and its associated code paths are not needed any more and should be removed.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mobly/keys.py`
Content:
```
1 #!/usr/bin/env python3.4
2 #
3 # Copyright 2016 Google Inc.
4 # 
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 # 
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 # 
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 
17 import enum
18 """This module has the global key values that are used across framework
19 modules.
20 """
21 
22 
23 class Config(enum.Enum):
24     """Enum values for test config related lookups.
25     """
26     # Keys used to look up values from test config files.
27     # These keys define the wording of test configs and their internal
28     # references.
29     key_log_path = "logpath"
30     key_testbed = "testbed"
31     key_testbed_name = "name"
32     key_config_path = "configpath"
33     key_test_paths = "testpaths"
34     key_port = "Port"
35     key_address = "Address"
36     # Internal keys, used internally, not exposed to user's config files.
37     ikey_user_param = "user_params"
38     ikey_testbed_name = "testbed_name"
39     ikey_logger = "log"
40     ikey_logpath = "log_path"
41     ikey_cli_args = "cli_args"
42 
43     # A list of keys whose values in configs should not be passed to test
44     # classes without unpacking first.
45     reserved_keys = (key_testbed, key_log_path, key_test_paths)
46 
47 
48 def get_name_by_value(value):
49     for name, member in Config.__members__.items():
50         if member.value == value:
51             return name
52     return None
53 
54 
55 def get_internal_value(external_value):
56     """Translates the value of an external key to the value of its
57     corresponding internal key.
58     """
59     return value_to_value(external_value, "i%s")
60 
61 
62 def get_module_name(name_in_config):
63     """Translates the name of a controller in config file to its module name.
64     """
65     return value_to_value(name_in_config, "m_%s")
66 
67 
68 def value_to_value(ref_value, pattern):
69     """Translates the value of a key to the value of its corresponding key. The
70     corresponding key is chosen based on the variable name pattern.
71     """
72     ref_key_name = get_name_by_value(ref_value)
73     if not ref_key_name:
74         return None
75     target_key_name = pattern % ref_key_name
76     try:
77         return getattr(Config, target_key_name).value
78     except AttributeError:
79         return None
80
```

Path: `mobly/config_parser.py`
Content:
```
1 #!/usr/bin/env python3.4
2 #
3 # Copyright 2016 Google Inc.
4 # 
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 # 
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 # 
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 
17 from builtins import str
18 
19 import os
20 import sys
21 
22 from mobly import keys
23 from mobly import utils
24 
25 # An environment variable defining the base location for Mobly logs.
26 _ENV_MOBLY_LOGPATH = 'MOBLY_LOGPATH'
27 
28 # An environment variable defining the test search paths for Mobly.
29 _ENV_MOBLY_TESTPATHS = 'MOBLY_TESTPATHS'
30 _PATH_SEPARATOR = ':'
31 
32 
33 class MoblyConfigError(Exception):
34     """Raised when there is a problem in test configuration file."""
35 
36 
37 def _validate_test_config(test_config):
38     """Validates the raw configuration loaded from the config file.
39 
40     Making sure all the required fields exist.
41     """
42     for k in keys.Config.reserved_keys.value:
43         if k not in test_config:
44             raise MoblyConfigError("Required key %s missing in test config." % k)
45 
46 
47 def _validate_testbed_name(name):
48     """Validates the name of a test bed.
49 
50     Since test bed names are used as part of the test run id, it needs to meet
51     certain requirements.
52 
53     Args:
54         name: The test bed's name specified in config file.
55 
56     Raises:
57         If the name does not meet any criteria, MoblyConfigError is raised.
58     """
59     if not name:
60         raise MoblyConfigError("Test bed names can't be empty.")
61     if not isinstance(name, str):
62         raise MoblyConfigError("Test bed names have to be string.")
63     for l in name:
64         if l not in utils.valid_filename_chars:
65             raise MoblyConfigError(
66                 "Char '%s' is not allowed in test bed names." % l)
67 
68 
69 def _validate_testbed_configs(testbed_configs):
70     """Validates the testbed configurations.
71 
72     Args:
73         testbed_configs: A list of testbed configuration json objects.
74 
75     Raises:
76         If any part of the configuration is invalid, MoblyConfigError is raised.
77     """
78     seen_names = set()
79     # Cross checks testbed configs for resource conflicts.
80     for config in testbed_configs:
81         # Check for conflicts between multiple concurrent testbed configs.
82         # No need to call it if there's only one testbed config.
83         name = config[keys.Config.key_testbed_name.value]
84         _validate_testbed_name(name)
85         # Test bed names should be unique.
86         if name in seen_names:
87             raise MoblyConfigError("Duplicate testbed name %s found." % name)
88         seen_names.add(name)
89 
90 
91 def _verify_test_class_name(test_cls_name):
92     if not test_cls_name.endswith("Test"):
93         raise MoblyConfigError(
94             ("Requested test class '%s' does not follow the test class naming "
95              "convention *Test.") % test_cls_name)
96 
97 
98 def gen_term_signal_handler(test_runners):
99     def termination_sig_handler(signal_num, frame):
100         for t in test_runners:
101             t.stop()
102         sys.exit(1)
103     return termination_sig_handler
104 
105 
106 def _parse_one_test_specifier(item):
107     """Parse one test specifier from command line input.
108 
109     This also verifies that the test class name and test case names follow
110     Mobly's naming conventions. A test class name has to end with "Test"; a test
111     case name has to start with "test".
112 
113     Args:
114         item: A string that specifies a test class or test cases in one test
115             class to run.
116 
117     Returns:
118         A tuple of a string and a list of strings. The string is the test class
119         name, the list of strings is a list of test case names. The list can be
120         None.
121     """
122     tokens = item.split(':')
123     if len(tokens) > 2:
124         raise MoblyConfigError("Syntax error in test specifier %s" % item)
125     if len(tokens) == 1:
126         # This should be considered a test class name
127         test_cls_name = tokens[0]
128         _verify_test_class_name(test_cls_name)
129         return (test_cls_name, None)
130     elif len(tokens) == 2:
131         # This should be considered a test class name followed by
132         # a list of test case names.
133         test_cls_name, test_case_names = tokens
134         clean_names = []
135         _verify_test_class_name(test_cls_name)
136         for elem in test_case_names.split(','):
137             test_case_name = elem.strip()
138             if not test_case_name.startswith("test_"):
139                 raise MoblyConfigError(("Requested test case '%s' in test class "
140                                  "'%s' does not follow the test case "
141                                  "naming convention test_*.") %
142                                 (test_case_name, test_cls_name))
143             clean_names.append(test_case_name)
144         return (test_cls_name, clean_names)
145 
146 
147 def parse_test_list(test_list):
148     """Parse user provided test list into internal format for test_runner.
149 
150     Args:
151         test_list: A list of test classes/cases.
152     """
153     result = []
154     for elem in test_list:
155         result.append(_parse_one_test_specifier(elem))
156     return result
157 
158 
159 def load_test_config_file(test_config_path, tb_filters=None):
160     """Processes the test configuration file provied by user.
161 
162     Loads the configuration file into a json object, unpacks each testbed
163     config into its own json object, and validate the configuration in the
164     process.
165 
166     Args:
167         test_config_path: Path to the test configuration file.
168         tb_filters: A subset of test bed names to be pulled from the config
169                     file. If None, then all test beds will be selected.
170 
171     Returns:
172         A list of test configuration json objects to be passed to
173         test_runner.TestRunner.
174     """
175     configs = utils.load_config(test_config_path)
176     if tb_filters:
177         tbs = []
178         for tb in configs[keys.Config.key_testbed.value]:
179             if tb[keys.Config.key_testbed_name.value] in tb_filters:
180                 tbs.append(tb)
181         if len(tbs) != len(tb_filters):
182             raise MoblyConfigError(
183                 ("Expect to find %d test bed configs, found %d. Check if"
184                  " you have the correct test bed names.") %
185                 (len(tb_filters), len(tbs)))
186         configs[keys.Config.key_testbed.value] = tbs
187 
188     if (not keys.Config.key_log_path.value in configs and
189             _ENV_MOBLY_LOGPATH in os.environ):
190         print('Using environment log path: %s' %
191               (os.environ[_ENV_MOBLY_LOGPATH]))
192         configs[keys.Config.key_log_path.value] = os.environ[_ENV_MOBLY_LOGPATH]
193     if (not keys.Config.key_test_paths.value in configs and
194             _ENV_MOBLY_TESTPATHS in os.environ):
195         print('Using environment test paths: %s' %
196               (os.environ[_ENV_MOBLY_TESTPATHS]))
197         configs[keys.Config.key_test_paths.value] = os.environ[
198             _ENV_MOBLY_TESTPATHS].split(_PATH_SEPARATOR)
199 
200     _validate_test_config(configs)
201     _validate_testbed_configs(configs[keys.Config.key_testbed.value])
202     k_log_path = keys.Config.key_log_path.value
203     configs[k_log_path] = utils.abs_path(configs[k_log_path])
204     config_path, _ = os.path.split(utils.abs_path(test_config_path))
205     configs[keys.Config.key_config_path] = config_path
206     tps = configs[keys.Config.key_test_paths.value]
207     # Unpack testbeds into separate json objects.
208     beds = configs.pop(keys.Config.key_testbed.value)
209     config_jsons = []
210     # TODO: See if there is a better way to do this: b/29836695
211     config_path, _ = os.path.split(utils.abs_path(test_config_path))
212     configs[keys.Config.key_config_path] = config_path
213     for original_bed_config in beds:
214         new_test_config = dict(configs)
215         new_test_config[keys.Config.key_testbed.value] = original_bed_config
216         # Keys in each test bed config will be copied to a level up to be
217         # picked up for user_params. If the key already exists in the upper
218         # level, the local one defined in test bed config overwrites the
219         # general one.
220         new_test_config.update(original_bed_config)
221         config_jsons.append(new_test_config)
222     return config_jsons
223 
224 
225 def parse_test_file(fpath):
226     """Parses a test file that contains test specifiers.
227 
228     Args:
229         fpath: A string that is the path to the test file to parse.
230 
231     Returns:
232         A list of strings, each is a test specifier.
233     """
234     with open(fpath, 'r') as f:
235         tf = []
236         for line in f:
237             line = line.strip()
238             if not line:
239                 continue
240             if len(tf) and (tf[-1].endswith(':') or tf[-1].endswith(',')):
241                 tf[-1] += line
242             else:
243                 tf.append(line)
244         return tf
245
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mobly/config_parser.py b/mobly/config_parser.py
--- a/mobly/config_parser.py
+++ b/mobly/config_parser.py
@@ -25,10 +25,6 @@
 # An environment variable defining the base location for Mobly logs.
 _ENV_MOBLY_LOGPATH = 'MOBLY_LOGPATH'
 
-# An environment variable defining the test search paths for Mobly.
-_ENV_MOBLY_TESTPATHS = 'MOBLY_TESTPATHS'
-_PATH_SEPARATOR = ':'
-
 
 class MoblyConfigError(Exception):
     """Raised when there is a problem in test configuration file."""
@@ -190,12 +186,6 @@
         print('Using environment log path: %s' %
               (os.environ[_ENV_MOBLY_LOGPATH]))
         configs[keys.Config.key_log_path.value] = os.environ[_ENV_MOBLY_LOGPATH]
-    if (not keys.Config.key_test_paths.value in configs and
-            _ENV_MOBLY_TESTPATHS in os.environ):
-        print('Using environment test paths: %s' %
-              (os.environ[_ENV_MOBLY_TESTPATHS]))
-        configs[keys.Config.key_test_paths.value] = os.environ[
-            _ENV_MOBLY_TESTPATHS].split(_PATH_SEPARATOR)
 
     _validate_test_config(configs)
     _validate_testbed_configs(configs[keys.Config.key_testbed.value])
@@ -203,7 +193,6 @@
     configs[k_log_path] = utils.abs_path(configs[k_log_path])
     config_path, _ = os.path.split(utils.abs_path(test_config_path))
     configs[keys.Config.key_config_path] = config_path
-    tps = configs[keys.Config.key_test_paths.value]
     # Unpack testbeds into separate json objects.
     beds = configs.pop(keys.Config.key_testbed.value)
     config_jsons = []
diff --git a/mobly/keys.py b/mobly/keys.py
--- a/mobly/keys.py
+++ b/mobly/keys.py
@@ -30,7 +30,6 @@
     key_testbed = "testbed"
     key_testbed_name = "name"
     key_config_path = "configpath"
-    key_test_paths = "testpaths"
     key_port = "Port"
     key_address = "Address"
     # Internal keys, used internally, not exposed to user's config files.
@@ -42,7 +41,7 @@
 
     # A list of keys whose values in configs should not be passed to test
     # classes without unpacking first.
-    reserved_keys = (key_testbed, key_log_path, key_test_paths)
+    reserved_keys = (key_testbed, key_log_path)
 
 
 def get_name_by_value(value):

{"golden_diff": "diff --git a/mobly/config_parser.py b/mobly/config_parser.py\n--- a/mobly/config_parser.py\n+++ b/mobly/config_parser.py\n@@ -25,10 +25,6 @@\n # An environment variable defining the base location for Mobly logs.\n _ENV_MOBLY_LOGPATH = 'MOBLY_LOGPATH'\n \n-# An environment variable defining the test search paths for Mobly.\n-_ENV_MOBLY_TESTPATHS = 'MOBLY_TESTPATHS'\n-_PATH_SEPARATOR = ':'\n-\n \n class MoblyConfigError(Exception):\n     \"\"\"Raised when there is a problem in test configuration file.\"\"\"\n@@ -190,12 +186,6 @@\n         print('Using environment log path: %s' %\n               (os.environ[_ENV_MOBLY_LOGPATH]))\n         configs[keys.Config.key_log_path.value] = os.environ[_ENV_MOBLY_LOGPATH]\n-    if (not keys.Config.key_test_paths.value in configs and\n-            _ENV_MOBLY_TESTPATHS in os.environ):\n-        print('Using environment test paths: %s' %\n-              (os.environ[_ENV_MOBLY_TESTPATHS]))\n-        configs[keys.Config.key_test_paths.value] = os.environ[\n-            _ENV_MOBLY_TESTPATHS].split(_PATH_SEPARATOR)\n \n     _validate_test_config(configs)\n     _validate_testbed_configs(configs[keys.Config.key_testbed.value])\n@@ -203,7 +193,6 @@\n     configs[k_log_path] = utils.abs_path(configs[k_log_path])\n     config_path, _ = os.path.split(utils.abs_path(test_config_path))\n     configs[keys.Config.key_config_path] = config_path\n-    tps = configs[keys.Config.key_test_paths.value]\n     # Unpack testbeds into separate json objects.\n     beds = configs.pop(keys.Config.key_testbed.value)\n     config_jsons = []\ndiff --git a/mobly/keys.py b/mobly/keys.py\n--- a/mobly/keys.py\n+++ b/mobly/keys.py\n@@ -30,7 +30,6 @@\n     key_testbed = \"testbed\"\n     key_testbed_name = \"name\"\n     key_config_path = \"configpath\"\n-    key_test_paths = \"testpaths\"\n     key_port = \"Port\"\n     key_address = \"Address\"\n     # Internal keys, used internally, not exposed to user's config files.\n@@ -42,7 +41,7 @@\n \n     # A list of keys whose values in configs should not be passed to test\n     # classes without unpacking first.\n-    reserved_keys = (key_testbed, key_log_path, key_test_paths)\n+    reserved_keys = (key_testbed, key_log_path)\n \n \n def get_name_by_value(value):\n", "issue": "Remove code paths related to mobly.test_runner.TestRunner.import_test_modules\nIn the new invocation model, the import_test_modules function and its associated code paths are not needed any more and should be removed.\n", "before_files": [{"content": "#!/usr/bin/env python3.4\n#\n# Copyright 2016 Google Inc.\n# \n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n# \n#     http://www.apache.org/licenses/LICENSE-2.0\n# \n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport enum\n\"\"\"This module has the global key values that are used across framework\nmodules.\n\"\"\"\n\n\nclass Config(enum.Enum):\n    \"\"\"Enum values for test config related lookups.\n    \"\"\"\n    # Keys used to look up values from test config files.\n    # These keys define the wording of test configs and their internal\n    # references.\n    key_log_path = \"logpath\"\n    key_testbed = \"testbed\"\n    key_testbed_name = \"name\"\n    key_config_path = \"configpath\"\n    key_test_paths = \"testpaths\"\n    key_port = \"Port\"\n    key_address = \"Address\"\n    # Internal keys, used internally, not exposed to user's config files.\n    ikey_user_param = \"user_params\"\n    ikey_testbed_name = \"testbed_name\"\n    ikey_logger = \"log\"\n    ikey_logpath = \"log_path\"\n    ikey_cli_args = \"cli_args\"\n\n    # A list of keys whose values in configs should not be passed to test\n    # classes without unpacking first.\n    reserved_keys = (key_testbed, key_log_path, key_test_paths)\n\n\ndef get_name_by_value(value):\n    for name, member in Config.__members__.items():\n        if member.value == value:\n            return name\n    return None\n\n\ndef get_internal_value(external_value):\n    \"\"\"Translates the value of an external key to the value of its\n    corresponding internal key.\n    \"\"\"\n    return value_to_value(external_value, \"i%s\")\n\n\ndef get_module_name(name_in_config):\n    \"\"\"Translates the name of a controller in config file to its module name.\n    \"\"\"\n    return value_to_value(name_in_config, \"m_%s\")\n\n\ndef value_to_value(ref_value, pattern):\n    \"\"\"Translates the value of a key to the value of its corresponding key. The\n    corresponding key is chosen based on the variable name pattern.\n    \"\"\"\n    ref_key_name = get_name_by_value(ref_value)\n    if not ref_key_name:\n        return None\n    target_key_name = pattern % ref_key_name\n    try:\n        return getattr(Config, target_key_name).value\n    except AttributeError:\n        return None\n", "path": "mobly/keys.py"}, {"content": "#!/usr/bin/env python3.4\n#\n# Copyright 2016 Google Inc.\n# \n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n# \n#     http://www.apache.org/licenses/LICENSE-2.0\n# \n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom builtins import str\n\nimport os\nimport sys\n\nfrom mobly import keys\nfrom mobly import utils\n\n# An environment variable defining the base location for Mobly logs.\n_ENV_MOBLY_LOGPATH = 'MOBLY_LOGPATH'\n\n# An environment variable defining the test search paths for Mobly.\n_ENV_MOBLY_TESTPATHS = 'MOBLY_TESTPATHS'\n_PATH_SEPARATOR = ':'\n\n\nclass MoblyConfigError(Exception):\n    \"\"\"Raised when there is a problem in test configuration file.\"\"\"\n\n\ndef _validate_test_config(test_config):\n    \"\"\"Validates the raw configuration loaded from the config file.\n\n    Making sure all the required fields exist.\n    \"\"\"\n    for k in keys.Config.reserved_keys.value:\n        if k not in test_config:\n            raise MoblyConfigError(\"Required key %s missing in test config.\" % k)\n\n\ndef _validate_testbed_name(name):\n    \"\"\"Validates the name of a test bed.\n\n    Since test bed names are used as part of the test run id, it needs to meet\n    certain requirements.\n\n    Args:\n        name: The test bed's name specified in config file.\n\n    Raises:\n        If the name does not meet any criteria, MoblyConfigError is raised.\n    \"\"\"\n    if not name:\n        raise MoblyConfigError(\"Test bed names can't be empty.\")\n    if not isinstance(name, str):\n        raise MoblyConfigError(\"Test bed names have to be string.\")\n    for l in name:\n        if l not in utils.valid_filename_chars:\n            raise MoblyConfigError(\n                \"Char '%s' is not allowed in test bed names.\" % l)\n\n\ndef _validate_testbed_configs(testbed_configs):\n    \"\"\"Validates the testbed configurations.\n\n    Args:\n        testbed_configs: A list of testbed configuration json objects.\n\n    Raises:\n        If any part of the configuration is invalid, MoblyConfigError is raised.\n    \"\"\"\n    seen_names = set()\n    # Cross checks testbed configs for resource conflicts.\n    for config in testbed_configs:\n        # Check for conflicts between multiple concurrent testbed configs.\n        # No need to call it if there's only one testbed config.\n        name = config[keys.Config.key_testbed_name.value]\n        _validate_testbed_name(name)\n        # Test bed names should be unique.\n        if name in seen_names:\n            raise MoblyConfigError(\"Duplicate testbed name %s found.\" % name)\n        seen_names.add(name)\n\n\ndef _verify_test_class_name(test_cls_name):\n    if not test_cls_name.endswith(\"Test\"):\n        raise MoblyConfigError(\n            (\"Requested test class '%s' does not follow the test class naming \"\n             \"convention *Test.\") % test_cls_name)\n\n\ndef gen_term_signal_handler(test_runners):\n    def termination_sig_handler(signal_num, frame):\n        for t in test_runners:\n            t.stop()\n        sys.exit(1)\n    return termination_sig_handler\n\n\ndef _parse_one_test_specifier(item):\n    \"\"\"Parse one test specifier from command line input.\n\n    This also verifies that the test class name and test case names follow\n    Mobly's naming conventions. A test class name has to end with \"Test\"; a test\n    case name has to start with \"test\".\n\n    Args:\n        item: A string that specifies a test class or test cases in one test\n            class to run.\n\n    Returns:\n        A tuple of a string and a list of strings. The string is the test class\n        name, the list of strings is a list of test case names. The list can be\n        None.\n    \"\"\"\n    tokens = item.split(':')\n    if len(tokens) > 2:\n        raise MoblyConfigError(\"Syntax error in test specifier %s\" % item)\n    if len(tokens) == 1:\n        # This should be considered a test class name\n        test_cls_name = tokens[0]\n        _verify_test_class_name(test_cls_name)\n        return (test_cls_name, None)\n    elif len(tokens) == 2:\n        # This should be considered a test class name followed by\n        # a list of test case names.\n        test_cls_name, test_case_names = tokens\n        clean_names = []\n        _verify_test_class_name(test_cls_name)\n        for elem in test_case_names.split(','):\n            test_case_name = elem.strip()\n            if not test_case_name.startswith(\"test_\"):\n                raise MoblyConfigError((\"Requested test case '%s' in test class \"\n                                 \"'%s' does not follow the test case \"\n                                 \"naming convention test_*.\") %\n                                (test_case_name, test_cls_name))\n            clean_names.append(test_case_name)\n        return (test_cls_name, clean_names)\n\n\ndef parse_test_list(test_list):\n    \"\"\"Parse user provided test list into internal format for test_runner.\n\n    Args:\n        test_list: A list of test classes/cases.\n    \"\"\"\n    result = []\n    for elem in test_list:\n        result.append(_parse_one_test_specifier(elem))\n    return result\n\n\ndef load_test_config_file(test_config_path, tb_filters=None):\n    \"\"\"Processes the test configuration file provied by user.\n\n    Loads the configuration file into a json object, unpacks each testbed\n    config into its own json object, and validate the configuration in the\n    process.\n\n    Args:\n        test_config_path: Path to the test configuration file.\n        tb_filters: A subset of test bed names to be pulled from the config\n                    file. If None, then all test beds will be selected.\n\n    Returns:\n        A list of test configuration json objects to be passed to\n        test_runner.TestRunner.\n    \"\"\"\n    configs = utils.load_config(test_config_path)\n    if tb_filters:\n        tbs = []\n        for tb in configs[keys.Config.key_testbed.value]:\n            if tb[keys.Config.key_testbed_name.value] in tb_filters:\n                tbs.append(tb)\n        if len(tbs) != len(tb_filters):\n            raise MoblyConfigError(\n                (\"Expect to find %d test bed configs, found %d. Check if\"\n                 \" you have the correct test bed names.\") %\n                (len(tb_filters), len(tbs)))\n        configs[keys.Config.key_testbed.value] = tbs\n\n    if (not keys.Config.key_log_path.value in configs and\n            _ENV_MOBLY_LOGPATH in os.environ):\n        print('Using environment log path: %s' %\n              (os.environ[_ENV_MOBLY_LOGPATH]))\n        configs[keys.Config.key_log_path.value] = os.environ[_ENV_MOBLY_LOGPATH]\n    if (not keys.Config.key_test_paths.value in configs and\n            _ENV_MOBLY_TESTPATHS in os.environ):\n        print('Using environment test paths: %s' %\n              (os.environ[_ENV_MOBLY_TESTPATHS]))\n        configs[keys.Config.key_test_paths.value] = os.environ[\n            _ENV_MOBLY_TESTPATHS].split(_PATH_SEPARATOR)\n\n    _validate_test_config(configs)\n    _validate_testbed_configs(configs[keys.Config.key_testbed.value])\n    k_log_path = keys.Config.key_log_path.value\n    configs[k_log_path] = utils.abs_path(configs[k_log_path])\n    config_path, _ = os.path.split(utils.abs_path(test_config_path))\n    configs[keys.Config.key_config_path] = config_path\n    tps = configs[keys.Config.key_test_paths.value]\n    # Unpack testbeds into separate json objects.\n    beds = configs.pop(keys.Config.key_testbed.value)\n    config_jsons = []\n    # TODO: See if there is a better way to do this: b/29836695\n    config_path, _ = os.path.split(utils.abs_path(test_config_path))\n    configs[keys.Config.key_config_path] = config_path\n    for original_bed_config in beds:\n        new_test_config = dict(configs)\n        new_test_config[keys.Config.key_testbed.value] = original_bed_config\n        # Keys in each test bed config will be copied to a level up to be\n        # picked up for user_params. If the key already exists in the upper\n        # level, the local one defined in test bed config overwrites the\n        # general one.\n        new_test_config.update(original_bed_config)\n        config_jsons.append(new_test_config)\n    return config_jsons\n\n\ndef parse_test_file(fpath):\n    \"\"\"Parses a test file that contains test specifiers.\n\n    Args:\n        fpath: A string that is the path to the test file to parse.\n\n    Returns:\n        A list of strings, each is a test specifier.\n    \"\"\"\n    with open(fpath, 'r') as f:\n        tf = []\n        for line in f:\n            line = line.strip()\n            if not line:\n                continue\n            if len(tf) and (tf[-1].endswith(':') or tf[-1].endswith(',')):\n                tf[-1] += line\n            else:\n                tf.append(line)\n        return tf\n", "path": "mobly/config_parser.py"}], "after_files": [{"content": "#!/usr/bin/env python3.4\n#\n# Copyright 2016 Google Inc.\n# \n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n# \n#     http://www.apache.org/licenses/LICENSE-2.0\n# \n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport enum\n\"\"\"This module has the global key values that are used across framework\nmodules.\n\"\"\"\n\n\nclass Config(enum.Enum):\n    \"\"\"Enum values for test config related lookups.\n    \"\"\"\n    # Keys used to look up values from test config files.\n    # These keys define the wording of test configs and their internal\n    # references.\n    key_log_path = \"logpath\"\n    key_testbed = \"testbed\"\n    key_testbed_name = \"name\"\n    key_config_path = \"configpath\"\n    key_port = \"Port\"\n    key_address = \"Address\"\n    # Internal keys, used internally, not exposed to user's config files.\n    ikey_user_param = \"user_params\"\n    ikey_testbed_name = \"testbed_name\"\n    ikey_logger = \"log\"\n    ikey_logpath = \"log_path\"\n    ikey_cli_args = \"cli_args\"\n\n    # A list of keys whose values in configs should not be passed to test\n    # classes without unpacking first.\n    reserved_keys = (key_testbed, key_log_path)\n\n\ndef get_name_by_value(value):\n    for name, member in Config.__members__.items():\n        if member.value == value:\n            return name\n    return None\n\n\ndef get_internal_value(external_value):\n    \"\"\"Translates the value of an external key to the value of its\n    corresponding internal key.\n    \"\"\"\n    return value_to_value(external_value, \"i%s\")\n\n\ndef get_module_name(name_in_config):\n    \"\"\"Translates the name of a controller in config file to its module name.\n    \"\"\"\n    return value_to_value(name_in_config, \"m_%s\")\n\n\ndef value_to_value(ref_value, pattern):\n    \"\"\"Translates the value of a key to the value of its corresponding key. The\n    corresponding key is chosen based on the variable name pattern.\n    \"\"\"\n    ref_key_name = get_name_by_value(ref_value)\n    if not ref_key_name:\n        return None\n    target_key_name = pattern % ref_key_name\n    try:\n        return getattr(Config, target_key_name).value\n    except AttributeError:\n        return None\n", "path": "mobly/keys.py"}, {"content": "#!/usr/bin/env python3.4\n#\n# Copyright 2016 Google Inc.\n# \n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n# \n#     http://www.apache.org/licenses/LICENSE-2.0\n# \n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom builtins import str\n\nimport os\nimport sys\n\nfrom mobly import keys\nfrom mobly import utils\n\n# An environment variable defining the base location for Mobly logs.\n_ENV_MOBLY_LOGPATH = 'MOBLY_LOGPATH'\n\n\nclass MoblyConfigError(Exception):\n    \"\"\"Raised when there is a problem in test configuration file.\"\"\"\n\n\ndef _validate_test_config(test_config):\n    \"\"\"Validates the raw configuration loaded from the config file.\n\n    Making sure all the required fields exist.\n    \"\"\"\n    for k in keys.Config.reserved_keys.value:\n        if k not in test_config:\n            raise MoblyConfigError(\"Required key %s missing in test config.\" % k)\n\n\ndef _validate_testbed_name(name):\n    \"\"\"Validates the name of a test bed.\n\n    Since test bed names are used as part of the test run id, it needs to meet\n    certain requirements.\n\n    Args:\n        name: The test bed's name specified in config file.\n\n    Raises:\n        If the name does not meet any criteria, MoblyConfigError is raised.\n    \"\"\"\n    if not name:\n        raise MoblyConfigError(\"Test bed names can't be empty.\")\n    if not isinstance(name, str):\n        raise MoblyConfigError(\"Test bed names have to be string.\")\n    for l in name:\n        if l not in utils.valid_filename_chars:\n            raise MoblyConfigError(\n                \"Char '%s' is not allowed in test bed names.\" % l)\n\n\ndef _validate_testbed_configs(testbed_configs):\n    \"\"\"Validates the testbed configurations.\n\n    Args:\n        testbed_configs: A list of testbed configuration json objects.\n\n    Raises:\n        If any part of the configuration is invalid, MoblyConfigError is raised.\n    \"\"\"\n    seen_names = set()\n    # Cross checks testbed configs for resource conflicts.\n    for config in testbed_configs:\n        # Check for conflicts between multiple concurrent testbed configs.\n        # No need to call it if there's only one testbed config.\n        name = config[keys.Config.key_testbed_name.value]\n        _validate_testbed_name(name)\n        # Test bed names should be unique.\n        if name in seen_names:\n            raise MoblyConfigError(\"Duplicate testbed name %s found.\" % name)\n        seen_names.add(name)\n\n\ndef _verify_test_class_name(test_cls_name):\n    if not test_cls_name.endswith(\"Test\"):\n        raise MoblyConfigError(\n            (\"Requested test class '%s' does not follow the test class naming \"\n             \"convention *Test.\") % test_cls_name)\n\n\ndef gen_term_signal_handler(test_runners):\n    def termination_sig_handler(signal_num, frame):\n        for t in test_runners:\n            t.stop()\n        sys.exit(1)\n    return termination_sig_handler\n\n\ndef _parse_one_test_specifier(item):\n    \"\"\"Parse one test specifier from command line input.\n\n    This also verifies that the test class name and test case names follow\n    Mobly's naming conventions. A test class name has to end with \"Test\"; a test\n    case name has to start with \"test\".\n\n    Args:\n        item: A string that specifies a test class or test cases in one test\n            class to run.\n\n    Returns:\n        A tuple of a string and a list of strings. The string is the test class\n        name, the list of strings is a list of test case names. The list can be\n        None.\n    \"\"\"\n    tokens = item.split(':')\n    if len(tokens) > 2:\n        raise MoblyConfigError(\"Syntax error in test specifier %s\" % item)\n    if len(tokens) == 1:\n        # This should be considered a test class name\n        test_cls_name = tokens[0]\n        _verify_test_class_name(test_cls_name)\n        return (test_cls_name, None)\n    elif len(tokens) == 2:\n        # This should be considered a test class name followed by\n        # a list of test case names.\n        test_cls_name, test_case_names = tokens\n        clean_names = []\n        _verify_test_class_name(test_cls_name)\n        for elem in test_case_names.split(','):\n            test_case_name = elem.strip()\n            if not test_case_name.startswith(\"test_\"):\n                raise MoblyConfigError((\"Requested test case '%s' in test class \"\n                                 \"'%s' does not follow the test case \"\n                                 \"naming convention test_*.\") %\n                                (test_case_name, test_cls_name))\n            clean_names.append(test_case_name)\n        return (test_cls_name, clean_names)\n\n\ndef parse_test_list(test_list):\n    \"\"\"Parse user provided test list into internal format for test_runner.\n\n    Args:\n        test_list: A list of test classes/cases.\n    \"\"\"\n    result = []\n    for elem in test_list:\n        result.append(_parse_one_test_specifier(elem))\n    return result\n\n\ndef load_test_config_file(test_config_path, tb_filters=None):\n    \"\"\"Processes the test configuration file provied by user.\n\n    Loads the configuration file into a json object, unpacks each testbed\n    config into its own json object, and validate the configuration in the\n    process.\n\n    Args:\n        test_config_path: Path to the test configuration file.\n        tb_filters: A subset of test bed names to be pulled from the config\n                    file. If None, then all test beds will be selected.\n\n    Returns:\n        A list of test configuration json objects to be passed to\n        test_runner.TestRunner.\n    \"\"\"\n    configs = utils.load_config(test_config_path)\n    if tb_filters:\n        tbs = []\n        for tb in configs[keys.Config.key_testbed.value]:\n            if tb[keys.Config.key_testbed_name.value] in tb_filters:\n                tbs.append(tb)\n        if len(tbs) != len(tb_filters):\n            raise MoblyConfigError(\n                (\"Expect to find %d test bed configs, found %d. Check if\"\n                 \" you have the correct test bed names.\") %\n                (len(tb_filters), len(tbs)))\n        configs[keys.Config.key_testbed.value] = tbs\n\n    if (not keys.Config.key_log_path.value in configs and\n            _ENV_MOBLY_LOGPATH in os.environ):\n        print('Using environment log path: %s' %\n              (os.environ[_ENV_MOBLY_LOGPATH]))\n        configs[keys.Config.key_log_path.value] = os.environ[_ENV_MOBLY_LOGPATH]\n\n    _validate_test_config(configs)\n    _validate_testbed_configs(configs[keys.Config.key_testbed.value])\n    k_log_path = keys.Config.key_log_path.value\n    configs[k_log_path] = utils.abs_path(configs[k_log_path])\n    config_path, _ = os.path.split(utils.abs_path(test_config_path))\n    configs[keys.Config.key_config_path] = config_path\n    # Unpack testbeds into separate json objects.\n    beds = configs.pop(keys.Config.key_testbed.value)\n    config_jsons = []\n    # TODO: See if there is a better way to do this: b/29836695\n    config_path, _ = os.path.split(utils.abs_path(test_config_path))\n    configs[keys.Config.key_config_path] = config_path\n    for original_bed_config in beds:\n        new_test_config = dict(configs)\n        new_test_config[keys.Config.key_testbed.value] = original_bed_config\n        # Keys in each test bed config will be copied to a level up to be\n        # picked up for user_params. If the key already exists in the upper\n        # level, the local one defined in test bed config overwrites the\n        # general one.\n        new_test_config.update(original_bed_config)\n        config_jsons.append(new_test_config)\n    return config_jsons\n\n\ndef parse_test_file(fpath):\n    \"\"\"Parses a test file that contains test specifiers.\n\n    Args:\n        fpath: A string that is the path to the test file to parse.\n\n    Returns:\n        A list of strings, each is a test specifier.\n    \"\"\"\n    with open(fpath, 'r') as f:\n        tf = []\n        for line in f:\n            line = line.strip()\n            if not line:\n                continue\n            if len(tf) and (tf[-1].endswith(':') or tf[-1].endswith(',')):\n                tf[-1] += line\n            else:\n                tf.append(line)\n        return tf\n", "path": "mobly/config_parser.py"}]}