go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/monitoring/components/luci_bisection_result/heuristic_result.tsx (about) 1 // Copyright 2024 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 import HelpOutline from '@mui/icons-material/HelpOutline'; 16 import IconButton from '@mui/material/IconButton'; 17 import Link from '@mui/material/Link'; 18 import Table from '@mui/material/Table'; 19 import TableBody from '@mui/material/TableBody'; 20 import TableCell from '@mui/material/TableCell'; 21 import TableContainer from '@mui/material/TableContainer'; 22 import TableHead from '@mui/material/TableHead'; 23 import TableRow from '@mui/material/TableRow'; 24 25 import { HeuristicAnalysis } from '@/monitoring/util/server_json'; 26 27 interface HeuristicResultProps { 28 heuristicResult: HeuristicAnalysis; 29 } 30 31 export const HeuristicResult = ({ heuristicResult }: HeuristicResultProps) => { 32 const suspects = heuristicResult?.suspects ?? []; 33 if (suspects.length === 0) { 34 return ( 35 <> 36 LUCI Bisection couldn't find any heuristic suspect for this 37 failure. 38 </> 39 ); 40 } 41 const heuristicTooltipTitle = 42 'The heuristic suspects were based on best-guess effort, so they may not be 100% accurate.'; 43 return ( 44 <TableContainer> 45 <Table sx={{ maxWidth: '1000px', tableLayout: 'fixed' }}> 46 <TableHead> 47 <TableRow> 48 <TableCell align="left" sx={{ width: '350px' }}> 49 Heuristic result 50 {/* We dont use Tooltip from MUI here as the MUI tooltip is attached in <body> tag 51 so style cannot be applied. */} 52 <span title={heuristicTooltipTitle}> 53 <IconButton> 54 <HelpOutline></HelpOutline> 55 </IconButton> 56 </span> 57 </TableCell> 58 <TableCell align="left" sx={{ width: '60px' }}> 59 Confidence 60 </TableCell> 61 <TableCell align="left">Justification</TableCell> 62 </TableRow> 63 </TableHead> 64 <TableBody> 65 {suspects.map((s) => ( 66 <TableRow key={s.reviewUrl}> 67 <TableCell align="left"> 68 <Link 69 href={s.reviewUrl} 70 target="_blank" 71 rel="noopener" 72 onClick={() => { 73 ga('send', { 74 hitType: 'event', 75 eventCategory: 'LuciBisection', 76 eventAction: 'ClickSuspectLink', 77 eventLabel: s.reviewUrl, 78 transport: 'beacon', 79 }); 80 }} 81 > 82 {s.reviewUrl} 83 </Link> 84 </TableCell> 85 <TableCell align="left"> 86 {confidenceText(s.confidence_level)} 87 </TableCell> 88 <TableCell align="left"> 89 <pre style={{ whiteSpace: 'pre-wrap' }}> 90 {shortenJustification(s.justification)} 91 </pre> 92 </TableCell> 93 </TableRow> 94 ))} 95 </TableBody> 96 </Table> 97 </TableContainer> 98 ); 99 }; 100 101 // Sometimes justification is too long if a CL touches many files. 102 // In such case we should shorten the justification to at most 3 lines 103 // and link to the detailed analysis if the sheriff wants to see the details 104 // (when the detail analysis page is ready). 105 function shortenJustification(justification: string) { 106 const lines = justification.split('\n'); 107 if (lines.length < 4) { 108 return justification; 109 } 110 return lines.slice(0, 3).join('\n') + '\n...'; 111 } 112 113 function confidenceText(confidenceLevel: number) { 114 switch (confidenceLevel) { 115 case 1: 116 return 'Low'; 117 case 2: 118 return 'Medium'; 119 case 3: 120 return 'High'; 121 default: 122 return 'N/A'; 123 } 124 }