feat(gradle): add support for implicit test suite dependencies (#33775)

This commit is contained in:
Johannes Feichtner
2025-01-27 08:48:22 +01:00
committed by GitHub
parent 6dd8ce48c2
commit abb00a702b
4 changed files with 72 additions and 12 deletions
+20 -1
View File
@@ -8,7 +8,11 @@ import {
parseKotlinSource,
parseProps,
} from './parser';
import { GRADLE_PLUGINS, REGISTRY_URLS } from './parser/common';
import {
GRADLE_PLUGINS,
GRADLE_TEST_SUITES,
REGISTRY_URLS,
} from './parser/common';
jest.mock('../../../util/fs');
@@ -1068,6 +1072,21 @@ describe('modules/manager/gradle/parser', () => {
});
});
describe('implicit gradle test suite dependencies', () => {
it.each`
def | input | output
${''} | ${'testing.suites.test { useJunit("1.2.3") } } }'} | ${{ depName: 'useJunit', packageName: GRADLE_TEST_SUITES['useJunit'], currentValue: '1.2.3' }}
${'baz = "1.2.3"'} | ${'testing.suites.test { useJUnitJupiter(baz) } } }'} | ${{ depName: 'useJUnitJupiter', packageName: GRADLE_TEST_SUITES['useJUnitJupiter'], currentValue: '1.2.3' }}
${''} | ${'testing.suites.test { useKotlinTest("1.2.3") } } }'} | ${{ depName: 'useKotlinTest', packageName: GRADLE_TEST_SUITES['useKotlinTest'], currentValue: '1.2.3' }}
${'baz = "1.2.3"'} | ${'testing { suites { test { useSpock(baz) } } }'} | ${{ depName: 'useSpock', packageName: GRADLE_TEST_SUITES['useSpock'], currentValue: '1.2.3' }}
${''} | ${'testing.suites.test { useSpock("1.2.3") } } }'} | ${{ depName: 'useSpock', packageName: GRADLE_TEST_SUITES['useSpock'], currentValue: '1.2.3' }}
${''} | ${'testing.suites.test { useTestNG("1.2.3") } } }'} | ${{ depName: 'useTestNG', packageName: GRADLE_TEST_SUITES['useTestNG'], currentValue: '1.2.3' }}
`('$def | $input', ({ def, input, output }) => {
const { deps } = parseGradle([def, input].join('\n'));
expect(deps).toMatchObject([output]);
});
});
describe('Kotlin object notation', () => {
it('simple objects', () => {
const input = codeBlock`
@@ -37,6 +37,14 @@ export const GRADLE_PLUGINS = {
spotbugs: ['toolVersion', 'com.github.spotbugs:spotbugs'],
};
export const GRADLE_TEST_SUITES = {
useJunit: 'junit:junit',
useJUnitJupiter: 'org.junit.jupiter:junit-jupiter',
useKotlinTest: 'org.jetbrains.kotlin:kotlin-test-junit',
useSpock: 'org.spockframework:spock-core',
useTestNG: 'org.testng:testng',
};
export function storeVarToken(ctx: Ctx, node: lexer.Token): Ctx {
ctx.varTokens.push(node);
return ctx;
@@ -3,6 +3,7 @@ import { regEx } from '../../../../util/regex';
import type { Ctx } from '../types';
import {
GRADLE_PLUGINS,
GRADLE_TEST_SUITES,
cleanupTempVars,
qArtifactId,
qDotOrBraceExpr,
@@ -15,7 +16,7 @@ import {
} from './common';
import {
handleDepString,
handleImplicitGradlePlugin,
handleImplicitDep,
handleKotlinShortNotationDep,
handleLongFormDep,
} from './handlers';
@@ -155,10 +156,10 @@ export const qLongFormDep = q
// pmd { toolVersion = "1.2.3" }
const qImplicitGradlePlugin = q
.alt(
...Object.keys(GRADLE_PLUGINS).map((pluginName) =>
...Object.keys(GRADLE_PLUGINS).map((implicitDepName) =>
q
.sym<Ctx>(pluginName, storeVarToken)
.handler((ctx) => storeInTokenMap(ctx, 'pluginName'))
.sym<Ctx>(implicitDepName, storeVarToken)
.handler((ctx) => storeInTokenMap(ctx, 'implicitDepName'))
.tree({
type: 'wrapped-tree',
maxDepth: 1,
@@ -167,7 +168,7 @@ const qImplicitGradlePlugin = q
endsWith: '}',
search: q
.sym<Ctx>(
GRADLE_PLUGINS[pluginName as keyof typeof GRADLE_PLUGINS][0],
GRADLE_PLUGINS[implicitDepName as keyof typeof GRADLE_PLUGINS][0],
)
.alt(
// toolVersion = "1.2.3"
@@ -186,7 +187,34 @@ const qImplicitGradlePlugin = q
}),
),
)
.handler(handleImplicitGradlePlugin)
.handler(handleImplicitDep)
.handler(cleanupTempVars);
// testing { suites { test { useSpock("1.2.3") } } }
const qImplicitTestSuites = qDotOrBraceExpr(
'testing',
qDotOrBraceExpr(
'suites',
qDotOrBraceExpr(
'test',
q
.sym(
regEx(`^(?:${Object.keys(GRADLE_TEST_SUITES).join('|')})$`),
storeVarToken,
)
.handler((ctx) => storeInTokenMap(ctx, 'implicitDepName'))
.tree({
type: 'wrapped-tree',
maxDepth: 1,
maxMatches: 1,
startsWith: '(',
endsWith: ')',
search: q.begin<Ctx>().join(qVersion).end(),
}),
),
),
)
.handler(handleImplicitDep)
.handler(cleanupTempVars);
export const qDependencies = q.alt(
@@ -196,6 +224,7 @@ export const qDependencies = q.alt(
qKotlinShortNotationDependencies,
qKotlinMapNotationDependencies,
qImplicitGradlePlugin,
qImplicitTestSuites,
// avoid heuristic matching of gradle feature variant capabilities
qDotOrBraceExpr('java', q.sym<Ctx>('registerFeature').tree()),
);
@@ -9,6 +9,7 @@ import type { Ctx, GradleManagerData } from '../types';
import { isDependencyString, parseDependencyString } from '../utils';
import {
GRADLE_PLUGINS,
GRADLE_TEST_SUITES,
REGISTRY_URLS,
findVariable,
interpolateString,
@@ -401,22 +402,25 @@ export function handleApplyFrom(ctx: Ctx): Ctx {
return ctx;
}
export function handleImplicitGradlePlugin(ctx: Ctx): Ctx {
const pluginName = loadFromTokenMap(ctx, 'pluginName')[0].value;
export function handleImplicitDep(ctx: Ctx): Ctx {
const implicitDepName = loadFromTokenMap(ctx, 'implicitDepName')[0].value;
const versionTokens = loadFromTokenMap(ctx, 'version');
const versionValue = interpolateString(versionTokens, ctx);
if (!versionValue) {
return ctx;
}
const groupIdArtifactId =
GRADLE_PLUGINS[pluginName as keyof typeof GRADLE_PLUGINS][1];
const isImplicitGradlePlugin = implicitDepName in GRADLE_PLUGINS;
const groupIdArtifactId = isImplicitGradlePlugin
? GRADLE_PLUGINS[implicitDepName as keyof typeof GRADLE_PLUGINS][1]
: GRADLE_TEST_SUITES[implicitDepName as keyof typeof GRADLE_TEST_SUITES];
const dep = parseDependencyString(`${groupIdArtifactId}:${versionValue}`);
if (!dep) {
return ctx;
}
dep.depName = pluginName;
dep.depName = implicitDepName;
dep.packageName = groupIdArtifactId;
dep.managerData = {
fileReplacePosition: versionTokens[0].offset,