diff --git a/src/clusterfuzz/_internal/base/utils.py b/src/clusterfuzz/_internal/base/utils.py index bb59e5c8651..a6dcd710fd2 100644 --- a/src/clusterfuzz/_internal/base/utils.py +++ b/src/clusterfuzz/_internal/base/utils.py @@ -23,6 +23,7 @@ import random import re import sys +import tempfile import time import urllib.parse import urllib.request @@ -1100,3 +1101,10 @@ def batched(iterator, batch_size): if batch: yield batch + + +def create_temp_file(directory: str, prefix: str, suffix: str) -> str: + """Creates a temporary file and returns its path.""" + fd, file_path = tempfile.mkstemp(dir=directory, prefix=prefix, suffix=suffix) + os.close(fd) + return file_path diff --git a/src/clusterfuzz/_internal/bot/tasks/utasks/fuzz_task.py b/src/clusterfuzz/_internal/bot/tasks/utasks/fuzz_task.py index 9aca0115983..6cc440104b8 100644 --- a/src/clusterfuzz/_internal/bot/tasks/utasks/fuzz_task.py +++ b/src/clusterfuzz/_internal/bot/tasks/utasks/fuzz_task.py @@ -18,6 +18,7 @@ import itertools import json import os +import queue import random import re import time @@ -1738,6 +1739,19 @@ def _emit_testcase_generation_time_metric(self, start_time, testcase_count, 'platform': environment.platform(), }) + def _drain_temp_queue(self, temp_queue: queue.Queue, + crashes: list[testcase_manager.Crash]) -> None: + """Pops items from temp_queue and processes crashes and run outputs.""" + while not temp_queue.empty(): + result: testcase_manager.TestcaseRunResult = temp_queue.get() + + if result.crash: + crashes.append(result.crash) + if result.fuzzer_run_output_data: + fuzzer_run_output = _to_fuzzer_run_output(result.fuzzer_run_output_data) + if fuzzer_run_output: + self.fuzz_task_output.fuzzer_run_outputs.append(fuzzer_run_output) + def do_blackbox_fuzzing(self, fuzzer, fuzzer_directory, job_type): """Run blackbox fuzzing. Currently also used for engine fuzzing.""" # Set the thread timeout values. @@ -1847,7 +1861,7 @@ def do_blackbox_fuzzing(self, fuzzer, fuzzer_directory, job_type): thread = process_handler.get_process()( target=testcase_manager.run_testcase_and_return_result_in_queue, args=(temp_queue, thread_index, testcase_file_path, gestures, - env_copy, not environment.is_uworker())) + env_copy)) try: thread.start() @@ -1880,9 +1894,7 @@ def do_blackbox_fuzzing(self, fuzzer, fuzzer_directory, job_type): process_handler.terminate_stale_application_instances() needs_stale_process_cleanup = False - while not temp_queue.empty(): - crashes.append(temp_queue.get()) - + self._drain_temp_queue(temp_queue, crashes) process_handler.close_queue(temp_queue) logs.info(f'Upto {test_number}') if thread_error_occurred: diff --git a/src/clusterfuzz/_internal/bot/testcase_manager.py b/src/clusterfuzz/_internal/bot/testcase_manager.py index 78eddd2c8cf..3d770604623 100644 --- a/src/clusterfuzz/_internal/bot/testcase_manager.py +++ b/src/clusterfuzz/_internal/bot/testcase_manager.py @@ -18,6 +18,7 @@ import dataclasses import datetime import os +import queue import re import zlib @@ -481,7 +482,7 @@ def _get_testcase_time(testcase_path): if stats: return datetime.datetime.utcfromtimestamp(float(stats.timestamp)) - return None + return datetime.datetime.utcnow() def upload_testcase(testcase_path, testcase_data, log_time, fuzzer_name=None): @@ -522,12 +523,9 @@ def _get_crash_output(output): return output[:crash_stacktrace_end_marker_index] -def run_testcase_and_return_result_in_queue(crash_queue, - thread_index, - file_path, - gestures, - env_copy, - upload_output=False): +def run_testcase_and_return_result_in_queue( + crash_queue: queue.Queue, thread_index: int, file_path: str, + gestures: list[str], env_copy: dict) -> None: """Run a single testcase and return crash results in the crash queue.""" # Since this is running in its own process, initialize the log handler again. # This is needed for Windows where instances are not shared across child @@ -539,21 +537,13 @@ def run_testcase_and_return_result_in_queue(crash_queue, # Also reinitialize NDB context for the same reason as above. with ndb_init.context(): - _do_run_testcase_and_return_result_in_queue( - crash_queue, - thread_index, - file_path, - gestures, - env_copy, - upload_output=upload_output) - - -def _do_run_testcase_and_return_result_in_queue(crash_queue, - thread_index, - file_path, - gestures, - env_copy, - upload_output=False): + _do_run_testcase_and_return_result_in_queue(crash_queue, thread_index, + file_path, gestures, env_copy) + + +def _do_run_testcase_and_return_result_in_queue( + crash_queue: queue.Queue, thread_index: int, file_path: str, + gestures: list[str], env_copy: dict) -> None: """Run a single testcase and return crash results in the crash queue.""" try: # Run testcase and check whether a crash occurred or not. @@ -571,9 +561,9 @@ def _do_run_testcase_and_return_result_in_queue(crash_queue, # To provide consistency between stats and logs, we use timestamp taken # from stats when uploading logs and testcase. - if upload_output: - log_time = _get_testcase_time(file_path) + log_time = _get_testcase_time(file_path) + crash = None if crash_result.is_crash(): # Initialize resource list with the testcase path. resource_list = [file_path] @@ -586,28 +576,36 @@ def _do_run_testcase_and_return_result_in_queue(crash_queue, utils.string_hash(file_path)) utils.write_data_to_file(crash_output, stack_file_path) - # Put crash/no-crash results in the crash queue. - crash_queue.put( - Crash( - file_path=file_path, - crash_time=crash_time, - return_code=return_code, - resource_list=resource_list, - gestures=gestures, - stack_file_path=stack_file_path)) - - # Don't upload uninteresting testcases (no crash) or if there is no log to - # correlate it with (not upload_output). - if upload_output: - upload_testcase(file_path, None, log_time) - - if upload_output: - # Include full output for uploaded logs (crash output, merge output, etc). - crash_result_full = CrashResult(return_code, crash_time, output) - upload_log(crash_result_full.get_stacktrace(), return_code, log_time) - except Exception: - logs.error('Exception occurred while running ' - 'run_testcase_and_return_result_in_queue.') + crash = Crash( + file_path=file_path, + crash_time=crash_time, + return_code=return_code, + resource_list=resource_list, + gestures=gestures, + stack_file_path=stack_file_path) + + # Save raw, unsymbolized execution output for deferred postprocessing. + crash_result_full = CrashResult(return_code, crash_time, output) + unsymbolized_output = crash_result_full.get_stacktrace(symbolized=False) + crash_path = file_path if crash else None + log_file_path = utils.create_temp_file( + directory=environment.get_value('BOT_TMPDIR'), + prefix='fuzzer_output_', + suffix='.log') + utils.write_data_to_file(unsymbolized_output, log_file_path) + fuzzer_run_output_data = FuzzerRunOutputData.from_file_path( + file_path=log_file_path, + crash_path=crash_path, + return_code=return_code, + log_time=log_time) + crash_queue.put( + TestcaseRunResult( + crash=crash, fuzzer_run_output_data=fuzzer_run_output_data)) + except Exception as e: + logs.error( + 'Exception occurred while running ' + 'run_testcase_and_return_result_in_queue.', + exception=e) def engine_reproduce(engine_impl: engine.Engine, target_name, testcase_path, diff --git a/src/clusterfuzz/_internal/tests/core/base/utils_test.py b/src/clusterfuzz/_internal/tests/core/base/utils_test.py index 69bcfd122a1..24ad522ebed 100644 --- a/src/clusterfuzz/_internal/tests/core/base/utils_test.py +++ b/src/clusterfuzz/_internal/tests/core/base/utils_test.py @@ -720,3 +720,16 @@ def test_additional_prefix(self): """Test parsing manifest data with an added prefix.""" file_data = 'prefix123-20250402153042-utc-40773ac0-username_test123-cad6977-prod' self.assertIsNone(utils.parse_manifest_data(file_data)) + + +class CreateTempFileTest(unittest.TestCase): + """Tests for create_temp_file.""" + + def test_create_temp_file(self): + """Test create_temp_file creates a file and returns its path.""" + with tempfile.TemporaryDirectory() as tmpdir: + file_path = utils.create_temp_file(tmpdir, 'prefix_', '_suffix') + self.assertTrue(os.path.exists(file_path)) + self.assertTrue(file_path.startswith(tmpdir)) + self.assertIn('prefix_', file_path) + self.assertTrue(file_path.endswith('_suffix')) diff --git a/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/fuzz_task_test.py b/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/fuzz_task_test.py index 640d6057457..7b65b27ece3 100644 --- a/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/fuzz_task_test.py +++ b/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/fuzz_task_test.py @@ -1332,6 +1332,39 @@ def test_trials(self): self.assertEqual('/app/app_1 -r=3 -x -y /tests/2', crashes[1].application_command_line) + def test_do_blackbox_fuzzing_log_packaging(self): + """Verify that do_blackbox_fuzzing invokes run_testcase_and_return_result_in_queue + and packages unsymbolized output.""" + fuzz_task_input = uworker_msg_pb2.FuzzTaskInput() + uworker_input = uworker_msg_pb2.Input( + fuzzer_name='fantasy_fuzz', + job_type='asan_test', + fuzz_task_input=fuzz_task_input) + + session = fuzz_task.FuzzingSession(uworker_input, 10) + session.generate_blackbox_testcases = mock.MagicMock() + expected_testcase_file_paths = ['/tests/0', '/tests/1'] + session.generate_blackbox_testcases.return_value = ( + fuzz_task.GenerateBlackboxTestcasesResult( + True, expected_testcase_file_paths, + {'fuzzer_binary_name': 'fantasy_fuzz'})) + + self.fs.create_file('/tests/0', contents='testcase_crashed') + self.fs.create_file('/tests/1', contents='testcase_passed') + self.mock.is_crash.side_effect = [True, False] + + mock_thread_cls = mock.MagicMock(wraps=threading.Thread) + self.mock.get_process.return_value = mock_thread_cls + + fuzzer = data_types.Fuzzer() + fuzzer.name = 'fantasy_fuzz' + + session.do_blackbox_fuzzing(fuzzer, '/fake-fuzz-dir', 'asan_test') + + self.assertEqual(2, mock_thread_cls.call_count) + for _, kwargs in mock_thread_cls.call_args_list: + self.assertEqual(5, len(kwargs['args'])) + @mock.patch( 'clusterfuzz._internal.bot.tasks.utasks.fuzz_task.FuzzingSession.generate_blackbox_testcases' ) @@ -1348,13 +1381,13 @@ def test_utask_main_blackbox_fuzzer(self, mock_generate_blackbox_testcases): fuzzer_name=fuzzer.name, job_type='asan_test', fuzz_task_input=fuzz_task_input, - setup_input=setup_input) + setup_input=setup_input, + uworker_env={'FUZZ_TEST_TIMEOUT': '10'}) # Mock out actual test-case generation for 3 tests. - expected_testcase_file_paths = ['/tests/0', '/tests/1', '/tests/2'] mock_generate_blackbox_testcases.return_value = ( fuzz_task.GenerateBlackboxTestcasesResult( - True, expected_testcase_file_paths, + True, ['/tests/0', '/tests/1', '/tests/2'], {'fuzzer_binary_name': fuzzer.name})) actual = fuzz_task.utask_main(uworker_input) @@ -1372,6 +1405,155 @@ def test_utask_main_blackbox_fuzzer(self, mock_generate_blackbox_testcases): self.assertEqual(3, actual.fuzz_task_output.testcases_executed) +class DoBlackboxFuzzingDeferredLoggingTest(fake_filesystem_unittest.TestCase): + """do_blackbox_fuzzing deferred log upload unit tests without datastore dependency.""" + + def setUp(self): + """Setup for blackbox fuzzing deferred logging test.""" + helpers.patch_environ(self) + helpers.patch(self, [ + 'clusterfuzz._internal.base.utils.random_element_from_list', + 'clusterfuzz._internal.base.utils.random_number', + 'clusterfuzz._internal.bot.fuzzers.engine_common.current_timestamp', + 'clusterfuzz._internal.bot.tasks.utasks.fuzz_task_knobs.pick_gestures', + 'clusterfuzz._internal.bot.testcase_manager.upload_log', + 'clusterfuzz._internal.bot.testcase_manager.upload_testcase', + 'clusterfuzz._internal.build_management.revisions.get_component_list', + 'clusterfuzz._internal.crash_analysis.crash_analyzer.is_crash', + 'clusterfuzz._internal.crash_analysis.stack_parsing.stack_analyzer.get_crash_data', + 'clusterfuzz._internal.datastore.ndb_init.context', + 'clusterfuzz._internal.bot.tasks.trials.Trials', + 'random.random', + 'clusterfuzz._internal.system.process_handler.close_queue', + 'clusterfuzz._internal.system.process_handler.get_process', + 'clusterfuzz._internal.system.process_handler.get_queue', + 'clusterfuzz._internal.system.process_handler.run_process', + 'clusterfuzz._internal.system.process_handler.terminate_hung_threads', + 'clusterfuzz._internal.system.process_handler.' + 'terminate_stale_application_instances', + ]) + + os.environ['APP_ARGS'] = '-x' + os.environ['APP_DIR'] = '/app' + os.environ['APP_NAME'] = 'app_1' + os.environ['APP_PATH'] = '/app/app_1' + os.environ['BOT_TMPDIR'] = '/tmp' + os.environ['CRASH_STACKTRACES_DIR'] = '/crash' + os.environ['ENABLE_GESTURES'] = 'False' + os.environ['FAIL_RETRIES'] = '1' + os.environ['FUZZER_DIR'] = '/fuzzer' + os.environ['INPUT_DIR'] = '/input' + os.environ['JOB_NAME'] = 'asan_test' + os.environ['MAX_FUZZ_THREADS'] = '1' + os.environ['MAX_TESTCASES'] = '2' + os.environ['ROOT_DIR'] = '/root' + os.environ['THREAD_ALIVE_CHECK_INTERVAL'] = '0.001' + os.environ['THREAD_DELAY'] = '0.001' + os.environ['USER_PROFILE_IN_MEMORY'] = 'True' + + test_utils.set_up_pyfakefs(self) + self.fs.create_dir('/crash') + self.fs.create_dir('/root/bot/logs') + self.fs.create_file('/tests/0', contents='testcase_0_bytes') + self.fs.create_file('/tests/1', contents='testcase_1_bytes') + + self.mock.random_element_from_list.return_value = 2.0 + self.mock.random_number.side_effect = [0, 0, 3] + self.mock.random.side_effect = [0.3, 0.3] + self.mock.pick_gestures.return_value = [] + self.mock.get_component_list.return_value = [{ + 'component': 'component', + 'link_text': 'rev', + }] + self.mock.current_timestamp.return_value = 0.0 + self.mock.run_process.return_value = (0, 0, 'raw_output') + self.mock.is_crash.side_effect = [True, False] + + mock_unsymbolized_info = mock.MagicMock() + mock_unsymbolized_info.crash_stacktrace = 'unsymbolized_stacktrace' + self.mock.get_crash_data.return_value = mock_unsymbolized_info + + self.mock.get_queue.return_value = queue.Queue() + self.mock.get_process.return_value = threading.Thread + + def test_do_blackbox_fuzzing_packaging(self): + """Verify that do_blackbox_fuzzing invokes run_testcase_and_return_result_in_queue + and correctly packages raw logs into fuzzer_run_outputs.""" + uworker_input = uworker_msg_pb2.Input( + fuzzer_name='fantasy_fuzz', + job_type='asan_test', + fuzz_task_input=uworker_msg_pb2.FuzzTaskInput()) + + session = fuzz_task.FuzzingSession(uworker_input, 10) + session.generate_blackbox_testcases = mock.MagicMock() + expected_testcase_file_paths = ['/tests/0', '/tests/1'] + session.generate_blackbox_testcases.return_value = ( + fuzz_task.GenerateBlackboxTestcasesResult( + True, expected_testcase_file_paths, + {'fuzzer_binary_name': 'fantasy_fuzz'})) + + fuzzer = data_types.Fuzzer() + fuzzer.name = 'fantasy_fuzz' + + mock_thread_cls = mock.MagicMock(wraps=threading.Thread) + self.mock.get_process.return_value = mock_thread_cls + + session.do_blackbox_fuzzing(fuzzer, '/fake-fuzz-dir', 'asan_test') + + self.assertEqual(2, mock_thread_cls.call_count) + for _, kwargs in mock_thread_cls.call_args_list: + self.assertEqual( + 5, len(kwargs['args']), + 'run_testcase_and_return_result_in_queue must take 5 positional args') + + # Verify fuzzer_run_outputs packaged in fuzz_task_output. + fuzzer_run_outputs = session.fuzz_task_output.fuzzer_run_outputs + self.assertEqual(2, len(fuzzer_run_outputs)) + + # First testcase crashed (/tests/0). + self.assertEqual(b'unsymbolized_stacktrace', fuzzer_run_outputs[0].output) + self.assertEqual(b'testcase_0_bytes', fuzzer_run_outputs[0].testcase) + + # Second testcase did not crash (/tests/1). + self.assertEqual(b'unsymbolized_stacktrace', fuzzer_run_outputs[1].output) + self.assertEqual(b'', fuzzer_run_outputs[1].testcase) + + def test_drain_temp_queue(self): + """Verify _drain_temp_queue unpacks tuples correctly and reads log files.""" + uworker_input = uworker_msg_pb2.Input( + fuzzer_name='fantasy_fuzz', + job_type='asan_test', + fuzz_task_input=uworker_msg_pb2.FuzzTaskInput()) + session = fuzz_task.FuzzingSession(uworker_input, 10) + + self.fs.create_file('/tmp/trace.log', contents='output_trace') + + temp_queue = queue.Queue() + crash = testcase_manager.Crash('/test/crash', 1, 1, [], [], '/stack') + fuzzer_run_output_data = testcase_manager.FuzzerRunOutputData.from_file_path( + file_path='/tmp/trace.log', + crash_path='/test/crash', + return_code=1, + log_time=datetime.datetime(2026, 7, 10)) + temp_queue.put( + testcase_manager.TestcaseRunResult( + crash=crash, fuzzer_run_output_data=fuzzer_run_output_data)) + + standalone_crash = testcase_manager.Crash('/test/crash2', 1, 2, [], [], + '/stack2') + temp_queue.put(testcase_manager.TestcaseRunResult(crash=standalone_crash)) + + crashes = [] + session._drain_temp_queue(temp_queue, crashes) + + self.assertTrue(temp_queue.empty()) + self.assertFalse(os.path.exists('/tmp/trace.log')) + self.assertEqual([crash, standalone_crash], crashes) + self.assertEqual(1, len(session.fuzz_task_output.fuzzer_run_outputs)) + self.assertEqual(b'output_trace', + session.fuzz_task_output.fuzzer_run_outputs[0].output) + + @test_utils.with_cloud_emulators('datastore') class DoEngineFuzzingTest(fake_filesystem_unittest.TestCase): """do_engine_fuzzing tests.""" 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 739b09f8335..9ce714c07ae 100644 --- a/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py +++ b/src/clusterfuzz/_internal/tests/core/bot/testcase_manager_test.py @@ -14,6 +14,7 @@ """Tests testcase_manager.""" import datetime import os +import queue import shutil import tempfile import unittest @@ -864,6 +865,102 @@ def test_test_for_reproducibility_greybox_succeed_after_multiple_tries(self): ]) +class RunTestcaseAndReturnResultInQueueTest(fake_filesystem_unittest.TestCase): + """Tests for run_testcase_and_return_result_in_queue.""" + + def setUp(self): + """Setup for run_testcase_and_return_result_in_queue tests.""" + test_helpers.patch_environ(self) + test_utils.set_up_pyfakefs(self) + test_helpers.patch(self, [ + 'clusterfuzz._internal.bot.testcase_manager.run_testcase', + 'clusterfuzz._internal.bot.testcase_manager._get_testcase_time', + 'clusterfuzz._internal.bot.testcase_manager.upload_testcase', + 'clusterfuzz._internal.bot.testcase_manager.upload_log', + 'clusterfuzz._internal.crash_analysis.crash_analyzer.is_crash', + 'clusterfuzz._internal.crash_analysis.stack_parsing.stack_analyzer.get_crash_data', + 'clusterfuzz._internal.datastore.ndb_init.context', + 'clusterfuzz._internal.metrics.logs.error', + ]) + os.environ['CRASH_STACKTRACES_DIR'] = '/crashes' + os.environ['ROOT_DIR'] = '/root' + os.environ['FAIL_RETRIES'] = '1' + os.environ['FAIL_WAIT'] = '1' + os.environ['BOT_TMPDIR'] = '/bot_tmp' + self.fs.create_dir('/crashes') + self.fs.create_dir('/root/bot/logs') + self.fs.create_dir('/bot_tmp') + self.fs.create_dir('/usr/local/google/home/ibarba/clusterfuzz/.worktrees/' + 'issue-2-blackbox-raw-logs/bot/logs') + + def test_run_no_crash(self): + """Verify run_testcase_and_return_result_in_queue returns unsymbolized output + written to file and puts path in queue without uploading when no crash.""" + crash_queue = queue.Queue() + self.mock.run_testcase.return_value = (0, 1.5, 'raw_output_trace') + self.mock.is_crash.return_value = False + self.mock._get_testcase_time.return_value = datetime.datetime(2026, 7, 10) + mock_unsymbolized_info = mock.MagicMock() + mock_unsymbolized_info.crash_stacktrace = 'unsymbolized_raw_trace' + self.mock.get_crash_data.return_value = mock_unsymbolized_info + + testcase_manager.run_testcase_and_return_result_in_queue( + crash_queue, 0, '/testcase', [], {}) + + self.assertEqual(0, self.mock.error.call_count, + f'Unexpected error: {self.mock.error.call_args_list}') + self.assertFalse(crash_queue.empty()) + result = crash_queue.get() + + self.assertIsNone(result.crash) + fuzzer_run_output_data = result.fuzzer_run_output_data + self.assertTrue( + isinstance(fuzzer_run_output_data._file_path, str) and + os.path.exists(fuzzer_run_output_data._file_path)) + self.assertEqual('unsymbolized_raw_trace', + fuzzer_run_output_data.get_output()) + self.assertIsNone(fuzzer_run_output_data.crash_path) + self.assertEqual(0, fuzzer_run_output_data.return_code) + self.assertEqual( + datetime.datetime(2026, 7, 10), fuzzer_run_output_data.log_time) + self.assertEqual(0, self.mock.upload_testcase.call_count) + self.assertEqual(0, self.mock.upload_log.call_count) + + def test_run_with_crash(self): + """Verify run_testcase_and_return_result_in_queue returns crash and log file + path without direct uploads on crash.""" + crash_queue = queue.Queue() + self.mock.run_testcase.return_value = (1, 2.0, 'raw_crash_trace') + self.mock.is_crash.return_value = True + self.mock._get_testcase_time.return_value = datetime.datetime(2026, 7, 10) + mock_unsymbolized_info = mock.MagicMock() + mock_unsymbolized_info.crash_stacktrace = 'unsymbolized_crash_trace' + self.mock.get_crash_data.return_value = mock_unsymbolized_info + + testcase_manager.run_testcase_and_return_result_in_queue( + crash_queue, 0, '/testcase', [], {}) + + self.assertEqual(0, self.mock.error.call_count, + f'Unexpected error: {self.mock.error.call_args_list}') + self.assertFalse(crash_queue.empty()) + result = crash_queue.get() + + self.assertIsNotNone(result.crash) + self.assertEqual('/testcase', result.crash.file_path) + fuzzer_run_output_data = result.fuzzer_run_output_data + self.assertTrue( + isinstance(fuzzer_run_output_data._file_path, str) and + os.path.exists(fuzzer_run_output_data._file_path)) + self.assertEqual('unsymbolized_crash_trace', + fuzzer_run_output_data.get_output()) + self.assertEqual('/testcase', fuzzer_run_output_data.crash_path) + self.assertEqual(1, fuzzer_run_output_data.return_code) + self.assertEqual( + datetime.datetime(2026, 7, 10), fuzzer_run_output_data.log_time) + self.assertEqual(0, self.mock.upload_testcase.call_count) + self.assertEqual(0, self.mock.upload_log.call_count) + + def _get_fuzz_target_from_preprocess(testcase): uworker_input = uworker_msg_pb2.Input() testcase_manager.preprocess_testcase_manager(testcase, uworker_input)