From ea393eaf7cb8c084f5cff7216242b50f9286852c Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Wed, 26 Aug 2026 22:50:04 +0000 Subject: [PATCH 1/5] [Android] Dinamically set test cases dir from android data --- src/clusterfuzz/_internal/bot/tasks/setup.py | 3 ++- src/clusterfuzz/_internal/bot/testcase_manager.py | 4 ++-- .../_internal/platforms/android/app.py | 2 +- .../_internal/platforms/android/constants.py | 10 +++++++++- .../_internal/platforms/android/device.py | 6 ++++-- .../tests/core/bot/testcase_manager_test.py | 4 +++- .../tests/core/platforms/android/app_test.py | 15 +++++++++++++++ 7 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/clusterfuzz/_internal/bot/tasks/setup.py b/src/clusterfuzz/_internal/bot/tasks/setup.py index 9e8aab54316..d45741b9db4 100644 --- a/src/clusterfuzz/_internal/bot/tasks/setup.py +++ b/src/clusterfuzz/_internal/bot/tasks/setup.py @@ -101,7 +101,8 @@ def _copy_testcase_to_device_and_setup_environment(testcase, relative_testcase_file_path = ( testcase_file_path[len(local_testcases_directory) + 1:]) device_testcase_file_path = os.path.join( - android.constants.DEVICE_TESTCASES_DIR, relative_testcase_file_path) + android.constants.get_testcases_directory(), + relative_testcase_file_path) android.adb.run_shell_command(['chmod', '0755', device_testcase_file_path]) diff --git a/src/clusterfuzz/_internal/bot/testcase_manager.py b/src/clusterfuzz/_internal/bot/testcase_manager.py index 5b235daa1f0..c35431da1d3 100644 --- a/src/clusterfuzz/_internal/bot/testcase_manager.py +++ b/src/clusterfuzz/_internal/bot/testcase_manager.py @@ -459,7 +459,7 @@ def convert_dependency_url_to_local_path(url): # Convert remote to local path for android. if environment.is_android(): - remote_testcases_directory = android.constants.DEVICE_TESTCASES_DIR + remote_testcases_directory = android.constants.get_testcases_directory() local_testcases_directory = environment.get_value('FUZZ_INPUTS') local_path = local_path.replace(remote_testcases_directory, local_testcases_directory) @@ -995,7 +995,7 @@ def get_command_line_for_application(file_to_run='', apps_argument = environment.get_value('APPS_ARG') crash_stacks_directory = environment.get_value('CRASH_STACKTRACES_DIR') debugger = environment.get_value('DEBUGGER_PATH') - device_testcases_directory = android.constants.DEVICE_TESTCASES_DIR + device_testcases_directory = android.constants.get_testcases_directory() fuzzer_directory = environment.get_value('FUZZER_DIR') extension_argument = environment.get_value('EXTENSION_ARG') input_directory = environment.get_value('INPUT_DIR') diff --git a/src/clusterfuzz/_internal/platforms/android/app.py b/src/clusterfuzz/_internal/platforms/android/app.py index 396782f5e9e..662a63f33b0 100644 --- a/src/clusterfuzz/_internal/platforms/android/app.py +++ b/src/clusterfuzz/_internal/platforms/android/app.py @@ -53,7 +53,7 @@ def get_launch_command(app_args, testcase_path, testcase_file_url): application_launch_command = application_launch_command.replace( '%APP_ARGS%', app_args) application_launch_command = application_launch_command.replace( - '%DEVICE_TESTCASES_DIR%', constants.DEVICE_TESTCASES_DIR) + '%DEVICE_TESTCASES_DIR%', constants.get_testcases_directory()) application_launch_command = application_launch_command.replace( '%PKG_NAME%', package_name) application_launch_command = application_launch_command.replace( diff --git a/src/clusterfuzz/_internal/platforms/android/constants.py b/src/clusterfuzz/_internal/platforms/android/constants.py index a797b3a5a0e..e056c3cf3ff 100644 --- a/src/clusterfuzz/_internal/platforms/android/constants.py +++ b/src/clusterfuzz/_internal/platforms/android/constants.py @@ -15,9 +15,11 @@ import re +from . import app + DEVICE_DOWNLOAD_DIR = '/sdcard/Download' -DEVICE_TESTCASES_DIR = '/sdcard/fuzzer-testcases' +DEVICE_TESTCASES_DIR = '/sdcard/Android/data/%PKG_NAME%/files' DEVICE_TMP_DIR = '/data/local/tmp' @@ -86,3 +88,9 @@ # Restrict pixel6 from picking up generic Android jobs to avoid # Binary Mismatch: Hence, 'ANDROID:PIXEL6' is added to the list. DEVICES_WITH_NO_FALLBACK_QUEUE_LIST = ['ANDROID:PIXEL6'] + + +def get_testcases_directory(): + """Returns the testcases directory.""" + package_name = app.get_package_name() or '' + return DEVICE_TESTCASES_DIR.replace('%PKG_NAME%', package_name) diff --git a/src/clusterfuzz/_internal/platforms/android/device.py b/src/clusterfuzz/_internal/platforms/android/device.py index 0fd828f0527..cc7b528068c 100755 --- a/src/clusterfuzz/_internal/platforms/android/device.py +++ b/src/clusterfuzz/_internal/platforms/android/device.py @@ -125,7 +125,9 @@ def clear_temp_directories(): def clear_testcase_directory(): """Clears testcase directory.""" - adb.remove_directory(constants.DEVICE_TESTCASES_DIR, recreate=True) + # Use wildcard to delete contents only, preserving the directory itself + adb.run_shell_command( + 'rm -rf %s/*' % constants.get_testcases_directory(), root=True) def configure_device_settings(): @@ -442,7 +444,7 @@ def push_testcases_to_device(): return adb.copy_local_directory_to_remote(local_testcases_directory, - constants.DEVICE_TESTCASES_DIR) + constants.get_testcases_directory()) def reboot(): diff --git a/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py b/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py index e8d74827a1a..4bd45c65bea 100644 --- a/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py +++ b/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py @@ -30,6 +30,7 @@ engine as libfuzzer_engine from clusterfuzz._internal.bot.untrusted_runner import file_host from clusterfuzz._internal.build_management import build_manager +from clusterfuzz._internal.platforms import android from clusterfuzz._internal.crash_analysis.crash_result import CrashResult from clusterfuzz._internal.datastore import data_types from clusterfuzz._internal.protos import uworker_msg_pb2 @@ -252,10 +253,11 @@ def setUp(self): def test_file_match_android(self): """Tests matching a file URL.""" self.mock.platform.return_value = 'ANDROID' + testcases_dir = android.constants.get_testcases_directory() self.assertEqual( '/mnt/scratch0/test.html', testcase_manager.convert_dependency_url_to_local_path( - 'file:///sdcard/fuzzer-testcases/test.html')) + f'file://{testcases_dir}/test.html')) self.mock.normalize_path.assert_called_once_with('/mnt/scratch0/test.html') def test_file_match_linux(self): diff --git a/src/clusterfuzz/_internal/tests/core/platforms/android/app_test.py b/src/clusterfuzz/_internal/tests/core/platforms/android/app_test.py index 7095042c150..3140fc459a9 100644 --- a/src/clusterfuzz/_internal/tests/core/platforms/android/app_test.py +++ b/src/clusterfuzz/_internal/tests/core/platforms/android/app_test.py @@ -18,6 +18,7 @@ from unittest import TestCase from clusterfuzz._internal.platforms.android import app +from clusterfuzz._internal.platforms.android import constants from clusterfuzz._internal.system import environment from clusterfuzz._internal.tests.test_libs import android_helpers from clusterfuzz._internal.tests.test_libs import helpers @@ -94,3 +95,17 @@ def test_install_with_valued_flags(self): app.install('/path/to/app.apk', abi='x86', no_streaming=True) self.mock_run_command.assert_called_once_with( ['install', '-r', '--abi', 'x86', '--no-streaming', '/path/to/app.apk']) + + +class GetTestcasesDirectoryTest(TestCase): + """Tests constants.get_testcases_directory.""" + + def setUp(self): + super().setUp() + helpers.patch_environ(self) + + def test_get_testcases_directory(self): + """Tests get_testcases_directory when package name is set.""" + environment.set_value('PKG_NAME', 'com.google.chrome') + self.assertEqual(constants.get_testcases_directory(), + '/sdcard/Android/data/com.google.chrome/files') From 293cf3b7a9bb530fbce5a437f69598ef9666e845 Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Wed, 26 Aug 2026 23:10:55 +0000 Subject: [PATCH 2/5] [Android] Fix linting errors --- src/clusterfuzz/_internal/platforms/android/constants.py | 8 ++++++-- src/clusterfuzz/_internal/platforms/android/device.py | 2 +- .../_internal/tests/core/bot/testcase_manager_test.py | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/clusterfuzz/_internal/platforms/android/constants.py b/src/clusterfuzz/_internal/platforms/android/constants.py index e056c3cf3ff..c5d3d48f844 100644 --- a/src/clusterfuzz/_internal/platforms/android/constants.py +++ b/src/clusterfuzz/_internal/platforms/android/constants.py @@ -15,11 +15,13 @@ import re +from clusterfuzz._internal.system import environment + from . import app DEVICE_DOWNLOAD_DIR = '/sdcard/Download' -DEVICE_TESTCASES_DIR = '/sdcard/Android/data/%PKG_NAME%/files' +_DEFAULT_DEVICE_TESTCASES_DIR = '/sdcard/Android/data/%PKG_NAME%/files' DEVICE_TMP_DIR = '/data/local/tmp' @@ -92,5 +94,7 @@ def get_testcases_directory(): """Returns the testcases directory.""" + testcases_dir = environment.get_value('DEVICE_TESTCASES_DIR', + _DEFAULT_DEVICE_TESTCASES_DIR) package_name = app.get_package_name() or '' - return DEVICE_TESTCASES_DIR.replace('%PKG_NAME%', package_name) + return testcases_dir.replace('%PKG_NAME%', package_name) diff --git a/src/clusterfuzz/_internal/platforms/android/device.py b/src/clusterfuzz/_internal/platforms/android/device.py index cc7b528068c..4f912fc95a8 100755 --- a/src/clusterfuzz/_internal/platforms/android/device.py +++ b/src/clusterfuzz/_internal/platforms/android/device.py @@ -127,7 +127,7 @@ def clear_testcase_directory(): """Clears testcase directory.""" # Use wildcard to delete contents only, preserving the directory itself adb.run_shell_command( - 'rm -rf %s/*' % constants.get_testcases_directory(), root=True) + f'rm -rf {constants.get_testcases_directory()}/*', root=True) def configure_device_settings(): diff --git a/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py b/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py index 4bd45c65bea..f821fa89971 100644 --- a/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py +++ b/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py @@ -30,9 +30,9 @@ engine as libfuzzer_engine from clusterfuzz._internal.bot.untrusted_runner import file_host from clusterfuzz._internal.build_management import build_manager -from clusterfuzz._internal.platforms import android from clusterfuzz._internal.crash_analysis.crash_result import CrashResult from clusterfuzz._internal.datastore import data_types +from clusterfuzz._internal.platforms import android from clusterfuzz._internal.protos import uworker_msg_pb2 from clusterfuzz._internal.system import environment from clusterfuzz._internal.tests.test_libs import helpers as test_helpers From a4a8514086b05eacb19182e240264fdba80b6a89 Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Fri, 4 Sep 2026 21:38:50 +0000 Subject: [PATCH 3/5] [Android] No longer allows to override device testcases dir --- src/clusterfuzz/_internal/platforms/android/constants.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/clusterfuzz/_internal/platforms/android/constants.py b/src/clusterfuzz/_internal/platforms/android/constants.py index c5d3d48f844..f8a79f96797 100644 --- a/src/clusterfuzz/_internal/platforms/android/constants.py +++ b/src/clusterfuzz/_internal/platforms/android/constants.py @@ -94,7 +94,5 @@ def get_testcases_directory(): """Returns the testcases directory.""" - testcases_dir = environment.get_value('DEVICE_TESTCASES_DIR', - _DEFAULT_DEVICE_TESTCASES_DIR) package_name = app.get_package_name() or '' return testcases_dir.replace('%PKG_NAME%', package_name) From ee9a529d0daba7204023578b0b0dd67109a60a26 Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Fri, 4 Sep 2026 21:49:12 +0000 Subject: [PATCH 4/5] Fix linter errors --- src/clusterfuzz/_internal/platforms/android/constants.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/clusterfuzz/_internal/platforms/android/constants.py b/src/clusterfuzz/_internal/platforms/android/constants.py index f8a79f96797..050e8cc914d 100644 --- a/src/clusterfuzz/_internal/platforms/android/constants.py +++ b/src/clusterfuzz/_internal/platforms/android/constants.py @@ -15,8 +15,6 @@ import re -from clusterfuzz._internal.system import environment - from . import app DEVICE_DOWNLOAD_DIR = '/sdcard/Download' @@ -95,4 +93,4 @@ def get_testcases_directory(): """Returns the testcases directory.""" package_name = app.get_package_name() or '' - return testcases_dir.replace('%PKG_NAME%', package_name) + return _DEFAULT_DEVICE_TESTCASES_DIR.replace('%PKG_NAME%', package_name) From e479619f24a9d15f56ff34648b62599ca617959c Mon Sep 17 00:00:00 2001 From: Ivan Barba Date: Tue, 8 Sep 2026 17:03:20 +0000 Subject: [PATCH 5/5] [Android] Moves get_testcase_dir() to app module --- src/clusterfuzz/_internal/bot/tasks/setup.py | 3 +-- src/clusterfuzz/_internal/bot/testcase_manager.py | 4 ++-- src/clusterfuzz/_internal/platforms/android/app.py | 9 +++++++-- .../_internal/platforms/android/constants.py | 11 ----------- src/clusterfuzz/_internal/platforms/android/device.py | 5 ++--- .../_internal/tests/core/bot/testcase_manager_test.py | 2 +- .../tests/core/platforms/android/app_test.py | 9 +++++++-- 7 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/clusterfuzz/_internal/bot/tasks/setup.py b/src/clusterfuzz/_internal/bot/tasks/setup.py index d45741b9db4..b5cd9abcb6f 100644 --- a/src/clusterfuzz/_internal/bot/tasks/setup.py +++ b/src/clusterfuzz/_internal/bot/tasks/setup.py @@ -101,8 +101,7 @@ def _copy_testcase_to_device_and_setup_environment(testcase, relative_testcase_file_path = ( testcase_file_path[len(local_testcases_directory) + 1:]) device_testcase_file_path = os.path.join( - android.constants.get_testcases_directory(), - relative_testcase_file_path) + android.app.get_testcases_directory(), relative_testcase_file_path) android.adb.run_shell_command(['chmod', '0755', device_testcase_file_path]) diff --git a/src/clusterfuzz/_internal/bot/testcase_manager.py b/src/clusterfuzz/_internal/bot/testcase_manager.py index 41bf1d7733c..e2e4a36f9ac 100644 --- a/src/clusterfuzz/_internal/bot/testcase_manager.py +++ b/src/clusterfuzz/_internal/bot/testcase_manager.py @@ -459,7 +459,7 @@ def convert_dependency_url_to_local_path(url): # Convert remote to local path for android. if environment.is_android(): - remote_testcases_directory = android.constants.get_testcases_directory() + remote_testcases_directory = android.app.get_testcases_directory() local_testcases_directory = environment.get_value('FUZZ_INPUTS') local_path = local_path.replace(remote_testcases_directory, local_testcases_directory) @@ -995,7 +995,7 @@ def get_command_line_for_application(file_to_run='', apps_argument = environment.get_value('APPS_ARG') crash_stacks_directory = environment.get_value('CRASH_STACKTRACES_DIR') debugger = environment.get_value('DEBUGGER_PATH') - device_testcases_directory = android.constants.get_testcases_directory() + device_testcases_directory = android.app.get_testcases_directory() fuzzer_directory = environment.get_value('FUZZER_DIR') extension_argument = environment.get_value('EXTENSION_ARG') input_directory = environment.get_value('INPUT_DIR') diff --git a/src/clusterfuzz/_internal/platforms/android/app.py b/src/clusterfuzz/_internal/platforms/android/app.py index 662a63f33b0..e2f0c72065b 100644 --- a/src/clusterfuzz/_internal/platforms/android/app.py +++ b/src/clusterfuzz/_internal/platforms/android/app.py @@ -21,7 +21,6 @@ from clusterfuzz._internal.system import environment from . import adb -from . import constants AAPT_CMD_TIMEOUT = 60 CHROME_CACHE_DIRS = [ @@ -53,7 +52,7 @@ def get_launch_command(app_args, testcase_path, testcase_file_url): application_launch_command = application_launch_command.replace( '%APP_ARGS%', app_args) application_launch_command = application_launch_command.replace( - '%DEVICE_TESTCASES_DIR%', constants.get_testcases_directory()) + '%DEVICE_TESTCASES_DIR%', get_testcases_directory()) application_launch_command = application_launch_command.replace( '%PKG_NAME%', package_name) application_launch_command = application_launch_command.replace( @@ -93,6 +92,12 @@ def get_package_name(apk_path=None): return match.group(1) +def get_testcases_directory(): + """Returns the testcases directory.""" + package_name = get_package_name() or '' + return f'/sdcard/Android/data/{package_name}/files' + + def install(package_apk_path: str, **kwargs): """Install a package from an apk path. diff --git a/src/clusterfuzz/_internal/platforms/android/constants.py b/src/clusterfuzz/_internal/platforms/android/constants.py index 050e8cc914d..86739d43fa3 100644 --- a/src/clusterfuzz/_internal/platforms/android/constants.py +++ b/src/clusterfuzz/_internal/platforms/android/constants.py @@ -15,12 +15,7 @@ import re -from . import app - DEVICE_DOWNLOAD_DIR = '/sdcard/Download' - -_DEFAULT_DEVICE_TESTCASES_DIR = '/sdcard/Android/data/%PKG_NAME%/files' - DEVICE_TMP_DIR = '/data/local/tmp' # Directory to keep fuzzing artifacts for grey-box fuzzers e.g. corpus. @@ -88,9 +83,3 @@ # Restrict pixel6 from picking up generic Android jobs to avoid # Binary Mismatch: Hence, 'ANDROID:PIXEL6' is added to the list. DEVICES_WITH_NO_FALLBACK_QUEUE_LIST = ['ANDROID:PIXEL6'] - - -def get_testcases_directory(): - """Returns the testcases directory.""" - package_name = app.get_package_name() or '' - return _DEFAULT_DEVICE_TESTCASES_DIR.replace('%PKG_NAME%', package_name) diff --git a/src/clusterfuzz/_internal/platforms/android/device.py b/src/clusterfuzz/_internal/platforms/android/device.py index 4f912fc95a8..4f76c7ff0b3 100755 --- a/src/clusterfuzz/_internal/platforms/android/device.py +++ b/src/clusterfuzz/_internal/platforms/android/device.py @@ -126,8 +126,7 @@ def clear_temp_directories(): def clear_testcase_directory(): """Clears testcase directory.""" # Use wildcard to delete contents only, preserving the directory itself - adb.run_shell_command( - f'rm -rf {constants.get_testcases_directory()}/*', root=True) + adb.run_shell_command(f'rm -rf {app.get_testcases_directory()}/*', root=True) def configure_device_settings(): @@ -444,7 +443,7 @@ def push_testcases_to_device(): return adb.copy_local_directory_to_remote(local_testcases_directory, - constants.get_testcases_directory()) + app.get_testcases_directory()) def reboot(): diff --git a/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py b/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py index de11d13c27d..1bb403235b0 100644 --- a/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py +++ b/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py @@ -253,7 +253,7 @@ def setUp(self): def test_file_match_android(self): """Tests matching a file URL.""" self.mock.platform.return_value = 'ANDROID' - testcases_dir = android.constants.get_testcases_directory() + testcases_dir = android.app.get_testcases_directory() self.assertEqual( '/mnt/scratch0/test.html', testcase_manager.convert_dependency_url_to_local_path( diff --git a/src/clusterfuzz/_internal/tests/core/platforms/android/app_test.py b/src/clusterfuzz/_internal/tests/core/platforms/android/app_test.py index 3140fc459a9..9c998bfb3ff 100644 --- a/src/clusterfuzz/_internal/tests/core/platforms/android/app_test.py +++ b/src/clusterfuzz/_internal/tests/core/platforms/android/app_test.py @@ -18,7 +18,6 @@ from unittest import TestCase from clusterfuzz._internal.platforms.android import app -from clusterfuzz._internal.platforms.android import constants from clusterfuzz._internal.system import environment from clusterfuzz._internal.tests.test_libs import android_helpers from clusterfuzz._internal.tests.test_libs import helpers @@ -107,5 +106,11 @@ def setUp(self): def test_get_testcases_directory(self): """Tests get_testcases_directory when package name is set.""" environment.set_value('PKG_NAME', 'com.google.chrome') - self.assertEqual(constants.get_testcases_directory(), + self.assertEqual(app.get_testcases_directory(), '/sdcard/Android/data/com.google.chrome/files') + + def test_get_testcases_directory_no_package_name(self): + """Tests get_testcases_directory when package name is not set returns malformed string.""" + environment.set_value('PKG_NAME', None) + self.assertEqual(app.get_testcases_directory(), + '/sdcard/Android/data//files')