Skip to content

[Tracing] Emiting IssueFilingEvent during triage #4851

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/clusterfuzz/_internal/cron/triage.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from clusterfuzz._internal.issue_management import issue_tracker_policy
from clusterfuzz._internal.issue_management import issue_tracker_utils
from clusterfuzz._internal.metrics import crash_stats
from clusterfuzz._internal.metrics import events
from clusterfuzz._internal.metrics import logs
from clusterfuzz._internal.metrics import monitoring_metrics

Expand Down Expand Up @@ -531,13 +532,24 @@ def _triage_testcase(testcase, excluded_jobs, all_jobs, throttler):
_emit_untriaged_testcase_age_metric(testcase, PENDING_FILING)
_increment_untriaged_testcase_count(testcase.job_type, PENDING_FILING)
logs.info(f'Issue filing failed for testcase id {testcase_id}')
events.emit(
events.IssueFilingEvent(
testcase=testcase,
issue_tracker_project=issue_tracker.project,
issue_created=False))
return

_set_testcase_stuck_state(testcase, False)

_create_filed_bug_metadata(testcase)
issue_filer.notify_issue_update(testcase, 'new')

events.emit(
events.IssueFilingEvent(
testcase=testcase,
issue_tracker_project=issue_tracker.project,
issue_id=str(testcase.bug_information),
issue_created=True))
logs.info('Filed new issue %s for testcase %d.' % (testcase.bug_information,
testcase_id))

Expand Down
4 changes: 2 additions & 2 deletions src/clusterfuzz/_internal/datastore/data_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1665,8 +1665,8 @@ class TestcaseLifecycleEvent(Model):
rejection_reason = ndb.StringProperty()

### Issue Filing.
# Name of the issue tracker (e.g., buganizer).
issue_tracker = ndb.StringProperty()
# Name of the project associated with the issue tracker.
issue_tracker_project = ndb.StringProperty()

# ID from issue tracker bug (same as `bug_information` for Testcase).
issue_id = ndb.StringProperty()
Expand Down
4 changes: 2 additions & 2 deletions src/clusterfuzz/_internal/metrics/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ class TestcaseRejectionEvent(BaseTestcaseEvent, BaseTaskEvent):
class IssueFilingEvent(BaseTestcaseEvent, BaseTaskEvent):
"""Issue filing event."""
event_type: str = field(default=EventTypes.ISSUE_FILING, init=False)
# Either buganizer or some_other_board.
issue_tracker: str | None = None
# Name of the project associate with the issue tracker.
issue_tracker_project: str | None = None
# The number of the issue on the issue tracker.
issue_id: str | None = None
# If the issue filing attempt was successful.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@

import datetime
import unittest
from unittest import mock

from clusterfuzz._internal.cron import triage
from clusterfuzz._internal.datastore import data_handler
from clusterfuzz._internal.datastore import data_types
from clusterfuzz._internal.metrics import events
from clusterfuzz._internal.tests.test_libs import appengine_test_utils
from clusterfuzz._internal.tests.test_libs import helpers
from clusterfuzz._internal.tests.test_libs import test_utils
Expand Down Expand Up @@ -561,3 +563,80 @@ def test_default_limit(self):
throttler._get_project_bugs_filing_max(testcase.job_type))
self.assertEqual(None,
throttler._get_job_bugs_filing_max(testcase.job_type))


@test_utils.with_cloud_emulators('datastore')
class IssueFilingEventEmitTest(unittest.TestCase):
"""Tests emission of IssueFilingEvent when filing an issue."""

def setUp(self):
helpers.patch(self, [
'clusterfuzz._internal.metrics.events.emit',
'clusterfuzz._internal.metrics.events._get_datetime_now',
'clusterfuzz._internal.cron.triage._file_issue',
'clusterfuzz._internal.cron.triage._is_bug_filed',
'clusterfuzz._internal.cron.triage._is_crash_important',
'clusterfuzz._internal.issue_management.issue_tracker_utils.get_issue_tracker_for_testcase',
'clusterfuzz._internal.datastore.data_handler.critical_tasks_completed',
'clusterfuzz._internal.cron.triage._check_and_update_similar_bug',
'clusterfuzz._internal.cron.triage._create_filed_bug_metadata',
'clusterfuzz._internal.cron.triage.issue_filer.notify_issue_update',
'clusterfuzz._internal.cron.triage._set_testcase_stuck_state',
'clusterfuzz._internal.cron.triage._emit_untriaged_testcase_age_metric',
'clusterfuzz._internal.cron.triage._increment_untriaged_testcase_count',
])
self.mock._get_datetime_now.return_value = datetime.datetime(2025, 1, 1)
self.mock._is_bug_filed.return_value = False
self.mock._is_crash_important.return_value = True
self.mock.critical_tasks_completed.return_value = True
self.mock._check_and_update_similar_bug.return_value = False
self.mock._create_filed_bug_metadata.return_value = None
self.mock.notify_issue_update.return_value = None
self.mock._set_testcase_stuck_state.return_value = None
self.mock._emit_untriaged_testcase_age_metric.return_value = None
self.mock._increment_untriaged_testcase_count.return_value = None

self.testcase = test_utils.create_generic_testcase()
self.testcase.set_metadata('ran_grouper', True)

issue_tracker = mock.MagicMock()
issue_tracker.project = 'oss-fuzz'
self.mock.get_issue_tracker_for_testcase.return_value = issue_tracker

def test_event_emitted(self):
"""Tests that the IssueFilingEvent is emitted on a filled testcase."""

def file_issue(testcase, issue_tracker_obj, throttler): # pylint: disable=unused-argument
testcase.bug_information = '99'
return True

self.mock._file_issue.side_effect = file_issue

triage._triage_testcase(
self.testcase,
excluded_jobs=[],
all_jobs=[self.testcase.job_type],
throttler=triage.Throttler())

self.mock.emit.assert_called_once_with(
events.IssueFilingEvent(
testcase=self.testcase,
issue_tracker_project='oss-fuzz',
issue_id='99',
issue_created=True))

def test_event_emitted_on_failure(self):
"""Tests that the IssueFilingEvent is emitted on a failed filing."""
self.mock._file_issue.return_value = False

triage._triage_testcase(
self.testcase,
excluded_jobs=[],
all_jobs=[self.testcase.job_type],
throttler=triage.Throttler())

self.mock.emit.assert_called_once_with(
events.IssueFilingEvent(
testcase=self.testcase,
issue_tracker_project='oss-fuzz',
issue_created=False))
12 changes: 6 additions & 6 deletions src/clusterfuzz/_internal/tests/core/metrics/events_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,13 @@ def test_issue_filing_event(self):
event_filing = events.IssueFilingEvent(
source=source,
testcase=testcase,
issue_tracker='buganizer',
issue_tracker_project='oss-fuzz',
issue_id='12345',
issue_created=True)
self._assert_event_common_fields(event_filing, event_type, source)
self._assert_testcase_fields(event_filing, testcase)
self._assert_task_fields(event_filing)
self.assertEqual(event_filing.issue_tracker, 'buganizer')
self.assertEqual(event_filing.issue_tracker_project, 'oss-fuzz')
self.assertEqual(event_filing.issue_id, '12345')
self.assertTrue(event_filing.issue_created)

Expand Down Expand Up @@ -307,7 +307,7 @@ def test_serialize_issue_filing_event(self):
event = events.IssueFilingEvent(
source='events_test',
testcase=testcase,
issue_tracker='buganizer',
issue_tracker_project='oss-fuzz',
issue_id='67890',
issue_created=False)
event_type = event.event_type
Expand All @@ -325,7 +325,7 @@ def test_serialize_issue_filing_event(self):
self._assert_task_fields(event_entity)

# IssueFilingEvent specific assertions
self.assertEqual(event_entity.issue_tracker, 'buganizer')
self.assertEqual(event_entity.issue_tracker_project, 'oss-fuzz')
self.assertEqual(event_entity.issue_id, '67890')
self.assertFalse(event_entity.issue_created)

Expand Down Expand Up @@ -437,7 +437,7 @@ def test_deserialize_issue_filing_event(self):
event_entity.fuzzer = 'fuzzer1'
event_entity.job = 'test_job'
event_entity.crash_revision = 2
event_entity.issue_tracker = 'buganizer'
event_entity.issue_tracker_project = 'oss-fuzz'
event_entity.issue_id = '13579'
event_entity.issue_created = True
event_entity.put()
Expand All @@ -458,7 +458,7 @@ def test_deserialize_issue_filing_event(self):
self.assertEqual(event.crash_revision, 2)

# IssueFilingEvent specific assertions
self.assertEqual(event.issue_tracker, 'buganizer')
self.assertEqual(event.issue_tracker_project, 'oss-fuzz')
self.assertEqual(event.issue_id, '13579')
self.assertTrue(event.issue_created)

Expand Down
Loading