Skip to content
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
18 changes: 14 additions & 4 deletions tools/ci/play_vitals.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,13 @@ def cmd_issues(token, args):
print(f" {issue.get('cause')}")
print(f" id {issue.get('name')}")
reports = search_reports(
token, issue.get("name", ""), start, end, tz, limit=args.reports
token,
issue.get("name", ""),
start,
end,
tz,
limit=args.reports,
version_code=args.version_code,
)
for line in report_lines(reports, traces=args.traces):
print(line)
Expand Down Expand Up @@ -278,18 +284,22 @@ def report_lines(reports, traces):
return lines


def search_reports(token, issue_name, start, end, tz, limit):
def search_reports(token, issue_name, start, end, tz, limit, version_code=None):
"""Returns the reports behind one cluster, each with its device and versions.

The issue carries sample report names, but only the names, and they are not a
resource path any GET accepts. errorReports:search filtered on the issue id is the
route that works.
route that works. A cluster holds several builds, so the filter must repeat the
version, or the printed sample can come from any of them.
"""
issue_id = issue_name.rsplit("/", 1)[-1]
terms = [f"errorIssueId = {issue_id}"]
if version_code:
terms.append(f"versionCode = {version_code}")
params = flatten(
{
"interval": {"startTime": datetime_at(start, tz), "endTime": datetime_at(end, tz)},
"filter": f"errorIssueId = {issue_id}",
"filter": " AND ".join(terms),
"pageSize": limit,
}
)
Expand Down
33 changes: 31 additions & 2 deletions tools/ci/play_vitals_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,20 @@ def test_search_reports_sends_a_dotted_interval_and_filter(self):
self.assertEqual(seen["params"]["filter"], ["errorIssueId = abc123"])
self.assertEqual(seen["params"]["pageSize"], ["1"])

def test_a_version_filter_reaches_the_report_search(self):
seen = self.captured_params(
lambda: pv.search_reports(
"t",
"apps/com.httrack.android/abc123",
datetime.date(2026, 10, 2),
datetime.date(2026, 10, 30),
"UTC",
1,
version_code=107,
)
)
self.assertEqual(seen["params"]["filter"], ["errorIssueId = abc123 AND versionCode = 107"])

def test_the_issue_id_is_taken_from_the_last_path_segment(self):
seen = self.captured_params(
lambda: pv.search_reports(
Expand Down Expand Up @@ -361,7 +375,7 @@ def test_probe_takes_no_options(self):
class Issues(unittest.TestCase):
"""cmd_issues must ask the API for as many reports as it was told to read."""

def run_issues(self, wanted, traces):
def run_issues(self, wanted, traces, version_code=None):
pool = [dict(FULL_REPORT, reportText=f"trace-{i}") for i in range(wanted + 2)]
asked = {}

Expand All @@ -375,7 +389,12 @@ def fake_call(_token, url, method="GET", body=None, params=None):
return {"errorReports": pool[: int(params["pageSize"][0])]}

args = argparse.Namespace(
kind="crash", days=28, limit=15, reports=wanted, traces=traces, version_code=None
kind="crash",
days=28,
limit=15,
reports=wanted,
traces=traces,
version_code=version_code,
)
out = io.StringIO()
with mock.patch("play_vitals.call", side_effect=fake_call):
Expand All @@ -387,6 +406,16 @@ def test_the_report_search_asks_for_the_number_of_reports_requested(self):
_, asked = self.run_issues(wanted=3, traces=1)
self.assertEqual(asked["reports"]["pageSize"], ["3"])

def test_one_version_restricts_the_reports_as_well_as_the_clusters(self):
_, asked = self.run_issues(wanted=3, traces=1, version_code=107)
self.assertIn("versionCode = 107", asked["issues"]["filter"][0])
self.assertIn("versionCode = 107", asked["reports"]["filter"][0])

def test_without_one_neither_search_names_a_version(self):
_, asked = self.run_issues(wanted=3, traces=1)
self.assertNotIn("versionCode", asked["issues"]["filter"][0])
self.assertNotIn("versionCode", asked["reports"]["filter"][0])

def test_the_cluster_search_never_asks_for_more_than_one_sample(self):
for wanted in (1, 3, 25):
_, asked = self.run_issues(wanted=wanted, traces=1)
Expand Down
Loading