Skip to content
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

Fix: issue card count #750

Merged
merged 1 commit into from
Jan 14, 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
134 changes: 112 additions & 22 deletions backend/kernelCI_app/helpers/filters.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, Dict, List, TypedDict, Literal, Any
from typing import Optional, Dict, List, Tuple, TypedDict, Literal, Any
from django.http import HttpResponseBadRequest
import re
from kernelCI_app.utils import getErrorResponseBody
Expand All @@ -15,8 +15,32 @@ def is_known_issue(issue_id: Optional[str]) -> bool:
return issue_id is not None and issue_id is not UNKNOWN_STRING


def is_issue_from_test(incident_test_id: Optional[str], is_known_issue: bool) -> bool:
return incident_test_id is not None or not is_known_issue
def is_exclusively_build_issue(issue_id: Optional[str], incident_test_id: Optional[str]) -> bool:
is_known_issue_result = is_known_issue(issue_id)
is_exclusively_build_issue_result = is_known_issue_result and incident_test_id is None
return is_exclusively_build_issue_result


def is_exclusively_test_issue(
*, issue_id: Optional[str], incident_test_id: Optional[str]
) -> bool:
is_known_issue_result = is_known_issue(issue_id)
is_exclusively_test_issue_result = is_known_issue_result and incident_test_id is not None
return is_exclusively_test_issue_result


def is_issue_from_test(*, incident_test_id: Optional[str], issue_id: Optional[str]) -> bool:
is_known_issue_result = is_known_issue(issue_id=issue_id)
is_possible_test_issue = incident_test_id is not None or not is_known_issue_result

return is_possible_test_issue


def is_issue_from_build(*, issue_id: Optional[str], incident_test_id: Optional[str]) -> bool:
is_known_issue_result = is_known_issue(issue_id=issue_id)
is_possible_build_issue = incident_test_id is None or not is_known_issue_result

return is_possible_build_issue


# TODO: consider issue_version in the filter
Expand All @@ -25,35 +49,96 @@ def is_issue_filtered_out(issue_id: Optional[str], issue_filters: set) -> bool:


def should_filter_test_issue(
issue_filters: set, issue_id: Optional[str], incident_test_id: Optional[str]
issue_filters: set, issue_id: Optional[str], incident_test_id: Optional[str], test_status: Optional[str]
) -> bool:
has_issue_filter = len(issue_filters) > 0
if (not has_issue_filter):
return False

is_known_issue_result = is_known_issue(issue_id)
is_issue_from_tests_result = is_issue_from_test(
incident_test_id, is_known_issue_result
if (test_status != "FAIL"):
return True

has_unknown_filter = UNKNOWN_STRING in issue_filters

is_exclusively_build_issue_result = is_exclusively_build_issue(
issue_id=issue_id, incident_test_id=incident_test_id
)

if is_exclusively_build_issue_result and has_unknown_filter:
return False
if is_exclusively_build_issue_result:
issue_id = UNKNOWN_STRING

is_issue_filtered_out_result = is_issue_filtered_out(issue_id, issue_filters)

return (
has_issue_filter and is_issue_from_tests_result and is_issue_filtered_out_result
return is_issue_filtered_out_result


def should_filter_build_issue(
*,
issue_filters: set,
issue_id: Optional[str],
incident_test_id: Optional[str],
build_valid: Optional[bool],
) -> bool:
has_issue_filter = len(issue_filters) > 0
if (not has_issue_filter):
return False

if (not is_build_invalid(build_valid)):
return True

has_unknown_filter = UNKNOWN_STRING in issue_filters

is_exclusively_test_issue_result = is_exclusively_test_issue(
issue_id=issue_id, incident_test_id=incident_test_id
)

if is_exclusively_test_issue_result and has_unknown_filter:
return False
if is_exclusively_test_issue_result:
issue_id = UNKNOWN_STRING

is_issue_filtered_out_result = is_issue_filtered_out(issue_id, issue_filters)

return is_issue_filtered_out_result


def should_increment_test_issue(
issue_id: Optional[str], incident_test_id: Optional[str]
) -> bool:
is_known_issue_result = is_known_issue(issue_id=issue_id)
is_exclusively_build_issue = is_known_issue_result and incident_test_id is None
if is_exclusively_build_issue:
issue_id = UNKNOWN_STRING
) -> Tuple[str, bool]:
is_exclusively_build_issue_result = is_exclusively_build_issue(
issue_id=issue_id, incident_test_id=incident_test_id
)
if is_exclusively_build_issue_result:
return (UNKNOWN_STRING, False)

is_issue_from_test_result = is_issue_from_test(
incident_test_id=incident_test_id, issue_id=issue_id
)

is_unknown_issue = issue_id is UNKNOWN_STRING
is_known_test_issue = incident_test_id is not None
is_issue_from_test = is_known_test_issue or is_unknown_issue
return (issue_id, is_issue_from_test_result)

return is_issue_from_test

def should_increment_build_issue(
*,
issue_id: Optional[str],
incident_test_id: Optional[str],
build_valid: Optional[bool],
) -> Tuple[str, bool]:
is_exclusively_test_issue_result = is_exclusively_test_issue(
issue_id=issue_id, incident_test_id=incident_test_id
)
if is_exclusively_test_issue_result:
return (UNKNOWN_STRING, False)

is_issue_from_build_result = is_issue_from_build(
issue_id=issue_id, incident_test_id=incident_test_id
)

result = is_issue_from_build_result and is_build_invalid(build_valid)

return (issue_id, result)


def toIntOrDefault(value, default):
Expand Down Expand Up @@ -320,6 +405,7 @@ def is_build_filtered_out(
duration: Optional[int],
valid: Optional[bool],
issue_id: Optional[str],
incident_test_id: Optional[str],
) -> bool:
return (
(
Expand All @@ -342,8 +428,12 @@ def is_build_filtered_out(
and (toIntOrDefault(duration, 0) < self.filterBuildDurationMin)
)
or (
len(self.filterIssues["build"]) > 0
and (issue_id not in self.filterIssues["build"] or valid is True)
should_filter_build_issue(
issue_filters=self.filterIssues["build"],
issue_id=issue_id,
incident_test_id=incident_test_id,
build_valid=valid,
)
)
)

Expand Down Expand Up @@ -422,7 +512,7 @@ def is_boot_filtered_out(
and (toIntOrDefault(duration, 0) < self.filterBootDurationMin)
)
or should_filter_test_issue(
self.filterIssues["boot"], issue_id, incident_test_id
self.filterIssues["boot"], issue_id, incident_test_id, status
)
or (
len(self.filterPlatforms["boot"]) > 0
Expand Down Expand Up @@ -464,7 +554,7 @@ def is_test_filtered_out(
and (toIntOrDefault(duration, 0) < self.filterTestDurationMin)
)
or should_filter_test_issue(
self.filterIssues["test"], issue_id, incident_test_id
self.filterIssues["test"], issue_id, incident_test_id, status
)
or (
len(self.filterPlatforms["test"]) > 0
Expand Down
31 changes: 23 additions & 8 deletions backend/kernelCI_app/helpers/treeDetails.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections.abc import Callable
from typing import Any
from kernelCI_app.helpers.filters import (
should_increment_build_issue,
should_increment_test_issue,
UNKNOWN_STRING,
)
Expand Down Expand Up @@ -103,6 +104,7 @@ def get_tree_details_data(request, commit_hash):
builds_filter.builds_id = incidents.build_id
LEFT JOIN issues
ON incidents.issue_id = issues.id
AND incidents.issue_version = issues.version
WHERE
tests.origin = %(origin_param)s OR
tests.origin IS NULL
Expand Down Expand Up @@ -258,8 +260,15 @@ def process_builds_issue(instance, row_data):
issue_report_url = row_data["issue_report_url"]
issue_version = row_data["issue_version"]
build_valid = row_data["build_valid"]
incident_test_id = row_data["incident_test_id"]

(issue_id, can_insert_issue) = should_increment_build_issue(
issue_id=issue_id,
incident_test_id=incident_test_id,
build_valid=build_valid,
)

if issue_id and (build_valid is False or build_valid is None):
if issue_id and issue_version is not None and can_insert_issue:
current_issue = instance.processed_build_issues.get((issue_id, issue_version))
if current_issue:
current_issue["incidents_info"]["incidentsCount"] += 1
Expand All @@ -276,16 +285,18 @@ def process_builds_issue(instance, row_data):


def process_tests_issue(instance, row_data):
testStatus = row_data["test_status"]
test_status = row_data["test_status"]
issue_id = row_data["issue_id"]
issue_comment = row_data["issue_comment"]
issue_version = row_data["issue_version"]
issue_report_url = row_data["issue_report_url"]
incident_test_id = row_data["incident_test_id"]

can_insert_issue = should_increment_test_issue(issue_id, incident_test_id)
(issue_id, can_insert_issue) = should_increment_test_issue(
issue_id, incident_test_id
)

if issue_id and can_insert_issue:
if issue_id and issue_version is not None and can_insert_issue:
currentIssue = instance.testIssuesTable.get((issue_id, issue_version))
if currentIssue:
currentIssue["incidents_info"]["incidentsCount"] += 1
Expand All @@ -296,7 +307,7 @@ def process_tests_issue(instance, row_data):
issue_comment=issue_comment,
issue_report_url=issue_report_url,
)
elif testStatus == "FAIL":
elif test_status == "FAIL":
instance.failedTestsWithUnknownIssues += 1


Expand All @@ -308,11 +319,11 @@ def process_boots_issue(instance, row_data):
issue_report_url = row_data["issue_report_url"]
incident_test_id = row_data["incident_test_id"]

can_insert_issue = should_increment_test_issue(
(issue_id, can_insert_issue) = should_increment_test_issue(
issue_id=issue_id, incident_test_id=incident_test_id
)

if issue_id and can_insert_issue:
if issue_id and issue_version is not None and can_insert_issue:
currentIssue = instance.bootsIssuesTable.get((issue_id, issue_version))
if currentIssue:
currentIssue["incidents_info"]["incidentsCount"] += 1
Expand All @@ -331,9 +342,13 @@ def decide_if_is_build_filtered_out(instance, row_data):
issue_id = row_data["issue_id"]
build_valid = row_data["build_valid"]
build_duration = row_data["build_duration"]
incident_test_id = row_data["incident_test_id"]

is_build_filtered_out = instance.filters.is_build_filtered_out(
valid=build_valid, duration=build_duration, issue_id=issue_id
valid=build_valid,
duration=build_duration,
issue_id=issue_id,
incident_test_id=incident_test_id,
)
return is_build_filtered_out

Expand Down
5 changes: 5 additions & 0 deletions backend/kernelCI_app/typeModels/issueDetails.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from typing import Dict, Tuple
from kernelCI_app.utils import Issue
from pydantic import BaseModel


class IssueDetailsPathParameters(BaseModel):
issue_id: str
version: int


type IssueDict = Dict[Tuple[str, str], Issue]
32 changes: 32 additions & 0 deletions backend/kernelCI_app/unitTests/treeDetails.test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import unittest
from kernelCI_app.helpers.filters import should_filter_test_issue, UNKNOWN_STRING


# TODO: replace with pytest
class TestShouldFilterTestIssue(unittest.TestCase):

def test_no_issue_filters(self):
self.assertFalse(
should_filter_test_issue(set(), UNKNOWN_STRING, "incident_test_1", "FAIL")
)

def test_unknown_filter_with_exclusively_build_issue(self):
self.assertTrue(
should_filter_test_issue(
{UNKNOWN_STRING}, "issue1", "incident_test_1", "PASS"
)
)

def test_unknown_issue_but_not_from_test(self):
self.assertFalse(
should_filter_test_issue(
{UNKNOWN_STRING},
"maestro:72697a4efbbd0eff7080781839b405bbf0902f79",
None,
"FAIL",
)
)


if __name__ == "__main__":
unittest.main()
Loading
Loading