Skip to content

Commit

Permalink
feat(gradle): add support for implicit test suite dependencies (#33775)
Browse files Browse the repository at this point in the history
  • Loading branch information
Churro authored Jan 27, 2025
1 parent 6dd8ce4 commit abb00a7
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 12 deletions.
21 changes: 20 additions & 1 deletion lib/modules/manager/gradle/parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down Expand Up @@ -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`
Expand Down
8 changes: 8 additions & 0 deletions lib/modules/manager/gradle/parser/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
41 changes: 35 additions & 6 deletions lib/modules/manager/gradle/parser/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { regEx } from '../../../../util/regex';
import type { Ctx } from '../types';
import {
GRADLE_PLUGINS,
GRADLE_TEST_SUITES,
cleanupTempVars,
qArtifactId,
qDotOrBraceExpr,
Expand All @@ -15,7 +16,7 @@ import {
} from './common';
import {
handleDepString,
handleImplicitGradlePlugin,
handleImplicitDep,
handleKotlinShortNotationDep,
handleLongFormDep,
} from './handlers';
Expand Down Expand Up @@ -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,
Expand All @@ -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"
Expand All @@ -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(
Expand All @@ -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()),
);
14 changes: 9 additions & 5 deletions lib/modules/manager/gradle/parser/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit abb00a7

Please sign in to comment.