-
Notifications
You must be signed in to change notification settings - Fork 226
Use sarif parser for reopened results #1457
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
Changes from 17 commits
c13319d
b3d7e78
c37c5bf
f0d0017
c5a816d
c1b3ee1
2b6dd6b
169a88a
fc32971
ca8f930
8c92a19
438607d
a07e549
9fe8fd8
d656db0
dcc51cc
badbed9
6bd649a
05c6efe
f75f0b7
f7dc7b7
50b3109
fbe0b98
93bd94c
86eaf9d
b849fa9
262fbee
75c8aa3
f37bf65
ca0c863
671f0e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ import { QueryStatus } from './query-status'; | |
| import { RemoteQueryHistoryItem } from './remote-queries/remote-query-history-item'; | ||
| import { QueryEvaluationInfo, QueryWithResults } from './run-queries-shared'; | ||
| import { formatLegacyMessage } from './legacy-query-server/run-queries'; | ||
| import { sarifParser } from './sarif-parser'; | ||
|
|
||
| /** | ||
| * query-results.ts | ||
|
|
@@ -160,10 +161,12 @@ export async function interpretResultsSarif( | |
| sourceInfo?: cli.SourceInfo | ||
| ): Promise<SarifInterpretationData> { | ||
| const { resultsPath, interpretedResultsPath } = resultsPaths; | ||
| let res; | ||
| if (await fs.pathExists(interpretedResultsPath)) { | ||
| return { ...JSON.parse(await fs.readFile(interpretedResultsPath, 'utf8')), t: 'SarifInterpretationData' }; | ||
| res = await sarifParser(interpretedResultsPath); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a thought....if this function throws an error because the file is not parseable, should we delete the file and re-run Possibly, we can do this as a followup.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, that's a good point 👍 I'll leave this comment up for now, while we work out the last invalid SARIF test issue.. |
||
| } else { | ||
| res = await cli.interpretBqrsSarif(ensureMetadataIsComplete(metadata), resultsPath, interpretedResultsPath, sourceInfo); | ||
| } | ||
| const res = await cli.interpretBqrsSarif(ensureMetadataIsComplete(metadata), resultsPath, interpretedResultsPath, sourceInfo); | ||
| return { ...res, t: 'SarifInterpretationData' }; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -11,6 +11,7 @@ import { tmpDir } from '../../helpers'; | |||||
| import { slurpQueryHistory, splatQueryHistory } from '../../query-serialization'; | ||||||
| import { formatLegacyMessage, QueryInProgress } from '../../legacy-query-server/run-queries'; | ||||||
| import { EvaluationResult, QueryResultType } from '../../pure/legacy-messages'; | ||||||
| import Sinon = require('sinon'); | ||||||
|
|
||||||
| describe('query-results', () => { | ||||||
| let disposeSpy: sinon.SinonSpy; | ||||||
|
|
@@ -155,68 +156,193 @@ describe('query-results', () => { | |||||
| }); | ||||||
| }); | ||||||
|
|
||||||
| it('should interpretResultsSarif', async () => { | ||||||
| const spy = sandbox.mock(); | ||||||
| spy.returns({ a: '1234' }); | ||||||
| const mockServer = { | ||||||
| interpretBqrsSarif: spy | ||||||
| } as unknown as CodeQLCliServer; | ||||||
|
|
||||||
| const interpretedResultsPath = path.join(tmpDir.name, 'interpreted.json'); | ||||||
| const resultsPath = '123'; | ||||||
| const sourceInfo = {}; | ||||||
| describe('interpretResultsSarif', () => { | ||||||
| let mockServer: CodeQLCliServer; | ||||||
| let spy: Sinon.SinonExpectation; | ||||||
| const metadata = { | ||||||
| kind: 'my-kind', | ||||||
| id: 'my-id' as string | undefined, | ||||||
| scored: undefined | ||||||
| }; | ||||||
| const results1 = await interpretResultsSarif( | ||||||
| mockServer, | ||||||
| metadata, | ||||||
| { | ||||||
| resultsPath, interpretedResultsPath | ||||||
| }, | ||||||
| sourceInfo as SourceInfo | ||||||
| ); | ||||||
| const resultsPath = '123'; | ||||||
| const interpretedResultsPath = path.join(tmpDir.name, 'interpreted.json'); | ||||||
| const sourceInfo = {}; | ||||||
|
|
||||||
| expect(results1).to.deep.eq({ a: '1234', t: 'SarifInterpretationData' }); | ||||||
| expect(spy).to.have.been.calledWith( | ||||||
| metadata, | ||||||
| resultsPath, interpretedResultsPath, sourceInfo | ||||||
| ); | ||||||
| beforeEach(() => { | ||||||
| spy = sandbox.mock(); | ||||||
| spy.returns({ a: '1234' }); | ||||||
|
|
||||||
| // Try again, but with no id | ||||||
| spy.reset(); | ||||||
| spy.returns({ a: '1234' }); | ||||||
| delete metadata.id; | ||||||
| const results2 = await interpretResultsSarif( | ||||||
| mockServer, | ||||||
| metadata, | ||||||
| { | ||||||
| resultsPath, interpretedResultsPath | ||||||
| }, | ||||||
| sourceInfo as SourceInfo | ||||||
| ); | ||||||
| expect(results2).to.deep.eq({ a: '1234', t: 'SarifInterpretationData' }); | ||||||
| expect(spy).to.have.been.calledWith( | ||||||
| { kind: 'my-kind', id: 'dummy-id', scored: undefined }, | ||||||
| resultsPath, interpretedResultsPath, sourceInfo | ||||||
| ); | ||||||
| mockServer = { | ||||||
| interpretBqrsSarif: spy | ||||||
| } as unknown as CodeQLCliServer; | ||||||
| }); | ||||||
|
|
||||||
| // try a third time, but this time we get from file | ||||||
| spy.reset(); | ||||||
| fs.writeFileSync(interpretedResultsPath, JSON.stringify({ | ||||||
| a: 6 | ||||||
| }), 'utf8'); | ||||||
| const results3 = await interpretResultsSarif( | ||||||
| mockServer, | ||||||
| metadata, | ||||||
| { | ||||||
| resultsPath, interpretedResultsPath | ||||||
| }, | ||||||
| sourceInfo as SourceInfo | ||||||
| ); | ||||||
| expect(results3).to.deep.eq({ a: 6, t: 'SarifInterpretationData' }); | ||||||
| afterEach(async () => { | ||||||
| sandbox.restore(); | ||||||
| await safeDel(interpretedResultsPath); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm actually seeing sometimes that
Suggested change
And add this import to the imports at the top of the file:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually the test was failing in CI (not locally) when I used
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to remove the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing that isn't very descriptive. Exit code 134 seems to be a |
||||||
| }); | ||||||
|
|
||||||
| it('should interpretResultsSarif', async function() { | ||||||
| // up to 2 minutes per test | ||||||
| this.timeout(2 * 60 * 1000); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you move this (and the other) timeout to the describe block above? You only need to set the timeout once up there.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get an
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Figured out how move this to the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind, that didn't work... had to revert |
||||||
|
|
||||||
| const results = await interpretResultsSarif( | ||||||
| mockServer, | ||||||
| metadata, | ||||||
| { | ||||||
| resultsPath, interpretedResultsPath | ||||||
| }, | ||||||
| sourceInfo as SourceInfo | ||||||
| ); | ||||||
|
|
||||||
| expect(results).to.deep.eq({ a: '1234', t: 'SarifInterpretationData' }); | ||||||
| expect(spy).to.have.been.calledWith( | ||||||
| metadata, | ||||||
| resultsPath, interpretedResultsPath, sourceInfo | ||||||
| ); | ||||||
| }); | ||||||
|
|
||||||
| it('should interpretBqrsSarif without ID', async function() { | ||||||
| // up to 2 minutes per test | ||||||
| this.timeout(2 * 60 * 1000); | ||||||
|
|
||||||
| delete metadata.id; | ||||||
| const results = await interpretResultsSarif( | ||||||
| mockServer, | ||||||
| metadata, | ||||||
| { | ||||||
| resultsPath, interpretedResultsPath | ||||||
| }, | ||||||
| sourceInfo as SourceInfo | ||||||
| ); | ||||||
| expect(results).to.deep.eq({ a: '1234', t: 'SarifInterpretationData' }); | ||||||
| expect(spy).to.have.been.calledWith( | ||||||
| { kind: 'my-kind', id: 'dummy-id', scored: undefined }, | ||||||
| resultsPath, interpretedResultsPath, sourceInfo | ||||||
| ); | ||||||
| }); | ||||||
|
|
||||||
| it('should use sarifParser on a valid small SARIF file', async function() { | ||||||
| // up to 2 minutes per test | ||||||
| this.timeout(2 * 60 * 1000); | ||||||
|
|
||||||
| fs.writeFileSync(interpretedResultsPath, JSON.stringify({ | ||||||
| runs: [{ results: [] }] // A run needs results to succeed. | ||||||
| }), 'utf8'); | ||||||
| const results = await interpretResultsSarif( | ||||||
| mockServer, | ||||||
| metadata, | ||||||
| { | ||||||
| resultsPath, interpretedResultsPath | ||||||
| }, | ||||||
| sourceInfo as SourceInfo | ||||||
| ); | ||||||
| // We do not re-interpret if we are reading from a SARIF file. | ||||||
| expect(spy).to.not.have.been.called; | ||||||
|
|
||||||
| expect(results).to.have.property('t', 'SarifInterpretationData'); | ||||||
| expect(results).to.have.nested.property('runs[0].results'); | ||||||
| }); | ||||||
|
|
||||||
| it('should throw an error on an invalid small SARIF file', async function() { | ||||||
| // up to 2 minutes per test | ||||||
| this.timeout(2 * 60 * 1000); | ||||||
|
|
||||||
| fs.writeFileSync(interpretedResultsPath, JSON.stringify({ | ||||||
| a: '6' // Invalid: no runs or results | ||||||
| }), 'utf8'); | ||||||
|
|
||||||
| await expect( | ||||||
| interpretResultsSarif( | ||||||
| mockServer, | ||||||
| metadata, | ||||||
| { | ||||||
| resultsPath, interpretedResultsPath | ||||||
| }, | ||||||
| sourceInfo as SourceInfo) | ||||||
| ).to.be.rejectedWith('Parsing output of interpretation failed: Invalid SARIF file: expecting at least one run with result.'); | ||||||
|
|
||||||
| // We do not attempt to re-interpret if we are reading from a SARIF file. | ||||||
| expect(spy).to.not.have.been.called; | ||||||
| }); | ||||||
|
|
||||||
| it('should use sarifParser on a valid large SARIF file', async function() { | ||||||
| // up to 2 minutes per test | ||||||
| this.timeout(2 * 60 * 1000); | ||||||
|
|
||||||
| const validSarifStream = fs.createWriteStream(interpretedResultsPath, { flags: 'w' }); | ||||||
|
|
||||||
| const finished = new Promise((res, rej) => { | ||||||
| validSarifStream.addListener('finish', res); | ||||||
| validSarifStream.addListener('error', rej); | ||||||
| }); | ||||||
|
|
||||||
| validSarifStream.write(JSON.stringify({ | ||||||
| runs: [{ results: [] }] // A run needs results to succeed. | ||||||
| }), 'utf8'); | ||||||
|
|
||||||
| const iterations = 10_000_000; | ||||||
| for (let i = 0; i < iterations; i++) { | ||||||
| validSarifStream.write(JSON.stringify({ | ||||||
| a: '6' | ||||||
| }), 'utf8'); | ||||||
| } | ||||||
| validSarifStream.end(); | ||||||
| await finished; | ||||||
|
|
||||||
| const results = await interpretResultsSarif( | ||||||
| mockServer, | ||||||
| metadata, | ||||||
| { | ||||||
| resultsPath, interpretedResultsPath | ||||||
| }, | ||||||
| sourceInfo as SourceInfo | ||||||
| ); | ||||||
| // We do not re-interpret if we are reading from a SARIF file. | ||||||
| expect(spy).to.not.have.been.called; | ||||||
|
|
||||||
| expect(results).to.have.property('t', 'SarifInterpretationData'); | ||||||
| expect(results).to.have.nested.property('runs[0].results'); | ||||||
| }); | ||||||
|
|
||||||
| it('should throw an error on an invalid large SARIF file', async function() { | ||||||
| // up to 2 minutes per test | ||||||
| this.timeout(2 * 60 * 1000); | ||||||
|
|
||||||
| const invalidSarifStream = fs.createWriteStream(interpretedResultsPath, { flags: 'w' }); | ||||||
|
|
||||||
| const finished = new Promise((res, rej) => { | ||||||
| invalidSarifStream.addListener('finish', res); | ||||||
| invalidSarifStream.addListener('error', rej); | ||||||
| }); | ||||||
|
|
||||||
| invalidSarifStream.write('[', 'utf8'); | ||||||
| const iterations = 10; | ||||||
| for (let i = 0; i < iterations; i++) { | ||||||
| invalidSarifStream.write(JSON.stringify({ | ||||||
| a: '6' | ||||||
| }), 'utf8'); | ||||||
| if (i < iterations - 1) { | ||||||
| invalidSarifStream.write(','); | ||||||
| } | ||||||
| } | ||||||
| invalidSarifStream.write(']', 'utf8'); | ||||||
| invalidSarifStream.end(); | ||||||
| await finished; | ||||||
|
|
||||||
| await expect( | ||||||
| interpretResultsSarif( | ||||||
| mockServer, | ||||||
| metadata, | ||||||
| { | ||||||
| resultsPath, interpretedResultsPath | ||||||
| }, | ||||||
| sourceInfo as SourceInfo) | ||||||
| ).to.be.rejectedWith('Parsing output of interpretation failed: Invalid SARIF file: expecting at least one run with result.'); | ||||||
|
|
||||||
| // We do not attempt to re-interpret if we are reading from a SARIF file. | ||||||
| expect(spy).to.not.have.been.called; | ||||||
| }); | ||||||
| }); | ||||||
|
|
||||||
| describe('splat and slurp', () => { | ||||||
|
|
@@ -364,4 +490,12 @@ describe('query-results', () => { | |||||
| } | ||||||
| return fqi; | ||||||
| } | ||||||
|
|
||||||
| function safeDel(file: string) { | ||||||
| try { | ||||||
| fs.unlinkSync(file); | ||||||
| } catch (e) { | ||||||
| // ignore | ||||||
| } | ||||||
| } | ||||||
|
angelapwen marked this conversation as resolved.
Outdated
|
||||||
| }); | ||||||
Uh oh!
There was an error while loading. Please reload this page.