Skip to content

Commit 7629e48

Browse files
committed
Enable AI Recommendation for possible issues
Signed-off-by: Lan Xia <[email protected]>
1 parent fdb2a13 commit 7629e48

File tree

5 files changed

+246
-87
lines changed

5 files changed

+246
-87
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const got = require('got');
2+
const { OutputDB, ObjectID } = require('../Database');
3+
const { removeTimestamp } = require('./utils/removeTimestamp');
4+
const { removeAnsiCode } = require('../Utils');
5+
6+
/**
7+
* getDuplicateIssues queries AI API and returns matched git issues
8+
* @param {string} testName Required. Search text string. i.e., jdk_math
9+
* @return {object} matched git issues
10+
*/
11+
12+
module.exports = async (req, res) => {
13+
try {
14+
const { testName, testOutputId } = req.query;
15+
const queryData = `jdk_nio_0 java/nio/channels/vthread/BlockingChannelOps.java#poller-modes FAILED BlockingChannelOps::testSocketChannelWriteInterrupt '\''testSocketChannelWriteInterrupt()'\''\n[2025-04-12T18:21:26.825Z] variation: Mode150\n[2025-04-12T18:21:26.825Z] JVM_OPTIONS: -XX:+UseCompressedOops -Xverbosegclog \n\n[2025-04-12T18:29:49.698Z] TEST: java/nio/channels/vthread/BlockingChannelOps.java#poller-modes\n\n[2025-04-12T18:29:49.701Z] STARTED BlockingChannelOps::testSocketChannelWriteInterrupt '\''testSocketChannelWriteInterrupt()'\''\n[2025-04-12T18:29:49.701Z] java.nio.channels.ClosedByInterruptException\n[2025-04-12T18:29:49.701Z] \tat java.base/java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:214)\n[2025-04-12T18:29:49.701Z] \tat java.base/sun.nio.ch.SocketChannelImpl.endRead(SocketChannelImpl.java:394)\n[2025-04-12T18:29:49.701Z] \tat java.base/sun.nio.ch.SocketChannelImpl.endConnect(SocketChannelImpl.java:895)\n[2025-04-12T18:29:49.701Z] \tat java.base/sun.nio.ch.SocketChannelImpl.blockingConnect(SocketChannelImpl.java:1317)\n[2025-04-12T18:29:49.701Z] \tat java.base/sun.nio.ch.SocketAdaptor.connect(SocketAdaptor.java:104)\n[2025-04-12T18:29:49.701Z] \tat java.base/sun.nio.ch.SocketAdaptor.connect(SocketAdaptor.java:82)\n[2025-04-12T18:29:49.701Z] \tat BlockingChannelOps$Connection.<init>(BlockingChannelOps.java:811)\n[2025-04-12T18:29:49.701Z] \tat BlockingChannelOps.lambda$testSocketChannelWriteInterrupt$0(BlockingChannelOps.java:226)\n[2025-04-12T18:29:49.702Z] \tat jdk.test.lib.thread.VThreadRunner.lambda$run$0(VThreadRunner.java:66)\n[2025-04-12T18:29:49.702Z] \tat java.base/java.lang.VirtualThread.run(VirtualThread.java:472)\n[2025-04-12T18:29:49.702Z] FAILED BlockingChannelOps::testSocketChannelWriteInterrupt '\''testSocketChannelWriteInterrupt()'\''\n\n[2025-04-12T18:37:13.559Z] jdk_nio_0_FAILED\n`;
16+
17+
if (testName && testOutputId) {
18+
const outputDB = new OutputDB();
19+
20+
const result = await outputDB
21+
.getData({ _id: new ObjectID(testOutputId) })
22+
.toArray();
23+
let output = '';
24+
if (result && result[0]) {
25+
output = removeTimestamp(result[0].output);
26+
output = removeAnsiCode(output);
27+
// TODO: output should be filter to find error and exeception
28+
}
29+
if (output) {
30+
output += testName + ' ';
31+
const response = await got.post(
32+
'http://9.46.100.175:8080/search',
33+
{
34+
json: {
35+
query: output,
36+
},
37+
responseType: 'json',
38+
}
39+
);
40+
41+
if (response && response.body) {
42+
res.send(response.body);
43+
} else {
44+
res.send({
45+
error: `No response from AI server`,
46+
});
47+
}
48+
} else {
49+
res.send({
50+
error: `Cannot find ${testName} output by testOutputId ${testOutputId}`,
51+
});
52+
}
53+
} else {
54+
res.send('expect testName query parameter');
55+
}
56+
} catch (error) {
57+
const rawError = error.response?.data || error.message;
58+
console.error('Error fetching repos:', rawError);
59+
res.status(500).json({ error: String(rawError) });
60+
}
61+
};

TestResultSummaryService/routes/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ app.get('/getTestInfoByBuildInfo', require('./getTestInfoByBuildInfo'));
3636
app.get('/getAzDoRun', require('./getAzDoRun'));
3737
app.get('/getParents', require('./getParents'));
3838
app.get('/getPerffarmRunCSV', require('./getPerffarmRunCSV'));
39+
app.get('/getPossibleIssuesByAI', require('./getPossibleIssuesByAI'));
3940
app.get('/getTabularData', require('./getTabularData'));
4041
app.get('/getTabularDropdown', require('./getTabularDropdown'));
4142
app.get('/getTestBuildsByMachine', require('./getTestBuildsByMachine'));

test-result-summary-client/src/Build/Output/Output.jsx

Lines changed: 102 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import classnames from 'classnames';
1313
import AlertMsg from '../AlertMsg';
1414
import './output.css';
1515
import PossibleIssues from '../PossibleIssues';
16+
import PossibleIssuesByAI from '../PossibleIssuesByAI';
1617

1718
const Output = () => {
1819
const location = useLocation();
@@ -47,6 +48,7 @@ const Output = () => {
4748
buildName: dataInfo.buildName,
4849
buildUrl: dataInfo.buildUrl,
4950
rerunLink: dataInfo.rerunLink,
51+
testOutputId: info.testOutputId,
5052
};
5153
} else {
5254
const results = await fetchData(`/api/getData?_id=${id}`);
@@ -90,99 +92,114 @@ const Output = () => {
9092
if (!outputType) return null;
9193
return (
9294
<div>
93-
{data.testId && data.result != 'PASSED' && (
94-
<PossibleIssues
95-
buildId={data.buildId}
96-
buildName={data.buildName}
97-
testId={data.testId}
98-
testName={data.name}
99-
/>
100-
)}
101-
<Row>
102-
<Col span={16}>
103-
<h2
104-
style={{
105-
color:
106-
data.result === 'PASSED'
107-
? '#2cbe4e'
108-
: '#f50',
109-
}}
110-
>
111-
{data.name}
112-
</h2>
113-
</Col>
114-
<Col span={8}>
115-
<div className="switch-wrapper">
116-
<Switch
117-
defaultChecked={false}
118-
onChange={(val) =>
119-
setTerminalTheme({
120-
terminalTheme: val ? 'black' : 'white',
121-
})
122-
}
123-
checkedChildren="black"
124-
unCheckedChildren="white"
125-
/>
126-
</div>
127-
</Col>
128-
</Row>
129-
<Row justify="end">
130-
<Col>
131-
{data.artifactory && (
132-
<a
133-
target="_blank"
134-
href={data.artifactory}
135-
rel="noopener noreferrer"
95+
<div>
96+
{data.testOutputId && data.result != 'PASSED' && (
97+
<PossibleIssuesByAI
98+
buildName={data.buildName}
99+
buildUrl={data.buildUrl}
100+
testName={data.name}
101+
testOutputId={data.testOutputId}
102+
/>
103+
)}
104+
</div>
105+
<br />
106+
<div>
107+
{data.testId && data.result != 'PASSED' && (
108+
<PossibleIssues
109+
buildId={data.buildId}
110+
buildName={data.buildName}
111+
testId={data.testId}
112+
testName={data.name}
113+
/>
114+
)}
115+
<Row>
116+
<Col span={16}>
117+
<h2
118+
style={{
119+
color:
120+
data.result === 'PASSED'
121+
? '#2cbe4e'
122+
: '#f50',
123+
}}
136124
>
137-
<Tooltip title="Artifactory Link">
138-
{' '}
139-
<DownloadOutlined />{' '}
140-
</Tooltip>{' '}
141-
</a>
142-
)}
143-
{data.buildUrl && (
144-
<>
145-
<Divider type="vertical" />
146-
<a
147-
target="_blank"
148-
href={data.buildUrl}
149-
rel="noopener noreferrer"
150-
>
151-
<Tooltip title="Jenkins Link">
152-
{' '}
153-
<LinkOutlined />{' '}
154-
</Tooltip>
155-
</a>
156-
</>
157-
)}
158-
{data.rerunLink && (
159-
<>
160-
<Divider type="vertical" />
161-
125+
{data.name}
126+
</h2>
127+
</Col>
128+
<Col span={8}>
129+
<div className="switch-wrapper">
130+
<Switch
131+
defaultChecked={false}
132+
onChange={(val) =>
133+
setTerminalTheme({
134+
terminalTheme: val
135+
? 'black'
136+
: 'white',
137+
})
138+
}
139+
checkedChildren="black"
140+
unCheckedChildren="white"
141+
/>
142+
</div>
143+
</Col>
144+
</Row>
145+
<Row justify="end">
146+
<Col>
147+
{data.artifactory && (
162148
<a
163149
target="_blank"
164-
href={data.rerunLink}
150+
href={data.artifactory}
165151
rel="noopener noreferrer"
166152
>
167-
<Tooltip title="Rerun Grinder">
153+
<Tooltip title="Artifactory Link">
168154
{' '}
169-
<SyncOutlined />{' '}
155+
<DownloadOutlined />{' '}
170156
</Tooltip>{' '}
171157
</a>
172-
</>
173-
)}
174-
</Col>
175-
</Row>
176-
<Row>
177-
<div
178-
className={classnames(
179-
'test-output-wrapper',
180-
terminalTheme
181-
)}
182-
>
183-
<div className="test-output">{data.output}</div>
184-
</div>
185-
</Row>
158+
)}
159+
{data.buildUrl && (
160+
<>
161+
<Divider type="vertical" />
162+
<a
163+
target="_blank"
164+
href={data.buildUrl}
165+
rel="noopener noreferrer"
166+
>
167+
<Tooltip title="Jenkins Link">
168+
{' '}
169+
<LinkOutlined />{' '}
170+
</Tooltip>
171+
</a>
172+
</>
173+
)}
174+
{data.rerunLink && (
175+
<>
176+
<Divider type="vertical" />
177+
178+
<a
179+
target="_blank"
180+
href={data.rerunLink}
181+
rel="noopener noreferrer"
182+
>
183+
<Tooltip title="Rerun Grinder">
184+
{' '}
185+
<SyncOutlined />{' '}
186+
</Tooltip>{' '}
187+
</a>
188+
</>
189+
)}
190+
</Col>
191+
</Row>
192+
<Row>
193+
<div
194+
className={classnames(
195+
'test-output-wrapper',
196+
terminalTheme
197+
)}
198+
>
199+
<div className="test-output">{data.output}</div>
200+
</div>
201+
</Row>
202+
</div>
186203
</div>
187204
);
188205
};

test-result-summary-client/src/Build/PossibleIssues.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,12 @@ const PossibleIssues = ({
283283
columns={columns}
284284
dataSource={dataSource[repoName]}
285285
bordered
286-
title={() => repoName}
286+
title={() => `Search Test Name in ${repoName}`}
287+
pagination={{ pageSize: 5 }}
287288
/>
288289
))
289290
) : (
290-
<span>No Possible Issues Found</span>
291+
<span>No Possible Issues Found via Git Search</span>
291292
))}
292293
</div>
293294
);
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { useState, useEffect } from 'react';
2+
import { Table } from 'antd';
3+
import { fetchData } from '../utils/Utils';
4+
5+
const PossibleIssuesByAI = ({
6+
buildName,
7+
buildUrl,
8+
testName,
9+
testOutputId,
10+
}) => {
11+
const [builds, setBuilds] = useState(null);
12+
useEffect(() => {
13+
const fetchBuilds = async () => {
14+
if (buildUrl && buildName && testName && testOutputId) {
15+
if (
16+
buildUrl.includes('hyc-runtimes') &&
17+
!buildName.includes('jck') &&
18+
!testName.includes('jck')
19+
) {
20+
const build = await fetchData(
21+
`/api/getPossibleIssuesByAI?testName=${testName}&testOutputId=${testOutputId}`
22+
);
23+
setBuilds(build);
24+
}
25+
}
26+
};
27+
28+
fetchBuilds();
29+
}, [buildName]);
30+
31+
if (!builds) {
32+
return null;
33+
}
34+
const columns = [
35+
{
36+
title: 'Possible Issues',
37+
dataIndex: 'title',
38+
key: 'title',
39+
render: (value, row, index) => {
40+
return (
41+
<a
42+
href={row.issue_url}
43+
target="_blank"
44+
rel="noopener noreferrer"
45+
>
46+
{row.issue_number}: {value}
47+
</a>
48+
);
49+
},
50+
},
51+
{
52+
title: 'Relevance Level',
53+
dataIndex: 'relevance_level',
54+
key: 'relevance_level',
55+
},
56+
{
57+
title: 'Rationale',
58+
dataIndex: 'rationale',
59+
key: 'rationale',
60+
},
61+
];
62+
63+
return builds && builds.length > 0 ? (
64+
<Table
65+
columns={columns}
66+
dataSource={builds}
67+
bordered
68+
title={() => 'AI Recommended Possible Issues (Openj9 issues only)'}
69+
pagination={{ pageSize: 5 }}
70+
/>
71+
) : (
72+
<span>
73+
No Possible Issues Recommended by AI.{' '}
74+
{builds && builds.error ? `Error: ${builds.error}` : ''}
75+
</span>
76+
);
77+
};
78+
79+
export default PossibleIssuesByAI;

0 commit comments

Comments
 (0)