diff --git a/src/clusterfuzz/_internal/bot/tasks/utasks/corpus_pruning_task.py b/src/clusterfuzz/_internal/bot/tasks/utasks/corpus_pruning_task.py index 7f4b003a11..ed9d8ce08f 100644 --- a/src/clusterfuzz/_internal/bot/tasks/utasks/corpus_pruning_task.py +++ b/src/clusterfuzz/_internal/bot/tasks/utasks/corpus_pruning_task.py @@ -876,6 +876,11 @@ def _process_corpus_crashes(output: uworker_msg_pb2.Output): # pylint: disable= fuzz_target=fuzz_target.project_qualified_name()) if existing_testcase: logs.info('_process_corpus_crashes figured out crash already exists.') + events.emit( + events.TestcaseRejectionEvent( + testcase=existing_testcase, + rejection_reason=events.RejectionReason.CORPUS_PRUNING_DUPLICATE + )) continue unit_name = os.path.basename(crash.unit_path) diff --git a/src/clusterfuzz/_internal/metrics/events.py b/src/clusterfuzz/_internal/metrics/events.py index 335f2cb51e..e5bcd4b900 100644 --- a/src/clusterfuzz/_internal/metrics/events.py +++ b/src/clusterfuzz/_internal/metrics/events.py @@ -54,6 +54,7 @@ class RejectionReason: """Explanation for the testcase rejection values.""" ANALYZE_NO_REPRO = 'analyze_no_repro' ANALYZE_FLAKE_ON_FIRST_ATTEMPT = 'analyze_flake_on_first_attempt' + CORPUS_PRUNING_DUPLICATE = 'corpus_pruning_duplicate' @dataclass(kw_only=True) diff --git a/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/corpus_pruning_task_test.py b/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/corpus_pruning_task_test.py index 06f45f7e89..0ffb9ae16d 100644 --- a/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/corpus_pruning_task_test.py +++ b/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/corpus_pruning_task_test.py @@ -296,6 +296,28 @@ def test_prune(self): self.assertEqual(self.mock.unpack_seed_corpus_if_needed.call_count, 1) + def test_rejection_event_duplicate(self): + """Test that a duplicate crash results in a rejection event.""" + existing_testcase = test_utils.create_generic_testcase() + + helpers.patch(self, [ + 'clusterfuzz._internal.datastore.data_handler.find_testcase', + ]) + self.mock.find_testcase.return_value = existing_testcase + + uworker_input = corpus_pruning_task.utask_preprocess( + job_type='libfuzzer_asan_job', + fuzzer_name='libFuzzer_test_fuzzer', + uworker_env={}) + output = corpus_pruning_task.utask_main(uworker_input) + output.uworker_input.CopyFrom(uworker_input) + corpus_pruning_task.utask_postprocess(output) + + self.mock.emit.assert_called_once_with( + events.TestcaseRejectionEvent( + testcase=existing_testcase, + rejection_reason=events.RejectionReason.CORPUS_PRUNING_DUPLICATE)) + def test_get_libfuzzer_flags(self): """Test get_libfuzzer_flags logic.""" fuzz_target = data_handler.get_fuzz_target('libFuzzer_test_fuzzer')