From 9f8cf1025d38397b1bcd0a6ad09b4e9ddec353da Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Thu, 8 Aug 2024 13:37:07 -0400 Subject: [PATCH] Use build description to check for unified testing binary (#1004) We were checking the `mtime` of the `.swift-testing` binary to determine if it was produced by the latest build. The issue with this is that when you run tests over and over without making any code changes the binary is never rewritten because it is already up to date. Instead of checking the `mtime` of the .swift-testing binary in the build folder to determine if it was produced, use the build `description.json` and check the builtTestProducts list to see if the legacy `.swift-testing` binary was produced by the latest build. --- src/TestExplorer/TestRunner.ts | 4 ---- src/debugger/buildConfig.ts | 31 ++++++++++++++++++------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/TestExplorer/TestRunner.ts b/src/TestExplorer/TestRunner.ts index 25262a2eb..dc6cb0a9b 100644 --- a/src/TestExplorer/TestRunner.ts +++ b/src/TestExplorer/TestRunner.ts @@ -430,7 +430,6 @@ export class TestRunner { fifoPipePath, this.testKind, this.testArgs.swiftTestArgs, - new Date(), true ); @@ -700,8 +699,6 @@ export class TestRunner { }); subscriptions.push(buildTask); - const buildStartTime = new Date(); - // Perform a build all before the tests are run. We want to avoid the "Debug Anyway" dialog // since choosing that with no prior build, when debugging swift-testing tests, will cause // the extension to start listening to the fifo pipe when no results will be produced, @@ -729,7 +726,6 @@ export class TestRunner { fifoPipePath, this.testKind, this.testArgs.swiftTestArgs, - buildStartTime, true ); diff --git a/src/debugger/buildConfig.ts b/src/debugger/buildConfig.ts index 6aa592984..13cc720b2 100644 --- a/src/debugger/buildConfig.ts +++ b/src/debugger/buildConfig.ts @@ -38,7 +38,6 @@ export class TestingDebugConfigurationFactory { fifoPipePath: string, testKind: TestKind, testList: string[], - buildStartTime: Date, expandEnvVariables = false ): Promise { return new TestingDebugConfigurationFactory( @@ -47,7 +46,6 @@ export class TestingDebugConfigurationFactory { testKind, TestLibrary.swiftTesting, testList, - buildStartTime, expandEnvVariables ).build(); } @@ -64,7 +62,6 @@ export class TestingDebugConfigurationFactory { testKind, TestLibrary.xctest, testList, - null, expandEnvVariables ).build(); } @@ -80,7 +77,6 @@ export class TestingDebugConfigurationFactory { testKind, testLibrary, [], - null, true ).testExecutableOutputPath(); } @@ -91,7 +87,6 @@ export class TestingDebugConfigurationFactory { private testKind: TestKind, private testLibrary: TestLibrary, private testList: string[], - private buildStartTime: Date | null, private expandEnvVariables = false ) {} @@ -454,23 +449,33 @@ export class TestingDebugConfigurationFactory { ); } + private buildDescriptionPath(): string { + return path.join(this.buildDirectory, this.artifactFolderForTestKind, "description.json"); + } + private async isUnifiedTestingBinary(): Promise { // Toolchains that contain https://github.com/swiftlang/swift-package-manager/commit/844bd137070dcd18d0f46dd95885ef7907ea0697 // no longer produce a .swift-testing binary, instead we want to use `unifiedTestingOutputPath`. // In order to determine if we're working with a unified binary we need to check if the .swift-testing - // binary _doesn't_ exist, and if it does exist we need to check that it wasn't built by an old toolchain - // and is still in the .build directory. We do this by checking its mtime and seeing if it is after - // the `buildStartTime`. + // binary was produced by the latest build. If it was then we are not using a unified binary. // TODO: When Swift 6 is released and enough time has passed that we're sure no one is building the .swift-testing - // binary anymore this fs.stat workaround can be removed and `swiftTestingPath` can be returned. The - // `buildStartTime` argument can be removed and the build config generation can be made synchronous again. + // binary anymore this workaround can be removed and `swiftTestingPath` can be returned, and the build config + // generation can be made synchronous again. try { - const stat = await fs.stat(this.swiftTestingOutputPath()); - return !this.buildStartTime || stat.mtime.getTime() < this.buildStartTime.getTime(); + const buildDescriptionStr = await fs.readFile(this.buildDescriptionPath(), "utf-8"); + const buildDescription = JSON.parse(buildDescriptionStr); + const testProducts = buildDescription.builtTestProducts as { binaryPath: string }[]; + if (!testProducts) { + return false; + } + const testBinaryPaths = testProducts.map(({ binaryPath }) => binaryPath); + const swiftTestingBinaryRealPath = await fs.realpath(this.swiftTestingOutputPath()); + return !testBinaryPaths.includes(swiftTestingBinaryRealPath); } catch { - // If the .swift-testing binary doesn't exist then swift-testing tests are in the unified binary. + // If the .swift-testing binary wasn't produced by the latest build then we assume the + // swift-testing tests are in the unified binary. return true; } }