Skip to content

Commit 3c7b00f

Browse files
authored
[Tracing] Display the testcase status board in ClusterFuzz testcase detail page (#4954)
This PR adds the testcase status board to the ClusterFuzz webpage, specifically in the testcase detail page. This board shows data about the last events from a given testcase, making it easier to analyze testcase information. Key changes: - Add `testcase-status-events.html` with how to display the testcase status board in the ClusterFuzz webpage. - Add the testcase status board information to `show.py` in order to enable accessing it in the html file by using the `info` object. - Display the testcase status board in the webpage by adding it to `testcase-detail.html`. - Add the issue id to the issue filing event information. Related to [b/435286563](b/435286563).
1 parent e60b36e commit 3c7b00f

File tree

5 files changed

+170
-6
lines changed

5 files changed

+170
-6
lines changed

src/appengine/handlers/testcase_detail/show.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from clusterfuzz._internal.metrics import crash_stats
3535
from clusterfuzz._internal.system import environment
3636
from handlers import base_handler
37+
from handlers.testcase_detail import testcase_status_events
3738
from libs import access
3839
from libs import auth
3940
from libs import form
@@ -483,10 +484,11 @@ def get_testcase_detail(testcase):
483484
memory_tool_display_label = memory_tool_display_string.split(':')[0]
484485
memory_tool_display_value = memory_tool_display_string.split(':')[1].strip()
485486

486-
helpers.log('Testcase %s' % testcase.key.id(), helpers.VIEW_OPERATION)
487+
testcase_id = testcase.key.id()
488+
helpers.log('Testcase %s' % testcase_id, helpers.VIEW_OPERATION)
487489
return {
488490
'id':
489-
testcase.key.id(),
491+
testcase_id,
490492
'crash_type':
491493
crash_type,
492494
'crash_address':
@@ -557,6 +559,8 @@ def get_testcase_detail(testcase):
557559
_parse_suspected_cls(metadata.get('predator_result')),
558560
'testcase':
559561
testcase,
562+
'testcase_status_info':
563+
testcase_status_events.get_testcase_status_info(testcase_id),
560564
'timestamp':
561565
utils.utc_datetime_to_timestamp(testcase.timestamp),
562566
'show_blame':

src/appengine/handlers/testcase_detail/testcase_status_events.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ def _format_issue_filing_event(self,
111111
event: events.IssueFilingEvent) -> EventInfo:
112112
"""Formats an issue filing event."""
113113
info = self._format_lifecycle_events_common_fields(event)
114-
info['event_info'] = ('Issue created' if event.issue_created else
115-
'Failed to create the issue')
114+
info['event_info'] = (f'Issue created ({event.issue_id})' if
115+
event.issue_created else 'Failed to create the issue')
116116
if event.issue_created and event.issue_reporter:
117117
info['event_info'] += f'\nManually created by {event.issue_reporter}'
118118
return info

src/appengine/private/components/testcase-detail/testcase-detail.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<link rel="import" href="find-similar-issues-panel.html">
2929
<link rel="import" href="redo-dialog.html">
3030
<link rel="import" href="refresh-button.html">
31+
<link rel="import" href="testcase-status-events.html">
3132
<link rel="import" href="testcase-variants.html">
3233
<link rel="import" href="set-security-dialog.html">
3334
<link rel="import" href="suspected-cls.html">
@@ -607,6 +608,9 @@
607608
<template is="dom-if" if="[[info.suspected_cls]]">
608609
<suspected-cls info="[[info]]"></suspected-cls>
609610
</template>
611+
612+
<testcase-status-events info="[[info]]"></testcase-status-events>
613+
610614
<div class="section">
611615
<div class="title">
612616
Crash Stacktrace
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<!--
2+
Copyright 2025 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
<link rel="import" href="../../bower_components/polymer/polymer.html">
17+
18+
<dom-module id="testcase-status-events">
19+
<link rel="import" href="../../stylesheets/main.css" type="css">
20+
<link rel="import" href="../technology/technology.css" type="css">
21+
<template>
22+
<style>
23+
:host {
24+
display: block;
25+
}
26+
27+
:host .section .title > div {
28+
display: inline-block;
29+
}
30+
31+
:host table.variant {
32+
width: 100%;
33+
table-layout: fixed;
34+
border-collapse: collapse;
35+
border-bottom: 1px solid #777;
36+
}
37+
38+
:host table.variant tr.title td {
39+
text-align: left;
40+
font-size: 14px;
41+
padding: 4px 6px;
42+
background-color: #373b50;
43+
color: #fff;
44+
border-left: 1px dotted #777;
45+
text-transform: uppercase;
46+
font-weight: normal;
47+
}
48+
49+
:host table.variant tr.title td:first-child {
50+
border-left: 0px;
51+
}
52+
53+
:host table.variant tr.body td {
54+
padding: 4px 6px;
55+
font-size: 14px;
56+
vertical-align: top;
57+
border-left: 1px dotted #777;
58+
border-bottom: 1px dotted #777;
59+
white-space: pre-wrap;
60+
overflow-wrap: break-word;
61+
}
62+
63+
:host table.variant tr.body td:first-child {
64+
border-left: 0px;
65+
}
66+
67+
:host table.variant tr.body:last-child td {
68+
border-bottom: 0;
69+
}
70+
71+
:host .title {
72+
position: relative;
73+
text-transform: uppercase;
74+
margin-top: 2px;
75+
}
76+
77+
:host .body .title {
78+
font-weight: bold;
79+
margin-top: 18px;
80+
margin-bottom: 8px;
81+
}
82+
83+
:host .body .events-title {
84+
margin-top: 24px;
85+
}
86+
87+
</style>
88+
<div class="section">
89+
<div class="title">
90+
<div title="Last testcase events.">
91+
Testcase Status
92+
</div>
93+
</div>
94+
<div class="body padding">
95+
<div class="title" title="Last testcase task events.">Task Events</div>
96+
<template is="dom-if" if="[[info.testcase_status_info.task_events_info.length]]">
97+
<table cellpadding="0" cellspacing="0" class="variant task-events">
98+
<tr class="title">
99+
<td>Task Name</td>
100+
<td>Task Stage</td>
101+
<td>Task Status</td>
102+
<td>Task Outcome</td>
103+
<td>Time</td>
104+
</tr>
105+
<template is="dom-repeat" items="[[info.testcase_status_info.task_events_info]]" as="taskEvent">
106+
<tr class="body">
107+
<td>[[taskEvent.task_name]]</td>
108+
<td>[[taskEvent.task_stage]]</td>
109+
<td>[[taskEvent.task_status]]</td>
110+
<td>[[taskEvent.task_outcome]]</td>
111+
<td>[[taskEvent.timestamp]]</td>
112+
</tr>
113+
</template>
114+
</table>
115+
</template>
116+
<template is="dom-if" if="[[!info.testcase_status_info.task_events_info.length]]">
117+
<div>
118+
No task events found.
119+
</div>
120+
</template>
121+
122+
<div class="title events-title" title="Last testcase non task events.">Lifecycle Events</div>
123+
<template is="dom-if" if="[[info.testcase_status_info.lifecycle_events_info.length]]">
124+
<table cellpadding="0" cellspacing="0" class="variant">
125+
<tr class="title">
126+
<td>Event Type</td>
127+
<td>Event Info</td>
128+
<td>Time</td>
129+
</tr>
130+
<template is="dom-repeat" items="[[info.testcase_status_info.lifecycle_events_info]]" as="event">
131+
<tr class="body">
132+
<td>[[event.event_type]]</td>
133+
<td>[[event.event_info]]</td>
134+
<td>[[event.timestamp]]</td>
135+
</tr>
136+
</template>
137+
</table>
138+
</template>
139+
<template is="dom-if" if="[[!info.testcase_status_info.lifecycle_events_info.length]]">
140+
<div>
141+
No lifecycle events found.
142+
</div>
143+
</template>
144+
</div>
145+
</div>
146+
</template>
147+
<script>
148+
Polymer({
149+
is: 'testcase-status-events',
150+
properties: {
151+
info: Object,
152+
},
153+
});
154+
</script>
155+
</dom-module>

src/clusterfuzz/_internal/tests/appengine/handlers/testcase_detail/testcase_status_events_test.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ def setUp(self):
141141
testcase_id=self.testcase_id,
142142
event_type=events.EventTypes.ISSUE_FILING,
143143
issue_created=True,
144+
issue_id='123456',
144145
issue_reporter='@gmail.com',
145146
timestamp=datetime.datetime(2023, 1, 3, 0, 0, 0)).put()
146147

@@ -196,7 +197,7 @@ def test_get_testcase_status_info(self):
196197
}, {
197198
'event_type': 'Issue Filing',
198199
'timestamp': '2023-01-03 00:00:00.000000 UTC',
199-
'event_info': 'Issue created\nManually created by @gmail.com',
200+
'event_info': 'Issue created (123456)\nManually created by @gmail.com',
200201
}, {
201202
'event_type': 'Testcase Grouping'
202203
}]
@@ -288,7 +289,7 @@ def test_format_issue_filing_event_success(self):
288289
expected = {
289290
'event_type': 'Issue Filing',
290291
'timestamp': '2023-01-03 00:00:00.000000 UTC',
291-
'event_info': 'Issue created\nManually created by @gmail.com'
292+
'event_info': 'Issue created (123456)\nManually created by @gmail.com'
292293
}
293294
self.assertEqual(result, expected)
294295

0 commit comments

Comments
 (0)