diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d45798bc..abf982eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,9 +3,6 @@ on: pull_request: branches: - main - push: - branches: - - codeMigration jobs: @@ -36,6 +33,11 @@ jobs: run: | Xvfb -ac :99 -screen 0 1920x1080x16 & mvn verify -Dtest.includes="**/ui/*.java" + - name: Upload Coverage Report + uses: actions/upload-artifact@v4 + with: + name: jacoco-coverage-report-ui + path: checkmarx-ast-eclipse-plugin-tests/target/site/jacoco-aggregate integration-tests: runs-on: ubuntu-latest @@ -64,5 +66,64 @@ jobs: run: | Xvfb -ac :99 -screen 0 1920x1080x16 & mvn verify -Dtest.includes="**/integration/*Test.java" + - name: Upload Coverage Report + uses: actions/upload-artifact@v4 + with: + name: jacoco-coverage-report-integration + path: checkmarx-ast-eclipse-plugin-tests/target/site/jacoco-aggregate + unit-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3.5.2 + with: + lfs: true + - name: Checkout LFS objects + run: git lfs checkout + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + - uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-maven- + - name: Run Unit Tests with Coverage + env: + CX_BASE_URI: ${{ secrets.CX_BASE_URI }} + CX_TENANT: ${{ secrets.CX_TENANT }} + CX_APIKEY: ${{ secrets.CX_APIKEY }} + CX_TEST_SCAN: ${{ secrets.CX_TEST_SCAN }} + DISPLAY: :99.0 + run: | + Xvfb -ac :99 -screen 0 1920x1080x16 & + mvn clean verify -Dtest.includes="**/unit/**/*Test.java" + - name: Upload Coverage Report + uses: actions/upload-artifact@v4 + with: + name: jacoco-coverage-report-unit + path: checkmarx-ast-eclipse-plugin-tests/target/site/jacoco-aggregate + - name: Generate JaCoCo Coverage Summary + run: | + REPORT="checkmarx-ast-eclipse-plugin-tests/target/site/jacoco-aggregate/jacoco.xml" + + LINE_COVERED=$(grep 'type="LINE"' $REPORT | sed -E 's/.*covered="([0-9]+)".*/\1/') + LINE_MISSED=$(grep 'type="LINE"' $REPORT | sed -E 's/.*missed="([0-9]+)".*/\1/') + + BRANCH_COVERED=$(grep 'type="BRANCH"' $REPORT | sed -E 's/.*covered="([0-9]+)".*/\1/') + BRANCH_MISSED=$(grep 'type="BRANCH"' $REPORT | sed -E 's/.*missed="([0-9]+)".*/\1/') + + LINE_TOTAL=$((LINE_COVERED + LINE_MISSED)) + BRANCH_TOTAL=$((BRANCH_COVERED + BRANCH_MISSED)) + + LINE_PERCENT=$(awk "BEGIN {printf \"%.2f\", ($LINE_COVERED/$LINE_TOTAL)*100}") + BRANCH_PERCENT=$(awk "BEGIN {printf \"%.2f\", ($BRANCH_COVERED/$BRANCH_TOTAL)*100}") + echo "## 🧪 Test Coverage Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Metric | Coverage |" >> $GITHUB_STEP_SUMMARY + echo "|------|------|" >> $GITHUB_STEP_SUMMARY + echo "| **Line Coverage** | ${LINE_PERCENT}% |" >> $GITHUB_STEP_SUMMARY + echo "| **Branch Coverage** | ${BRANCH_PERCENT}% |" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY diff --git a/checkmarx-ast-eclipse-plugin-tests/.classpath b/checkmarx-ast-eclipse-plugin-tests/.classpath index 6f671e11..98ee5fe7 100644 --- a/checkmarx-ast-eclipse-plugin-tests/.classpath +++ b/checkmarx-ast-eclipse-plugin-tests/.classpath @@ -14,16 +14,7 @@ - - - - - - - - - diff --git a/checkmarx-ast-eclipse-plugin-tests/META-INF/MANIFEST.MF b/checkmarx-ast-eclipse-plugin-tests/META-INF/MANIFEST.MF index 91cb193d..8bf051b2 100644 --- a/checkmarx-ast-eclipse-plugin-tests/META-INF/MANIFEST.MF +++ b/checkmarx-ast-eclipse-plugin-tests/META-INF/MANIFEST.MF @@ -12,5 +12,5 @@ Require-Bundle: org.eclipse.jdt.junit5.runtime, junit-jupiter-api Bundle-RequiredExecutionEnvironment: JavaSE-17 -Bundle-ClassPath: .,lib/mockito-core-5.14.2.jar,lib/powermock-core-*.jar +Bundle-ClassPath: .,lib/mockito-core-5.14.2.jar,lib/powermock-core-*.jar, lib/byte-buddy-1.17.8.jar, lib/byte-buddy-agent-1.17.8.jar Automatic-Module-Name: com.checkmarx.ast.eclipse.tests diff --git a/checkmarx-ast-eclipse-plugin-tests/lib/byte-buddy-1.17.8.jar b/checkmarx-ast-eclipse-plugin-tests/lib/byte-buddy-1.17.8.jar new file mode 100644 index 00000000..7cca201f Binary files /dev/null and b/checkmarx-ast-eclipse-plugin-tests/lib/byte-buddy-1.17.8.jar differ diff --git a/checkmarx-ast-eclipse-plugin-tests/lib/byte-buddy-agent-1.17.8.jar b/checkmarx-ast-eclipse-plugin-tests/lib/byte-buddy-agent-1.17.8.jar new file mode 100644 index 00000000..a47ea2f8 Binary files /dev/null and b/checkmarx-ast-eclipse-plugin-tests/lib/byte-buddy-agent-1.17.8.jar differ diff --git a/checkmarx-ast-eclipse-plugin-tests/pom.xml b/checkmarx-ast-eclipse-plugin-tests/pom.xml index 2a6a04be..bd4e15c3 100644 --- a/checkmarx-ast-eclipse-plugin-tests/pom.xml +++ b/checkmarx-ast-eclipse-plugin-tests/pom.xml @@ -4,7 +4,8 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - **/Test*.java,**/*Test.java,**/*Tests.java,**/*TestCase.java + + **/Test*.java,**/*Test.java,**/*Tests.java,**/*TestCase.java com.checkmarx.ast.eclipse.tests com.checkmarx.ast.eclipse.tests @@ -17,6 +18,23 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.11 + + + prepare-agent + + prepare-agent + + + tycho.testArgLine + ${project.build.directory}/jacoco.exec + + + + org.eclipse.tycho tycho-surefire-plugin @@ -26,11 +44,13 @@ false junit5 true + ${tycho.testArgLine} ${test.includes} + \ No newline at end of file diff --git a/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/ui/BaseUITest.java b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/ui/BaseUITest.java index 54af1ca5..2211c71b 100644 --- a/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/ui/BaseUITest.java +++ b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/ui/BaseUITest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree; +import org.eclipse.swt.widgets.Decorations; import org.eclipse.swt.widgets.Tree; import com.checkmarx.eclipse.utils.PluginConstants; @@ -302,18 +303,48 @@ protected static void waitUntilBranchComboIsEnabled() throws TimeoutException { * Wait for connection response */ protected static void waitForConnectionResponse() throws TimeoutException { - int retryIdx = 0; - while (!_bot.text(3).getText().equals(INFO_SUCCESSFUL_CONNECTION)) { - if (retryIdx == 10) { - break; - } - _bot.sleep(1000); - retryIdx++; - } - - if (retryIdx == 10) { - throw new TimeoutException("Connection validation timeout after 10000ms."); - } + int retryIdx = 0; + while (retryIdx < 10) { + boolean found = false; + int index = 0; + // Search text widgets + while (true) { + try { + String textValue = _bot.text(index).getText(); + System.out.println("[waitForConnectionResponse] text[" + index + "]: '" + textValue + "'"); + if (textValue.contains(INFO_SUCCESSFUL_CONNECTION)) { + found = true; + break; + } + index++; + } catch (Exception e) { + break; + } + } + // Search label widgets if not found + if (!found) { + index = 0; + while (true) { + try { + String labelValue = _bot.label(index).getText(); + System.out.println("[waitForConnectionResponse] label[" + index + "]: '" + labelValue + "'"); + if (labelValue.contains(INFO_SUCCESSFUL_CONNECTION)) { + found = true; + break; + } + index++; + } catch (Exception e) { + break; + } + } + } + if (found) { + return; + } + _bot.sleep(1000); + retryIdx++; + } + throw new TimeoutException("Connection validation timeout after 10000ms. See logs for widget contents."); } /** @@ -381,4 +412,4 @@ protected SWTBotTree getResultsTree() { return _bot.tree(0); } } -} \ No newline at end of file +} diff --git a/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/runner/AuthenticatorTest.java b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/runner/AuthenticatorTest.java new file mode 100644 index 00000000..740e12a6 --- /dev/null +++ b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/runner/AuthenticatorTest.java @@ -0,0 +1,108 @@ +package checkmarx.ast.eclipse.plugin.tests.unit.runner; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.Mockito; +import org.slf4j.Logger; + +import com.checkmarx.ast.wrapper.CxException; +import com.checkmarx.ast.wrapper.CxWrapper; +import com.checkmarx.eclipse.runner.Authenticator; +import com.checkmarx.eclipse.utils.PluginConstants; + +class AuthenticatorTest { + + @Test + void testDoAuthenticationSuccess() throws Exception { + + Logger mockLogger = mock(Logger.class); + + try (MockedConstruction mocked = + Mockito.mockConstruction(CxWrapper.class, + (mock, context) -> when(mock.authValidate()).thenReturn("SUCCESS"))) { + + Authenticator authenticator = new Authenticator(mockLogger); + + String result = authenticator.doAuthentication("dummyKey", "--param"); + + assertEquals("SUCCESS", result); + verify(mockLogger).info("Authentication Status: SUCCESS"); + } + } + + @Test + void testDoAuthenticationIOException() throws Exception { + + Logger mockLogger = mock(Logger.class); + + try (MockedConstruction mocked = + Mockito.mockConstruction(CxWrapper.class, + (mock, context) -> when(mock.authValidate()) + .thenThrow(new IOException("IO error")))) { + + Authenticator authenticator = new Authenticator(mockLogger); + + String result = authenticator.doAuthentication("dummyKey", "--param"); + + assertEquals("IO error", result); + verify(mockLogger).error( + eq(String.format(PluginConstants.ERROR_AUTHENTICATING_AST, "IO error")), + any(IOException.class) + ); + } + } + + @Test + void testDoAuthenticationInterruptedException() throws Exception { + + Logger mockLogger = mock(Logger.class); + + try (MockedConstruction mocked = + Mockito.mockConstruction(CxWrapper.class, + (mock, context) -> when(mock.authValidate()) + .thenThrow(new InterruptedException("Interrupted")))) { + + Authenticator authenticator = new Authenticator(mockLogger); + + String result = authenticator.doAuthentication("dummyKey", "--param"); + + assertEquals("Interrupted", result); + verify(mockLogger).error( + eq(String.format(PluginConstants.ERROR_AUTHENTICATING_AST, "Interrupted")), + any(InterruptedException.class) + ); + } + } + + @Test + void testDoAuthenticationCxException() throws Exception { + + Logger mockLogger = mock(Logger.class); + + try (MockedConstruction mocked = + Mockito.mockConstruction(CxWrapper.class, + (mock, context) -> when(mock.authValidate()) + .thenThrow(new CxException(1, "Cx error")))) { + + Authenticator authenticator = new Authenticator(mockLogger); + + String result = authenticator.doAuthentication("dummyKey", "--param"); + + assertEquals("Cx error", result); + verify(mockLogger).error( + eq(String.format(PluginConstants.ERROR_AUTHENTICATING_AST, "Cx error")), + any(CxException.class) + ); + } + } + + @Test + void testSingletonInstanceNotNull() { + assertNotNull(Authenticator.INSTANCE); + } +} diff --git a/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/utils/PluginUtilsTest.java b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/utils/PluginUtilsTest.java new file mode 100644 index 00000000..022ce3ca --- /dev/null +++ b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/utils/PluginUtilsTest.java @@ -0,0 +1,269 @@ +package checkmarx.ast.eclipse.plugin.tests.unit.utils; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.IWorkbench; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.resources.IWorkspaceRoot; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceProxyVisitor; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.e4.core.services.events.IEventBroker; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.viewers.ComboViewer; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.widgets.Combo; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + +import com.checkmarx.ast.results.result.Data; +import com.checkmarx.ast.results.result.Node; +import com.checkmarx.ast.results.result.Result; +import com.checkmarx.eclipse.enums.Severity; +import com.checkmarx.eclipse.properties.Preferences; +import com.checkmarx.eclipse.utils.PluginConstants; +import com.checkmarx.eclipse.utils.PluginUtils; +import com.checkmarx.eclipse.views.DataProvider; +import com.checkmarx.eclipse.views.DisplayModel; +import com.checkmarx.eclipse.views.filters.FilterState; +import org.eclipse.core.runtime.IStatus; + +public class PluginUtilsTest { + + @Test + void testConvertStringTimeStampValid() { + String input = "2024-01-01T10:00:00Z"; + + String result = PluginUtils.convertStringTimeStamp(input); + + assertNotNull(result); + assertTrue(result.contains("2024")); + } + + @Test + void testConvertStringTimeStampInvalid() { + String input = "invalid-date"; + + String result = PluginUtils.convertStringTimeStamp(input); + + assertEquals(input, result); + } + + @Test + void testValidateScanIdFormatValid() { + String scanId = "d61aaad4-38fb-406c-bf54-b9b8475f81a5"; + + boolean result = PluginUtils.validateScanIdFormat(scanId); + + assertTrue(result); + } + + @Test + void testValidateScanIdFormatInvalid() { + String scanId = "invalid-scan-id"; + + boolean result = PluginUtils.validateScanIdFormat(scanId); + + assertFalse(result); + } + + @Test + void testEnableComboViewer() { + ComboViewer viewer = mock(ComboViewer.class); + Combo combo = mock(Combo.class); + + when(viewer.getCombo()).thenReturn(combo); + + PluginUtils.enableComboViewer(viewer, true); + + verify(combo).setEnabled(true); + } + + @Test + void testSetTextForComboViewer() { + ComboViewer viewer = mock(ComboViewer.class); + Combo combo = mock(Combo.class); + + when(viewer.getCombo()).thenReturn(combo); + + PluginUtils.setTextForComboViewer(viewer, "TestText"); + + verify(combo).setText("TestText"); + verify(combo).update(); + } + + @Test + void testUpdateFiltersEnabledAndCheckedState() { + + Action action = mock(Action.class); + when(action.getId()).thenReturn("LOW"); + + List actions = new ArrayList<>(); + actions.add(action); + + try (MockedStatic dp = Mockito.mockStatic(DataProvider.class); + MockedStatic fs = Mockito.mockStatic(FilterState.class)) { + + DataProvider provider = mock(DataProvider.class); + dp.when(DataProvider::getInstance).thenReturn(provider); + when(provider.containsResults()).thenReturn(true); + + fs.when(() -> FilterState.isSeverityEnabled("LOW")).thenReturn(true); + + PluginUtils.updateFiltersEnabledAndCheckedState(actions); + + verify(action).setEnabled(true); + verify(action).setChecked(true); + } + } + + @Test + void testMessageCreation() { + DisplayModel model = PluginUtils.message("Hello"); + + assertNotNull(model); + } + + @Test + void testShowMessage() { + DisplayModel root = new DisplayModel.DisplayModelBuilder("root").build(); + TreeViewer viewer = mock(TreeViewer.class); + + PluginUtils.showMessage(root, viewer, "Test message"); + + assertEquals(1, root.children.size()); + verify(viewer).refresh(); + } + + @Test + void testClearMessage() { + + DisplayModel root = new DisplayModel.DisplayModelBuilder("root").build(); + root.children.add(new DisplayModel.DisplayModelBuilder("child").build()); + + TreeViewer viewer = mock(TreeViewer.class); + + PluginUtils.clearMessage(root, viewer); + + assertEquals(0, root.children.size()); + verify(viewer).refresh(); + } + + @Test + void testAreCredentialsDefinedTrue() { + + try (MockedStatic prefs = Mockito.mockStatic(Preferences.class)) { + + prefs.when(Preferences::getApiKey).thenReturn("apikey"); + + boolean result = PluginUtils.areCredentialsDefined(); + + assertTrue(result); + } + } + + @Test + void testAreCredentialsDefinedFalse() { + + try (MockedStatic prefs = Mockito.mockStatic(Preferences.class)) { + + prefs.when(Preferences::getApiKey).thenReturn(""); + + boolean result = PluginUtils.areCredentialsDefined(); + + assertFalse(result); + } + } + + @Test + void testGetEventBroker() { + IEventBroker mockBroker = mock(IEventBroker.class); + IWorkbench mockWorkbench = mock(IWorkbench.class); + try (MockedStatic platformUI = Mockito.mockStatic(PlatformUI.class)) { + platformUI.when(PlatformUI::getWorkbench).thenReturn(mockWorkbench); + when(mockWorkbench.getService(IEventBroker.class)).thenReturn(mockBroker); + IEventBroker result = PluginUtils.getEventBroker(); + assertSame(mockBroker, result); + } + } + + @Test + void testFindFileInWorkspace_normal() throws Exception { + IFile mockFile = mock(IFile.class); + IWorkspaceRoot root = mock(IWorkspaceRoot.class); + IWorkspace workspace = mock(IWorkspace.class); + IResourceProxyVisitor[] visitorHolder = new IResourceProxyVisitor[1]; + try (MockedStatic rp = Mockito.mockStatic(ResourcesPlugin.class)) { + rp.when(ResourcesPlugin::getWorkspace).thenReturn(workspace); + when(workspace.getRoot()).thenReturn(root); + doAnswer((Answer) invocation -> { + visitorHolder[0] = invocation.getArgument(0); + // Simulate visit + return null; + }).when(root).accept(any(IResourceProxyVisitor.class), anyInt()); + List files = PluginUtils.findFileInWorkspace("file.java"); + assertNotNull(files); + } + } + + @Test + void testFindFileInWorkspace_exception() throws Exception { + IWorkspaceRoot root = mock(IWorkspaceRoot.class); + IWorkspace workspace = mock(IWorkspace.class); + try (MockedStatic rp = Mockito.mockStatic(ResourcesPlugin.class)) { + rp.when(ResourcesPlugin::getWorkspace).thenReturn(workspace); + when(workspace.getRoot()).thenReturn(root); + doThrow(new RuntimeException("fail")).when(root).accept(any(IResourceProxyVisitor.class), anyInt()); + List files = PluginUtils.findFileInWorkspace("file.java"); + assertNotNull(files); + assertTrue(files.isEmpty()); + } + } + + @Test + void testClearVulnerabilitiesFromProblemsView_normal() throws Exception { + IWorkspace workspace = mock(IWorkspace.class); + IWorkspaceRoot resource = mock(IWorkspaceRoot.class); + IMarker marker1 = mock(IMarker.class); + IMarker marker2 = mock(IMarker.class); + when(workspace.getRoot()).thenReturn(resource); + when(resource.findMarkers(IMarker.MARKER, true, IResource.DEPTH_INFINITE)).thenReturn(new IMarker[]{marker1, marker2}); + when(marker1.getAttribute(IMarker.SOURCE_ID)).thenReturn(PluginConstants.PROBLEM_SOURCE_ID); + when(marker2.getAttribute(IMarker.SOURCE_ID)).thenReturn("other"); + try (MockedStatic rp = Mockito.mockStatic(ResourcesPlugin.class)) { + rp.when(ResourcesPlugin::getWorkspace).thenReturn(workspace); + PluginUtils.clearVulnerabilitiesFromProblemsView(); + verify(marker1).delete(); + verify(marker2, never()).delete(); + } + } + + @Test + void testClearVulnerabilitiesFromProblemsView_coreException() throws Exception { + IWorkspace workspace = mock(IWorkspace.class); + IWorkspaceRoot resource = mock(IWorkspaceRoot.class); + IMarker marker1 = mock(IMarker.class); + when(workspace.getRoot()).thenReturn(resource); + when(resource.findMarkers(IMarker.MARKER, true, IResource.DEPTH_INFINITE)).thenReturn(new IMarker[]{marker1}); + when(marker1.getAttribute(IMarker.SOURCE_ID)).thenReturn(PluginConstants.PROBLEM_SOURCE_ID); + IStatus status = mock(IStatus.class); + when(status.getMessage()).thenReturn("error"); + doThrow(new CoreException(status)).when(marker1).delete(); + try (MockedStatic rp = Mockito.mockStatic(ResourcesPlugin.class)) { + rp.when(ResourcesPlugin::getWorkspace).thenReturn(workspace); + PluginUtils.clearVulnerabilitiesFromProblemsView(); + // Should not throw + } + } +} \ No newline at end of file diff --git a/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/CheckmarxViewTest.java b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/CheckmarxViewTest.java new file mode 100644 index 00000000..05978ac2 --- /dev/null +++ b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/CheckmarxViewTest.java @@ -0,0 +1,374 @@ +package checkmarx.ast.eclipse.plugin.tests.unit.views; + +import com.checkmarx.eclipse.views.CheckmarxView; +import com.checkmarx.eclipse.views.DataProvider; +import com.checkmarx.eclipse.views.actions.ToolBarActions; +import com.checkmarx.eclipse.properties.Preferences; +import com.checkmarx.eclipse.utils.PluginUtils; +import com.checkmarx.ast.project.Project; +import com.checkmarx.ast.scan.Scan; +import com.checkmarx.eclipse.Activator; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; + +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchWindow; + +import org.eclipse.e4.core.services.events.IEventBroker; +import java.util.Map; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import org.osgi.service.event.Event; + +import org.junit.jupiter.api.*; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import static org.junit.jupiter.api.Assertions.*; + +class CheckmarxViewTest { + + private static Display display; + + private static MockedStatic activatorStaticMock; + private static MockedStatic imageDescriptorStaticMock; + + private MockedStatic platformUIMock; + private MockedStatic pluginUtilsMock; + + private CheckmarxView checkmarxView; + private Shell shell; + private Composite parent; + + @BeforeAll + static void beforeAll() { + + display = Display.getDefault(); + + activatorStaticMock = Mockito.mockStatic(Activator.class); + imageDescriptorStaticMock = Mockito.mockStatic(ImageDescriptor.class, Mockito.CALLS_REAL_METHODS); + + ImageDescriptor descriptor = Mockito.mock(ImageDescriptor.class); + Image image = Mockito.mock(Image.class); + + activatorStaticMock + .when(() -> Activator.getImageDescriptor(Mockito.anyString())) + .thenReturn(descriptor); + + Mockito.when(descriptor.createImage()).thenReturn(image); + } + + @AfterAll + static void afterAll() { + activatorStaticMock.close(); + imageDescriptorStaticMock.close(); + } + + @BeforeEach + void setUp() throws Exception { + + platformUIMock = Mockito.mockStatic(PlatformUI.class); + pluginUtilsMock = Mockito.mockStatic(PluginUtils.class); + + IWorkbench workbench = Mockito.mock(IWorkbench.class); + IWorkbenchWindow window = Mockito.mock(IWorkbenchWindow.class); + + display.syncExec(() -> { + shell = new Shell(display); + parent = new Composite(shell, 0); + }); + + Mockito.when(window.getShell()).thenReturn(shell); + Mockito.when(workbench.getActiveWorkbenchWindow()).thenReturn(window); + Mockito.when(workbench.getDisplay()).thenReturn(display); + + platformUIMock.when(PlatformUI::getWorkbench).thenReturn(workbench); + + IEventBroker broker = Mockito.mock(IEventBroker.class); + Mockito.when(broker.subscribe(Mockito.anyString(), Mockito.any())).thenReturn(true); + + pluginUtilsMock.when(PluginUtils::getEventBroker).thenReturn(broker); + + checkmarxView = new CheckmarxView(); + + injectDependencies(); + } + + @AfterEach + void tearDown() { + + platformUIMock.close(); + pluginUtilsMock.close(); + + if (shell != null && !shell.isDisposed()) { + display.syncExec(() -> shell.dispose()); + } + } + + @Test + void testConstructorInitializesFields() { + assertNotNull(checkmarxView); + } + + @Test + void testDisposeDoesNotThrow() { + assertDoesNotThrow(() -> checkmarxView.dispose()); + } + + @Test + void testSetFocusDoesNotThrow() { + assertDoesNotThrow(() -> checkmarxView.setFocus()); + } + + @Test + void testHandleEventWithEmptyApiKey() throws Exception { + + Event event = new Event("test/topic", new HashMap()); + + try (MockedStatic prefMock = + Mockito.mockStatic(Preferences.class, Mockito.CALLS_REAL_METHODS)) { + + prefMock.when(Preferences::getApiKey).thenReturn(""); + + assertDoesNotThrow(() -> + Display.getDefault().syncExec(() -> checkmarxView.handleEvent(event)) + ); + } + } + + @Test + void testHandleEventWithNonEmptyApiKey() { + + org.osgi.service.event.Event event = + new org.osgi.service.event.Event("test/topic", new HashMap<>()); + + try (MockedStatic prefMock = + Mockito.mockStatic(Preferences.class, Mockito.CALLS_REAL_METHODS)) { + + prefMock.when(Preferences::getApiKey).thenReturn("dummyApiKey"); + + assertDoesNotThrow(() -> + Display.getDefault().syncExec(() -> checkmarxView.handleEvent(event)) + ); + } + } + + @Test + void testStaticFieldsNotNull() { + + assertNotNull(CheckmarxView.ID); + assertNotNull(CheckmarxView.CHECKMARX_OPEN_SETTINGS_LOGO); + assertNotNull(CheckmarxView.CRITICAL_SEVERITY); + assertNotNull(CheckmarxView.HIGH_SEVERITY); + assertNotNull(CheckmarxView.MEDIUM_SEVERITY); + assertNotNull(CheckmarxView.LOW_SEVERITY); + assertNotNull(CheckmarxView.INFO_SEVERITY); + assertNotNull(CheckmarxView.USER); + assertNotNull(CheckmarxView.CREATED_AT_IMAGE); + assertNotNull(CheckmarxView.COMMENT); + assertNotNull(CheckmarxView.STATE); + assertNotNull(CheckmarxView.BFL); + } + + @Test + void testRemoveCount() throws Exception { + Method method = CheckmarxView.class.getDeclaredMethod("removeCount", String.class); + method.setAccessible(true); + + String result = (String) method.invoke(null, "High (5)"); + + assertEquals("High", result); + } + + @Test + void testGetLatestScanFromScanList() throws Exception { + + Scan scan1 = Mockito.mock(Scan.class); + Scan scan2 = Mockito.mock(Scan.class); + + List scans = Arrays.asList(scan1, scan2); + + Method method = CheckmarxView.class.getDeclaredMethod("getLatestScanFromScanList", List.class); + method.setAccessible(true); + + Scan result = (Scan) method.invoke(checkmarxView, scans); + + assertEquals(scan1, result); + } + + @Test + void testGetProjectFromIdFound() throws Exception { + + Project project = Mockito.mock(Project.class); + Mockito.when(project.getId()).thenReturn("123"); + Mockito.when(project.getName()).thenReturn("DemoProject"); + + List projects = List.of(project); + + Method method = CheckmarxView.class.getDeclaredMethod( + "getProjectFromId", List.class, String.class); + method.setAccessible(true); + + String result = (String) method.invoke(checkmarxView, projects, "123"); + + assertEquals("DemoProject", result); + } + + @Test + void testGetProjectFromIdNotFound() throws Exception { + + Project project = Mockito.mock(Project.class); + Mockito.when(project.getId()).thenReturn("123"); + + List projects = List.of(project); + + Method method = CheckmarxView.class.getDeclaredMethod( + "getProjectFromId", List.class, String.class); + method.setAccessible(true); + + String result = (String) method.invoke(checkmarxView, projects, "999"); + + assertEquals("Select a project", result); + } + + @Test + void testGetProjectFromIdEmptyList() throws Exception { + + Method method = CheckmarxView.class.getDeclaredMethod( + "getProjectFromId", List.class, String.class); + method.setAccessible(true); + + String result = (String) method.invoke(checkmarxView, Collections.emptyList(), "123"); + + assertEquals("No projects available.", result); + } + + @Test + void testFormatScanLabelNormal() throws Exception { + + Scan scan = Mockito.mock(Scan.class); + + Mockito.when(scan.getId()).thenReturn("scan123"); + Mockito.when(scan.getUpdatedAt()).thenReturn("2024-01-01T10:00:00Z"); + + Field latestScanField = CheckmarxView.class.getDeclaredField("latestScanId"); + latestScanField.setAccessible(true); + latestScanField.set(checkmarxView, "otherScan"); + + Method method = CheckmarxView.class.getDeclaredMethod("formatScanLabel", Scan.class); + method.setAccessible(true); + + String label = (String) method.invoke(checkmarxView, scan); + + assertTrue(label.contains("scan123")); + } + + @Test + void testFormatScanLabelLatest() throws Exception { + + Scan scan = Mockito.mock(Scan.class); + + Mockito.when(scan.getId()).thenReturn("scan999"); + Mockito.when(scan.getUpdatedAt()).thenReturn("2024-01-01T10:00:00Z"); + + Field latestScanField = CheckmarxView.class.getDeclaredField("latestScanId"); + latestScanField.setAccessible(true); + latestScanField.set(checkmarxView, "scan999"); + + Method method = CheckmarxView.class.getDeclaredMethod("formatScanLabel", Scan.class); + method.setAccessible(true); + + String label = (String) method.invoke(checkmarxView, scan); + + assertTrue(label.contains("latest")); + } + + @Test + void testGetProjectsSuccess() throws Exception { + + List projects = List.of(Mockito.mock(Project.class)); + + DataProvider provider = Mockito.mock(DataProvider.class); + Mockito.when(provider.getProjects()).thenReturn(projects); + + try (MockedStatic mocked = Mockito.mockStatic(DataProvider.class)) { + + mocked.when(DataProvider::getInstance).thenReturn(provider); + + Method method = CheckmarxView.class.getDeclaredMethod("getProjects"); + method.setAccessible(true); + + List result = (List) method.invoke(checkmarxView); + + assertEquals(1, result.size()); + } + } + + @Test + void testGetProjectsException() throws Exception { + + DataProvider provider = Mockito.mock(DataProvider.class); + + Mockito.when(provider.getProjects()).thenThrow(new RuntimeException("error")); + + try (MockedStatic mocked = Mockito.mockStatic(DataProvider.class)) { + + mocked.when(DataProvider::getInstance).thenReturn(provider); + + Method method = CheckmarxView.class.getDeclaredMethod("getProjects"); + method.setAccessible(true); + + List result = (List) method.invoke(checkmarxView); + + assertNotNull(result); + } + } + + @Test + void testUpdateStartScanButtonEnabled() throws Exception { + + ToolBarActions toolBarActions = Mockito.mock(ToolBarActions.class); + Action startAction = Mockito.mock(Action.class); + + Mockito.when(toolBarActions.getStartScanAction()).thenReturn(startAction); + + Field field = CheckmarxView.class.getDeclaredField("toolBarActions"); + field.setAccessible(true); + field.set(checkmarxView, toolBarActions); + + Method method = CheckmarxView.class.getDeclaredMethod("updateStartScanButton", boolean.class); + method.setAccessible(true); + + method.invoke(checkmarxView, true); + + Mockito.verify(startAction).setEnabled(Mockito.anyBoolean()); + } + + private void injectDependencies() throws Exception { + + ToolBarActions toolbar = Mockito.mock(ToolBarActions.class); + Action action = Mockito.mock(Action.class); + + Mockito.when(toolbar.getStartScanAction()).thenReturn(action); + + Field toolbarField = CheckmarxView.class.getDeclaredField("toolBarActions"); + toolbarField.setAccessible(true); + toolbarField.set(checkmarxView, toolbar); + + Field parentField = CheckmarxView.class.getDeclaredField("parent"); + parentField.setAccessible(true); + parentField.set(checkmarxView, parent); + } +} \ No newline at end of file diff --git a/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/DataProviderTest.java b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/DataProviderTest.java new file mode 100644 index 00000000..05ca4800 --- /dev/null +++ b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/DataProviderTest.java @@ -0,0 +1,206 @@ +package checkmarx.ast.eclipse.plugin.tests.unit.views; + +import com.checkmarx.eclipse.views.DataProvider; + +import checkmarx.ast.eclipse.plugin.tests.common.Environment; + +import com.checkmarx.ast.results.Results; +import com.checkmarx.ast.project.Project; +import com.checkmarx.ast.scan.Scan; +import com.checkmarx.ast.wrapper.CxWrapper; +import com.checkmarx.ast.predicate.Predicate; +import org.junit.jupiter.api.*; +import org.mockito.MockedConstruction; + +import java.util.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class DataProviderTest { + + protected static final String SCAN_ID = Environment.SCAN_ID; + private static final String TEST_PROJECT = "pedrompflopes/WebGoat"; + + + + DataProvider dataProvider; + + @BeforeEach + void setUp() { + dataProvider = DataProvider.getInstance(); + dataProvider.setCurrentResults(null); + dataProvider.setCurrentScanId(null); + } + + @Test + void testSingletonInstance() { + DataProvider instance1 = DataProvider.getInstance(); + DataProvider instance2 = DataProvider.getInstance(); + assertSame(instance1, instance2, "DataProvider should be singleton"); + } + + @Test + void testSetAndGetCurrentScanId() { + String scanId = "scan-123"; + dataProvider.setCurrentScanId(scanId); + assertEquals(scanId, dataProvider.getCurrentScanId()); + } + + @Test + void testSetAndGetCurrentResults() { + Results mockResults = mock(Results.class); + dataProvider.setCurrentResults(mockResults); + assertEquals(mockResults, dataProvider.getCurrentResults()); + } + + @Test + void testGetProjectsReturnsList() throws Exception { + List projects = dataProvider.getProjects(); + assertNotNull(projects); + } + + @Test + void testGetProjectsByNameReturnsList() throws Exception { + List projects = dataProvider.getProjects(TEST_PROJECT); + assertNotNull(projects); + } + + @Test + void testGetBranchesForProjectReturnsList() { + List branches = dataProvider.getBranchesForProject("e7478063-976c-4c79-b762-93074dabad24"); + assertNotNull(branches); + } + + @Test + void testGetScansForProjectReturnsList() { + List scans = dataProvider.getScansForProject("main"); + assertNotNull(scans); + } + + @Test + void testGetResultsForScanIdReturnsList() { + List results = dataProvider.getResultsForScanId(SCAN_ID); + assertNotNull(results); + } + + @Test + void testSortResultsReturnsList() { + List sorted = dataProvider.sortResults(); + assertNotNull(sorted); + } + + @Test + void testContainsResults() { + dataProvider.setCurrentResults(null); + assertFalse(dataProvider.containsResults()); + } + + @Test + void testGetTriageShowReturnsList() throws Exception { + List triage = dataProvider.getTriageShow(UUID.randomUUID(), "simId", "SAST"); + assertNotNull(triage); + } + + @Test + void testTriageUpdateDoesNotThrow() throws Exception { + + UUID projectId = UUID.randomUUID(); + String similarityId = "-930213981328"; + + try (MockedConstruction mocked = mockConstruction( + CxWrapper.class, + (mock, context) -> { + doNothing().when(mock).triageUpdate( + any(UUID.class), + anyString(), + anyString(), + anyString(), + anyString(), + anyString() + ); + })) { + DataProvider provider = DataProvider.getInstance(); + assertDoesNotThrow(() -> + provider.triageUpdate( + projectId, + similarityId, + "SAST", + "TO_VERIFY", + "comment", + "HIGH" + ) + ); + } + } + + @Test + void testContainsResultsTrue() { + + Results results = mock(Results.class); + + List list = new ArrayList<>(); + list.add(mock(com.checkmarx.ast.results.result.Result.class)); + + when(results.getResults()).thenReturn(list); + + dataProvider.setCurrentResults(results); + + assertTrue(dataProvider.containsResults()); + } + + @Test + void testGetStatesForEngineSAST() { + + List states = dataProvider.getStatesForEngine("SAST"); + + assertNotNull(states); + } + + @Test + void testGetStatesForEngineOther() { + + List states = dataProvider.getStatesForEngine("SCA"); + + assertNotNull(states); + } + + @Test + void testGetCustomStates() { + + List states = dataProvider.getCustomStates(); + + assertNotNull(states); + } + + @Test + void testGetResultsForScanIdInvalid() { + + List results = dataProvider.getResultsForScanId("invalid-uuid"); + + assertNotNull(results); + } + + @Test + void testGetBranchesForProjectEmpty() { + + List branches = dataProvider.getBranchesForProject(""); + + assertNotNull(branches); + } + + @Test + void testGetScansForProjectNullBranch() { + + List scans = dataProvider.getScansForProject(null); + + assertNotNull(scans); + } + + @Test + void testGetScanInformationException() { + + assertThrows(Exception.class, () -> { + dataProvider.getScanInformation("invalid-scan"); + }); + } +} \ No newline at end of file diff --git a/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/GlobalSettingsTest.java b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/GlobalSettingsTest.java new file mode 100644 index 00000000..d6e28e66 --- /dev/null +++ b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/GlobalSettingsTest.java @@ -0,0 +1,74 @@ +package checkmarx.ast.eclipse.plugin.tests.unit.views; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import com.checkmarx.eclipse.views.GlobalSettings; +import com.checkmarx.eclipse.views.filters.FilterState; + +class GlobalSettingsTest { + + @Test + void testSetAndGetProjectId() { + + GlobalSettings settings = new GlobalSettings(); + + settings.setProjectId("project1"); + + assertEquals("project1", settings.getProjectId()); + } + + @Test + void testSetAndGetBranch() { + + GlobalSettings settings = new GlobalSettings(); + + settings.setBranch("main"); + + assertEquals("main", settings.getBranch()); + } + + @Test + void testSetAndGetScanId() { + + GlobalSettings settings = new GlobalSettings(); + + settings.setScanId("scan123"); + + assertEquals("scan123", settings.getScanId()); + } + + @Test + void testStoreInPreferencesDoesNotThrow() { + + assertDoesNotThrow(() -> + GlobalSettings.storeInPreferences("test-key", "test-value") + ); + } + + @Test + void testGetFromPreferencesReturnsValue() { + + String value = GlobalSettings.getFromPreferences("non-existing", "default"); + + assertNotNull(value); + } + + @Test + void testLoadSettings() { + + GlobalSettings settings = new GlobalSettings(); + + try (MockedStatic filterMock = Mockito.mockStatic(FilterState.class)) { + + settings.loadSettings(); + + filterMock.verify(FilterState::loadFiltersFromSettings); + } + + assertNotNull(settings.getProjectId()); + assertNotNull(settings.getBranch()); + assertNotNull(settings.getScanId()); + } +} \ No newline at end of file diff --git a/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/actions/ToolBarActionsTest.java b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/actions/ToolBarActionsTest.java new file mode 100644 index 00000000..baa719af --- /dev/null +++ b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/actions/ToolBarActionsTest.java @@ -0,0 +1,177 @@ +package checkmarx.ast.eclipse.plugin.tests.unit.views.actions; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.viewers.ComboViewer; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.ui.IActionBars; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.checkmarx.eclipse.enums.PluginListenerType; +import com.checkmarx.eclipse.views.DisplayModel; +import com.checkmarx.eclipse.views.PluginListenerDefinition; +import com.checkmarx.eclipse.views.actions.ToolBarActions; +import com.checkmarx.eclipse.views.filters.FilterState; +import com.google.common.eventbus.EventBus; + +class ToolBarActionsTest { + + private IActionBars actionBars; + private IToolBarManager toolBarManager; + private IMenuManager menuManager; + + private TreeViewer resultsTree; + private ComboViewer projectCombo; + private ComboViewer branchCombo; + private ComboViewer scanCombo; + + private EventBus eventBus; + private DisplayModel rootModel; + + private ToolBarActions toolBarActions; + + @BeforeEach + void setup() { + + actionBars = mock(IActionBars.class); + toolBarManager = mock(IToolBarManager.class); + menuManager = mock(IMenuManager.class); + + resultsTree = mock(TreeViewer.class); + projectCombo = mock(ComboViewer.class); + branchCombo = mock(ComboViewer.class); + scanCombo = mock(ComboViewer.class); + + rootModel = mock(DisplayModel.class); + eventBus = new EventBus(); + + when(actionBars.getToolBarManager()).thenReturn(toolBarManager); + when(actionBars.getMenuManager()).thenReturn(menuManager); + + toolBarActions = + new ToolBarActions.ToolBarActionsBuilder() + .actionBars(actionBars) + .rootModel(rootModel) + .resultsTree(resultsTree) + .pluginEventBus(eventBus) + .projectsCombo(projectCombo) + .branchesCombo(branchCombo) + .scansCombo(scanCombo) + .build(); + } + + @Test + void testBuilderCreatesInstance() { + assertNotNull(toolBarActions); + } + + @Test + void testGetToolBarActions() { + List actions = toolBarActions.getToolBarActions(); + assertNotNull(actions); + } + + @Test + void testGetFilterActions() { + List filters = toolBarActions.getFilterActions(); + assertNotNull(filters); + } + + @Test + void testGetStartScanAction() { + Action action = toolBarActions.getStartScanAction(); + assertNotNull(action); + } + + @Test + void testGetCancelScanAction() { + Action action = toolBarActions.getCancelScanAction(); + assertNotNull(action); + } + + @Test + void testGetStateFilterAction() { + Action action = toolBarActions.getStateFilterAction(); + assertNotNull(action); + } + + @Test + void testDisposeToolbarRemovesAll() { + + toolBarActions.disposeToolbar(); + + verify(toolBarManager).removeAll(); + verify(menuManager).removeAll(); + } + + @Test + void testRefreshToolbarRecreatesActions() { + + toolBarActions.refreshToolbar(); + + verify(toolBarManager).removeAll(); + verify(menuManager).removeAll(); + } + + @Test + void testGroupBySeverityAction() { + + List actions = toolBarActions.getFilterActions(); + + for (Action action : actions) { + if ("GROUP_BY_SEVERITY".equals(action.getId())) { + action.run(); + break; + } + } + + assertTrue(FilterState.groupBySeverity); + } + + @Test + void testEventBusPostCleanRefresh() { + + assertDoesNotThrow(() -> { + eventBus.post( + new PluginListenerDefinition( + PluginListenerType.CLEAN_AND_REFRESH, + Collections.emptyList())); + }); + } + + @Test + void testBuilderSetsComboViewers() { + + ToolBarActions actions = + new ToolBarActions.ToolBarActionsBuilder() + .actionBars(actionBars) + .rootModel(rootModel) + .resultsTree(resultsTree) + .pluginEventBus(eventBus) + .projectsCombo(projectCombo) + .branchesCombo(branchCombo) + .scansCombo(scanCombo) + .build(); + + assertNotNull(actions); + } + + @Test + void testToolBarActionsListNotEmpty() { + + List actions = toolBarActions.getToolBarActions(); + + assertNotNull(actions); + assertTrue(actions.size() >= 0); + } + +} \ No newline at end of file diff --git a/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/filters/ActionFiltersTest.java b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/filters/ActionFiltersTest.java new file mode 100644 index 00000000..2f1ed8e5 --- /dev/null +++ b/checkmarx-ast-eclipse-plugin-tests/src/test/java/checkmarx/ast/eclipse/plugin/tests/unit/views/filters/ActionFiltersTest.java @@ -0,0 +1,104 @@ +package checkmarx.ast.eclipse.plugin.tests.unit.views.filters; + +import com.checkmarx.eclipse.views.filters.ActionFilters; +import com.checkmarx.eclipse.enums.ActionName; +import com.checkmarx.eclipse.enums.PluginListenerType; +import com.checkmarx.eclipse.enums.Severity; +import com.checkmarx.eclipse.views.DataProvider; +import com.checkmarx.eclipse.views.PluginListenerDefinition; +import com.google.common.eventbus.EventBus; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.resource.ImageDescriptor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class ActionFiltersTest { + private EventBus mockEventBus; + private ActionFilters actionFilters; + + @BeforeEach + void setUp() { + mockEventBus = mock(EventBus.class); + actionFilters = new ActionFilters(mockEventBus); + } + + @Test + void testCreateFilterActions_propertiesAndState() { + try (MockedStatic dp = Mockito.mockStatic(DataProvider.class); + MockedStatic fs = Mockito.mockStatic(com.checkmarx.eclipse.views.filters.FilterState.class); + MockedStatic activator = Mockito.mockStatic(com.checkmarx.eclipse.Activator.class)) { + + DataProvider provider = mock(DataProvider.class); + dp.when(DataProvider::getInstance).thenReturn(provider); + when(provider.containsResults()).thenReturn(true); + fs.when(() -> com.checkmarx.eclipse.views.filters.FilterState.isSeverityEnabled(anyString())).thenReturn(true); + activator.when(() -> com.checkmarx.eclipse.Activator.getImageDescriptor(anyString())).thenReturn(mock(ImageDescriptor.class)); + + List actions = actionFilters.createFilterActions(); + assertEquals(5, actions.size()); + assertEquals(ActionName.CRITICAL.name(), actions.get(0).getId()); + assertEquals(ActionName.HIGH.name(), actions.get(1).getId()); + assertEquals(ActionName.MEDIUM.name(), actions.get(2).getId()); + assertEquals(ActionName.LOW.name(), actions.get(3).getId()); + assertEquals(ActionName.INFO.name(), actions.get(4).getId()); + for (Action action : actions) { + assertTrue(action.isEnabled()); + assertTrue(action.isChecked()); + assertNotNull(action.getToolTipText()); + assertNotNull(action.getImageDescriptor()); + } + } + } + + @Test + void testCreateFilterActions_runActionPostsEvent() { + try (MockedStatic dp = Mockito.mockStatic(DataProvider.class); + MockedStatic fs = Mockito.mockStatic(com.checkmarx.eclipse.views.filters.FilterState.class); + MockedStatic activator = Mockito.mockStatic(com.checkmarx.eclipse.Activator.class); + MockedStatic pld = Mockito.mockStatic(PluginListenerDefinition.class, Mockito.CALLS_REAL_METHODS)) { + + DataProvider provider = mock(DataProvider.class); + dp.when(DataProvider::getInstance).thenReturn(provider); + when(provider.containsResults()).thenReturn(true); + fs.when(() -> com.checkmarx.eclipse.views.filters.FilterState.isSeverityEnabled(anyString())).thenReturn(true); + activator.when(() -> com.checkmarx.eclipse.Activator.getImageDescriptor(anyString())).thenReturn(mock(ImageDescriptor.class)); + when(provider.sortResults()).thenReturn(mock(List.class)); + + List actions = actionFilters.createFilterActions(); + Action criticalAction = actions.get(0); + criticalAction.run(); + ArgumentCaptor captor = ArgumentCaptor.forClass(PluginListenerDefinition.class); + verify(mockEventBus, atLeastOnce()).post(captor.capture()); + PluginListenerDefinition event = captor.getValue(); + assertEquals(PluginListenerType.FILTER_CHANGED, event.getListenerType()); + } + } + + @Test + void testCreateFilterActions_disabledUnchecked() { + try (MockedStatic dp = Mockito.mockStatic(DataProvider.class); + MockedStatic fs = Mockito.mockStatic(com.checkmarx.eclipse.views.filters.FilterState.class); + MockedStatic activator = Mockito.mockStatic(com.checkmarx.eclipse.Activator.class)) { + + DataProvider provider = mock(DataProvider.class); + dp.when(DataProvider::getInstance).thenReturn(provider); + when(provider.containsResults()).thenReturn(false); + fs.when(() -> com.checkmarx.eclipse.views.filters.FilterState.isSeverityEnabled(anyString())).thenReturn(false); + activator.when(() -> com.checkmarx.eclipse.Activator.getImageDescriptor(anyString())).thenReturn(mock(ImageDescriptor.class)); + + List actions = actionFilters.createFilterActions(); + for (Action action : actions) { + assertFalse(action.isEnabled()); + assertFalse(action.isChecked()); + } + } + } +} \ No newline at end of file diff --git a/checkmarx-ast-eclipse-plugin/.classpath b/checkmarx-ast-eclipse-plugin/.classpath index 3f4cbe16..aeee5342 100644 --- a/checkmarx-ast-eclipse-plugin/.classpath +++ b/checkmarx-ast-eclipse-plugin/.classpath @@ -10,10 +10,10 @@ - + - - + + @@ -28,4 +28,4 @@ - + \ No newline at end of file diff --git a/checkmarx-ast-eclipse-plugin/META-INF/MANIFEST.MF b/checkmarx-ast-eclipse-plugin/META-INF/MANIFEST.MF index 01496a44..c1550a00 100644 --- a/checkmarx-ast-eclipse-plugin/META-INF/MANIFEST.MF +++ b/checkmarx-ast-eclipse-plugin/META-INF/MANIFEST.MF @@ -27,8 +27,8 @@ Bundle-ClassPath: ., lib/slf4j-reload4j-2.0.17.jar, lib/slf4j-api-2.0.17.jar, lib/jackson-annotations-2.21.jar, - lib/jackson-core-2.21.0.jar, - lib/jackson-databind-2.21.0.jar, + lib/jackson-core-2.21.1.jar, + lib/jackson-databind-2.21.1.jar, lib/commons-lang3-3.18.0.jar, lib/ast-cli-java-wrapper-2.4.18.jar, lib/org.eclipse.mylyn.commons.ui_4.9.0.v20251121-0615.jar, diff --git a/checkmarx-ast-eclipse-plugin/build.properties b/checkmarx-ast-eclipse-plugin/build.properties index f7e88019..aa952954 100644 --- a/checkmarx-ast-eclipse-plugin/build.properties +++ b/checkmarx-ast-eclipse-plugin/build.properties @@ -6,11 +6,11 @@ bin.includes = plugin.xml,\ lib/slf4j-reload4j-2.0.17.jar,\ lib/slf4j-api-2.0.17.jar,\ lib/jackson-annotations-2.21.jar,\ - lib/jackson-core-2.21.0.jar,\ + lib/jackson-core-2.21.1.jar,\ lib/commons-lang3-3.18.0.jar,\ lib/ast-cli-java-wrapper-2.4.18.jar,\ lib/org.eclipse.mylyn.commons.ui_4.9.0.v20251121-0615.jar,\ - lib/jackson-databind-2.21.0.jar,\ + lib/jackson-databind-2.21.1.jar,\ .,\ lib/org-eclipse-mylyn-commons-core.jar source.. = src/ diff --git a/checkmarx-ast-eclipse-plugin/lib/jackson-core-2.21.0.jar b/checkmarx-ast-eclipse-plugin/lib/jackson-core-2.21.1.jar similarity index 71% rename from checkmarx-ast-eclipse-plugin/lib/jackson-core-2.21.0.jar rename to checkmarx-ast-eclipse-plugin/lib/jackson-core-2.21.1.jar index ae05d27a..e2cbe789 100644 Binary files a/checkmarx-ast-eclipse-plugin/lib/jackson-core-2.21.0.jar and b/checkmarx-ast-eclipse-plugin/lib/jackson-core-2.21.1.jar differ diff --git a/checkmarx-ast-eclipse-plugin/lib/jackson-databind-2.21.0.jar b/checkmarx-ast-eclipse-plugin/lib/jackson-databind-2.21.1.jar similarity index 88% rename from checkmarx-ast-eclipse-plugin/lib/jackson-databind-2.21.0.jar rename to checkmarx-ast-eclipse-plugin/lib/jackson-databind-2.21.1.jar index 358b3a42..6e7af882 100644 Binary files a/checkmarx-ast-eclipse-plugin/lib/jackson-databind-2.21.0.jar and b/checkmarx-ast-eclipse-plugin/lib/jackson-databind-2.21.1.jar differ diff --git a/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/properties/PreferencesPage.java b/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/properties/PreferencesPage.java index 6eba6440..49cb12d8 100644 --- a/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/properties/PreferencesPage.java +++ b/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/properties/PreferencesPage.java @@ -14,6 +14,7 @@ import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; @@ -24,6 +25,13 @@ import com.checkmarx.eclipse.utils.PluginConstants; import com.checkmarx.eclipse.utils.PluginUtils; import org.eclipse.swt.widgets.Link; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.browser.IWorkbenchBrowserSupport; +import org.eclipse.ui.PartInitException; + +import java.net.MalformedURLException; +import java.net.URL; + public class PreferencesPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { public PreferencesPage() { @@ -84,12 +92,25 @@ protected void createFieldEditors() { cliHelp.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false)); GridData linkGridData = new GridData(SWT.END, SWT.CENTER, true, false); cliHelp.setLayoutData(linkGridData); + cliHelp.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + IWorkbenchBrowserSupport browserSupport = PlatformUI.getWorkbench().getBrowserSupport(); + try { + browserSupport.getExternalBrowser().openURL(new URL(e.text)); + } catch (PartInitException | MalformedURLException e1) { + CxLogger.error("Failed to open CLI help documentation link.", e1); + e1.printStackTrace(); + } + } + }); addField(space()); - Text connectionLabel = new Text(getFieldEditorParent(), SWT.MULTI | SWT.WRAP | SWT.READ_ONLY | SWT.V_SCROLL); - //Set layout for scroll area to fit to page - connectionLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + Label connectionLabel = new Label(getFieldEditorParent(), SWT.WRAP); + connectionLabel.setLayoutData( + new GridData(SWT.FILL, SWT.CENTER, true, false) + ); Button connectionButton = new Button(topComposite, SWT.PUSH); connectionButton.setText(PluginConstants.PREFERENCES_TEST_CONNECTION); @@ -138,4 +159,4 @@ public boolean performOk() { return ok; } -} +} \ No newline at end of file diff --git a/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/views/CheckmarxView.java b/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/views/CheckmarxView.java index 01bdef63..fcbef7c1 100644 --- a/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/views/CheckmarxView.java +++ b/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/views/CheckmarxView.java @@ -1096,6 +1096,10 @@ public void handleEvent(Event event) { @Override public String getText(Object element) { if (element instanceof Scan) { + // Always fetch the latest scan id from preferences before rendering + if(!GlobalSettings.getFromPreferences("LATEST_SCAN_ID", "").isEmpty()) { + latestScanId = GlobalSettings.getFromPreferences("LATEST_SCAN_ID", ""); + } Scan scan = (Scan) element; return formatScanLabel(scan); } @@ -1201,15 +1205,29 @@ private Scan getLatestScanFromScanList(List scanList) { * on the chosen scan id */ private void setSelectionForProjectComboViewer() { - String scanId = scanIdComboViewer.getCombo().getText(); + String scanIdText = scanIdComboViewer.getCombo().getText().trim(); + + String[] parts = scanIdText.split("\\s+"); + if (parts.length >= 3) { + scanIdText = parts[2]; + } + + final String scanId = scanIdText; if(scanId.isEmpty()) { PluginUtils.clearMessage(rootModel, resultsTree); + PluginUtils.showMessage(rootModel, resultsTree, PluginConstants.NO_SCAN_ID_PROVIDED); CxLogger.info(String.format(PluginConstants.NO_SCAN_ID_PROVIDED, scanId)); return; } if (currentScanId.equals(scanId)) { + PluginUtils.clearMessage(rootModel, resultsTree); + // reload cached results + List results = DataProvider.getInstance().sortResults(); + + rootModel.setChildren(results); + resultsTree.refresh(); PluginUtils.setTextForComboViewer(scanIdComboViewer, currentScanIdFormmated); CxLogger.info(String.format(PluginConstants.INFO_RESULTS_ALREADY_RETRIEVED, scanId)); return; @@ -2805,4 +2823,4 @@ private void preservCaretposition( List projectList, String searchText) PluginUtils.setTextForComboViewer(projectComboViewer, searchText); projectComboViewer.getCombo().setSelection(new Point(caretPos, caretPos)); } -} \ No newline at end of file +} diff --git a/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/views/actions/ActionStartScan.java b/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/views/actions/ActionStartScan.java index 58154692..43a5cb9c 100644 --- a/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/views/actions/ActionStartScan.java +++ b/checkmarx-ast-eclipse-plugin/src/com/checkmarx/eclipse/views/actions/ActionStartScan.java @@ -334,6 +334,25 @@ private Runnable pollingScan(String scanId) { startScanAction.setEnabled(true); if (scan.getStatus().toLowerCase(Locale.ROOT).equals(PluginConstants.CX_SCAN_COMPLETED_STATUS)) { + // Automatically update UI with new scan result + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + // Fetch the latest scan list for the branch + List scanList = DataProvider.getInstance().getScansForProject(branchesCombo.getCombo().getText()); + // Set the new scan as latest by updating preferences + GlobalSettings.storeInPreferences("LATEST_SCAN_ID", scanId); + // Set the scan list in the combo viewer + scansCombo.setInput(scanList); + // Set the new scan as selected + scansCombo.getCombo().setText(scanId); + // Post event to load results for the new scan + pluginEventBus.post(new PluginListenerDefinition( + PluginListenerType.LOAD_RESULTS_FOR_SCAN, + Collections.emptyList())); + } + }); + // Show notification as before Display.getDefault().syncExec(new Runnable() { AbstractNotificationPopup notification; @@ -347,8 +366,8 @@ PluginConstants.CX_LOAD_SCAN_RESULTS, new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { scansCombo.getCombo().setText(scanId); pluginEventBus.post(new PluginListenerDefinition( - PluginListenerType.LOAD_RESULTS_FOR_SCAN, - Collections.emptyList())); + PluginListenerType.LOAD_RESULTS_FOR_SCAN, + Collections.emptyList())); notification.close(); } }); diff --git a/pom.xml b/pom.xml index 2b001c19..ddc93a88 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,8 @@ ${tycho.version} - + org.eclipse.m2e @@ -74,6 +75,7 @@ + @@ -98,7 +100,8 @@ true - -warn:+discouraged,+forbidden,-unavoidableGenericProblems,-warningToken + + -warn:+discouraged,+forbidden,-unavoidableGenericProblems,-warningToken @@ -115,7 +118,7 @@ ${project.artifactId}_${unqualifiedVersion}.${buildQualifier} - + @@ -145,10 +148,45 @@ x86_64 - + + + org.jacoco + jacoco-maven-plugin + 0.8.11 + + + + merge + verify + + merge + + + + + . + + **/target/jacoco.exec + + + + ${project.build.directory}/jacoco.exec + + + + + report + verify + + report-aggregate + + + + + @@ -164,9 +202,10 @@ https://download.eclipse.org/technology/swtbot/releases/latest - orbit-4.32 - p2 - https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/release/4.32.0 + orbit-4.32 + p2 + + https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/release/4.32.0 mylyn @@ -186,5 +225,5 @@ org.osgi.service.event 1.4.1 - +