|
| 1 | +/******************************************************************************* |
| 2 | + * Copyright (c) 2024 Renesas Electronics Europe. |
| 3 | + * |
| 4 | + * This program and the accompanying materials |
| 5 | + * are made available under the terms of the Eclipse Public License 2.0 |
| 6 | + * which accompanies this distribution, and is available at |
| 7 | + * https://www.eclipse.org/legal/epl-2.0/ |
| 8 | + * |
| 9 | + * SPDX-License-Identifier: EPL-2.0 |
| 10 | + *******************************************************************************/ |
| 11 | +package org.eclipse.cdt.core.build; |
| 12 | + |
| 13 | +import static org.hamcrest.MatcherAssert.assertThat; |
| 14 | +import static org.mockito.Mockito.mock; |
| 15 | +import static org.mockito.Mockito.when; |
| 16 | + |
| 17 | +import org.eclipse.cdt.cmake.core.CMakeNature; |
| 18 | +import org.eclipse.cdt.cmake.core.internal.CMakeBuildConfigurationProvider; |
| 19 | +import org.eclipse.cdt.core.CCProjectNature; |
| 20 | +import org.eclipse.cdt.core.CCorePlugin; |
| 21 | +import org.eclipse.cdt.core.CProjectNature; |
| 22 | +import org.eclipse.cdt.core.parser.IScannerInfoProvider; |
| 23 | +import org.eclipse.cdt.core.testplugin.ResourceHelper; |
| 24 | +import org.eclipse.cdt.core.testplugin.util.BaseTestCase5; |
| 25 | +import org.eclipse.cdt.debug.core.CDebugCorePlugin; |
| 26 | +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsScannerInfoProvider; |
| 27 | +import org.eclipse.core.resources.IBuildConfiguration; |
| 28 | +import org.eclipse.core.resources.IProject; |
| 29 | +import org.eclipse.core.resources.IProjectDescription; |
| 30 | +import org.eclipse.core.runtime.NullProgressMonitor; |
| 31 | +import org.eclipse.debug.core.ILaunchManager; |
| 32 | +import org.junit.jupiter.api.Test; |
| 33 | + |
| 34 | +public class CBuildConfigurationManagerTests extends BaseTestCase5 { |
| 35 | + protected ICBuildConfigurationManager configManager = CDebugCorePlugin |
| 36 | + .getService(ICBuildConfigurationManager.class); |
| 37 | + |
| 38 | + /** |
| 39 | + * Tests that CBuildConfigurationManager.getBuildConfiguration(IProject, IToolChain, String, IProgressMonitor) |
| 40 | + * calls CCorePlugin.resetCachedScannerInfoProvider(IProject) after creating a new ICBuildConfiguration. |
| 41 | + * |
| 42 | + * Tests the following failure mode. |
| 43 | + * When the project's active IBuildConfiguration has the default name and the chosen ICBuildConfigurationProvider. |
| 44 | + * getCBuildConfiguration does not support the IBuildConfiguration.DEFAULT_CONFIG_NAME and returns null, this can |
| 45 | + * cause the project's ScannerInfoProvider to become "stuck" |
| 46 | + * (https://bugs.eclipse.org/bugs/show_bug.cgi?id=413357) on the wrong setting (eg |
| 47 | + * LanguageSettingsScannerInfoProvider instead of ICBuildConfiguration) until Eclipse is restarted or the project |
| 48 | + * is closed and reopened. When this happens, the indexer does not function. |
| 49 | + * |
| 50 | + * This problem may arise if an ISV contributes a ICBuildConfigurationProvider which has very specific naming |
| 51 | + * conventions for it's build configurations. |
| 52 | + * |
| 53 | + * Test approach: |
| 54 | + * The test requires that a faulty build configuration is setup for the project to trick the getScannerInfoProvider |
| 55 | + * into thinking it's not a core build project. Typically ContainerGCCToolChainProvider may find a valid gcc toolchain |
| 56 | + * and then the provider's getCBuildConfiguration will return a valid ICBuildConfiguration. So need to set the active |
| 57 | + * build configuration to one which does not use the default name. |
| 58 | + * |
| 59 | + * (1) |
| 60 | + * In a CMake project, add a new build configuration which doesn't use the default name and add this to the project |
| 61 | + * using an invalid ICBuildConfiguration and set this as the active build configuration. |
| 62 | + * |
| 63 | + * (2) |
| 64 | + * Later, when the indexer gets the project's active build configuration it is null and so the project's |
| 65 | + * IScannerInfoProvider is set to the wrong type and is cached. |
| 66 | + * |
| 67 | + * (3) |
| 68 | + * Later still, a new build configuration is created, this time with a valid ICBuildConfiguration, which is set as the |
| 69 | + * project's active build configuration. |
| 70 | + * |
| 71 | + * (4) |
| 72 | + * With the fix in place (resetCachedScannerInfoProvider), the next time the indexer gets the project's |
| 73 | + * IScannerInfoProvider it will be recomputed and return the project's expected IScannerInfoProvider |
| 74 | + * (ie: ICBuildConfiguration). |
| 75 | + */ |
| 76 | + @Test |
| 77 | + public void testResetCachedScannerInfoProvider() throws Exception { |
| 78 | + // (1) create a CMake project |
| 79 | + IProject project = createCMakeProject(); |
| 80 | + |
| 81 | + CMakeBuildConfigurationProvider provider = new CMakeBuildConfigurationProvider(); |
| 82 | + String buildConfigBaseName = "notDefaultName"; |
| 83 | + // Create a new IBuildConfiguration with a name that is not the default name. |
| 84 | + IBuildConfiguration buildConfiguration = configManager.createBuildConfiguration(provider, project, |
| 85 | + buildConfigBaseName, new NullProgressMonitor()); |
| 86 | + // Add the IBuildConfiguration/ICBuildConfiguration combo using an invalid ICBuildConfiguration |
| 87 | + configManager.addBuildConfiguration(buildConfiguration, null); |
| 88 | + // Set the IBuildConfiguration, with this name, as the active build config |
| 89 | + IProjectDescription description = project.getDescription(); |
| 90 | + String buildConfigName = provider.getId() + "/" + buildConfigBaseName; |
| 91 | + description.setActiveBuildConfig(buildConfigName); |
| 92 | + project.setDescription(description, new NullProgressMonitor()); |
| 93 | + |
| 94 | + // (2) The project's scannerInfoProvider is expected to be the wrong type here |
| 95 | + IScannerInfoProvider scannerInfoProvider = CCorePlugin.getDefault().getScannerInfoProvider(project); |
| 96 | + assertThat("scannerInfoProvider expected to be LanguageSettingsScannerInfoProvider", |
| 97 | + scannerInfoProvider instanceof LanguageSettingsScannerInfoProvider); |
| 98 | + |
| 99 | + // (3) Setup a toolchain ready to use for creating the valid ICBuildConfiguration |
| 100 | + IToolChain mockToolchain = mock(IToolChain.class); |
| 101 | + when(mockToolchain.getProperty(IToolChain.ATTR_OS)).thenReturn("osDummy"); |
| 102 | + when(mockToolchain.getProperty(IToolChain.ATTR_ARCH)).thenReturn("archDummy"); |
| 103 | + when(mockToolchain.getTypeId()).thenReturn("tc_typeId"); |
| 104 | + when(mockToolchain.getId()).thenReturn("tcId"); |
| 105 | + when(mockToolchain.getBuildConfigNameFragment()).thenReturn("buildConfigName"); |
| 106 | + |
| 107 | + ICBuildConfiguration cBuildConfiguration = configManager.getBuildConfiguration(project, mockToolchain, |
| 108 | + ILaunchManager.RUN_MODE, new NullProgressMonitor()); |
| 109 | + assertThat("The cBuildConfiguration should of type CBuildConfiguration", |
| 110 | + cBuildConfiguration instanceof CBuildConfiguration); |
| 111 | + CBuildConfiguration cbc = (CBuildConfiguration) cBuildConfiguration; |
| 112 | + // Set this ICBuildConfiguration as the active build configuration |
| 113 | + cbc.setActive(new NullProgressMonitor()); |
| 114 | + |
| 115 | + // (4) The project's scannerInfoProvider is expected to be the correct type here |
| 116 | + scannerInfoProvider = CCorePlugin.getDefault().getScannerInfoProvider(project); |
| 117 | + assertThat("scannerInfoProvider expected to be ICBuildConfiguration", |
| 118 | + scannerInfoProvider instanceof ICBuildConfiguration); |
| 119 | + } |
| 120 | + |
| 121 | + private IProject createCMakeProject() throws Exception { |
| 122 | + // Create a plain Eclipse project |
| 123 | + IProject project = ResourceHelper.createProject(this.getName()); |
| 124 | + // Add C/C++ and CMake natures to make it a CMake project |
| 125 | + IProjectDescription description = project.getDescription(); |
| 126 | + description.setNatureIds( |
| 127 | + new String[] { CProjectNature.C_NATURE_ID, CCProjectNature.CC_NATURE_ID, CMakeNature.ID }); |
| 128 | + project.setDescription(description, null); |
| 129 | + return project; |
| 130 | + } |
| 131 | +} |
0 commit comments