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 b42aa03b60..792e59495c 100644 --- a/src/clusterfuzz/_internal/bot/tasks/utasks/corpus_pruning_task.py +++ b/src/clusterfuzz/_internal/bot/tasks/utasks/corpus_pruning_task.py @@ -479,8 +479,8 @@ def run(self, initial_corpus_path, minimized_corpus_path, bad_units_path): raise CorpusPruningError( 'Corpus pruning timed out while minimizing corpus\n' + repr(e)) except engine.Error as e: - raise CorpusPruningError('Corpus pruning failed to minimize corpus\n' + - repr(e)) + logs.warning('Corpus pruning failed to minimize corpus\n' + repr(e)) + raise CorpusPruningError('Corpus pruning failed to minimize corpus') symbolized_output = stack_symbolizer.symbolize_stacktrace(result.logs) 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 fcb5495c76..5a945460cf 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 @@ -20,7 +20,7 @@ import shutil import tempfile import unittest -from unittest.mock import patch +from unittest.mock import MagicMock, patch from clusterfuzz._internal.bot.fuzzers import options from clusterfuzz._internal.bot.fuzzers.centipede import \ @@ -30,6 +30,7 @@ from clusterfuzz._internal.bot.tasks import commands from clusterfuzz._internal.bot.tasks.utasks import corpus_pruning_task from clusterfuzz._internal.bot.tasks.utasks import uworker_io +from clusterfuzz._internal.metrics import logs from clusterfuzz._internal.datastore import data_handler from clusterfuzz._internal.datastore import data_types from clusterfuzz._internal.google_cloud_utils import blobs @@ -584,3 +585,35 @@ def tearDown(self): shutil.rmtree('a') shutil.rmtree('c') shutil.rmtree(self.temp_dir) + + +class CorpusPruningDedupTest(unittest.TestCase): + """Tests for corpus pruning error deduplication.""" + + def test_minimize_corpus_error_dedup(self): + """Test that corpus minimization errors are deduplicated.""" + runner = MagicMock() + runner.get_fuzzer_flags.return_value = [] + runner.target_path = '/tmp/target' + runner.context = MagicMock() + + # We need a predictable repr for the error + err = engine.Error("Variable error content 12345") + runner.minimize_corpus.side_effect = err + + pruner = corpus_pruning_task.CorpusPrunerBase(runner) + + with patch('clusterfuzz._internal.system.shell.get_directory_file_count', return_value=1), \ + patch('clusterfuzz._internal.bot.fuzzers.engine_common.unpack_seed_corpus_if_needed'), \ + patch('clusterfuzz._internal.system.environment.reset_current_memory_tool_options'), \ + patch('clusterfuzz._internal.metrics.logs.warning') as mock_log_warning, \ + patch('clusterfuzz._internal.metrics.logs.info'): + + with self.assertRaises(corpus_pruning_task.CorpusPruningError) as cm: + pruner.run('/tmp/initial', '/tmp/minimized', '/tmp/bad') + + # This is the expected behavior AFTER fix. + self.assertEqual(str(cm.exception), "Corpus pruning failed to minimize corpus") + + # Check detailed log + mock_log_warning.assert_called_with("Corpus pruning failed to minimize corpus\n" + repr(err))