Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions qt-cpp/test/helper.mts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ type ResolveResult = {
source: 'env-QT_VERSION_FOR_TEST' | 'default-6.9.0' | 'fallback-lower';
/** Args to pass to CMake configure() */
cmakeArgs: string[];
/** Full Qt installation directory */
qtDir: string;
};

/**
Expand Down Expand Up @@ -238,14 +240,18 @@ export function prepareCMakeQtEnvWithVersion(opts?: {
source: ResolveResult['source']
): ResolveResult => {
const qt6Dir = path.join(leaf, 'lib', 'cmake', 'Qt6');
process.env.Qt6_DIR = qt6Dir; // <-- the only knob we set

logs.push(`[qt-test] Using Qt ${version} (${source})`);
logs.push(`[qt-test] leaf='${leaf}'`);
logs.push(`[qt-test] Qt6_DIR='${qt6Dir}'`);
if (verbose) logs.forEach((l) => console.log(l));

return { leaf, version, source, cmakeArgs: [`-DQt6_DIR=${qt6Dir}`] };
return {
leaf,
version,
source,
cmakeArgs: [`-DQt6_DIR=${qt6Dir}`],
qtDir: qt6Dir
};
};

// 1) Respect explicit request
Expand Down Expand Up @@ -299,10 +305,24 @@ export async function cleanBuildDir(
return buildDir;
}

export async function setCMakeConfigurationForPlatform(
ws: vscode.WorkspaceFolder,
settingName: string,
value: unknown
) {
await vscode.workspace
.getConfiguration('cmake', ws.uri)
.update(settingName, value, vscode.ConfigurationTarget.Workspace);
}

export function getPlatformCMakeGenerator(): string {
return process.platform === 'win32' ? 'Ninja' : 'Unix Makefiles';
}

export async function setCMakeGeneratorForPlatform(
ws: vscode.WorkspaceFolder
): Promise<void> {
const generator = process.platform === 'win32' ? 'Ninja' : 'Unix Makefiles';
const generator = getPlatformCMakeGenerator();
await vscode.workspace
.getConfiguration('cmake', ws.uri)
.update('generator', generator, vscode.ConfigurationTarget.Workspace);
Expand Down
6 changes: 5 additions & 1 deletion qt-cpp/test/runTest.build.mts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ async function main() {
setupVSCodeSettings(userDataDir, qtRoot, {
'cmake.configureOnOpen': false
});
installRequiredExtensions(cli, args, localQtCoreVsix);
const extensions = [
{ idOrVsix: 'ms-vscode.cmake-tools' },
{ idOrVsix: localQtCoreVsix }
];
installRequiredExtensions(cli, args, extensions);

// The workspace folder we want to open
const projectDir = path.resolve(__dirname, '../../test/projectFolder');
Expand Down
6 changes: 5 additions & 1 deletion qt-cpp/test/runTest.mts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ async function main() {
await setupTestInfrastructure(vscodeExecutablePath);

setupVSCodeSettings(userDataDir, qtRoot);
installRequiredExtensions(cli, args, localQtCoreVsix);
const extensions = [
{ idOrVsix: 'ms-vscode.cmake-tools' },
{ idOrVsix: localQtCoreVsix }
];
installRequiredExtensions(cli, args, extensions);

// Run the integration tests (no need to pass launchArgs; we reused the same dirs)
await runTests({
Expand Down
23 changes: 6 additions & 17 deletions qt-cpp/test/runTest.natvis.mts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,8 @@
import * as path from 'path';
import * as os from 'os';
import * as fsp from 'fs/promises';

import { downloadAndUnzipVSCode, runTests } from '@vscode/test-electron';

import { getQuietVSCodeArgs } from '../../qt-lib/src/test-constants.js';

import {
installExtensionWithRetry,
debugListExtensions,
assertExtensionsInstalled
} from '../../qt-lib/src/test-vscode-install.js';

import {
setupVSCodeSettings,
setupTestInfrastructure,
Expand Down Expand Up @@ -46,14 +37,12 @@ async function main() {
});

// Install core required extensions (CMake Tools + qt-core) via helper
installRequiredExtensions(cli, args, localQtCoreVsix);

const quietArgs = [...args, ...getQuietVSCodeArgs()];
installExtensionWithRetry(cli, quietArgs, 'ms-vscode.cpptools');

// Final extension check: ensure cpptools is present
debugListExtensions(cli, quietArgs);
assertExtensionsInstalled(cli, quietArgs, ['ms-vscode.cpptools']);
const extensions = [
{ idOrVsix: 'ms-vscode.cmake-tools' },
{ idOrVsix: 'ms-vscode.cpptools' },
{ idOrVsix: localQtCoreVsix }
];
installRequiredExtensions(cli, args, extensions);

// The workspace folder we want to open
const projectDir = path.resolve(
Expand Down
12 changes: 6 additions & 6 deletions qt-cpp/test/runTestHelper.mts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
installExtensionWithRetry,
debugListExtensions,
assertExtensionsInstalled,
getDebugLevel
getDebugLevel,
ExtensionInstallInfo
} from '../../qt-lib/src/test-vscode-install.js';

const QT_INS_ROOT_CONFIG_NAME = 'qtInstallationRoot';
Expand Down Expand Up @@ -108,18 +109,17 @@ export function setupVSCodeSettings(
export function installRequiredExtensions(
cli: string,
args: string[],
localQtCoreVsix: string
extensions: ExtensionInstallInfo[],
requiredIDs: string[] = ['ms-vscode.cmake-tools', 'theqtcompany.qt-core']
): void {
const quietArgs = [...args, ...getQuietVSCodeArgs()];
const required = ['ms-vscode.cmake-tools', localQtCoreVsix];
const requiredIds = ['ms-vscode.cmake-tools', 'theqtcompany.qt-core'];

for (const ext of required) {
for (const ext of extensions) {
installExtensionWithRetry(cli, quietArgs, ext);
}

debugListExtensions(cli, args);
assertExtensionsInstalled(cli, args, requiredIds);
assertExtensionsInstalled(cli, args, requiredIDs);
}

/**
Expand Down
153 changes: 150 additions & 3 deletions qt-cpp/test/suite/build.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';

import { delay } from 'qt-lib';
import { delay, getCoreApi, CoreKey } from 'qt-lib';
import {
setupSandboxLifecycleHooks,
waitForVSCodeIdle,
Expand All @@ -18,9 +18,14 @@ import {
setCMakeGeneratorForPlatform,
prepareStandardCMakeArgs,
readCMakeCacheVar,
selectAndApplyKit
selectAndApplyKit,
setCMakeConfigurationForPlatform
} from '../helper.mts';

// Test timing constants
const FS_SETTLE_DELAY_MS = 500; // Wait for file system to settle after writing files
const DISK_FLUSH_DELAY_MS = 400; // Wait for build artifacts to flush to disk

describe('build: minimal Qt project (index-build)', function () {
this.timeout(150_000);

Expand Down Expand Up @@ -48,7 +53,11 @@ describe('build: minimal Qt project (index-build)', function () {
if (typeof qtRoot !== 'string' || qtRoot.trim() === '') {
throw new Error('qt-core.qtInstallationRoot is not configured.');
}
prepareCMakeQtEnvWithVersion({ topLevel: qtRoot, verbose: true });
const result = prepareCMakeQtEnvWithVersion({
topLevel: qtRoot,
verbose: true
});
process.env.Qt6_DIR = result.qtDir;

// Standard args
prepareStandardCMakeArgs();
Expand Down Expand Up @@ -85,5 +94,143 @@ describe('build: minimal Qt project (index-build)', function () {
expect(fs.existsSync(outPath), `Expected build artifact at ${outPath}`).to
.be.true;
expect(errSpy.called, 'Unexpected error popups during build').to.be.false;
process.env.Qt6_DIR = '';
});

it('configures and builds a tiny Qt app with CMake Presets', async function () {
const wsFolder = getWorkspaceFolderOrThrow();
await setCMakeConfigurationForPlatform(
wsFolder,
'useCMakePresets',
'always'
);
await waitForVSCodeIdle();
const projectDir = wsFolder.uri.fsPath;
const buildDir = await cleanBuildDir(projectDir, 'build-presets');
const qtRoot = vscode.workspace
.getConfiguration('qt-core')
.get<string>('qtInstallationRoot');
if (typeof qtRoot !== 'string' || qtRoot.trim() === '') {
throw new Error('qt-core.qtInstallationRoot is not configured.');
}
const qtEnv = prepareCMakeQtEnvWithVersion({
topLevel: qtRoot,
verbose: true
});
const presetsPath = path.join(projectDir, 'CMakePresets.json');

// Create a CMake Presets configuration
// Note: generator is omitted because CMake will use the default generator
// or the one specified in CMake Tools settings
const presets = {
version: 3,
configurePresets: [
{
name: 'qt-debug',
displayName: 'Qt Debug Configuration',
description: 'Debug build using Qt with CMake Presets',
binaryDir: buildDir,
cacheVariables: {
CMAKE_BUILD_TYPE: 'Debug',
CMAKE_PREFIX_PATH: qtEnv.leaf
}
}
]
};

fs.writeFileSync(presetsPath, JSON.stringify(presets, null, 2), 'utf-8');
console.log(
'Created/Updated CMakePresets.json with CMAKE_PREFIX_PATH:',
qtEnv.leaf
);
console.log('Using projectDir (Presets):', projectDir);

// Wait for file system to settle after writing CMakePresets.json
await delay(FS_SETTLE_DELAY_MS);
await waitForVSCodeIdle();

// Disable automatic configuration to have precise control over test flow
// configureOnOpen would trigger configure before we can set the preset
// automaticReconfigure would interfere with our explicit configure call
await setCMakeConfigurationForPlatform(wsFolder, 'configureOnOpen', false);
await setCMakeConfigurationForPlatform(
wsFolder,
'automaticReconfigure',
false
);

// spy on error messages
const errSpy = sb.spy(vscode.window, 'showErrorMessage');

// Set the configure preset using the correct CMake Tools command
console.log('Setting configure preset: qt-debug');
await vscode.commands.executeCommand(
'cmake.setConfigurePreset',
'qt-debug'
);
await waitForVSCodeIdle();

console.log('Running cmake.configure with presets...');
const rcCfg =
await vscode.commands.executeCommand<number>('cmake.configure');
await waitForVSCodeIdle();
expect(rcCfg, `cmake.configure failed (rc=${rcCfg})`).to.equal(0);

// Confirm what CMake used
if (process.env.QT_TEST_DEBUG === '1') {
console.log('== WHAT CMAKE USED (Presets) ==');
console.log(
' CMAKE_PREFIX_PATH =',
readCMakeCacheVar(buildDir, 'CMAKE_PREFIX_PATH') ?? '<unknown>'
);
}

// Build
const rcBuild = await vscode.commands.executeCommand<number>('cmake.build');
await waitForVSCodeIdle();
expect(rcBuild, `cmake.build failed (rc=${rcBuild})`).to.equal(0);

// Wait for build artifacts to be written to disk
await delay(DISK_FLUSH_DELAY_MS);

const bin =
process.platform === 'win32' ? path.join('Debug', 'hello.exe') : 'hello';
const outPath = path.join(buildDir, bin);
console.log('Checking for binary at', outPath);

expect(fs.existsSync(outPath), `Expected build artifact at ${outPath}`).to
.be.true;
expect(errSpy.called, 'Unexpected error popups during build').to.be.false;

// Verify that INSTALLATION_PATH is set correctly in CoreAPI
const coreAPI = await getCoreApi();
if (!coreAPI) {
throw new Error('CoreAPI is not available');
}

const installationPath = coreAPI.getValue<string>(
wsFolder,
CoreKey.INSTALLATION_PATH
);
console.log('CoreAPI INSTALLATION_PATH:', installationPath);
expect(
installationPath,
'INSTALLATION_PATH should be set in CoreAPI'
).to.equal(qtEnv.leaf);

// Cleanup: remove CMakePresets.json and reset useCMakePresets
try {
if (fs.existsSync(presetsPath)) {
fs.unlinkSync(presetsPath);
}
await setCMakeConfigurationForPlatform(
wsFolder,
'useCMakePresets',
undefined
);
await waitForVSCodeIdle();
} catch (e) {
console.warn('Cleanup warning:', e);
}
});
});
Loading
Loading