From 8586f463ea0b182fe274c2d4c50a79792c860390 Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Tue, 16 Aug 2022 09:14:38 +0200
Subject: [PATCH] test(gradle-wrapper): modernize tests (#17204)

---
 .../__snapshots__/artifacts.spec.ts.snap      |  73 -----
 .../gradle-wrapper/artifacts-real.spec.ts     | 279 ------------------
 .../manager/gradle-wrapper/artifacts.spec.ts  | 198 +++++++++----
 .../manager/gradle-wrapper/artifacts.ts       |  11 +-
 .../manager/gradle-wrapper/util.spec.ts       |  49 ++-
 lib/modules/manager/gradle-wrapper/utils.ts   |  14 +-
 6 files changed, 194 insertions(+), 430 deletions(-)
 delete mode 100644 lib/modules/manager/gradle-wrapper/__snapshots__/artifacts.spec.ts.snap
 delete mode 100644 lib/modules/manager/gradle-wrapper/artifacts-real.spec.ts

diff --git a/lib/modules/manager/gradle-wrapper/__snapshots__/artifacts.spec.ts.snap b/lib/modules/manager/gradle-wrapper/__snapshots__/artifacts.spec.ts.snap
deleted file mode 100644
index c3f9cfe07c..0000000000
--- a/lib/modules/manager/gradle-wrapper/__snapshots__/artifacts.spec.ts.snap
+++ /dev/null
@@ -1,73 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`modules/manager/gradle-wrapper/artifacts gradlew failed 1`] = `
-Array [
-  Object {
-    "cmd": "<gradlew> wrapper --gradle-version 5.6.4",
-    "options": Object {
-      "cwd": "/root/project/lib/modules/manager/gradle-wrapper/__fixtures__/testFiles",
-      "encoding": "utf-8",
-      "env": Object {
-        "GRADLE_OPTS": "-Dorg.gradle.parallel=true -Dorg.gradle.configureondemand=true -Dorg.gradle.daemon=false -Dorg.gradle.caching=false",
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
-
-exports[`modules/manager/gradle-wrapper/artifacts replaces existing value 1`] = `
-Array [
-  Object {
-    "cmd": "<gradlew> wrapper --gradle-distribution-url https://services.gradle.org/distributions/gradle-6.3-bin.zip",
-    "options": Object {
-      "cwd": "/root/project/lib/modules/manager/gradle-wrapper/__fixtures__/testFiles",
-      "encoding": "utf-8",
-      "env": Object {
-        "GRADLE_OPTS": "-Dorg.gradle.parallel=true -Dorg.gradle.configureondemand=true -Dorg.gradle.daemon=false -Dorg.gradle.caching=false",
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
-
-exports[`modules/manager/gradle-wrapper/artifacts updates distributionSha256Sum 1`] = `
-Array [
-  Object {
-    "cmd": "<gradlew> wrapper --gradle-distribution-url https://services.gradle.org/distributions/gradle-6.3-bin.zip --gradle-distribution-sha256-sum 038794feef1f4745c6347107b6726279d1c824f3fc634b60f86ace1e9fbd1768",
-    "options": Object {
-      "cwd": "/root/project/lib/modules/manager/gradle-wrapper/__fixtures__/testFiles",
-      "encoding": "utf-8",
-      "env": Object {
-        "GRADLE_OPTS": "-Dorg.gradle.parallel=true -Dorg.gradle.configureondemand=true -Dorg.gradle.daemon=false -Dorg.gradle.caching=false",
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
diff --git a/lib/modules/manager/gradle-wrapper/artifacts-real.spec.ts b/lib/modules/manager/gradle-wrapper/artifacts-real.spec.ts
deleted file mode 100644
index f99fa0851f..0000000000
--- a/lib/modules/manager/gradle-wrapper/artifacts-real.spec.ts
+++ /dev/null
@@ -1,279 +0,0 @@
-import { readFile } from 'fs-extra';
-import Git from 'simple-git';
-import { resolve } from 'upath';
-import * as httpMock from '../../../../test/http-mock';
-import { git, partial } from '../../../../test/util';
-import { GlobalConfig } from '../../../config/global';
-import type { RepoGlobalConfig } from '../../../config/types';
-import type { StatusResult } from '../../../util/git/types';
-import { ifSystemSupportsGradle } from '../gradle/__testutil__/gradle';
-import type { UpdateArtifactsConfig } from '../types';
-import * as gradleWrapper from '.';
-
-jest.mock('../../../util/git');
-
-const fixtures = resolve(__dirname, './__fixtures__');
-
-const adminConfig: RepoGlobalConfig = {
-  localDir: resolve(fixtures, './testFiles'),
-};
-
-const config: UpdateArtifactsConfig = {
-  newValue: '5.6.4',
-};
-
-function readString(...paths: string[]): Promise<string> {
-  return readFile(resolve(fixtures, ...paths), 'utf8');
-}
-
-async function readBin(...paths: string[]): Promise<Buffer> {
-  return await readFile(resolve(fixtures, ...paths));
-}
-
-async function compareFile(file: string, path: string): Promise<void> {
-  expect(await readBin(`./testFiles/${file}`)).toEqual(
-    await readBin(`./${path}/${file}`)
-  );
-}
-
-describe('modules/manager/gradle-wrapper/artifacts-real', () => {
-  ifSystemSupportsGradle(6).describe('real tests', () => {
-    jest.setTimeout(60 * 1000);
-
-    beforeEach(() => {
-      jest.resetAllMocks();
-      GlobalConfig.set(adminConfig);
-    });
-
-    afterEach(async () => {
-      await Git(fixtures).checkout(['HEAD', '--', '.']);
-      GlobalConfig.reset();
-    });
-
-    it('replaces existing value', async () => {
-      git.getRepoStatus.mockResolvedValue({
-        modified: [
-          'gradle/wrapper/gradle-wrapper.properties',
-          'gradle/wrapper/gradle-wrapper.jar',
-          'gradlew',
-          'gradlew.bat',
-        ],
-      } as StatusResult);
-
-      const res = await gradleWrapper.updateArtifacts({
-        packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
-        updatedDeps: [],
-        newPackageFileContent: await readString(
-          `./expectedFiles/gradle/wrapper/gradle-wrapper.properties`
-        ),
-        config: { ...config, newValue: '6.3' },
-      });
-
-      expect(res).toEqual(
-        await Promise.all(
-          [
-            'gradle/wrapper/gradle-wrapper.properties',
-            'gradle/wrapper/gradle-wrapper.jar',
-            'gradlew',
-            'gradlew.bat',
-          ].map(async (fileProjectPath) => ({
-            file: {
-              contents: await readBin(`./testFiles/${fileProjectPath}`),
-              path: fileProjectPath,
-              type: 'addition',
-            },
-          }))
-        )
-      );
-
-      for (const file of [
-        'gradle/wrapper/gradle-wrapper.properties',
-        'gradle/wrapper/gradle-wrapper.jar',
-        'gradlew',
-        'gradlew.bat',
-      ]) {
-        await compareFile(file, 'expectedFiles');
-      }
-    });
-
-    it('updates from version', async () => {
-      git.getRepoStatus.mockResolvedValueOnce(
-        partial<StatusResult>({
-          modified: ['gradle/wrapper/gradle-wrapper.properties'],
-        })
-      );
-
-      const result = await gradleWrapper.updateArtifacts({
-        packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
-        updatedDeps: [],
-        newPackageFileContent: ``,
-        config: { ...config, newValue: '6.3' },
-      });
-
-      expect(result).toHaveLength(1);
-      expect(result?.[0].artifactError).toBeUndefined();
-
-      await compareFile(
-        'gradle/wrapper/gradle-wrapper.properties',
-        'expectedFiles'
-      );
-    });
-
-    it('up to date', async () => {
-      git.getRepoStatus.mockResolvedValue(
-        partial<StatusResult>({
-          modified: [],
-        })
-      );
-
-      const res = await gradleWrapper.updateArtifacts({
-        packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
-        updatedDeps: [],
-        newPackageFileContent: await readString(
-          `./testFiles/gradle/wrapper/gradle-wrapper.properties`
-        ),
-        config,
-      });
-
-      expect(res).toBeEmptyArray();
-
-      // 5.6.4 => 5.6.4 (updates execs)
-      // 6.3 => (5.6.4) (downgrades execs)
-      // looks like a bug in Gradle
-      for (const file of ['gradle/wrapper/gradle-wrapper.properties']) {
-        await compareFile(file, 'testFiles-copy');
-      }
-    });
-
-    it('getRepoStatus fails', async () => {
-      git.getRepoStatus.mockImplementation(() => {
-        throw new Error('failed');
-      });
-
-      const res = await gradleWrapper.updateArtifacts({
-        packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
-        updatedDeps: [],
-        newPackageFileContent: await readString(
-          `./testFiles/gradle/wrapper/gradle-wrapper.properties`
-        ),
-        config,
-      });
-
-      expect(res?.[0].artifactError?.lockFile).toBe(
-        'gradle/wrapper/gradle-wrapper.properties'
-      );
-      expect(res?.[0].artifactError?.stderr).toBe('failed');
-
-      // 5.6.4 => 5.6.4 (updates execs) - unexpected behavior (looks like a bug in Gradle)
-      for (const file of ['gradle/wrapper/gradle-wrapper.properties']) {
-        await compareFile(file, 'testFiles-copy');
-      }
-    });
-
-    it('gradlew failed', async () => {
-      const wrongCmdConfig = {
-        ...adminConfig,
-        localDir: resolve(fixtures, './wrongCmd'),
-      };
-
-      GlobalConfig.set(wrongCmdConfig);
-      const res = await gradleWrapper.updateArtifacts({
-        packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
-        updatedDeps: [],
-        newPackageFileContent: await readString(
-          `./testFiles/gradle/wrapper/gradle-wrapper.properties`
-        ),
-        config,
-      });
-
-      expect(res?.[0].artifactError?.lockFile).toBe(
-        'gradle/wrapper/gradle-wrapper.properties'
-      );
-      expect(res?.[0].artifactError?.stderr).not.toBeNull();
-      expect(res?.[0].artifactError?.stderr).not.toBe('');
-
-      // 5.6.4 => 5.6.4 (updates execs) - unexpected behavior (looks like a bug in Gradle)
-      for (const file of ['gradle/wrapper/gradle-wrapper.properties']) {
-        await compareFile(file, 'testFiles-copy');
-      }
-    });
-
-    it('gradlew not found', async () => {
-      GlobalConfig.set({ localDir: 'some-dir' });
-      const res = await gradleWrapper.updateArtifacts({
-        packageFileName: 'gradle-wrapper.properties',
-        updatedDeps: [],
-        newPackageFileContent: undefined as never, // TODO #7154
-        config: {},
-      });
-
-      expect(res).toBeNull();
-    });
-
-    it('updates distributionSha256Sum', async () => {
-      httpMock
-        .scope('https://services.gradle.org')
-        .get('/distributions/gradle-6.3-bin.zip.sha256')
-        .reply(
-          200,
-          '038794feef1f4745c6347107b6726279d1c824f3fc634b60f86ace1e9fbd1768'
-        );
-
-      git.getRepoStatus.mockResolvedValueOnce(
-        partial<StatusResult>({
-          modified: ['gradle/wrapper/gradle-wrapper.properties'],
-        })
-      );
-
-      const newContent = await readString(`./gradle-wrapper-sum.properties`);
-
-      const result = await gradleWrapper.updateArtifacts({
-        packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
-        updatedDeps: [],
-        newPackageFileContent: newContent.replace(
-          '038794feef1f4745c6347107b6726279d1c824f3fc634b60f86ace1e9fbd1768',
-          '1f3067073041bc44554d0efe5d402a33bc3d3c93cc39ab684f308586d732a80d'
-        ),
-        config: {
-          ...config,
-          newValue: '6.3',
-          currentValue: '5.6.4',
-        },
-      });
-
-      expect(result).toHaveLength(1);
-      expect(result?.[0].artifactError).toBeUndefined();
-
-      expect(
-        await readString(
-          // TODO #7154
-          adminConfig.localDir!,
-          `gradle/wrapper/gradle-wrapper.properties`
-        )
-      ).toEqual(newContent);
-    });
-
-    it('distributionSha256Sum 404', async () => {
-      httpMock
-        .scope('https://services.gradle.org')
-        .get('/distributions/gradle-6.3-bin.zip.sha256')
-        .reply(404);
-
-      const result = await gradleWrapper.updateArtifacts({
-        packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
-        updatedDeps: [],
-        newPackageFileContent: `distributionSha256Sum=336b6898b491f6334502d8074a6b8c2d73ed83b92123106bd4bf837f04111043\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.3-bin.zip`,
-        config,
-      });
-
-      expect(result).toEqual([
-        {
-          artifactError: {
-            lockFile: 'gradle/wrapper/gradle-wrapper.properties',
-            stderr: 'Response code 404 (Not Found)',
-          },
-        },
-      ]);
-    });
-  });
-});
diff --git a/lib/modules/manager/gradle-wrapper/artifacts.spec.ts b/lib/modules/manager/gradle-wrapper/artifacts.spec.ts
index 1e6a84f64c..e05c33037d 100644
--- a/lib/modules/manager/gradle-wrapper/artifacts.spec.ts
+++ b/lib/modules/manager/gradle-wrapper/artifacts.spec.ts
@@ -1,44 +1,36 @@
 import type { Stats } from 'fs';
-import { readFile } from 'fs-extra';
-import { resolve } from 'upath';
+import os from 'os';
+import { join } from 'upath';
 import { envMock, mockExecAll } from '../../../../test/exec-util';
+import { Fixtures } from '../../../../test/fixtures';
 import * as httpMock from '../../../../test/http-mock';
-import {
-  addReplacingSerializer,
-  env,
-  fs,
-  git,
-  partial,
-} from '../../../../test/util';
+import { env, fs, git, mockedFunction, partial } from '../../../../test/util';
 import { GlobalConfig } from '../../../config/global';
 import type { RepoGlobalConfig } from '../../../config/types';
 import { resetPrefetchedImages } from '../../../util/exec/docker';
 import type { StatusResult } from '../../../util/git/types';
+import { getPkgReleases } from '../../datasource';
 import type { UpdateArtifactsConfig } from '../types';
-import * as gradleWrapper from '.';
+import { updateArtifacts } from '.';
 
 jest.mock('../../../util/fs');
 jest.mock('../../../util/git');
 jest.mock('../../../util/exec/env');
+jest.mock('../../datasource');
 
-const fixtures = resolve(__dirname, './__fixtures__');
+process.env.BUILDPACK = 'true';
 
 const adminConfig: RepoGlobalConfig = {
-  localDir: resolve(fixtures, './testFiles'),
+  // `join` fixes Windows CI
+  localDir: join('/tmp/github/some/repo'),
+  cacheDir: join('/tmp/cache'),
 };
 
-const dockerAdminConfig = { ...adminConfig, binarySource: 'docker' };
-
 const config: UpdateArtifactsConfig = {
   newValue: '5.6.4',
 };
 
-addReplacingSerializer('gradlew.bat', '<gradlew>');
-addReplacingSerializer('./gradlew', '<gradlew>');
-
-function readString(...paths: string[]): Promise<string> {
-  return readFile(resolve(fixtures, ...paths), 'utf8');
-}
+jest.spyOn(os, 'platform').mockReturnValue('linux');
 
 describe('modules/manager/gradle-wrapper/artifacts', () => {
   beforeEach(() => {
@@ -60,28 +52,35 @@ describe('modules/manager/gradle-wrapper/artifacts', () => {
         mode: 0o555,
       })
     );
-  });
 
-  afterEach(() => {
-    GlobalConfig.reset();
+    // java
+    mockedFunction(getPkgReleases).mockResolvedValueOnce({
+      releases: [
+        { version: '8.0.1' },
+        { version: '11.0.1' },
+        { version: '16.0.1' },
+        { version: '17.0.0' },
+      ],
+    });
   });
 
   it('replaces existing value', async () => {
-    git.getRepoStatus.mockResolvedValue({
-      modified: [
-        'gradle/wrapper/gradle-wrapper.properties',
-        'gradlew',
-        'gradlew.bat',
-      ],
-    } as StatusResult);
-
     const execSnapshots = mockExecAll();
+    git.getRepoStatus.mockResolvedValue(
+      partial<StatusResult>({
+        modified: [
+          'gradle/wrapper/gradle-wrapper.properties',
+          'gradlew',
+          'gradlew.bat',
+        ],
+      })
+    );
 
-    const res = await gradleWrapper.updateArtifacts({
+    const res = await updateArtifacts({
       packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
       updatedDeps: [],
-      newPackageFileContent: await readString(
-        `./expectedFiles/gradle/wrapper/gradle-wrapper.properties`
+      newPackageFileContent: Fixtures.get(
+        'expectedFiles/gradle/wrapper/gradle-wrapper.properties'
       ),
       config: { ...config, newValue: '6.3' },
     });
@@ -99,25 +98,39 @@ describe('modules/manager/gradle-wrapper/artifacts', () => {
         },
       }))
     );
-    expect(execSnapshots).toMatchSnapshot();
+    expect(execSnapshots).toMatchObject([
+      {
+        cmd: './gradlew wrapper --gradle-distribution-url https://services.gradle.org/distributions/gradle-6.3-bin.zip',
+        options: {
+          cwd: '/tmp/github/some/repo',
+          encoding: 'utf-8',
+          env: {
+            GRADLE_OPTS:
+              '-Dorg.gradle.parallel=true -Dorg.gradle.configureondemand=true -Dorg.gradle.daemon=false -Dorg.gradle.caching=false',
+          },
+        },
+      },
+    ]);
   });
 
   it('gradlew not found', async () => {
+    const execSnapshots = mockExecAll();
     fs.statLocalFile.mockResolvedValue(
       partial<Stats>({
         isFile: () => false,
         mode: 0o555,
       })
     );
-    GlobalConfig.set({ ...adminConfig, localDir: 'some-dir' });
-    const res = await gradleWrapper.updateArtifacts({
-      packageFileName: 'gradle-wrapper.properties',
+
+    const result = await updateArtifacts({
+      packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
       updatedDeps: [],
       newPackageFileContent: '',
       config: {},
     });
 
-    expect(res).toBeNull();
+    expect(result).toBeNull();
+    expect(execSnapshots).toBeEmptyArray();
   });
 
   it('gradlew failed', async () => {
@@ -127,18 +140,24 @@ describe('modules/manager/gradle-wrapper/artifacts', () => {
         modified: [],
       })
     );
-    const res = await gradleWrapper.updateArtifacts({
-      packageFileName: 'gradle-wrapper.properties',
+    const result = await updateArtifacts({
+      packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
       updatedDeps: [],
       newPackageFileContent: '',
       config,
     });
 
-    expect(execSnapshots).toMatchSnapshot();
-    expect(res).toBeEmptyArray();
+    expect(result).toBeEmptyArray();
+    expect(execSnapshots).toMatchObject([
+      {
+        cmd: './gradlew wrapper --gradle-version 5.6.4',
+        options: { cwd: '/tmp/github/some/repo' },
+      },
+    ]);
   });
 
-  it('updates distributionSha256Sum', async () => {
+  it('updates distributionSha256Sum (docker)', async () => {
+    const execSnapshots = mockExecAll();
     httpMock
       .scope('https://services.gradle.org')
       .get('/distributions/gradle-6.3-bin.zip.sha256')
@@ -146,39 +165,101 @@ describe('modules/manager/gradle-wrapper/artifacts', () => {
         200,
         '038794feef1f4745c6347107b6726279d1c824f3fc634b60f86ace1e9fbd1768'
       );
-
     git.getRepoStatus.mockResolvedValueOnce(
       partial<StatusResult>({
         modified: ['gradle/wrapper/gradle-wrapper.properties'],
       })
     );
+    GlobalConfig.set({ ...adminConfig, binarySource: 'docker' });
+
+    const result = await updateArtifacts({
+      packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
+      updatedDeps: [],
+      newPackageFileContent: `distributionSha256Sum=336b6898b491f6334502d8074a6b8c2d73ed83b92123106bd4bf837f04111043\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.3-bin.zip`,
+      config,
+    });
+
+    expect(result).toEqual([
+      {
+        file: {
+          contents: 'test',
+          path: 'gradle/wrapper/gradle-wrapper.properties',
+          type: 'addition',
+        },
+      },
+    ]);
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'docker pull renovate/sidecar' },
+      { cmd: 'docker ps --filter name=renovate_sidecar -aq' },
+      {
+        cmd:
+          'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
+          '-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
+          '-v "/tmp/cache":"/tmp/cache" ' +
+          '-e GRADLE_OPTS ' +
+          '-e BUILDPACK_CACHE_DIR ' +
+          '-w "/tmp/github/some/repo" ' +
+          'renovate/sidecar' +
+          ' bash -l -c "' +
+          'install-tool java 11.0.1' +
+          ' && ' +
+          './gradlew wrapper --gradle-distribution-url https://services.gradle.org/distributions/gradle-6.3-bin.zip --gradle-distribution-sha256-sum 038794feef1f4745c6347107b6726279d1c824f3fc634b60f86ace1e9fbd1768' +
+          '"',
+        options: { cwd: '/tmp/github/some/repo' },
+      },
+    ]);
+  });
 
+  it('updates distributionSha256Sum (install)', async () => {
     const execSnapshots = mockExecAll();
+    httpMock
+      .scope('https://services.gradle.org')
+      .get('/distributions/gradle-6.3-bin.zip.sha256')
+      .reply(
+        200,
+        '038794feef1f4745c6347107b6726279d1c824f3fc634b60f86ace1e9fbd1768'
+      );
+    git.getRepoStatus.mockResolvedValueOnce(
+      partial<StatusResult>({
+        modified: ['gradle/wrapper/gradle-wrapper.properties'],
+      })
+    );
+    GlobalConfig.set({ ...adminConfig, binarySource: 'install' });
 
-    const result = await gradleWrapper.updateArtifacts({
-      packageFileName: 'gradle-wrapper.properties',
+    const result = await updateArtifacts({
+      packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
       updatedDeps: [],
       newPackageFileContent: `distributionSha256Sum=336b6898b491f6334502d8074a6b8c2d73ed83b92123106bd4bf837f04111043\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.3-bin.zip`,
-      config: {
-        ...config,
-        ...dockerAdminConfig,
-      },
+      config,
     });
 
-    expect(result).toHaveLength(1);
-    expect(result?.[0].artifactError).toBeUndefined();
-
-    expect(execSnapshots).toMatchSnapshot();
+    expect(result).toEqual([
+      {
+        file: {
+          contents: 'test',
+          path: 'gradle/wrapper/gradle-wrapper.properties',
+          type: 'addition',
+        },
+      },
+    ]);
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'install-tool java 11.0.1' },
+      {
+        cmd: './gradlew wrapper --gradle-distribution-url https://services.gradle.org/distributions/gradle-6.3-bin.zip --gradle-distribution-sha256-sum 038794feef1f4745c6347107b6726279d1c824f3fc634b60f86ace1e9fbd1768',
+        options: { cwd: '/tmp/github/some/repo' },
+      },
+    ]);
   });
 
   it('distributionSha256Sum 404', async () => {
+    const execSnapshots = mockExecAll();
     httpMock
       .scope('https://services.gradle.org')
       .get('/distributions/gradle-6.3-bin.zip.sha256')
       .reply(404);
 
-    const result = await gradleWrapper.updateArtifacts({
-      packageFileName: 'gradle-wrapper.properties',
+    const result = await updateArtifacts({
+      packageFileName: 'gradle/wrapper/gradle-wrapper.properties',
       updatedDeps: [],
       newPackageFileContent: `distributionSha256Sum=336b6898b491f6334502d8074a6b8c2d73ed83b92123106bd4bf837f04111043\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.3-bin.zip`,
       config,
@@ -187,10 +268,11 @@ describe('modules/manager/gradle-wrapper/artifacts', () => {
     expect(result).toEqual([
       {
         artifactError: {
-          lockFile: 'gradle-wrapper.properties',
+          lockFile: 'gradle/wrapper/gradle-wrapper.properties',
           stderr: 'Response code 404 (Not Found)',
         },
       },
     ]);
+    expect(execSnapshots).toBeEmptyArray();
   });
 });
diff --git a/lib/modules/manager/gradle-wrapper/artifacts.ts b/lib/modules/manager/gradle-wrapper/artifacts.ts
index 477b029d89..0fe7f16f9e 100644
--- a/lib/modules/manager/gradle-wrapper/artifacts.ts
+++ b/lib/modules/manager/gradle-wrapper/artifacts.ts
@@ -10,12 +10,7 @@ import type { StatusResult } from '../../../util/git/types';
 import { Http } from '../../../util/http';
 import { newlineRegex } from '../../../util/regex';
 import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
-import {
-  extraEnv,
-  getJavaConstraint,
-  gradleWrapperFileName,
-  prepareGradleCommand,
-} from './utils';
+import { extraEnv, getJavaConstraint, prepareGradleCommand } from './utils';
 
 const http = new Http('gradle-wrapper');
 
@@ -60,12 +55,12 @@ export async function updateArtifacts({
 }: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> {
   try {
     logger.debug({ updatedDeps }, 'gradle-wrapper.updateArtifacts()');
-    const gradlewFile = gradleWrapperFileName();
-    let cmd = await prepareGradleCommand(gradlewFile, `wrapper`);
+    let cmd = await prepareGradleCommand();
     if (!cmd) {
       logger.info('No gradlew found - skipping Artifacts update');
       return null;
     }
+    cmd += ' wrapper';
     const distributionUrl = getDistributionUrl(newPackageFileContent);
     if (distributionUrl) {
       cmd += ` --gradle-distribution-url ${distributionUrl}`;
diff --git a/lib/modules/manager/gradle-wrapper/util.spec.ts b/lib/modules/manager/gradle-wrapper/util.spec.ts
index 0a019c034e..13fffb6b2c 100644
--- a/lib/modules/manager/gradle-wrapper/util.spec.ts
+++ b/lib/modules/manager/gradle-wrapper/util.spec.ts
@@ -1,7 +1,20 @@
+import type { Stats } from 'fs';
+import os from 'os';
+import { fs, partial } from '../../../../test/util';
 import { GlobalConfig } from '../../../config/global';
-import { extractGradleVersion, getJavaConstraint } from './utils';
+import {
+  extractGradleVersion,
+  getJavaConstraint,
+  gradleWrapperFileName,
+  prepareGradleCommand,
+} from './utils';
+
+const platform = jest.spyOn(os, 'platform');
+jest.mock('../../../util/fs');
 
 describe('modules/manager/gradle-wrapper/util', () => {
+  beforeEach(() => GlobalConfig.reset());
+
   describe('getJavaConstraint()', () => {
     it('return ^8.0.0 for global mode', () => {
       expect(getJavaConstraint('4')).toBe('^8.0.0');
@@ -34,4 +47,38 @@ describe('modules/manager/gradle-wrapper/util', () => {
       expect(extractGradleVersion(undefined as never)).toBeNull();
     });
   });
+
+  describe('gradleWrapperFileName()', () => {
+    it('works on windows', () => {
+      platform.mockReturnValueOnce('win32');
+      expect(gradleWrapperFileName()).toBe('gradlew.bat');
+    });
+
+    it('works on linux', () => {
+      platform.mockReturnValueOnce('linux');
+      expect(gradleWrapperFileName()).toBe('./gradlew');
+    });
+  });
+
+  describe('prepareGradleCommand', () => {
+    it('works', async () => {
+      platform.mockReturnValueOnce('linux');
+      fs.statLocalFile.mockResolvedValue(
+        partial<Stats>({
+          isFile: () => true,
+          mode: 0o550,
+        })
+      );
+      expect(await prepareGradleCommand()).toBe('./gradlew');
+    });
+
+    it('returns null', async () => {
+      fs.statLocalFile.mockResolvedValue(
+        partial<Stats>({
+          isFile: () => false,
+        })
+      );
+      expect(await prepareGradleCommand()).toBeNull();
+    });
+  });
 });
diff --git a/lib/modules/manager/gradle-wrapper/utils.ts b/lib/modules/manager/gradle-wrapper/utils.ts
index c037bb7b3a..14c6ffcc81 100644
--- a/lib/modules/manager/gradle-wrapper/utils.ts
+++ b/lib/modules/manager/gradle-wrapper/utils.ts
@@ -11,7 +11,6 @@ export const extraEnv = {
     '-Dorg.gradle.parallel=true -Dorg.gradle.configureondemand=true -Dorg.gradle.daemon=false -Dorg.gradle.caching=false',
 };
 
-// istanbul ignore next
 export function gradleWrapperFileName(): string {
   if (
     os.platform() === 'win32' &&
@@ -22,23 +21,16 @@ export function gradleWrapperFileName(): string {
   return './gradlew';
 }
 
-export async function prepareGradleCommand(
-  gradlewName: string,
-  args: string | null
-): Promise<string | null> {
+export async function prepareGradleCommand(): Promise<string | null> {
   const gradlewFile = gradleWrapperFileName();
   const gradlewStat = await statLocalFile(gradlewFile);
-  // istanbul ignore if
   if (gradlewStat?.isFile() === true) {
     // if the file is not executable by others
     if ((gradlewStat.mode & 0o1) === 0) {
       // add the execution permission to the owner, group and others
-      await chmodLocalFile(gradlewName, gradlewStat.mode | 0o111);
+      await chmodLocalFile(gradlewFile, gradlewStat.mode | 0o111);
     }
-    if (args === null) {
-      return gradlewName;
-    }
-    return `${gradlewName} ${args}`;
+    return gradlewFile;
   }
   /* eslint-enable no-bitwise */
   return null;
-- 
GitLab