Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/47 visualize latest pitest report #95

Merged
merged 10 commits into from
Dec 5, 2023
29 changes: 14 additions & 15 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
1 change: 0 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,3 @@ jobs:
VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
lheimbs marked this conversation as resolved.
Show resolved Hide resolved

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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
}
}
Expand All @@ -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)
Expand Down Expand Up @@ -80,4 +78,4 @@ class HighlightGutterRenderer(color: String): GutterIconRenderer() {
return 16
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
}
2 changes: 1 addition & 1 deletion pitmutationmate/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
Read more: https://plugins.jetbrains.com/docs/intellij/plugin-extension-points.html -->
<extensions defaultExtensionNs="com.intellij">

<toolWindow id="Pitest Result" secondary="false" icon="AllIcons.Toolwindows.WebToolWindow" anchor="right"
<toolWindow id="Pitest" secondary="false" icon="AllIcons.Toolwindows.WebToolWindow" anchor="right"
factoryClass="com.amos.pitmutationmate.pitmutationmate.MutationTestToolWindowFactory"/>

<runLineMarkerContributor implementationClass="com.amos.pitmutationmate.pitmutationmate.actions.GutterMarker"
Expand Down