diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index b9d4ef3c..faa3d6eb 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -14,20 +14,19 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 11 - - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 11 - - name: Build with Gradle - run: ./gradlew build --no-daemon - working-directory: 'pitmutationmate' - - - name: Run Tests - run: ./gradlew test --no-daemon - working-directory: 'pitmutationmate' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + - name: Build with Gradle + run: ./gradlew build --no-daemon + working-directory: "pitmutationmate" + + - name: Run Tests + run: ./gradlew test --no-daemon + working-directory: "pitmutationmate" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3d747a45..6a2389ec 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -31,4 +31,3 @@ jobs: VALIDATE_ALL_CODEBASE: false DEFAULT_BRANCH: main GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - diff --git a/README.md b/README.md index a78489e1..71dcec77 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ SPDX-FileCopyrightText: 2023 ## About the Project [PIT (Pitest)](https://pitest.org/) is a popular mutation testing framework for Java / JVM-based languages. -This project is to develop a plugin for the [IntelliJ](https://www.jetbrains.com/idea/) / [Android Studio (Jetbrains)](https://developer.android.com/studio) IDE so that mutation tests can be conveniently run from within the IDE, similar to the JUnit plugins available for most IDEs. +This project is to develop a plugin for the [IntelliJ](https://www.jetbrains.com/idea/) / [Android Studio (JetBrains)](https://developer.android.com/studio) IDE so that mutation tests can be conveniently run from within the IDE, similar to the JUnit plugins available for most IDEs. Users of the IDE shall be able to - Start a Pitest run for a class directly in the code editor @@ -48,6 +48,6 @@ Dynamic Test Configuration: A core feature of our plugin will be to enable dynam Result Visualization: The plugin will provide visualizations of Mutation Testing results. This will make it more comfortable for developers to interpret PiTest outputs. -User-Centric Design: The interface and functionality of the plugin will be designed with a strong focus on user experience, ensuring that it is both powerful and easy to use. +User-Centric Design: The interface and functionality of the plugin will be designed with a strong focus on user experience, ensuring that it is both powerful and easy to use. By following these steps, we aim to not only enhance PiTest’s functionality within IntelliJ IDE but also empower developers with more efficient, precise, and user-friendly software testing tools, ultimately leading to higher quality software development. diff --git a/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/MutationTestToolWindowFactory.kt b/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/MutationTestToolWindowFactory.kt index 117289b2..85de683a 100644 --- a/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/MutationTestToolWindowFactory.kt +++ b/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/MutationTestToolWindowFactory.kt @@ -3,7 +3,7 @@ package com.amos.pitmutationmate.pitmutationmate import com.amos.pitmutationmate.pitmutationmate.visualization.BarGraph -import com.amos.pitmutationmate.pitmutationmate.visualization.CustomProgressBar +import com.amos.pitmutationmate.pitmutationmate.visualization.LatestPiTestReport import com.amos.pitmutationmate.pitmutationmate.visualization.LineGraph import com.amos.pitmutationmate.pitmutationmate.visualization.treetable.JTreeTable import com.intellij.openapi.project.DumbAware @@ -12,18 +12,16 @@ import com.intellij.openapi.wm.ToolWindow import com.intellij.openapi.wm.ToolWindowFactory import com.intellij.ui.content.ContentFactory - internal class MutationTestToolWindowFactory : ToolWindowFactory, DumbAware { override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { - val progressBar = ContentFactory.getInstance().createContent(CustomProgressBar(), "Progressbar", false) + val latestPiTestReport = ContentFactory.getInstance().createContent(LatestPiTestReport(), "Latest Result", false) val table = ContentFactory.getInstance().createContent(JTreeTable(), "Mutationtest Coverage", false) val lineChart = ContentFactory.getInstance().createContent(LineGraph(), "Line Chart", false) val barChart = ContentFactory.getInstance().createContent(BarGraph(), "Bar Chart", false) - toolWindow.contentManager.addContent(progressBar) + toolWindow.contentManager.addContent(latestPiTestReport) toolWindow.contentManager.addContent(table) toolWindow.contentManager.addContent(lineChart) toolWindow.contentManager.addContent(barChart) - } } diff --git a/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/visualization/CustomProgressBar.kt b/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/visualization/CustomProgressBar.kt index 7affe15e..b790bb02 100644 --- a/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/visualization/CustomProgressBar.kt +++ b/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/visualization/CustomProgressBar.kt @@ -5,36 +5,70 @@ package com.amos.pitmutationmate.pitmutationmate.visualization import java.awt.Color +import java.awt.Font +import java.awt.FontMetrics import java.awt.Graphics import javax.swing.JComponent -import javax.swing.JPanel -import javax.swing.JProgressBar -import javax.swing.plaf.basic.BasicProgressBarUI - -internal class CustomProgressBar : JPanel() { - - private val progressBar = JProgressBar(0, 100) - - init { - //UIManager.put("ProgressBar.background", Color.ORANGE); - //UIManager.put("ProgressBar.foreground", Color.BLUE); - progressBar.setStringPainted(true) - //progressBar.setValue(0) - progressBar.setBackground(Color.RED) - progressBar.setForeground(Color.GREEN) - //progressBar.setString("49%") - progressBar.setValue(35) - progressBar.setUI(object : BasicProgressBarUI() { - override fun paint(g: Graphics, c: JComponent) { - c.foreground = Color.GREEN - c.background = Color.RED - g.color = Color.BLACK - super.paint(g, c) - } - }) - this.add(progressBar) - //this.isVisible = true; - - //UIManager.put("nimbusOrange", Color(38, 139, 210)) + +internal class CustomProgressBar(coveragePercentage: Int, ratioText: String) : JComponent() { + private val coveragePercentage = coveragePercentage + private val ratioText = ratioText + private val font = Font("Arial", Font.BOLD, 12) // You can adjust the font as needed + private val barWidth = 150 + private val barHeight = 20 + private val spaceBetweenTextAndBar = 5 + + override fun getWidth(): Int { + return barWidth + getTextWidth(ratioText) + spaceBetweenTextAndBar + } + + override fun getHeight(): Int { + return barHeight + } + + override fun paintComponent(g: Graphics) { + super.paintComponent(g) + + val greenWidth = (coveragePercentage * barWidth) / 100 + val redWidth = barWidth - greenWidth + + // add space between ratio Text and the bar + var ratioTextStartX = barWidth + spaceBetweenTextAndBar + + drawText(g, ratioText, ratioTextStartX) + + // Draw grey border + g.color = Color(170, 170, 170) + g.drawRect(0, 0, barWidth - 1, barHeight - 1) + + // Draw green segment + g.color = Color(221, 255, 221) + g.fillRect(1, 1, greenWidth, barHeight - 2) + + // Draw red segment + g.color = Color(255, 170, 170) + g.fillRect(greenWidth + 1, 1, redWidth - 2, barHeight - 2) + + val percentageText = "$coveragePercentage%" + drawText(g, percentageText, (barWidth - getTextWidth(percentageText)) / 2) + } + + private fun getTextWidth(text: String): Int { + return getFontMetrics(font).stringWidth(text) + } + + private fun drawText(g: Graphics, text: String, x: Int): Int { + g.font = font + + val fontMetrics: FontMetrics = g.fontMetrics + val textWidth: Int = fontMetrics.stringWidth(text) + val textHeight: Int = fontMetrics.height + + val y = (barHeight - textHeight) / 2 + fontMetrics.ascent + + g.color = Color.BLACK // Set the color for the text + g.drawString(text, x, y) + + return textWidth } } diff --git a/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/visualization/HighlightGutterRenderer.kt b/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/visualization/HighlightGutterRenderer.kt index 9a62c8ab..25846f43 100644 --- a/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/visualization/HighlightGutterRenderer.kt +++ b/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/visualization/HighlightGutterRenderer.kt @@ -8,8 +8,7 @@ import java.awt.Component import java.awt.Graphics import javax.swing.Icon - -class HighlightGutterRenderer(color: String): GutterIconRenderer() { +class HighlightGutterRenderer(color: String) : GutterIconRenderer() { private val toolTip = "PITest run" val toolTipProvider: (PsiElement) -> String = { _ -> toolTip } val color: String = color @@ -24,9 +23,9 @@ class HighlightGutterRenderer(color: String): GutterIconRenderer() { override fun getIcon(): Icon { return if (this.color == "red") { RedBarIcon() - }else if (this.color == "green") { + } else if (this.color == "green") { GreenBarIcon() - }else { + } else { YellowBarIcon() } } @@ -35,7 +34,6 @@ class HighlightGutterRenderer(color: String): GutterIconRenderer() { return Alignment.LEFT } - private class RedBarIcon : Icon { override fun paintIcon(c: Component?, g: Graphics, x: Int, y: Int) { g.setColor(Color.RED) @@ -80,4 +78,4 @@ class HighlightGutterRenderer(color: String): GutterIconRenderer() { return 16 } } -} \ No newline at end of file +} diff --git a/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/visualization/LatestPiTestReport.kt b/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/visualization/LatestPiTestReport.kt new file mode 100644 index 00000000..7fe98f1d --- /dev/null +++ b/pitmutationmate/src/main/kotlin/com/amos/pitmutationmate/pitmutationmate/visualization/LatestPiTestReport.kt @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2023 2023 +// +// SPDX-License-Identifier: MIT + +package com.amos.pitmutationmate.pitmutationmate.visualization + +import com.intellij.ui.components.JBLabel +import com.intellij.ui.components.JBScrollPane +import com.intellij.ui.table.JBTable +import com.intellij.util.ui.JBUI +import java.awt.BorderLayout +import java.awt.Component +import java.awt.Dimension +import java.awt.Font +import javax.swing.JPanel +import javax.swing.JTable +import javax.swing.ListSelectionModel +import javax.swing.table.DefaultTableCellRenderer +import javax.swing.table.DefaultTableModel + +class LatestPiTestReport : JPanel() { + + init { + val lineCoverageBar = CustomProgressBar(30, "1/5") + val mutationCoverageBar = CustomProgressBar(50, "3000/30000") + val testStrengthBar = CustomProgressBar(93, "200/2000") + + val data = arrayOf( + arrayOf(getLabel("Class Name"), getLabel("Test.java")), + arrayOf(getLabel("Line Coverage"), lineCoverageBar), + arrayOf(getLabel("Mutation Coverage"), mutationCoverageBar), + arrayOf(getLabel("Test Strength"), testStrengthBar) + ) + + val columnNames = arrayOf("Pit Test Coverage Report", "") + + val model = object : DefaultTableModel(data, columnNames) { + override fun isCellEditable(row: Int, column: Int): Boolean { + return false + } + } + + val table = JBTable(model) + + table.setRowHeight(lineCoverageBar.height + 2) + table.tableHeader.reorderingAllowed = false + table.tableHeader.resizingAllowed = false + table.tableHeader.font = Font("Arial", Font.BOLD, 16) + + table.border = JBUI.Borders.empty() + table.setShowGrid(false) + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION) + table.columnSelectionAllowed = false + table.intercellSpacing = Dimension(0, 0) + + table.columnModel.getColumn(0).cellRenderer = CustomProgressBarRenderer() + + val firstColumnWidth = table.tableHeader.getFontMetrics(table.tableHeader.font).stringWidth(" Pit Test Coverage Report ") + 5 + table.columnModel.getColumn(0).maxWidth = firstColumnWidth + table.columnModel.getColumn(0).minWidth = firstColumnWidth + table.columnModel.getColumn(0).preferredWidth = firstColumnWidth + table.columnModel.getColumn(0).width = firstColumnWidth + + table.columnModel.getColumn(1).cellRenderer = CustomProgressBarRenderer() + + layout = BorderLayout() + + add(JBScrollPane(table)) + } + + private fun getLabel(text: String): JBLabel { + val label = JBLabel(text) + label.font = Font("Arial", Font.BOLD, 12) + return label + } + + private class CustomProgressBarRenderer : DefaultTableCellRenderer() { + + override fun getTableCellRendererComponent( + table: JTable?, + value: Any?, + isSelected: Boolean, + hasFocus: Boolean, + row: Int, + column: Int + ): Component { + if (value is CustomProgressBar) { + return value + } else if (value is JBLabel) { + return value + } else { + return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column) + } + } + } +} diff --git a/pitmutationmate/src/main/resources/META-INF/plugin.xml b/pitmutationmate/src/main/resources/META-INF/plugin.xml index 1a94a00d..b5b16a4c 100644 --- a/pitmutationmate/src/main/resources/META-INF/plugin.xml +++ b/pitmutationmate/src/main/resources/META-INF/plugin.xml @@ -31,7 +31,7 @@ Read more: https://plugins.jetbrains.com/docs/intellij/plugin-extension-points.html --> -