Merge tag 'android-13.0.0_r32' into int/13/fp3
Android 13.0.0 release 32
* tag 'android-13.0.0_r32':
ATest: Add a sso authentication when upload test results.
Manual merge "Remove --rebuild-module-info suggestion"
Manual merge of "Avoid KeyError when running native tests"
Change-Id: I7143563ccce54dd49456d45b515cf0ba824b24f2
diff --git a/atest/atest.py b/atest/atest.py
index a6e9a3d..794be87 100755
--- a/atest/atest.py
+++ b/atest/atest.py
@@ -233,6 +233,7 @@
'collect_tests_only': constants.COLLECT_TESTS_ONLY,
'custom_args': constants.CUSTOM_ARGS,
'disable_teardown': constants.DISABLE_TEARDOWN,
+ 'disable_upload_result': constants.DISABLE_UPLOAD_RESULT,
'dry_run': constants.DRY_RUN,
'enable_device_preparer': constants.ENABLE_DEVICE_PREPARER,
'flakes_info': constants.FLAKES_INFO,
diff --git a/atest/atest_arg_parser.py b/atest/atest_arg_parser.py
index c7caaa0..a285653 100644
--- a/atest/atest_arg_parser.py
+++ b/atest/atest_arg_parser.py
@@ -73,7 +73,14 @@
REBUILD_MODULE_INFO = ('Forces a rebuild of the module-info.json file. '
'This may be necessary following a repo sync or '
'when writing a new test.')
-REQUEST_UPLOAD_RESULT = 'Request permission to upload test result.'
+
+REQUEST_UPLOAD_RESULT = ('Request permission to upload test result. This option '
+ 'only needs to set once and takes effect until '
+ '--disable-upload-result is set.')
+DISABLE_UPLOAD_RESULT = ('Turn off the upload of test result. This option '
+ 'only needs to set once and takes effect until '
+ '--request-upload-result is set')
+
RERUN_UNTIL_FAILURE = ('Rerun all tests until a failure occurs or the max '
'iteration is reached. (default: forever!)')
# For Integer.MAX_VALUE == (2**31 - 1) and not possible to give a larger integer
@@ -175,8 +182,13 @@
action='store_true')
self.add_argument('-w', '--wait-for-debugger', action='store_true',
help=WAIT_FOR_DEBUGGER)
- self.add_argument('--request-upload-result', action='store_true',
+ # Options for request/disable upload results. They are mutually
+ # exclusive in a command line.
+ ugroup = self.add_mutually_exclusive_group()
+ ugroup.add_argument('--request-upload-result', action='store_true',
help=REQUEST_UPLOAD_RESULT)
+ ugroup.add_argument('--disable-upload-result', action='store_true',
+ help=DISABLE_UPLOAD_RESULT)
# Options related to Test Mapping
self.add_argument('-p', '--test-mapping', action='store_true',
@@ -338,6 +350,7 @@
CLEAR_CACHE=CLEAR_CACHE,
COLLECT_TESTS_ONLY=COLLECT_TESTS_ONLY,
DISABLE_TEARDOWN=DISABLE_TEARDOWN,
+ DISABLE_UPLOAD_RESULT=DISABLE_UPLOAD_RESULT,
DRY_RUN=DRY_RUN,
ENABLE_DEVICE_PREPARER=ENABLE_DEVICE_PREPARER,
ENABLE_FILE_PATTERNS=ENABLE_FILE_PATTERNS,
diff --git a/atest/atest_enum.py b/atest/atest_enum.py
index 57dcbb4..825f786 100644
--- a/atest/atest_enum.py
+++ b/atest/atest_enum.py
@@ -21,7 +21,7 @@
@unique
class DetectType(IntEnum):
"""An Enum class for local_detect_event."""
- # Detect type for local_detect_event; next expansion: 20
+ # Detect type for local_detect_event; next expansion: 22
BUG_DETECTED = 0
ACLOUD_CREATE = 1
FIND_BUILD = 2
@@ -48,6 +48,8 @@
TEST_NULL_ARGS = 17
MODULE_MERGE = 18
MODULE_INFO_INIT_TIME = 19
+ MODULE_MERGE_MS = 20
+ NATIVE_TEST_NOT_FOUND = 21
@unique
class ExitCode(IntEnum):
diff --git a/atest/atest_utils.py b/atest/atest_utils.py
index 4749063..036b291 100644
--- a/atest/atest_utils.py
+++ b/atest/atest_utils.py
@@ -406,9 +406,6 @@
return True
except subprocess.CalledProcessError as err:
logging.error('Build failure when running: %s', ' '.join(cmd))
- print(constants.REBUILD_MODULE_INFO_MSG.format(
- colorize(constants.REBUILD_MODULE_INFO_FLAG,
- constants.RED)))
if err.output:
logging.error(err.output)
return False
diff --git a/atest/cli_translator.py b/atest/cli_translator.py
index 945428a..a7554c3 100644
--- a/atest/cli_translator.py
+++ b/atest/cli_translator.py
@@ -254,13 +254,6 @@
if find_test_err_msg:
print('%s\n' % (atest_utils.colorize(
find_test_err_msg, constants.MAGENTA)))
- else:
- # TODO: remove "self.mod_info is None" after refactoring module_info
- if self.mod_info is None or not self.mod_info.force_build:
- print(constants.REBUILD_MODULE_INFO_MSG.format(
- atest_utils.colorize(constants.REBUILD_MODULE_INFO_FLAG,
- constants.RED)))
- print('')
return None
def _get_test_infos(self, tests, test_mapping_test_details=None):
diff --git a/atest/constants_default.py b/atest/constants_default.py
index 065ee12..e3122a0 100644
--- a/atest/constants_default.py
+++ b/atest/constants_default.py
@@ -61,6 +61,7 @@
TF_EARLY_DEVICE_RELEASE = 'TF_EARLY_DEVICE_RELEASE'
BAZEL_MODE_FEATURES = 'BAZEL_MODE_FEATURES'
REQUEST_UPLOAD_RESULT = 'REQUEST_UPLOAD_RESULT'
+DISABLE_UPLOAD_RESULT = 'DISABLE_UPLOAD_RESULT'
MODULES_IN = 'MODULES-IN-'
NO_ENABLE_ROOT = 'NO_ENABLE_ROOT'
VERIFY_ENV_VARIABLE = 'VERIFY_ENV_VARIABLE'
@@ -329,9 +330,10 @@
DISCOVERY_SERVICE = ''
STORAGE2_TEST_URI = ''
-# messages that share among libraries.
-REBUILD_MODULE_INFO_MSG = ('(This can happen after a repo sync or if the test'
- ' is new. Running with "{}" may resolve the issue.)')
+# SSO constants.
+TOKEN_EXCHANGE_COMMAND = ''
+TOKEN_EXCHANGE_REQUEST = ''
+SCOPE = ''
# Example arguments used in ~/.atest/config
ATEST_EXAMPLE_ARGS = ('## Specify only one option per line; any test name/path will be ignored automatically.\n'
diff --git a/atest/logstorage/atest_gcp_utils.py b/atest/logstorage/atest_gcp_utils.py
index 4d95577..a1cc684 100644
--- a/atest/logstorage/atest_gcp_utils.py
+++ b/atest/logstorage/atest_gcp_utils.py
@@ -28,8 +28,10 @@
"""
from __future__ import print_function
-import os
+import getpass
import logging
+import os
+import subprocess
import uuid
try:
import httplib2
@@ -37,9 +39,8 @@
logging.debug('Import error due to %s', e)
from pathlib import Path
-import constants
+from socket import socket
-from logstorage import logstorage_utils
try:
# pylint: disable=import-error
from oauth2client import client as oauth2_client
@@ -48,7 +49,9 @@
except ModuleNotFoundError as e:
logging.debug('Import error due to %s', e)
+from logstorage import logstorage_utils
import atest_utils
+import constants
class RunFlowFlags():
"""Flags for oauth2client.tools.run_flow."""
@@ -117,6 +120,18 @@
Returns:
An oauth2client.OAuth2Credentials instance.
"""
+ credentials = None
+ # SSO auth
+ try:
+ token = self._get_sso_access_token()
+ credentials = oauth2_client.AccessTokenCredentials(
+ token , 'atest')
+ if credentials:
+ return credentials
+ # pylint: disable=broad-except
+ except Exception as e:
+ logging.debug('Exception:%s', e)
+ # GCP auth flow
credentials = self.get_refreshed_credential_from_file(creds_file_path)
if not credentials:
storage = multistore_file.get_credential_storage(
@@ -130,21 +145,53 @@
def _run_auth_flow(self, storage):
"""Get user oauth2 credentials.
+ Using the loopback IP address flow for desktop clients.
+
Args:
storage: GCP storage object.
Returns:
An oauth2client.OAuth2Credentials instance.
"""
- flags = RunFlowFlags(browser_auth=False)
+ flags = RunFlowFlags(browser_auth=True)
+
+ # Get a free port on demand.
+ port = None
+ while not port or port < 10000:
+ with socket() as local_socket:
+ local_socket.bind(('',0))
+ _, port = local_socket.getsockname()
+ _localhost_port = port
+ _direct_uri = f'http://localhost:{_localhost_port}'
flow = oauth2_client.OAuth2WebServerFlow(
client_id=self.client_id,
client_secret=self.client_secret,
scope=self.scope,
- user_agent=self.user_agent)
+ user_agent=self.user_agent,
+ redirect_uri=f'{_direct_uri}')
credentials = oauth2_tools.run_flow(
flow=flow, storage=storage, flags=flags)
return credentials
+ def _get_sso_access_token(self):
+ """Use stubby command line to exchange corp sso to a scoped oauth
+ token.
+
+ Returns:
+ A token string.
+ """
+ if not constants.TOKEN_EXCHANGE_COMMAND:
+ return None
+
+ request = constants.TOKEN_EXCHANGE_REQUEST.format(
+ user=getpass.getuser(), scope=constants.SCOPE)
+ # The output format is: oauth2_token: "<TOKEN>"
+ return subprocess.run(constants.TOKEN_EXCHANGE_COMMAND,
+ input=request,
+ check=True,
+ text=True,
+ shell=True,
+ stdout=subprocess.PIPE).stdout.split('"')[1]
+
def do_upload_flow(extra_args):
"""Run upload flow.
@@ -157,9 +204,7 @@
tuple(invocation, workunit)
"""
config_folder = os.path.join(atest_utils.get_misc_dir(), '.atest')
- creds = request_consent_of_upload_test_result(
- config_folder,
- extra_args.get(constants.REQUEST_UPLOAD_RESULT, None))
+ creds = fetch_credential(config_folder, extra_args)
if creds:
inv, workunit, local_build_id, build_target = _prepare_data(creds)
extra_args[constants.INVOCATION_ID] = inv['invocationId']
@@ -169,56 +214,54 @@
if not os.path.exists(os.path.dirname(constants.TOKEN_FILE_PATH)):
os.makedirs(os.path.dirname(constants.TOKEN_FILE_PATH))
with open(constants.TOKEN_FILE_PATH, 'w') as token_file:
- token_file.write(creds.token_response['access_token'])
+ if creds.token_response:
+ token_file.write(creds.token_response['access_token'])
+ else:
+ token_file.write(creds.access_token)
return creds, inv
return None, None
-def request_consent_of_upload_test_result(config_folder,
- request_to_upload_result):
- """Request the consent of upload test results at the first time.
+def fetch_credential(config_folder, extra_args):
+ """Fetch the credential whenever --request-upload-result is specified.
Args:
- config_folder: The directory path to put config file.
- request_to_upload_result: Prompt message for user determine.
+ config_folder: The directory path to put config file. The default path
+ is ~/.atest.
+ extra_args: Dict of extra args to add to test run.
Return:
The credential object.
"""
if not os.path.exists(config_folder):
os.makedirs(config_folder)
- not_upload_file = os.path.join(config_folder,
- constants.DO_NOT_UPLOAD)
+ not_upload_file = os.path.join(config_folder, constants.DO_NOT_UPLOAD)
# Do nothing if there are no related config or DO_NOT_UPLOAD exists.
if (not constants.CREDENTIAL_FILE_NAME or
not constants.TOKEN_FILE_PATH):
return None
creds_f = os.path.join(config_folder, constants.CREDENTIAL_FILE_NAME)
- yn_result = False
- if request_to_upload_result:
- yn_result = atest_utils.prompt_with_yn_result(
- constants.UPLOAD_TEST_RESULT_MSG, False)
- if yn_result:
- if os.path.exists(not_upload_file):
- os.remove(not_upload_file)
- else:
+ if extra_args.get(constants.REQUEST_UPLOAD_RESULT):
+ if os.path.exists(not_upload_file):
+ os.remove(not_upload_file)
+ else:
+ if extra_args.get(constants.DISABLE_UPLOAD_RESULT):
if os.path.exists(creds_f):
os.remove(creds_f)
+ Path(not_upload_file).touch()
- # If the credential file exists or the user says “Yes”, ATest will
- # try to get the credential from the file, else will create a
- # DO_NOT_UPLOAD to keep the user's decision.
+ # If DO_NOT_UPLOAD not exist, ATest will try to get the credential
+ # from the file.
if not os.path.exists(not_upload_file):
- if os.path.exists(creds_f) or yn_result:
- return GCPHelper(
- client_id=constants.CLIENT_ID,
- client_secret=constants.CLIENT_SECRET,
- user_agent='atest').get_credential_with_auth_flow(creds_f)
+ return GCPHelper(
+ client_id=constants.CLIENT_ID,
+ client_secret=constants.CLIENT_SECRET,
+ user_agent='atest').get_credential_with_auth_flow(creds_f)
- Path(not_upload_file).touch()
atest_utils.colorful_print(
- 'WARNING: In order to allow upload local test results to AnTS, it '
- 'is recommended you add the option --request-upload-result.',
- constants.YELLOW)
+ 'WARNING: In order to allow uploading local test results to AnTS, it '
+ 'is recommended you add the option --request-upload-result. This option'
+ ' only needs to set once and takes effect until --disable-upload-result'
+ ' is set.', constants.YELLOW)
return None
def _prepare_data(creds):
@@ -256,9 +299,9 @@
"""
default_branch = ('git_master'
if constants.CREDENTIAL_FILE_NAME else 'aosp-master')
- local_branch = atest_utils.get_manifest_branch()
- branches = [b['name'] for b in build_client.list_branch()['branches']]
- return local_branch if local_branch in branches else default_branch
+ local_branch = "git_%s" % atest_utils.get_manifest_branch()
+ branch = build_client.get_branch(local_branch)
+ return local_branch if branch else default_branch
def _get_target(branch, build_client):
"""Get local build selected target.
diff --git a/atest/logstorage/atest_gcp_utils_unittest.py b/atest/logstorage/atest_gcp_utils_unittest.py
index 3a2ea05..89975bd 100644
--- a/atest/logstorage/atest_gcp_utils_unittest.py
+++ b/atest/logstorage/atest_gcp_utils_unittest.py
@@ -20,6 +20,7 @@
import tempfile
import unittest
+from pathlib import Path
from unittest import mock
import constants
@@ -30,7 +31,7 @@
"""Unit tests for atest_gcp_utils.py"""
@mock.patch.object(atest_gcp_utils, '_prepare_data')
- @mock.patch.object(atest_gcp_utils, 'request_consent_of_upload_test_result')
+ @mock.patch.object(atest_gcp_utils, 'fetch_credential')
def test_do_upload_flow(self, mock_request, mock_prepare):
"""test do_upload_flow method."""
fake_extra_args = {}
@@ -62,41 +63,77 @@
self.assertEqual(None, inv)
@mock.patch.object(atest_gcp_utils.GCPHelper,
- 'get_credential_with_auth_flow')
- @mock.patch('builtins.input')
- def test_request_consent_of_upload_test_result_yes(
- self, mock_input, mock_get_credential_with_auth_flow):
- """test request_consent_of_upload_test_result method."""
+ 'get_credential_with_auth_flow')
+ def test_fetch_credential_with_request_option(
+ self, mock_get_credential_with_auth_flow):
+ """test fetch_credential method."""
constants.CREDENTIAL_FILE_NAME = 'cred_file'
constants.GCP_ACCESS_TOKEN = 'access_token'
tmp_folder = tempfile.mkdtemp()
- mock_input.return_value = 'Y'
not_upload_file = os.path.join(tmp_folder,
constants.DO_NOT_UPLOAD)
- atest_gcp_utils.request_consent_of_upload_test_result(tmp_folder, True)
+ atest_gcp_utils.fetch_credential(tmp_folder,
+ {constants.REQUEST_UPLOAD_RESULT:True})
self.assertEqual(1, mock_get_credential_with_auth_flow.call_count)
self.assertFalse(os.path.exists(not_upload_file))
- atest_gcp_utils.request_consent_of_upload_test_result(tmp_folder, True)
+ atest_gcp_utils.fetch_credential(tmp_folder,
+ {constants.REQUEST_UPLOAD_RESULT:True})
self.assertEqual(2, mock_get_credential_with_auth_flow.call_count)
self.assertFalse(os.path.exists(not_upload_file))
@mock.patch.object(atest_gcp_utils.GCPHelper,
'get_credential_with_auth_flow')
- @mock.patch('builtins.input')
- def test_request_consent_of_upload_test_result_no(
- self, mock_input, mock_get_credential_with_auth_flow):
- """test request_consent_of_upload_test_result method."""
- mock_input.return_value = 'N'
+ def test_fetch_credential_with_disable_option(
+ self, mock_get_credential_with_auth_flow):
+ """test fetch_credential method."""
constants.CREDENTIAL_FILE_NAME = 'cred_file'
constants.GCP_ACCESS_TOKEN = 'access_token'
tmp_folder = tempfile.mkdtemp()
not_upload_file = os.path.join(tmp_folder,
constants.DO_NOT_UPLOAD)
- atest_gcp_utils.request_consent_of_upload_test_result(tmp_folder, True)
+ atest_gcp_utils.fetch_credential(tmp_folder,
+ {constants.DISABLE_UPLOAD_RESULT:True})
self.assertTrue(os.path.exists(not_upload_file))
self.assertEqual(0, mock_get_credential_with_auth_flow.call_count)
- atest_gcp_utils.request_consent_of_upload_test_result(tmp_folder, True)
+
+ atest_gcp_utils.fetch_credential(tmp_folder,
+ {constants.DISABLE_UPLOAD_RESULT:True})
self.assertEqual(0, mock_get_credential_with_auth_flow.call_count)
+
+ @mock.patch.object(atest_gcp_utils.GCPHelper,
+ 'get_credential_with_auth_flow')
+ def test_fetch_credential_no_upload_option(
+ self, mock_get_credential_with_auth_flow):
+ """test fetch_credential method."""
+ constants.CREDENTIAL_FILE_NAME = 'cred_file'
+ constants.GCP_ACCESS_TOKEN = 'access_token'
+ tmp_folder = tempfile.mkdtemp()
+ not_upload_file = os.path.join(tmp_folder,
+ constants.DO_NOT_UPLOAD)
+ fake_cred_file = os.path.join(tmp_folder,
+ constants.CREDENTIAL_FILE_NAME)
+
+ # mock cred file not exist, and not_upload file exists.
+ if os.path.exists(fake_cred_file):
+ os.remove(fake_cred_file)
+ if os.path.exists(not_upload_file):
+ os.remove(not_upload_file)
+ Path(not_upload_file).touch()
+
+ atest_gcp_utils.fetch_credential(tmp_folder, dict())
+ self.assertEqual(0, mock_get_credential_with_auth_flow.call_count)
+ self.assertTrue(os.path.exists(not_upload_file))
+
+ # mock cred file exists, and not_upload_file not exist.
+ if os.path.exists(fake_cred_file):
+ os.remove(fake_cred_file)
+ Path(fake_cred_file).touch()
+ if os.path.exists(not_upload_file):
+ os.remove(not_upload_file)
+
+ atest_gcp_utils.fetch_credential(tmp_folder, dict())
+ self.assertEqual(1, mock_get_credential_with_auth_flow.call_count)
+ self.assertFalse(os.path.exists(not_upload_file))
diff --git a/atest/logstorage/logstorage_utils.py b/atest/logstorage/logstorage_utils.py
index a18e7bb..5f056f2 100644
--- a/atest/logstorage/logstorage_utils.py
+++ b/atest/logstorage/logstorage_utils.py
@@ -52,6 +52,7 @@
logging.debug('Import error due to: %s', e)
import constants
+import metrics
class BuildClient:
@@ -79,6 +80,19 @@
return self.client.target().list(branch=branch,
maxResults=10000).execute()
+ def get_branch(self, branch):
+ """Get BuildInfo for specific branch.
+ Args:
+ branch: A string of branch name to query.
+ """
+ query_branch = ''
+ try:
+ query_branch = self.client.branch().get(resourceId=branch).execute()
+ # pylint: disable=broad-except
+ except Exception:
+ return ''
+ return query_branch
+
def insert_local_build(self, external_id, target, branch):
"""Insert a build record.
Args:
@@ -129,6 +143,7 @@
A build invocation object.
"""
sponge_invocation_id = str(uuid.uuid4())
+ user_email = metrics.metrics_base.get_user_email()
invocation = {
"primaryBuild": {
"buildId": build_record['buildId'],
@@ -138,6 +153,7 @@
"schedulerState": "running",
"runner": "atest",
"scheduler": "atest",
+ "users": [user_email],
"properties": [{
'name': 'sponge_invocation_id',
'value': sponge_invocation_id,
diff --git a/atest/metrics/metrics_base.py b/atest/metrics/metrics_base.py
index b2cabc8..a058c8c 100644
--- a/atest/metrics/metrics_base.py
+++ b/atest/metrics/metrics_base.py
@@ -47,6 +47,25 @@
EXTERNAL_USER: 934
}
+def get_user_email():
+ """Get user mail in git config.
+
+ Returns:
+ user's email.
+ """
+ try:
+ output = subprocess.check_output(
+ ['git', 'config', '--get', 'user.email'], universal_newlines=True)
+ return output.strip() if output else ''
+ except OSError:
+ # OSError can be raised when running atest_unittests on a host
+ # without git being set up.
+ logging.debug('Unable to determine if this is an external run, git is '
+ 'not found.')
+ except subprocess.CalledProcessError:
+ logging.debug('Unable to determine if this is an external run, email '
+ 'is not found in git config.')
+ return ''
def get_user_type():
"""Get user type.
diff --git a/atest/test_finders/test_finder_utils.py b/atest/test_finders/test_finder_utils.py
index 7686dc7..5de7922 100644
--- a/atest/test_finders/test_finder_utils.py
+++ b/atest/test_finders/test_finder_utils.py
@@ -33,11 +33,11 @@
import atest_decorator
import atest_error
-import atest_enum
import atest_utils
import constants
-from metrics import metrics_utils
+from atest_enum import AtestEnum, DetectType
+from metrics import metrics, metrics_utils
from tools import atest_tools
# Helps find apk files listed in a test config (AndroidTest.xml) file.
@@ -107,11 +107,11 @@
# 3. INTEGRATION: XML file name in one of the 4 integration config directories.
# 4. CC_CLASS: Name of a cc class.
-FIND_REFERENCE_TYPE = atest_enum.AtestEnum(['CLASS',
- 'QUALIFIED_CLASS',
- 'PACKAGE',
- 'INTEGRATION',
- 'CC_CLASS'])
+FIND_REFERENCE_TYPE = AtestEnum(['CLASS',
+ 'QUALIFIED_CLASS',
+ 'PACKAGE',
+ 'INTEGRATION',
+ 'CC_CLASS'])
# Get cpu count.
_CPU_COUNT = 0 if os.uname()[0] == 'Linux' else multiprocessing.cpu_count()
@@ -1176,6 +1176,7 @@
return set()
+# pylint: disable=too-many-branches
def get_cc_class_info(test_path):
"""Get the class info of the given cc input test_path.
@@ -1213,6 +1214,7 @@
if os.stat(_test_path.name).st_size != 0:
file_to_parse = _test_path.name
+ # TODO: b/234531695 support reading header files as well.
with open(file_to_parse) as class_file:
logging.debug('Parsing: %s', test_path)
content = class_file.read()
@@ -1225,24 +1227,38 @@
classes = {cls[1] for cls in method_matches}
class_info = {}
+ test_not_found = False
for cls in classes:
class_info.setdefault(cls, {'methods': set(),
'prefixes': set(),
'typed': False})
logging.debug('Probing TestCase.TestName pattern:')
for match in method_matches:
- logging.debug(' Found %s.%s', match[1], match[2])
- class_info[match[1]]['methods'].add(match[2])
+ if class_info.get(match[1]):
+ logging.debug(' Found %s.%s', match[1], match[2])
+ class_info[match[1]]['methods'].add(match[2])
+ else:
+ test_not_found = True
# Parameterized test.
logging.debug('Probing InstantiationName/TestCase pattern:')
for match in prefix_matches:
- logging.debug(' Found %s/%s', match[0], match[1])
- class_info[match[1]]['prefixes'].add(match[0])
+ if class_info.get(match[1]):
+ logging.debug(' Found %s/%s', match[0], match[1])
+ class_info[match[1]]['prefixes'].add(match[0])
+ else:
+ test_not_found = True
# Typed test
logging.debug('Probing typed test names:')
for match in typed_matches:
- logging.debug(' Found %s', match)
- class_info[match]['typed'] = True
+ if class_info.get(match):
+ logging.debug(' Found %s', match)
+ class_info[match]['typed'] = True
+ else:
+ test_not_found = True
+ if test_not_found:
+ metrics.LocalDetectEvent(
+ detect_type=DetectType.NATIVE_TEST_NOT_FOUND,
+ result=DetectType.NATIVE_TEST_NOT_FOUND)
return class_info
def get_cc_class_type(class_info, classname):
diff --git a/atest/test_runners/atest_tf_test_runner.py b/atest/test_runners/atest_tf_test_runner.py
index f352db5..ed15b73 100644
--- a/atest/test_runners/atest_tf_test_runner.py
+++ b/atest/test_runners/atest_tf_test_runner.py
@@ -1064,7 +1064,8 @@
constants.ENABLE_DEVICE_PREPARER,
constants.DRY_RUN,
constants.VERIFY_ENV_VARIABLE,
- constants.FLAKES_INFO):
+ constants.FLAKES_INFO,
+ constants.DISABLE_UPLOAD_RESULT):
continue
unsupported_args.append(arg)
return supported_args, unsupported_args