Accept multiple parameters for parameterized tests

Previously Atest only splits test name and methods with comma, which
leads unexpected parsing result when giving is multiple parameters like
    test_method1[[2,3],15],test_method2[2,3],test_method[0]
This change resolves the parsing problem.

Bug: 260183137
Test: atest com.google.android.camera.pts.MultiCamera2022Test#testBasicPhysicalCameraPairConcurrentStreaming[[2,3],37]
Change-Id: Iccbd8dc3fb4a3093e248e22d122a72c91a535c7c
diff --git a/atest/atest_error.py b/atest/atest_error.py
index 7ab8b5f..30afad3 100644
--- a/atest/atest_error.py
+++ b/atest/atest_error.py
@@ -35,6 +35,9 @@
 class TooManyMethodsError(TestDiscoveryException):
     """Raised when input string contains more than one # character."""
 
+class MoreThanOneClassError(TestDiscoveryException):
+    """Raised when multiple classes given in 'classA,classB' pattern."""
+
 class MethodWithoutClassError(TestDiscoveryException):
     """Raised when method is appended via # but no class file specified."""
 
diff --git a/atest/test_finders/test_finder_utils.py b/atest/test_finders/test_finder_utils.py
index 2be0261..3e56959 100644
--- a/atest/test_finders/test_finder_utils.py
+++ b/atest/test_finders/test_finder_utils.py
@@ -173,7 +173,6 @@
 _VTS_BINARY_SRC_DELIM_RE = re.compile(r'.*::(?P<target>.*)$')
 _VTS_OUT_DATA_APP_PATH = 'DATA/app'
 
-# pylint: disable=inconsistent-return-statements
 def split_methods(user_input):
     """Split user input string into test reference and list of methods.
 
@@ -196,16 +195,39 @@
             class1#method,class2#method
             path1#method,path2#method
     """
+    error_msg = (
+        'Too many "{}" characters in user input:\n\t{}\n'
+        'Multiple classes should be separated by space, and methods belong to '
+        'the same class should be separated by comma. Example syntaxes are:\n'
+        '\tclass1 class2#method1 class3#method2,method3\n'
+        '\tclass1#method class2#method')
+    if not '#' in user_input:
+        if ',' in user_input:
+            raise atest_error.MoreThanOneClassError(
+                error_msg.format(',', user_input))
+        return user_input, frozenset()
     parts = user_input.split('#')
-    if len(parts) == 1:
-        return parts[0], frozenset()
-    if len(parts) == 2:
-        return parts[0], frozenset(parts[1].split(','))
-    raise atest_error.TooManyMethodsError(
-        'Too many methods specified with # character in user input: %s.'
-        '\n\nOnly one class#method combination supported per positional'
-        ' argument. Multiple classes should be separated by spaces: '
-        'class#method class#method')
+    if len(parts) > 2:
+        raise atest_error.TooManyMethodsError(
+            error_msg.format('#', user_input))
+    # (b/260183137) Support parsing multiple parameters.
+    parsed_methods = []
+    brackets = ('[', ']')
+    for part in parts[1].split(','):
+        count = {part.count(p) for p in brackets}
+        # If brackets are in pair, the length of count should be 1.
+        if len(count) == 1:
+            parsed_methods.append(part)
+        else:
+            # The front part of the pair, e.g. 'method[1'
+            if re.compile(r'^[a-zA-Z0-9]+\[').match(part):
+                parsed_methods.append(part)
+                continue
+            # The rear part of the pair, e.g. '5]]', accumulate this part to
+            # the last index of parsed_method.
+            else:
+                parsed_methods[-1] += f',{part}'
+    return parts[0], frozenset(parsed_methods)
 
 
 # pylint: disable=inconsistent-return-statements
diff --git a/atest/test_finders/test_finder_utils_unittest.py b/atest/test_finders/test_finder_utils_unittest.py
index 96bd7e4..193be76 100755
--- a/atest/test_finders/test_finder_utils_unittest.py
+++ b/atest/test_finders/test_finder_utils_unittest.py
@@ -129,6 +129,10 @@
         self.assertRaises(
             atest_error.TooManyMethodsError, test_finder_utils.split_methods,
             'class.name#Method,class.name.2#method')
+        self.assertRaises(
+            atest_error.MoreThanOneClassError, test_finder_utils.split_methods,
+            'class.name1,class.name2,class.name3'
+        )
         # Path
         unittest_utils.assert_strict_equal(
             self,
@@ -138,6 +142,11 @@
             self,
             test_finder_utils.split_methods('foo/bar/class.java#Method'),
             ('foo/bar/class.java', {'Method'}))
+        # Multiple parameters
+        unittest_utils.assert_strict_equal(
+            self,
+            test_finder_utils.split_methods('Class.Name#method[1],method[2,[3,4]]'),
+            ('Class.Name', {'method[1]', 'method[2,[3,4]]'}))
 
     @mock.patch.object(test_finder_utils, 'has_method_in_file',
                        return_value=False)