├── plugin
├── testdata
│ ├── EmptyFile.java
│ ├── EmptyClass.java
│ ├── traceFiles
│ │ ├── PointTest,test.tr
│ │ └── BadPointTest,test.tr
│ ├── TwoTestClassesTest.java
│ ├── EmptyTest.java
│ ├── Math.java
│ ├── Point.java
│ ├── BrokenTernary.java
│ ├── Math2.java
│ ├── MixedTestAndNoTestMethods.java
│ ├── expected
│ │ ├── AssertionSuggestionServiceTest.testGetAssertionsReturnsNothing.java
│ │ ├── AssertionSuggestionServiceTest.testGetAssertionsOnReturnType.java
│ │ ├── AssertionSuggestionServiceTest.testGetAssertionsWithSideEffects.java
│ │ ├── Dijkstra.testDijkstraTest.java
│ │ ├── DuplicateTestsServiceTest.testDuplicateMethod.java
│ │ └── DuplicateTestsServiceTest.testDuplicateMethodUnderCaret.java
│ ├── BrokenClass.java
│ ├── TestDuplication.java
│ ├── Tests.java
│ ├── Dijkstra.java
│ ├── PointTest.java
│ ├── PointTestFullAnnotations.java
│ └── AnimalTest.java
├── src
│ ├── main
│ │ ├── resources
│ │ │ ├── intentionDescriptions
│ │ │ │ ├── Trace test
│ │ │ │ │ ├── after.java.template
│ │ │ │ │ ├── before.java.template
│ │ │ │ │ └── description.html
│ │ │ │ └── Assertion suggestions
│ │ │ │ │ ├── before.java.template
│ │ │ │ │ ├── after.java.template
│ │ │ │ │ └── description.html
│ │ │ ├── messages
│ │ │ │ ├── ServerBundle.properties
│ │ │ │ ├── TestMethodGenerationMessageBundle.properties
│ │ │ │ ├── AssertionSuggestionMessageBundle.properties
│ │ │ │ └── TestingChecklistMessageBundle.properties
│ │ │ ├── icons
│ │ │ │ └── pluginIcon.svg
│ │ │ └── META-INF
│ │ │ │ └── pluginIcon.svg
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── testknight
│ │ │ ├── models
│ │ │ ├── AssertionSuggestion.kt
│ │ │ ├── TestCoverageData.kt
│ │ │ ├── CoverageStatsObject.kt
│ │ │ ├── testingChecklist
│ │ │ │ ├── TestingChecklist.kt
│ │ │ │ └── leafNodes
│ │ │ │ │ ├── CustomChecklistNode.kt
│ │ │ │ │ ├── loopStatements
│ │ │ │ │ ├── DoWhileStatementChecklistNode.kt
│ │ │ │ │ ├── ForLoopStatementChecklistNode.kt
│ │ │ │ │ ├── WhileStatementChecklistNode.kt
│ │ │ │ │ └── ForEachStatementChecklistNode.kt
│ │ │ │ │ ├── ConditionChecklistNode.kt
│ │ │ │ │ ├── ThrowStatementChecklistNode.kt
│ │ │ │ │ ├── ParameterChecklistNode.kt
│ │ │ │ │ └── branchingStatements
│ │ │ │ │ ├── TryStatementChecklistNode.kt
│ │ │ │ │ └── SwitchStatementChecklistNode.kt
│ │ │ ├── ChecklistUserObject.kt
│ │ │ ├── TestClassData.kt
│ │ │ ├── TestMethodData.kt
│ │ │ ├── TestMethodUserObject.kt
│ │ │ ├── ActionData.kt
│ │ │ ├── CoverageDiffObject.kt
│ │ │ ├── UsageData.kt
│ │ │ ├── sideEffectAnalysis
│ │ │ │ └── Class.kt
│ │ │ ├── TruthTable.kt
│ │ │ └── HighlightedTextData.kt
│ │ │ ├── exceptions
│ │ │ ├── ProjectNotFoundException.kt
│ │ │ ├── TestKnightException.kt
│ │ │ ├── NoTestCoverageDataException.kt
│ │ │ ├── DocumentNotFoundException.kt
│ │ │ ├── TraceFileNotFoundException.kt
│ │ │ ├── CorruptedTraceFileException.kt
│ │ │ ├── InvalidActionIdException.kt
│ │ │ ├── InvalidVirtualFileException.kt
│ │ │ ├── InvalidConfigurationException.kt
│ │ │ └── InvalidTreePathException.kt
│ │ │ ├── listeners
│ │ │ ├── AppClosedListener.kt
│ │ │ ├── TestLoggingListener.kt
│ │ │ ├── TestKnightExecutionListener.kt
│ │ │ ├── InitializationListener.kt
│ │ │ ├── FileEditorListener.kt
│ │ │ ├── checklist
│ │ │ │ ├── ClassChecklistIconHandler.kt
│ │ │ │ ├── MethodChecklistIconHandler.kt
│ │ │ │ ├── CheckListKeyboardListener.kt
│ │ │ │ ├── CheckedNodeListener.kt
│ │ │ │ └── ChecklistMouseListener.kt
│ │ │ └── testlist
│ │ │ │ └── TestListSelectionListener.kt
│ │ │ ├── utilities
│ │ │ ├── PsiConverter.kt
│ │ │ └── StringFormatter.kt
│ │ │ ├── checklistGenerationStrategies
│ │ │ ├── leafStrategies
│ │ │ │ ├── LeafChecklistGeneratorStrategy.kt
│ │ │ │ └── ThrowStatementChecklistGenerationStrategy.kt
│ │ │ ├── parentStrategies
│ │ │ │ ├── ParentChecklistGeneratorStrategy.kt
│ │ │ │ └── ClassChecklistGenerationStrategy.kt
│ │ │ └── ChecklistGeneratorStrategy.kt
│ │ │ ├── actions
│ │ │ ├── diffcoverage
│ │ │ │ ├── HideDiffCovHighlights.kt
│ │ │ │ └── RefreshDiffHighlights.kt
│ │ │ ├── HideTracedTestHighlightsAction.kt
│ │ │ ├── settings
│ │ │ │ ├── EditElementAction.kt
│ │ │ │ ├── DeleteElementAction.kt
│ │ │ │ ├── AddClassAction.kt
│ │ │ │ └── AddTypeAction.kt
│ │ │ ├── checklist
│ │ │ │ ├── ClearChecklistAction.kt
│ │ │ │ ├── ChecklistClassLineMarkerProvider.kt
│ │ │ │ ├── ChecklistMethodLineMarkerProvider.kt
│ │ │ │ ├── DeleteElementChecklistAction.kt
│ │ │ │ └── GenerateChecklistUnderCaretAction.kt
│ │ │ └── testlist
│ │ │ │ ├── ClearTestAction.kt
│ │ │ │ └── DuplicateTestUnderCaretAction.kt
│ │ │ ├── messageBundleHandlers
│ │ │ ├── ServerMessageBundleHandler.kt
│ │ │ ├── TestingChecklistMessageBundleHandler.kt
│ │ │ ├── AssertionSuggestionMessageBundleHandler.kt
│ │ │ └── TestMethodGenerationMessageBundleHandler.kt
│ │ │ ├── highlightResolutionStrategies
│ │ │ ├── MagicNumberStrategy.kt
│ │ │ ├── ConstructorArgsStrategy.kt
│ │ │ ├── HighlightResolutionStrategy.kt
│ │ │ └── AssertionArgsStrategy.kt
│ │ │ ├── services
│ │ │ ├── GotoTestService.kt
│ │ │ ├── TestAnalyzerService.kt
│ │ │ ├── checklist
│ │ │ │ └── ChecklistTreePersistent.kt
│ │ │ ├── ExceptionHandlerService.kt
│ │ │ ├── LoadTestsService.kt
│ │ │ ├── TestMethodGenerationService.kt
│ │ │ └── GlobalHighlighter.kt
│ │ │ ├── views
│ │ │ ├── UserInterfaceFactory.kt
│ │ │ └── coverage
│ │ │ │ └── CoverageStatsCellRenderer.kt
│ │ │ └── extensions
│ │ │ ├── DiffCoverageLineMarkerRenderer.kt
│ │ │ ├── TestKnightTestCase.kt
│ │ │ └── DiffCoverageListener.kt
│ └── test
│ │ └── kotlin
│ │ └── com
│ │ └── testknight
│ │ ├── models
│ │ ├── ActionDataTest.kt
│ │ ├── UsageDataTest.kt
│ │ └── TruthTableTest.kt
│ │ ├── highlightResolutionStrategies
│ │ ├── MagicNumberStrategyTest.kt
│ │ ├── AssertionArgsStrategyTest.kt
│ │ └── ConstructorArgsStrategyTest.kt
│ │ ├── services
│ │ ├── TestAnalyzerServiceTest.kt
│ │ ├── TestTracingServiceTest.kt
│ │ └── UsageDataServiceTest.kt
│ │ └── checklistGenerationStrategies
│ │ ├── TestKnightJavaPsiVisitorTest.kt
│ │ └── leafStrategies
│ │ ├── ThrowStatementChecklistGenerationStrategyTest.kt
│ │ ├── loopStatements
│ │ └── ForEachStatementChecklistGenerationStrategyTest.kt
│ │ └── branchingStatements
│ │ └── IfStatementChecklistGenerationStrategyTest.kt
├── CHANGELOG.md
└── gradle.properties
├── .gitignore
├── server
├── config
│ ├── checkstyle
│ │ └── checkstyle-suppressions.xml
│ ├── spotbugs
│ │ └── excludeFilter.xml
│ └── pmd
│ │ └── pmd.xml
├── lombok.config
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── testknight
│ │ │ │ └── TestKnightTelemetryServer
│ │ │ │ ├── security
│ │ │ │ ├── Hasher.java
│ │ │ │ └── Md5Hasher.java
│ │ │ │ ├── repositories
│ │ │ │ ├── ActionRepository.java
│ │ │ │ └── UsageRecordRepository.java
│ │ │ │ ├── dataTransferObjects
│ │ │ │ ├── responses
│ │ │ │ │ ├── UsageDataAddedDto.java
│ │ │ │ │ └── ExceptionDto.java
│ │ │ │ └── requests
│ │ │ │ │ ├── RequestDto.java
│ │ │ │ │ ├── ActionEventDto.java
│ │ │ │ │ └── UsageDataDto.java
│ │ │ │ ├── exceptions
│ │ │ │ ├── ValidationException.java
│ │ │ │ ├── InvalidActionIdException.java
│ │ │ │ ├── InvalidHashException.java
│ │ │ │ └── NullFieldException.java
│ │ │ │ ├── domain
│ │ │ │ ├── model
│ │ │ │ │ ├── Action.java
│ │ │ │ │ └── UsageRecord.java
│ │ │ │ └── factories
│ │ │ │ │ └── UsageRecordFactory.java
│ │ │ │ ├── TestKnightTelemetryServerApplication.java
│ │ │ │ ├── validation
│ │ │ │ ├── BaseValidator.java
│ │ │ │ ├── RequestValidator.java
│ │ │ │ ├── HashValidator.java
│ │ │ │ └── ContentValidator.java
│ │ │ │ └── controllers
│ │ │ │ └── UsageDataController.java
│ │ └── resources
│ │ │ ├── application.properties
│ │ │ └── populate_database.sql
│ └── test
│ │ ├── java
│ │ └── com
│ │ │ └── testknight
│ │ │ └── TestKnightTelemetryServer
│ │ │ ├── SmokeTest.java
│ │ │ ├── domain
│ │ │ ├── model
│ │ │ │ └── ActionTest.java
│ │ │ └── factories
│ │ │ │ └── UsageRecordFactoryTest.java
│ │ │ └── validation
│ │ │ └── ContentValidatorTest.java
│ │ └── resources
│ │ ├── application.properties
│ │ └── import.sql
├── .gitignore
└── build.gradle
├── screenshots
├── diff-cov.png
├── test-list.gif
├── diff-window.png
├── traceability.gif
├── duplicate-test.gif
├── testing-checklist.png
└── assertion-suggestions.gif
├── settings.gradle.kts
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── documentation
├── design
│ ├── detailedSystemArchitecture.jpg
│ └── telemetryDesign
│ │ ├── clientStates.png
│ │ ├── databaseDesign.jpg
│ │ ├── ActionIds.md
│ │ └── testKnightTelemtryApi.yaml
└── mockups
│ ├── Checklist
│ ├── ChecklistEditor.png
│ ├── ChecklistSideBar.png
│ └── DetailedChecklist.md
│ ├── CodeCoverage
│ ├── CoverageDiffView.png
│ ├── CoverageFullWindowView.png
│ └── DetailedCodeCoverage.md
│ └── AssertionSuggestion
│ ├── AssertionSuggestionOverallUI.png
│ └── DetailedAssertionSuggestionMockup.md
├── .github
└── workflows
│ ├── server.yml
│ └── plugin.yml
├── .run
├── Run IDE with Plugin.run.xml
├── Run Plugin Tests.run.xml
└── Run Plugin Verification.run.xml
└── LICENSE
/plugin/testdata/EmptyFile.java:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | .idea
3 | build
4 | out
5 |
--------------------------------------------------------------------------------
/server/config/checkstyle/checkstyle-suppressions.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/lombok.config:
--------------------------------------------------------------------------------
1 | config.stopbubbling = true
2 | lombok.addLombokGeneratedAnnotation = true
--------------------------------------------------------------------------------
/plugin/testdata/EmptyClass.java:
--------------------------------------------------------------------------------
1 | public class EmptyClass {
2 | //There is nothing here!
3 | }
--------------------------------------------------------------------------------
/screenshots/diff-cov.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/screenshots/diff-cov.png
--------------------------------------------------------------------------------
/screenshots/test-list.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/screenshots/test-list.gif
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "TestKnight"
2 |
3 | include("plugin")
4 | include("server")
5 |
--------------------------------------------------------------------------------
/screenshots/diff-window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/screenshots/diff-window.png
--------------------------------------------------------------------------------
/screenshots/traceability.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/screenshots/traceability.gif
--------------------------------------------------------------------------------
/screenshots/duplicate-test.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/screenshots/duplicate-test.gif
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/screenshots/testing-checklist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/screenshots/testing-checklist.png
--------------------------------------------------------------------------------
/server/config/spotbugs/excludeFilter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/intentionDescriptions/Trace test/after.java.template:
--------------------------------------------------------------------------------
1 | public void setY(int y) {
2 | this.y = y;
3 | }
--------------------------------------------------------------------------------
/screenshots/assertion-suggestions.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/screenshots/assertion-suggestions.gif
--------------------------------------------------------------------------------
/server/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/server/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/plugin/testdata/traceFiles/PointTest,test.tr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/plugin/testdata/traceFiles/PointTest,test.tr
--------------------------------------------------------------------------------
/plugin/testdata/traceFiles/BadPointTest,test.tr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/plugin/testdata/traceFiles/BadPointTest,test.tr
--------------------------------------------------------------------------------
/plugin/src/main/resources/messages/ServerBundle.properties:
--------------------------------------------------------------------------------
1 | debugUrl=localhost:8080
2 | hashString=exampleMagicString
3 | usageData=http://{0}/usagedata
4 |
--------------------------------------------------------------------------------
/documentation/design/detailedSystemArchitecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/documentation/design/detailedSystemArchitecture.jpg
--------------------------------------------------------------------------------
/documentation/design/telemetryDesign/clientStates.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/documentation/design/telemetryDesign/clientStates.png
--------------------------------------------------------------------------------
/documentation/mockups/Checklist/ChecklistEditor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/documentation/mockups/Checklist/ChecklistEditor.png
--------------------------------------------------------------------------------
/documentation/mockups/Checklist/ChecklistSideBar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/documentation/mockups/Checklist/ChecklistSideBar.png
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/AssertionSuggestion.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | data class AssertionSuggestion(var message: String)
4 |
--------------------------------------------------------------------------------
/documentation/design/telemetryDesign/databaseDesign.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/documentation/design/telemetryDesign/databaseDesign.jpg
--------------------------------------------------------------------------------
/documentation/mockups/CodeCoverage/CoverageDiffView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/documentation/mockups/CodeCoverage/CoverageDiffView.png
--------------------------------------------------------------------------------
/documentation/mockups/CodeCoverage/CoverageFullWindowView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/documentation/mockups/CodeCoverage/CoverageFullWindowView.png
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/exceptions/ProjectNotFoundException.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.exceptions
2 |
3 | class ProjectNotFoundException : Exception("Project not found")
4 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/intentionDescriptions/Assertion suggestions/before.java.template:
--------------------------------------------------------------------------------
1 | @Test
2 | void setXTest(){
3 | Point p = new Point(0, 0);
4 |
5 | p.setX(5);
6 | }
--------------------------------------------------------------------------------
/documentation/mockups/AssertionSuggestion/AssertionSuggestionOverallUI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SERG-Delft/testknight/HEAD/documentation/mockups/AssertionSuggestion/AssertionSuggestionOverallUI.png
--------------------------------------------------------------------------------
/plugin/src/main/resources/intentionDescriptions/Trace test/before.java.template:
--------------------------------------------------------------------------------
1 | @Test
2 | void setYTest2() {
3 | Point p = new Point(0, 0);
4 |
5 | p.setY(5);
6 |
7 | assertEquals(5, p.getY());
8 | }
9 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/TestCoverageData.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | class TestCoverageData(val testName: String) {
4 | val classes: MutableMap> = mutableMapOf()
5 | }
6 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/security/Hasher.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.security;
2 |
3 | public interface Hasher {
4 |
5 | public String hash(String message);
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/CoverageStatsObject.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | data class CoverageStatsObject(
4 | val coveredLines: Int,
5 | val allLines: Int,
6 | val percentageCovered: Int,
7 | val percentChange: Int
8 | )
9 |
--------------------------------------------------------------------------------
/server/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/intentionDescriptions/Assertion suggestions/after.java.template:
--------------------------------------------------------------------------------
1 | @Test
2 | void setXTest(){
3 | Point p = new Point(0, 0);
4 |
5 | p.setX(5);
6 |
7 | /**
8 | * Assert that "this.x" is re-assigned properly.
9 | */
10 |
11 | }
--------------------------------------------------------------------------------
/plugin/testdata/TwoTestClassesTest.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | public class FirstTest {
4 | @Test
5 | public void firstTest() {
6 | assertEquals(1,1);
7 | }
8 | }
9 |
10 | public class SecondTest {
11 | @Test
12 | public void secondTest() {
13 | assertEquals(2,2);
14 | }
15 | }
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/exceptions/TestKnightException.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.exceptions
2 |
3 | import com.intellij.notification.NotificationType
4 |
5 | abstract class TestKnightException : Exception() {
6 | abstract var title: String
7 | abstract var content: String
8 | abstract var type: NotificationType
9 | }
10 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/intentionDescriptions/Assertion suggestions/description.html:
--------------------------------------------------------------------------------
1 | This intention action generates the assertion suggestions for the selected testing method.
2 | Assertion suggestions are generated as comments at the bottom of the test method.
3 | Assertion suggestions can only be generated in test methods.
4 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/repositories/ActionRepository.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.repositories;
2 |
3 | import com.testknight.TestKnightTelemetryServer.domain.model.*;
4 | import org.springframework.data.repository.*;
5 |
6 | public interface ActionRepository extends CrudRepository {
7 | }
8 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/listeners/AppClosedListener.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.listeners
2 |
3 | import com.intellij.ide.AppLifecycleListener
4 | import com.testknight.services.UsageDataService
5 |
6 | class AppClosedListener : AppLifecycleListener {
7 |
8 | override fun appWillBeClosed(isRestart: Boolean) = UsageDataService.instance.sendUserData()
9 | }
10 |
--------------------------------------------------------------------------------
/plugin/testdata/EmptyTest.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.BeforeAll;
5 | import org.junit.jupiter.api.AfterAll;
6 | import org.junit.jupiter.params.ParameterizedTest;
7 | import org.junit.jupiter.params.provider.CsvSource;
8 |
9 | import static org.junit.jupiter.api.Assertions.*;
10 |
11 | public class EmptyTest {
12 |
13 | }
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/security/Md5Hasher.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.security;
2 |
3 |
4 | import org.apache.commons.codec.digest.*;
5 |
6 | public class Md5Hasher implements Hasher {
7 |
8 | @Override
9 | public String hash(String message) {
10 | return DigestUtils.md5Hex(message);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/dataTransferObjects/responses/UsageDataAddedDto.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.dataTransferObjects.responses;
2 |
3 | import lombok.*;
4 |
5 | @Data
6 | @AllArgsConstructor
7 | @NoArgsConstructor
8 | public class UsageDataAddedDto {
9 | private String message = "Successfully added usage data";
10 | }
11 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/testingChecklist/TestingChecklist.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.testingChecklist
2 |
3 | import com.testknight.models.testingChecklist.parentNodes.TestingChecklistClassNode
4 |
5 | data class TestingChecklist(var classChecklists: MutableList = mutableListOf())
6 |
7 | open class TestingChecklistNode(open var checked: Int = 0)
8 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/models/ActionDataTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import org.junit.Test
4 | import kotlin.test.assertEquals
5 |
6 | class ActionDataTest {
7 |
8 | @Test
9 | fun testHashString() {
10 | val actionData = ActionData("actionId")
11 | assertEquals(actionData.toHashString(), "actionId${actionData.dateTime}")
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/plugin/testdata/Math.java:
--------------------------------------------------------------------------------
1 | public class Math {
2 |
3 | public int add(int a, int b) {
4 | return a + b;
5 | }
6 |
7 | public int sub(int a, int b) {
8 | return a - b;
9 | }
10 |
11 | public int mult(int a, int b) {
12 | return a * b;
13 | }
14 |
15 | public double div(int a, int b) {
16 | return (double) a / b;
17 | }
18 |
19 |
20 | }
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/dataTransferObjects/responses/ExceptionDto.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.dataTransferObjects.responses;
2 |
3 | import lombok.*;
4 |
5 | @Data
6 | @AllArgsConstructor
7 | @NoArgsConstructor
8 | public class ExceptionDto {
9 |
10 | private Integer errorCode;
11 | private String message;
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/plugin/testdata/Point.java:
--------------------------------------------------------------------------------
1 | class Point {
2 |
3 | int x;
4 | int y;
5 |
6 | public Point(int x, int y) {
7 | this.x = x;
8 | this.y = y;
9 | }
10 |
11 | public void move(int x, int y) {
12 | this.x = x;
13 | this.y = y;
14 | }
15 |
16 | public void translate(int dx, int dy) {
17 | x += dx;
18 | y += dy;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/exceptions/ValidationException.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.exceptions;
2 |
3 | public class ValidationException extends RuntimeException {
4 |
5 | public static final long serialVersionUID = 2336261092312323L;
6 |
7 | public ValidationException(String message) {
8 | super(message);
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/exceptions/NoTestCoverageDataException.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.exceptions
2 |
3 | import com.intellij.notification.NotificationType
4 |
5 | class NoTestCoverageDataException : TestKnightException() {
6 | override var title: String = "No Test Coverage Data"
7 | override var content: String = ""
8 | override var type: NotificationType = NotificationType.ERROR
9 | }
10 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/domain/model/Action.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.domain.model;
2 |
3 | import lombok.*;
4 |
5 | import javax.persistence.*;
6 |
7 | @Data
8 | @AllArgsConstructor
9 | @NoArgsConstructor
10 | @Entity
11 | @Table(name = "ACTIONS")
12 | public class Action {
13 | @Id
14 | @Column(name = "action_Id")
15 | private String actionId;
16 | }
17 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/exceptions/DocumentNotFoundException.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.exceptions
2 |
3 | import com.intellij.notification.NotificationType
4 |
5 | class DocumentNotFoundException : TestKnightException() {
6 | override var title: String = "Document not found"
7 | override var content: String = "Cannot found document for the file"
8 | override var type: NotificationType = NotificationType.ERROR
9 | }
10 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/exceptions/TraceFileNotFoundException.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.exceptions
2 |
3 | import com.intellij.notification.NotificationType
4 |
5 | class TraceFileNotFoundException : TestKnightException() {
6 | override var content: String = "Could not find coverage data"
7 | override var title: String = "Trace File not found:"
8 | override var type: NotificationType = NotificationType.ERROR
9 | }
10 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/utilities/PsiConverter.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.utilities
2 |
3 | import com.intellij.psi.PsiElement
4 | import com.intellij.util.xmlb.Converter
5 |
6 | class PsiConverter : Converter() {
7 | override fun toString(value: PsiElement): String? {
8 | return "null"
9 | }
10 |
11 | override fun fromString(value: String): PsiElement? {
12 | return null
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/ChecklistUserObject.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import com.testknight.models.testingChecklist.TestingChecklistNode
4 |
5 | /**
6 | * Custom user object which gets passed into checkListTree which contains useful information used by the renderer.
7 | *
8 | * @param checklistNode Information regarding the node.
9 | */
10 | data class ChecklistUserObject(var checklistNode: TestingChecklistNode)
11 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/exceptions/CorruptedTraceFileException.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.exceptions
2 |
3 | import com.intellij.notification.NotificationType
4 |
5 | class CorruptedTraceFileException(ex: Exception) : TestKnightException() {
6 |
7 | override var content: String = ex.message.toString()
8 | override var title: String = "Failed to read trace file"
9 | override var type: NotificationType = NotificationType.ERROR
10 | }
11 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/exceptions/InvalidActionIdException.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.exceptions
2 |
3 | import com.intellij.notification.NotificationType
4 |
5 | class InvalidActionIdException(actionId: String) : TestKnightException() {
6 | override var title: String = "Invalid Action ID"
7 | override var content: String = "$actionId is not a valid action ID"
8 | override var type: NotificationType = NotificationType.ERROR
9 | }
10 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/exceptions/InvalidActionIdException.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.exceptions;
2 |
3 | public class InvalidActionIdException extends ValidationException {
4 |
5 | public static final long serialVersionUID = 927626852988215140L;
6 |
7 |
8 | public InvalidActionIdException(String actionId) {
9 | super(actionId + " is not a valid actionId");
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/exceptions/InvalidHashException.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.exceptions;
2 |
3 | public class InvalidHashException extends ValidationException {
4 |
5 | public static final long serialVersionUID = 12162635991005290L;
6 |
7 | public InvalidHashException() {
8 | super("The hash included in the request does not come from a verified client");
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/dataTransferObjects/requests/RequestDto.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.dataTransferObjects.requests;
2 |
3 | public interface RequestDto {
4 |
5 | /**
6 | * Creates a string representation
7 | * of the DTO that is suitable for
8 | * hashing.
9 | *
10 | * @return the string that can be used
11 | * to generate a hash.
12 | */
13 | public String toHashString();
14 | }
15 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/messages/TestMethodGenerationMessageBundle.properties:
--------------------------------------------------------------------------------
1 | switchTestCaseName=testSwitchStatement
2 | tryTestCaseName=testTryStatement
3 | doWhileTestCaseName=testDoWhileLoop
4 | forEachTestCaseName=testForEachLoop
5 | forLoopTestCaseName=testForLoop
6 | whileLoopTestCaseName=testWhileLoop
7 | conditionTestCaseName=testCondition
8 | parameterTestCaseName=testParameterValue
9 | throwTestCaseName=testThrows
10 | customNodeTestCaseName=testCustomCase
11 | testMethodComment=Generated based on: {0}
--------------------------------------------------------------------------------
/plugin/testdata/BrokenTernary.java:
--------------------------------------------------------------------------------
1 | //A class containing compilation errors
2 | // for testing incorrect ternary operator usage
3 |
4 | class BrokenClass {
5 |
6 | private int a = 24;
7 | private int b = 42;
8 |
9 | public int incompleteConditionalExpression(){
10 | int a=5;
11 | int b=10;
12 | int c = null ? a:b;
13 | return c;
14 | }
15 |
16 | public int conditionalExpressionWithLiteral() {
17 | return true ? a : b;
18 | }
19 |
20 |
21 |
22 | }
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/repositories/UsageRecordRepository.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.repositories;
2 |
3 | import com.testknight.TestKnightTelemetryServer.domain.model.*;
4 | import org.springframework.data.repository.*;
5 | import org.springframework.stereotype.Repository;
6 |
7 | import javax.transaction.*;
8 |
9 | @Transactional
10 | @Repository
11 | public interface UsageRecordRepository extends CrudRepository {
12 | }
13 |
--------------------------------------------------------------------------------
/server/src/test/java/com/testknight/TestKnightTelemetryServer/SmokeTest.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class SmokeTest {
8 |
9 | /**
10 | * This test ensures that the sprint context
11 | * starts without errors.
12 | */
13 | @Test
14 | @SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert")
15 | void contextLoads() {
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/checklistGenerationStrategies/leafStrategies/LeafChecklistGeneratorStrategy.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.checklistGenerationStrategies.leafStrategies
2 |
3 | import com.intellij.psi.PsiElement
4 | import com.testknight.checklistGenerationStrategies.ChecklistGeneratorStrategy
5 | import com.testknight.models.testingChecklist.leafNodes.TestingChecklistLeafNode
6 |
7 | interface LeafChecklistGeneratorStrategy : ChecklistGeneratorStrategy>
8 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/messages/AssertionSuggestionMessageBundle.properties:
--------------------------------------------------------------------------------
1 | fieldReassignment=Assert that attribute "{0}" is re-assigned properly.
2 | classFieldModification=Assert that method "{0}" modifies field "{1}" properly.
3 | transitiveFieldReassignment=Assert that field "{0}" of the object at field "{1}" is re-assigned properly.
4 | methodParameterModification=Assert that method "{0}" modifies argument "{1}" properly.
5 | parameterFieldReassignment=Assert that field "{0}" of argument "{1}" is re-assigned properly.
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/exceptions/InvalidVirtualFileException.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.exceptions
2 |
3 | import com.intellij.notification.NotificationType
4 |
5 | class InvalidVirtualFileException(className: String, invalidReason: String) : TestKnightException() {
6 | override var title: String = "Virtual file is invalid"
7 | override var content: String = "Invalid virtual file for class $className. Reason: $invalidReason"
8 | override var type: NotificationType = NotificationType.ERROR
9 | }
10 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/listeners/TestLoggingListener.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.listeners
2 |
3 | import com.intellij.execution.testframework.AbstractTestProxy
4 | import com.intellij.execution.testframework.TestStatusListener
5 | import com.testknight.services.UsageDataService
6 |
7 | class TestLoggingListener : TestStatusListener() {
8 |
9 | override fun testSuiteFinished(root: AbstractTestProxy?) {
10 | root ?: return
11 | UsageDataService.instance.logTests(root)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/TestClassData.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import com.intellij.psi.PsiClass
4 |
5 | /**
6 | * Contains information about a test class.
7 | *
8 | * @param name the name of the test class
9 | * @param methods list of TestMethodData objects representing the methods in the class
10 | * @param psiClass the reference to the actual class in the PSI tree
11 | */
12 | data class TestClassData(val name: String, val methods: List, val psiClass: PsiClass)
13 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/TestKnightTelemetryServerApplication.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class TestKnightTelemetryServerApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(TestKnightTelemetryServerApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/checklistGenerationStrategies/parentStrategies/ParentChecklistGeneratorStrategy.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.checklistGenerationStrategies.parentStrategies
2 |
3 | import com.intellij.psi.PsiElement
4 | import com.testknight.checklistGenerationStrategies.ChecklistGeneratorStrategy
5 | import com.testknight.models.testingChecklist.parentNodes.TestingChecklistParentNode
6 |
7 | interface ParentChecklistGeneratorStrategy<
8 | E : PsiElement,
9 | G : TestingChecklistParentNode> : ChecklistGeneratorStrategy
10 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/exceptions/InvalidConfigurationException.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.exceptions
2 |
3 | import com.intellij.notification.NotificationType
4 |
5 | class InvalidConfigurationException(private val property: String, private val invalidConfiguration: String) :
6 | TestKnightException() {
7 | override var title: String = "Invalid Configuration"
8 | override var content: String = "Property $property cannot be set to $invalidConfiguration"
9 | override var type: NotificationType = NotificationType.ERROR
10 | }
11 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/diffcoverage/HideDiffCovHighlights.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.diffcoverage
2 |
3 | import com.intellij.openapi.actionSystem.AnAction
4 | import com.intellij.openapi.actionSystem.AnActionEvent
5 | import com.intellij.openapi.components.service
6 | import com.testknight.services.CoverageHighlighterService
7 |
8 | class HideDiffCovHighlights : AnAction() {
9 |
10 | override fun actionPerformed(e: AnActionEvent) {
11 | e.project?.service()?.removeHighlights()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/diffcoverage/RefreshDiffHighlights.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.diffcoverage
2 |
3 | import com.intellij.openapi.actionSystem.AnAction
4 | import com.intellij.openapi.actionSystem.AnActionEvent
5 | import com.intellij.openapi.components.service
6 | import com.testknight.services.CoverageHighlighterService
7 |
8 | class RefreshDiffHighlights : AnAction() {
9 |
10 | override fun actionPerformed(e: AnActionEvent) {
11 | e.project?.service()?.rebuildHighlights()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/exceptions/NullFieldException.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.exceptions;
2 |
3 | public class NullFieldException extends ValidationException {
4 |
5 | public static final long serialVersionUID = 2567201903468237233L;
6 |
7 | /**
8 | * Constructs a new NullFieldException
9 | *
10 | * @param nameOfField the name of the null field.
11 | */
12 | public NullFieldException(String nameOfField) {
13 | super("Field " + nameOfField + " cannot be null");
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/exceptions/InvalidTreePathException.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.exceptions
2 |
3 | import com.intellij.notification.NotificationType
4 |
5 | /**
6 | * Invalid tree path exception.
7 | * Doesn't need to be notified to the user (In case ParameterSuggestion tree throws this exception).
8 | */
9 | class InvalidTreePathException(reason: String) : TestKnightException() {
10 | override var title: String = "Invalid Tree Path"
11 | override var content: String = "Reason: $reason"
12 | override var type: NotificationType = NotificationType.ERROR
13 | }
14 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/messageBundleHandlers/ServerMessageBundleHandler.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.messageBundleHandlers
2 |
3 | import com.intellij.AbstractBundle
4 | import org.jetbrains.annotations.NonNls
5 | import org.jetbrains.annotations.PropertyKey
6 |
7 | @NonNls
8 | private const val BUNDLE = "messages.ServerBundle"
9 |
10 | object ServerMessageBundleHandler : AbstractBundle(BUNDLE) {
11 |
12 | @Suppress("SpreadOperator")
13 | @JvmStatic
14 | fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) =
15 | getMessage(key, *params)
16 | }
17 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/listeners/TestKnightExecutionListener.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.listeners
2 |
3 | import com.intellij.execution.ExecutionListener
4 | import com.intellij.execution.process.ProcessHandler
5 | import com.intellij.execution.runners.ExecutionEnvironment
6 | import com.testknight.services.UsageDataService
7 |
8 | class TestKnightExecutionListener : ExecutionListener {
9 |
10 | override fun processStarted(executorId: String, env: ExecutionEnvironment, handler: ProcessHandler) {
11 | if (executorId == "Coverage") UsageDataService.instance.recordRunWithCoverage()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 | bin/
17 | !**/src/main/**/bin/
18 | !**/src/test/**/bin/
19 |
20 | ### IntelliJ IDEA ###
21 | .idea
22 | *.iws
23 | *.iml
24 | *.ipr
25 | out/
26 | !**/src/main/**/out/
27 | !**/src/test/**/out/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
--------------------------------------------------------------------------------
/plugin/testdata/Math2.java:
--------------------------------------------------------------------------------
1 | public class Math2 {
2 |
3 | public void add(){
4 | int [] a = {0, 1}
5 | for(Int i : a){
6 | print(a[i])
7 | }
8 | }
9 |
10 | public void add2(){
11 | int [] a = {0, 1}
12 | for(Int i : a){
13 | print(a[i])
14 | }
15 | }
16 |
17 | // public int sub(int a, int b) {
18 | // return a - b;
19 | // }
20 | //
21 | // public int mult(int a, int b) {
22 | // return a * b;
23 | // }
24 | //
25 | // public double div(int a, int b) {
26 | // return (double) a / b;
27 | // }
28 |
29 |
30 | }
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/checklistGenerationStrategies/ChecklistGeneratorStrategy.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.checklistGenerationStrategies
2 |
3 | import com.intellij.psi.PsiElement
4 |
5 | interface ChecklistGeneratorStrategy {
6 |
7 | /**
8 | * Generates the checklist of all tests that should be created for a given PsiElement.
9 | *
10 | * @param psiElement the PsiElement for which the checklist should be generated.
11 | * @return a list of TestingChecklistItem objects corresponding to suggested test cases.
12 | */
13 | fun generateChecklist(psiElement: E): G
14 | }
15 |
--------------------------------------------------------------------------------
/plugin/testdata/MixedTestAndNoTestMethods.java:
--------------------------------------------------------------------------------
1 | public class MixedTestAndNoTestMethods {
2 |
3 | @Test
4 | public void test() {
5 | getAnInt();
6 | doNothing();
7 | }
8 |
9 | @Test
10 | public void testMethod() {
11 | String noun = "Testing";
12 | String adverb = "awesome";
13 | methodUnderTest(noun, adverb);
14 | }
15 |
16 | public void methodUnderTest(String a, String b) {
17 | a.append(" is ", b);
18 | }
19 |
20 | public int getAnInt() {
21 | return 1;
22 | }
23 |
24 | public void doNothing() {
25 | return;
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/server/src/test/resources/application.properties:
--------------------------------------------------------------------------------
1 | #This file contains the Spring configuration to be used when running tests
2 | server.port=8080
3 | #H2 Configurations
4 | spring.datasource.url=jdbc:h2:mem:testdb
5 | spring.datasource.driverClassName=org.h2.Driver
6 | spring.datasource.username=sa
7 | spring.datasource.password=
8 | spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
9 | spring.h2.console.enabled=true
10 | spring.jpa.hibernate.ddl-auto=create-drop
11 | spring.sql.init.enabled=false
12 | #Set those to "true" for debugging
13 | spring.jpa.show-sql=true
14 | spring.jpa.properties.hibernate.format_sql=true
15 |
16 |
17 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/messageBundleHandlers/TestingChecklistMessageBundleHandler.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.messageBundleHandlers
2 |
3 | import com.intellij.AbstractBundle
4 | import org.jetbrains.annotations.NonNls
5 | import org.jetbrains.annotations.PropertyKey
6 |
7 | @NonNls
8 | private const val BUNDLE = "messages.TestingChecklistMessageBundle"
9 |
10 | object TestingChecklistMessageBundleHandler : AbstractBundle(BUNDLE) {
11 |
12 | @Suppress("SpreadOperator")
13 | @JvmStatic
14 | fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) =
15 | getMessage(key, *params)
16 | }
17 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/HideTracedTestHighlightsAction.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions
2 |
3 | import com.intellij.openapi.actionSystem.AnAction
4 | import com.intellij.openapi.actionSystem.AnActionEvent
5 | import com.intellij.openapi.components.service
6 | import com.testknight.services.TestTracingService
7 |
8 | class HideTracedTestHighlightsAction : AnAction() {
9 |
10 | override fun actionPerformed(e: AnActionEvent) {
11 | val project = e.project ?: return
12 | val testTracingService = project.service()
13 |
14 | testTracingService.removeHighlights()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/TestMethodData.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import com.intellij.psi.PsiMethod
4 |
5 | /**
6 | * The TestMethodData class contains information about a test method.
7 | *
8 | * @param name the name of the test method.
9 | * @param testClassName the name of the class the method is part of.
10 | * If for some reason the method is not part of a class this will be the empty string.
11 | * @param psiMethod the reference to the actual method as part of the PsiTree.
12 | */
13 | data class TestMethodData(val name: String, val testClassName: String, val psiMethod: PsiMethod)
14 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/messageBundleHandlers/AssertionSuggestionMessageBundleHandler.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.messageBundleHandlers
2 |
3 | import com.intellij.AbstractBundle
4 | import org.jetbrains.annotations.NonNls
5 | import org.jetbrains.annotations.PropertyKey
6 |
7 | @NonNls
8 | private const val BUNDLE = "messages.AssertionSuggestionMessageBundle"
9 |
10 | object AssertionSuggestionMessageBundleHandler : AbstractBundle(BUNDLE) {
11 |
12 | @Suppress("SpreadOperator")
13 | @JvmStatic
14 | fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) =
15 | getMessage(key, *params)
16 | }
17 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/messageBundleHandlers/TestMethodGenerationMessageBundleHandler.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.messageBundleHandlers
2 |
3 | import com.intellij.AbstractBundle
4 | import org.jetbrains.annotations.NonNls
5 | import org.jetbrains.annotations.PropertyKey
6 |
7 | @NonNls
8 | private const val BUNDLE = "messages.TestMethodGenerationMessageBundle"
9 |
10 | object TestMethodGenerationMessageBundleHandler : AbstractBundle(BUNDLE) {
11 |
12 | @Suppress("SpreadOperator")
13 | @JvmStatic
14 | fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) =
15 | getMessage(key, *params)
16 | }
17 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/TestMethodUserObject.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import com.intellij.openapi.editor.Editor
4 | import com.intellij.openapi.project.Project
5 |
6 | /**
7 | * The user object which is stored inside the node of the test case tree.
8 | * Contains additional information such as the current project and current editor (which can be null)
9 | *
10 | * @param reference reference to the TestMethodData
11 | * @param project Current project, can be null.
12 | * @param editor Current editor, can be null.
13 | */
14 | data class TestMethodUserObject(val reference: TestMethodData, val project: Project?, val editor: Editor?)
15 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/intentionDescriptions/Trace test/description.html:
--------------------------------------------------------------------------------
1 | Using this intention action you can quickly inspect the lines covered by a specific method invocation. If you have Run with Coverage before, this intention action will take you to the method declaration and highlight the covered lines. This relies on information collected during the latest coverage with tracing run.
2 | Therefore for it to work properly you first have to enable tracing by going to Run -> Edit Configurations -> Code coverage and select the Tracing option. Afterward, you have to run the test-suite containing the invocation with Coverage.
3 |
--------------------------------------------------------------------------------
/plugin/testdata/expected/AssertionSuggestionServiceTest.testGetAssertionsReturnsNothing.java:
--------------------------------------------------------------------------------
1 | public class MixedTestAndNoTestMethods {
2 |
3 | @Test
4 | public void test() {
5 | getAnInt();
6 | doNothing();
7 | }
8 |
9 | @Test
10 | public void testMethod() {
11 | String noun = "Testing";
12 | String adverb = "awesome";
13 | methodUnderTest(noun, adverb);
14 | }
15 |
16 | public void methodUnderTest(String a, String b) {
17 | a.append(" is ", b);
18 | }
19 |
20 | public int getAnInt() {
21 | return 1;
22 | }
23 |
24 | public void doNothing() {
25 | return;
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/ActionData.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import java.time.LocalDateTime
4 | import java.time.format.DateTimeFormatter
5 |
6 | /**
7 | * Represents a logged action.
8 | *
9 | * @param actionId the action logged
10 | */
11 | data class ActionData(
12 | val actionId: String,
13 | val dateTime: String = DateTimeFormatter.ISO_DATE_TIME.format(LocalDateTime.now())
14 | ) {
15 |
16 | /**
17 | * Creates a string representation
18 | * of the DTO that is suitable for
19 | * hashing.
20 | *
21 | * @return the string that can be used
22 | * to generate a hash.
23 | */
24 | fun toHashString() = "$actionId$dateTime"
25 | }
26 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/models/UsageDataTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import com.testknight.extensions.TestKnightTestCase
4 | import com.testknight.settings.SettingsService
5 | import org.junit.Test
6 |
7 | class UsageDataTest : TestKnightTestCase() {
8 |
9 | @Test
10 | fun testToHashString() {
11 | val userId = SettingsService.instance.state.userId
12 | val a1 = ActionData("action1")
13 | val a2 = ActionData("action2")
14 | val usageData = UsageData(listOf(a1, a2))
15 | val hashString = usageData.toHashString()
16 | val expected = "${userId}${a1.toHashString()}${a2.toHashString()}"
17 | assertEquals(hashString, expected)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/server/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | #This file contains the Spring configuration to be used when running in production
2 | server.port=8080
3 | #MySql Configurations
4 | #spring.datasource.url=jdbc:mysql://databaseServerUrlHere
5 | #spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
6 | #spring.datasource.username=databaseUsernameHere
7 | #spring.datasource.password=databaseUserPasswordHere
8 | #spring.jpa.hibernate.ddl-auto=update
9 | spring.datasource.url=
10 | spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
11 | spring.datasource.username=
12 | spring.datasource.password=
13 | spring.jpa.hibernate.ddl-auto=update
14 | #Set those to "true" for debugging
15 | spring.jpa.show-sql=false
16 | spring.jpa.properties.hibernate.format_sql=false
17 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/highlightResolutionStrategies/MagicNumberStrategy.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.highlightResolutionStrategies
2 |
3 | import com.intellij.psi.PsiLiteralExpression
4 | import com.intellij.psi.PsiMethod
5 | import com.intellij.psi.util.PsiTreeUtil
6 | import com.testknight.models.HighlightedTextData
7 |
8 | object MagicNumberStrategy : HighlightResolutionStrategy {
9 |
10 | override val priority: Int = 1
11 |
12 | override val settingsName = "Highlight literals"
13 |
14 | override fun getElements(psiMethod: PsiMethod): List {
15 | return PsiTreeUtil.findChildrenOfType(psiMethod, PsiLiteralExpression::class.java)
16 | .map {
17 | HighlightedTextData(it)
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/plugin/testdata/expected/AssertionSuggestionServiceTest.testGetAssertionsOnReturnType.java:
--------------------------------------------------------------------------------
1 | public class MixedTestAndNoTestMethods {
2 |
3 | @Test
4 | public void test() {
5 | getAnInt();
6 | doNothing();
7 |
8 | /**
9 | * Assert that "getAnInt" returns the proper "int".
10 | */
11 |
12 | }
13 |
14 | @Test
15 | public void testMethod() {
16 | String noun = "Testing";
17 | String adverb = "awesome";
18 | methodUnderTest(noun, adverb);
19 | }
20 |
21 | public void methodUnderTest(String a, String b) {
22 | a.append(" is ", b);
23 | }
24 |
25 | public int getAnInt() {
26 | return 1;
27 | }
28 |
29 | public void doNothing() {
30 | return;
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/highlightResolutionStrategies/MagicNumberStrategyTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.highlightResolutionStrategies
2 |
3 | import com.intellij.psi.PsiMethod
4 | import com.testknight.extensions.TestKnightTestCase
5 | import junit.framework.TestCase
6 | import org.junit.Test
7 |
8 | internal class MagicNumberStrategyTest : TestKnightTestCase() {
9 |
10 | @Test
11 | fun testBasic() {
12 | val data = getBasicTestInfo("PointTest.java")
13 |
14 | val testMethod = data.psiClass!!.findMethodsByName("translateTest")[0] as PsiMethod
15 |
16 | val toBeChangedTxt = MagicNumberStrategy.getElements(testMethod).map { it.text }.toSet()
17 |
18 | TestCase.assertEquals(toBeChangedTxt, setOf("0", "0", "1", "2", "1", "2"))
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/plugin/testdata/BrokenClass.java:
--------------------------------------------------------------------------------
1 | //A class containing compilation errors
2 | //for testing purposes.
3 | class BrokenClass {
4 |
5 | private int a = 24;
6 | private int b = 42;
7 |
8 | public int incompleteCondition() {
9 | if () {
10 | return a + b;
11 | } else {
12 | return b;
13 | }
14 | }
15 |
16 | public int conditionalWithLiteral() {
17 | if (true) {
18 | return a;
19 | }
20 | return b;
21 | }
22 |
23 | public int incompleteConditionalExpression(){
24 | int a=5;
25 | int b=10;
26 | int c = null ? a:b;
27 | return c;
28 | }
29 |
30 | public int conditionalExpressionWithLiteral() {
31 | return true ? a : b;
32 | }
33 |
34 |
35 |
36 | }
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/dataTransferObjects/requests/ActionEventDto.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.dataTransferObjects.requests;
2 |
3 |
4 | import lombok.Data;
5 | import lombok.AllArgsConstructor;
6 |
7 | import java.time.LocalDateTime;
8 |
9 | @Data
10 | @AllArgsConstructor
11 | public class ActionEventDto implements RequestDto {
12 |
13 | private String actionId;
14 | private LocalDateTime dateTime;
15 |
16 | /**
17 | * Creates a string representation
18 | * of the DTO that is suitable for
19 | * hashing.
20 | *
21 | * @return the string that can be used
22 | * to generate a hash.
23 | */
24 | @Override
25 | public String toHashString() {
26 | return actionId + dateTime.toString();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/highlightResolutionStrategies/AssertionArgsStrategyTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.highlightResolutionStrategies
2 |
3 | import com.intellij.psi.PsiMethod
4 | import com.intellij.testFramework.UsefulTestCase
5 | import com.testknight.extensions.TestKnightTestCase
6 | import org.junit.Test
7 |
8 | internal class AssertionArgsStrategyTest : TestKnightTestCase() {
9 |
10 | @Test
11 | fun testBasic() {
12 | val data = getBasicTestInfo("PointTest.java")
13 |
14 | val testMethod = data.psiClass!!.findMethodsByName("translateTest")[0] as PsiMethod
15 |
16 | val toBeChangedTxt = AssertionArgsStrategy.getElements(testMethod).map { it.text }
17 |
18 | assertContainsElements(toBeChangedTxt, "p1", "p2")
19 | UsefulTestCase.assertSize(2, toBeChangedTxt)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/services/GotoTestService.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.services
2 |
3 | import com.intellij.openapi.editor.Editor
4 | import com.intellij.openapi.editor.ScrollType
5 | import com.intellij.refactoring.suggested.startOffset
6 | import com.testknight.models.TestMethodData
7 |
8 | /**
9 | * Centers user view to the specified method.
10 | *
11 | * @param editor the current instance of the text editor.
12 | * @param testMethodData custom object containing a reference to the PSI method where the user view should be centered.
13 | */
14 | class GotoTestService {
15 |
16 | fun gotoMethod(editor: Editor, testMethodData: TestMethodData) {
17 | editor.caretModel.primaryCaret.moveToOffset(testMethodData.psiMethod.startOffset)
18 | editor.scrollingModel.scrollToCaret(ScrollType.CENTER)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/views/UserInterfaceFactory.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.views
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.openapi.wm.ToolWindow
5 | import com.intellij.openapi.wm.ToolWindowFactory
6 |
7 | class UserInterfaceFactory : ToolWindowFactory {
8 | /**
9 | * Create the tool window content.
10 | *
11 | * @param project current project
12 | * @param toolWindow current tool window
13 | */
14 | override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
15 | val userInterface = UserInterface(project)
16 | val contentManager = toolWindow.contentManager
17 | val content = contentManager.factory.createContent(userInterface.getContent(), null, false)
18 | toolWindow.contentManager.addContent(content)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/highlightResolutionStrategies/ConstructorArgsStrategyTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.highlightResolutionStrategies
2 |
3 | import com.intellij.psi.PsiMethod
4 | import com.intellij.testFramework.UsefulTestCase
5 | import com.testknight.extensions.TestKnightTestCase
6 | import org.junit.Test
7 |
8 | internal class ConstructorArgsStrategyTest : TestKnightTestCase() {
9 |
10 | @Test
11 | fun testGetConstructorArgs() {
12 | val data = getBasicTestInfo("PointTest.java")
13 |
14 | val testMethod = data.psiClass!!.findMethodsByName("translateTest")[0] as PsiMethod
15 | val toBeChangedTxt = ConstructorArgsStrategy.getElements(testMethod).map { it.text }
16 |
17 | assertContainsElements(toBeChangedTxt, "0", "0", "1", "2")
18 | UsefulTestCase.assertSize(4, toBeChangedTxt)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/server/src/test/resources/import.sql:
--------------------------------------------------------------------------------
1 | insert into ACTIONS (action_Id) values ('duplicateTest');
2 | insert into ACTIONS (action_Id) values ('gotoTest');
3 | insert into ACTIONS (action_Id) values ('suggestAssertion');
4 | insert into ACTIONS (action_Id) values ('generateChecklist');
5 | insert into ACTIONS (action_Id) values ('splitDiffView');
6 | insert into ACTIONS (action_Id) values ('integratedDiffView');
7 | insert into ACTIONS (action_Id) values ('traceTest');
8 | insert into ACTIONS (action_Id) values ('generateTest');
9 | insert into ACTIONS (action_Id) values ('itemMarked');
10 | insert into ACTIONS (action_Id) values ('itemDeleted');
11 | insert into ACTIONS (action_Id) values ('runWithCoverage');
12 | insert into ACTIONS (action_Id) values ('testRun');
13 | insert into ACTIONS (action_Id) values ('testFail');
14 | insert into ACTIONS (action_Id) values ('testAdd');
15 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/extensions/DiffCoverageLineMarkerRenderer.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.extensions
2 |
3 | import com.intellij.coverage.LineMarkerRendererWithErrorStripe
4 | import com.intellij.openapi.editor.Editor
5 | import com.intellij.openapi.editor.markup.LineMarkerRendererEx.Position
6 | import java.awt.Color
7 | import java.awt.Graphics
8 | import java.awt.Rectangle
9 |
10 | class DiffCoverageLineMarkerRenderer(val color: Color) : LineMarkerRendererWithErrorStripe {
11 |
12 | override fun paint(editor: Editor, g: Graphics, r: Rectangle) {
13 | g.color = color
14 | g.fillRect(r.x, r.y, r.width, +r.height)
15 | }
16 |
17 | override fun getPosition(): Position {
18 | return Position.LEFT
19 | }
20 |
21 | override fun getErrorStripeColor(editor: Editor?): Color {
22 | return Color.CYAN
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/plugin/testdata/expected/AssertionSuggestionServiceTest.testGetAssertionsWithSideEffects.java:
--------------------------------------------------------------------------------
1 | public class MixedTestAndNoTestMethods {
2 |
3 | @Test
4 | public void test() {
5 | getAnInt();
6 | doNothing();
7 | }
8 |
9 | @Test
10 | public void testMethod() {
11 | String noun = "Testing";
12 | String adverb = "awesome";
13 | methodUnderTest(noun, adverb);
14 |
15 | /**
16 | * Assert that method "append" modifies argument "noun" properly.
17 | * Assert that method "append" modifies argument "adverb" properly.
18 | */
19 |
20 | }
21 |
22 | public void methodUnderTest(String a, String b) {
23 | a.append(" is ", b);
24 | }
25 |
26 | public int getAnInt() {
27 | return 1;
28 | }
29 |
30 | public void doNothing() {
31 | return;
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/listeners/InitializationListener.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.listeners
2 |
3 | import com.intellij.coverage.CoverageDataManager
4 | import com.intellij.openapi.components.service
5 | import com.intellij.openapi.project.Project
6 | import com.intellij.openapi.project.ProjectManagerListener
7 | import com.testknight.extensions.DiffCoverageListener
8 | import com.testknight.services.CoverageDataService
9 | import com.testknight.services.checklist.ChecklistTreeService
10 |
11 | class InitializationListener : ProjectManagerListener {
12 |
13 | override fun projectOpened(project: Project) {
14 | val covDataManager = CoverageDataManager.getInstance(project)
15 | val covDataService = project.service()
16 | project.service().initUiTree()
17 | covDataManager.addSuiteListener(DiffCoverageListener(project), covDataService)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.github/workflows/server.yml:
--------------------------------------------------------------------------------
1 | name: Server verification
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - '/server/**/'
7 | push:
8 | branches:
9 | - master
10 | - develop
11 |
12 | jobs:
13 |
14 | build:
15 | name: Compiles the server
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 |
21 | - name: build
22 | run: gradle assemble
23 |
24 | staticAnalysis:
25 | name: Code style static analysis
26 | runs-on: ubuntu-latest
27 |
28 | steps:
29 | - uses: actions/checkout@v2
30 |
31 | - name: checkStyle
32 | run: gradle checkstyleMain
33 |
34 | - name: PMD
35 | run: gradle pmdMain
36 |
37 | test:
38 | name: Tests the server
39 | runs-on: ubuntu-latest
40 |
41 | steps:
42 | - uses: actions/checkout@v2
43 |
44 | - name: test
45 | run: gradle test
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/.run/Run IDE with Plugin.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
17 |
18 |
19 | true
20 |
21 |
22 |
--------------------------------------------------------------------------------
/server/src/main/resources/populate_database.sql:
--------------------------------------------------------------------------------
1 | -- You can use this script to insert default action ids in the actions table
2 | -- You need to manually run it
3 | insert into actions (action_Id) values ('duplicateTest');
4 | insert into actions (action_Id) values ('gotoTest');
5 | insert into actions (action_Id) values ('suggestAssertion');
6 | insert into actions (action_Id) values ('generateChecklist');
7 | insert into actions (action_Id) values ('splitDiffView');
8 | insert into actions (action_Id) values ('integratedDiffView');
9 | insert into actions (action_Id) values ('traceTest');
10 | insert into actions (action_Id) values ('generateTest');
11 | insert into actions (action_Id) values ('itemMarked');
12 | insert into actions (action_Id) values ('itemDeleted');
13 | insert into actions (action_Id) values ('runWithCoverage');
14 | insert into actions (action_Id) values ('testRun');
15 | insert into actions (action_Id) values ('testFail');
16 | insert into actions (action_Id) values ('testAdd');
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/CoverageDiffObject.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import com.intellij.openapi.vfs.VirtualFile
4 |
5 | @Suppress("LongParameterList")
6 | class CoverageDiffObject(
7 | val allLinesPrev: Set = emptySet(),
8 | val allLinesNow: Set = emptySet(),
9 | val coveredPrev: Set = emptySet(),
10 | val coveredNow: Set = emptySet(),
11 | var prevStamp: Long = 0,
12 | var currStamp: Long = 0,
13 | val virtualFile: VirtualFile? = null
14 | ) {
15 |
16 | val linesNewlyRemoved: Set
17 | val linesNewlyAdded: Set
18 | val linesCoveredInBoth: Set
19 | val linesNotCovered: Set
20 |
21 | init {
22 | linesNewlyRemoved = coveredPrev - coveredNow
23 | linesNewlyAdded = coveredNow - coveredPrev
24 | linesCoveredInBoth = coveredPrev.intersect(coveredNow)
25 | linesNotCovered = (allLinesNow - coveredPrev).intersect(allLinesNow - coveredNow)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/testingChecklist/leafNodes/CustomChecklistNode.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.testingChecklist.leafNodes
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.psi.PsiElement
5 | import com.intellij.psi.PsiMethod
6 | import com.intellij.util.xmlb.annotations.OptionTag
7 | import com.testknight.messageBundleHandlers.TestMethodGenerationMessageBundleHandler
8 | import com.testknight.utilities.PsiConverter
9 |
10 | class CustomChecklistNode(
11 | override var description: String = "",
12 | @OptionTag(converter = PsiConverter::class)
13 | override var element: PsiElement? = null,
14 | override var checked: Int = 0
15 | ) : TestingChecklistLeafNode(description, element) {
16 | override fun generateTestMethod(project: Project): PsiMethod {
17 | val methodName = TestMethodGenerationMessageBundleHandler.message("customNodeTestCaseName")
18 | return super.generateTestMethod(project, methodName)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/plugin/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # TestKnight Changelog
4 |
5 |
22 |
23 | ## [1.0.2]
24 |
25 | ### Added
26 | - Added support for build 213.
27 |
28 | ## [1.0.1]
29 |
30 | ### Added
31 | - Added support for build 212.
32 | ### Changed
33 | - Improved checklist generation parsing strategy.
34 | - Changed how notifications are registered to 2020.3 and later version.
35 | ### Removed
36 | - Removed support for build 202.
37 | ### Fixed
38 | - Fix coverage not showing for classes inside a package.
39 | - Fix parameter suggestions tree adding empty class/values.
40 |
41 | ## [1.0.0]
42 | - Initial release.
43 |
44 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/messages/TestingChecklistMessageBundle.properties:
--------------------------------------------------------------------------------
1 | switchVariableDefault=Test {0} is different from all the switch cases
2 | switchVariableCase=Test {0} is {1}
3 | tryBlockSuccess=Test with the try block running successfully
4 | tryBlockGeneralException=Test with the try block throwing an exception
5 | tryBlockThrowsSpecificException=Test with the try block throwing a {0}
6 | doWhileMultipleTimes=Test where do-while loop runs multiple times
7 | forEachEmpty=Test where {0} is empty
8 | forEachNull=Test where {0} is null
9 | forEachOnce=Test where {0} has one element
10 | forEachMultiple=Test where foreach loop runs multiple times
11 | forLoopMultiple=Test where for loop runs multiple times
12 | whileLoopMultiple=Test where while loop runs multiple times
13 | conditionBaseMessage=Test where in the condition "{0}",
14 | conditionSingleMessage=Test where
15 | conditionAssignmentMessage="{0}" is {1},
16 | parameterBaseMessage=Test method parameter "{0}" equal to: {1}
17 | throw=Test when {0} is thrown
--------------------------------------------------------------------------------
/.github/workflows/plugin.yml:
--------------------------------------------------------------------------------
1 | name: Plugin verification
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - '/plugin/**/'
7 | push:
8 | branches:
9 | - master
10 | - develop
11 |
12 | jobs:
13 |
14 | build:
15 | name: Compiles the plugin
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 |
21 | - name: build
22 | run: |
23 | gradle assemble
24 | gradle testClasses
25 |
26 | staticAnalysis:
27 | name: Code style static analysis
28 | runs-on: ubuntu-latest
29 |
30 | steps:
31 | - uses: actions/checkout@v2
32 |
33 | - name: detekt
34 | run: gradle detekt
35 |
36 | - name: ktlint
37 | run: gradle ktlintCheck
38 |
39 | test:
40 | name: Tests the plugin
41 | runs-on: ubuntu-latest
42 |
43 | steps:
44 | - uses: actions/checkout@v2
45 |
46 | - name: test
47 | run: gradle test
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/highlightResolutionStrategies/ConstructorArgsStrategy.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.highlightResolutionStrategies
2 |
3 | import com.intellij.psi.PsiConstructorCall
4 | import com.intellij.psi.PsiMethod
5 | import com.intellij.psi.util.PsiTreeUtil
6 | import com.testknight.models.HighlightedTextData
7 |
8 | object ConstructorArgsStrategy : HighlightResolutionStrategy {
9 |
10 | override val priority = 1
11 |
12 | override val settingsName = "Highlight constructor arguments"
13 |
14 | override fun getElements(psiMethod: PsiMethod): List {
15 | val res = arrayListOf()
16 |
17 | PsiTreeUtil.findChildrenOfType(psiMethod, PsiConstructorCall::class.java)
18 | .forEach { constructorCall ->
19 | constructorCall.argumentList?.expressions?.forEach {
20 | res.add(HighlightedTextData(it))
21 | }
22 | }
23 |
24 | return res
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/plugin/testdata/TestDuplication.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.BeforeAll;
5 | import org.junit.jupiter.api.AfterAll;
6 | import org.junit.jupiter.params.ParameterizedTest;
7 | import org.junit.jupiter.params.provider.CsvSource;
8 |
9 | import static org.junit.jupiter.api.Assertions.*;
10 |
11 | public class PointTest {
12 |
13 | @Test
14 | void duplicate() {
15 | Point p = new Point(0, 1);
16 | }
17 |
18 | @Test
19 | void containing() {
20 | Point p = new Point(0, somefunc(1, 2));
21 | }
22 |
23 | @Test
24 | void nestedContains() {
25 | Point p = new Point(0, somefunc(foo(), bar(1, 2, 3)), dar());
26 | }
27 |
28 | @Test
29 | void strAndChar() {
30 | int s = "string";
31 | char c = 'c';
32 | }
33 |
34 | @Test
35 | void hasAll() {
36 | Point p = new Point(0, 0);
37 |
38 | p.x += 1;
39 | p.y += 1;
40 |
41 | assertEquals(1, p.x);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/listeners/FileEditorListener.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.listeners
2 |
3 | import com.intellij.openapi.components.service
4 | import com.intellij.openapi.editor.Editor
5 | import com.intellij.openapi.fileEditor.FileEditorManager
6 | import com.intellij.openapi.fileEditor.FileEditorManagerListener
7 | import com.intellij.openapi.fileEditor.TextEditor
8 | import com.intellij.openapi.project.Project
9 | import com.intellij.openapi.vfs.VirtualFile
10 | import com.testknight.services.CoverageHighlighterService
11 | import com.testknight.services.TestTracingService
12 |
13 | class FileEditorListener(val project: Project) : FileEditorManagerListener {
14 |
15 | override fun fileOpened(source: FileEditorManager, file: VirtualFile) {
16 |
17 | val editors = mutableListOf()
18 | source.getEditors(file).forEach { if (it is TextEditor) editors.add(it.editor) }
19 |
20 | project.service().addHighlights(editors)
21 | project.service().addHighlights(editors)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/testingChecklist/leafNodes/loopStatements/DoWhileStatementChecklistNode.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.testingChecklist.leafNodes.loopStatements
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.psi.PsiElement
5 | import com.intellij.psi.PsiMethod
6 | import com.intellij.util.xmlb.annotations.OptionTag
7 | import com.testknight.messageBundleHandlers.TestMethodGenerationMessageBundleHandler
8 | import com.testknight.models.testingChecklist.leafNodes.TestingChecklistLeafNode
9 | import com.testknight.utilities.PsiConverter
10 |
11 | data class DoWhileStatementChecklistNode(
12 | override var description: String = "",
13 | @OptionTag(converter = PsiConverter::class)
14 | override var element: PsiElement? = null
15 | ) : TestingChecklistLeafNode(description, element) {
16 | override fun generateTestMethod(project: Project): PsiMethod {
17 | val methodName = TestMethodGenerationMessageBundleHandler.message("doWhileTestCaseName")
18 | return super.generateTestMethod(project, methodName)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/services/TestAnalyzerServiceTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.services
2 |
3 | import com.testknight.extensions.TestKnightTestCase
4 | import junit.framework.TestCase
5 | import org.junit.Test
6 |
7 | internal class TestAnalyzerServiceTest : TestKnightTestCase() {
8 |
9 | @Test
10 | fun testIsTestClassTrue() {
11 | val data = getBasicTestInfo("/PointTest.java")
12 | val testAnalyzerService = TestAnalyzerService()
13 | TestCase.assertTrue(testAnalyzerService.isTestClass(data.psiClass!!))
14 | }
15 |
16 | @Test
17 | fun testIsTestClassFalse() {
18 | val data = getBasicTestInfo("/Person.java")
19 | val testAnalyzerService = TestAnalyzerService()
20 | TestCase.assertFalse(testAnalyzerService.isTestClass(data.psiClass!!))
21 | }
22 |
23 | @Test
24 | fun testIsTestClassEmptyClass() {
25 | val data = getBasicTestInfo("/EmptyClass.java")
26 | val testAnalyzerService = TestAnalyzerService()
27 | TestCase.assertFalse(testAnalyzerService.isTestClass(data.psiClass!!))
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/.run/Run Plugin Tests.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | true
20 | true
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/server/config/pmd/pmd.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 | Add only the desired pmd rules.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/validation/BaseValidator.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.validation;
2 |
3 | import com.testknight.TestKnightTelemetryServer.dataTransferObjects.requests.*;
4 | import com.testknight.TestKnightTelemetryServer.exceptions.*;
5 |
6 | public abstract class BaseValidator implements RequestValidator {
7 |
8 | private RequestValidator next;
9 |
10 | /**
11 | * Sets the next validator in the chain.
12 | *
13 | * @param nextValidator the validator to be used next.
14 | */
15 | @Override
16 | public void setNext(RequestValidator nextValidator) {
17 | this.next = nextValidator;
18 | }
19 |
20 | /**
21 | * Handles the request.
22 | *
23 | * @param requestDto the request to be handled.
24 | * @throws ValidationException thrown iff the request is invalid.
25 | */
26 | @Override
27 | public void handle(E requestDto) throws ValidationException {
28 | if (this.next != null) {
29 | next.handle(requestDto);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/testdata/Tests.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.BeforeAll;
5 | import org.junit.jupiter.api.AfterAll;
6 | import org.junit.jupiter.params.ParameterizedTest;
7 | import org.junit.jupiter.params.provider.CsvSource;
8 |
9 | import static org.junit.jupiter.api.Assertions.*;
10 |
11 | public class PointTest {
12 |
13 | @Test
14 | void basic() {
15 | // contents
16 | }
17 |
18 | @Test
19 | public static void hasModifiers() {
20 | // contents
21 | }
22 |
23 | @Test
24 | String hasReturnTy() {
25 | // contents
26 | }
27 |
28 | @Test
29 | void hasParams(int x, int y) {
30 | // contents
31 | int expected = x;
32 | int actual = SomeClass.magic(x, y);
33 | assertEquals(expected, actual);
34 | }
35 |
36 | @Test
37 | void hasTypeParams() {
38 | // contents
39 | }
40 |
41 | @Test
42 | void throwsException() throws Exception {
43 | // contents
44 | }
45 |
46 | @Test
47 | void hasAssertion() {
48 | assertEquals(2, 1 + 1)
49 | }
50 | }
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/dataTransferObjects/requests/UsageDataDto.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.dataTransferObjects.requests;
2 |
3 | import lombok.Data;
4 | import lombok.AllArgsConstructor;
5 |
6 | import java.util.List;
7 |
8 | @Data
9 | @AllArgsConstructor
10 | public class UsageDataDto implements RequestDto {
11 |
12 | private String userId;
13 | private List actionsRecorded;
14 | private String hash;
15 |
16 | /**
17 | * Creates a string representation
18 | * of the DTO that is suitable for
19 | * hashing.
20 | *
21 | * @return the string that can be used
22 | * to generate a hash.
23 | */
24 | @Override
25 | public String toHashString() {
26 | StringBuilder builder = new StringBuilder();
27 | builder.append(userId);
28 | if (actionsRecorded != null) {
29 | for (ActionEventDto actionEventDto : actionsRecorded) {
30 | builder.append(actionEventDto.toHashString());
31 | }
32 | }
33 | return builder.toString();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/documentation/mockups/AssertionSuggestion/DetailedAssertionSuggestionMockup.md:
--------------------------------------------------------------------------------
1 | # Description of the Assertion Suggestions mockup
2 |
3 | - The user can request a list of assertion suggestions for a specific test case by pressing a button near the method name.
4 | - Currently, the icon in the mockup is a "+" icon, but it is just a placeholder.
5 | - This action can also be called by a keyboard hotkey on the code the text cursor currently points to.
6 | - If no assertion suggestion can be made for any reason, a message can pop up in the form of a balloon.
7 | - The list of the assertions suggestions will look like in the following at the bottom of the test case:
8 | ```
9 | /*
10 | TODO Create an assertion for the attribute x of the classY.
11 | assert(...., x)
12 |
13 | TODO Create an assertion for the attribute x of the classY.
14 | assert(...., x)
15 |
16 | TODO Create an assertion for the attribute x of the classY.
17 | assert(...., x)
18 | */
19 | ```
20 | - Using this approach, It is easy for the user to copy the snippet code.
21 |
22 | # Graphical mockup
23 | ### Editor view
24 | 
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Software Engineering Research Group
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/testingChecklist/leafNodes/ConditionChecklistNode.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.testingChecklist.leafNodes
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.psi.PsiElement
5 | import com.intellij.psi.PsiMethod
6 | import com.intellij.util.xmlb.annotations.OptionTag
7 | import com.testknight.messageBundleHandlers.TestMethodGenerationMessageBundleHandler
8 | import com.testknight.utilities.PsiConverter
9 |
10 | data class ConditionChecklistNode(
11 | override var description: String = "",
12 | @OptionTag(converter = PsiConverter::class)
13 | override var element: PsiElement? = null
14 | ) : TestingChecklistLeafNode(description, element) {
15 | /**
16 | * Generate a test method for a condition statement.
17 | *
18 | * @param project the project.
19 | * @return the PsiMethod representing the test.
20 | */
21 | override fun generateTestMethod(project: Project): PsiMethod {
22 | val methodName = TestMethodGenerationMessageBundleHandler.message("conditionTestCaseName")
23 | return super.generateTestMethod(project, methodName)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/plugin/gradle.properties:
--------------------------------------------------------------------------------
1 | # IntelliJ Platform Artifacts Repositories
2 | # -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html
3 |
4 | pluginGroup = com.testknight
5 | pluginName = TestKnight
6 | pluginVersion = 1.0.2
7 | pluginSinceBuild = 203
8 | pluginUntilBuild = 213.*
9 | # Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl
10 | # See https://jb.gg/intellij-platform-builds-list for available build versions
11 | pluginVerifierIdeVersions = 2020.3.4, 2021.1.3, 2021.2.1, 2021.3.1
12 |
13 | platformType = IC
14 | platformVersion = 2020.3.4
15 | platformDownloadSources = true
16 |
17 | # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
18 | # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
19 | platformPlugins = com.intellij.java, java, coverage
20 |
21 | pluginDescription = The IDE plugin that helps you during testing.
22 |
23 | # Opt-out flag for bundling Kotlin standard library.
24 | # See https://kotlinlang.org/docs/reference/using-gradle.html#dependency-on-the-standard-library for details.
25 | kotlin.stdlib.default.dependency = false
26 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/domain/model/UsageRecord.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.domain.model;
2 |
3 | import lombok.*;
4 |
5 | import javax.persistence.*;
6 | import java.time.*;
7 |
8 | @Data
9 | @AllArgsConstructor
10 | @NoArgsConstructor
11 | @Entity
12 | @Table(name = "USAGE_RECORDS")
13 | public class UsageRecord {
14 |
15 | @Id
16 | @Column(name = "id")
17 | @GeneratedValue(strategy = GenerationType.IDENTITY)
18 | private Long id;
19 |
20 | @Column(name = "user_Id")
21 | private String userId;
22 |
23 | @ManyToOne
24 | private Action actionId;
25 |
26 | @Column(name = "datetime")
27 | private LocalDateTime dateTime;
28 |
29 | /**
30 | * Creates a new UsageRecord object.
31 | *
32 | * @param userId the id of the user.
33 | * @param actionId the id of the action.
34 | * @param dateTime the date-time of the event.
35 | */
36 | public UsageRecord(String userId, Action actionId, LocalDateTime dateTime) {
37 | this.userId = userId;
38 | this.actionId = actionId;
39 | this.dateTime = dateTime;
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/highlightResolutionStrategies/HighlightResolutionStrategy.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.highlightResolutionStrategies
2 |
3 | import com.intellij.openapi.application.ApplicationManager
4 | import com.intellij.psi.PsiMethod
5 | import com.testknight.models.HighlightedTextData
6 | import com.testknight.settings.SettingsService
7 |
8 | interface HighlightResolutionStrategy {
9 |
10 | /**
11 | * Represents the priority of the strategy. Lower is higher priority
12 | */
13 | val priority: Int
14 |
15 | val settingsName: String
16 |
17 | /**
18 | * @return true if the strategy is enabled in the settings
19 | */
20 | fun isEnabled() = ApplicationManager
21 | .getApplication()
22 | .getService(SettingsService::class.java)
23 | .state
24 | .testListSettings.highlightStrategies[settingsName]!!
25 |
26 | /**
27 | * Gets a list of PSI elements to be highlighted from a PSI method
28 | *
29 | * @param psiMethod the method
30 | * @return a list of PSI elements to be highlighted
31 | */
32 | fun getElements(psiMethod: PsiMethod): List
33 | }
34 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/listeners/checklist/ClassChecklistIconHandler.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.listeners.checklist
2 |
3 | import com.intellij.codeInsight.daemon.GutterIconNavigationHandler
4 | import com.intellij.openapi.components.service
5 | import com.intellij.psi.PsiElement
6 | import com.testknight.actions.checklist.LoadChecklistAction
7 | import com.testknight.utilities.UserInterfaceHelper
8 | import java.awt.event.MouseEvent
9 |
10 | /**
11 | * Handler for the gutter icons which are used for the class.
12 | */
13 | class ClassChecklistIconHandler : GutterIconNavigationHandler {
14 |
15 | /**
16 | * This handler generates the checklist for the chosen class.
17 | *
18 | * @param event MouseEvent received from the click
19 | * @param element PsiElement for which you have to generate the checklist
20 | */
21 | override fun navigate(event: MouseEvent?, element: PsiElement?) {
22 | if (element == null) return
23 | val project = element.project
24 | if (LoadChecklistAction().actionPerformed(project, element.parent)) {
25 | project.service().showTab("Checklist")
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/settings/EditElementAction.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.settings
2 |
3 | import com.intellij.openapi.actionSystem.AnAction
4 | import com.intellij.openapi.actionSystem.AnActionEvent
5 | import com.intellij.ui.treeStructure.Tree
6 |
7 | class EditElementAction : AnAction() {
8 |
9 | var tree: Tree? = null
10 | /**
11 | * Starts editing at the selected node.
12 | *
13 | * @param e Carries information on the invocation place
14 | */
15 | override fun actionPerformed(e: AnActionEvent) {
16 | tree!!.startEditingAtPath(tree!!.selectionPath)
17 | }
18 |
19 | /**
20 | * Updates the state of the action.
21 | * The action is enabled only if the tree is not null and atleast one node has been selected.
22 | *
23 | * @param e Carries information on the invocation place
24 | */
25 | override fun update(e: AnActionEvent) {
26 | e.presentation.isEnabled = (tree != null && tree!!.selectionCount > 0)
27 | }
28 |
29 | /**
30 | * Sets the tree attribute which is used for accessing tree functions.
31 | */
32 | fun init(tree: Tree) {
33 | this.tree = tree
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/listeners/checklist/MethodChecklistIconHandler.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.listeners.checklist
2 |
3 | import com.intellij.codeInsight.daemon.GutterIconNavigationHandler
4 | import com.intellij.openapi.components.service
5 | import com.intellij.psi.PsiElement
6 | import com.testknight.actions.checklist.LoadChecklistAction
7 | import com.testknight.utilities.UserInterfaceHelper
8 | import java.awt.event.MouseEvent
9 |
10 | /**
11 | * Handler for the gutter icons which are used for the methods.
12 | */
13 | class MethodChecklistIconHandler : GutterIconNavigationHandler {
14 |
15 | /**
16 | * This handler generates the checklist for the chosen method.
17 | *
18 | * @param event MouseEvent received from the click
19 | * @param element PsiElement for which you have to generate the checklist
20 | */
21 | override fun navigate(event: MouseEvent?, element: PsiElement?) {
22 | if (element == null) return
23 | val project = element.project
24 | if (LoadChecklistAction().actionPerformed(project, element.parent)) {
25 | project.service().showTab("Checklist")
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/UsageData.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import com.testknight.messageBundleHandlers.ServerMessageBundleHandler
4 | import com.testknight.settings.SettingsService
5 | import org.apache.commons.codec.digest.DigestUtils
6 |
7 | /**
8 | * Represents a session usage data
9 | *
10 | * @param actionsRecorded the list of actions to include in the usageData
11 | */
12 | data class UsageData(
13 | val actionsRecorded: List,
14 | val userId: String = SettingsService.instance.state.userId,
15 | ) {
16 |
17 | // the hash is used to verify the request.
18 | val hash: String = DigestUtils.md5Hex("${toHashString()}${ServerMessageBundleHandler.message("hashString")}")
19 |
20 | /**
21 | * Creates a string representation
22 | * of the DTO that is suitable for
23 | * hashing.
24 | *
25 | * @return the string that can be used
26 | * to generate a hash.
27 | */
28 | fun toHashString(): String {
29 | val builder = StringBuilder()
30 | builder.append(userId)
31 | for (actionEventDto in actionsRecorded) {
32 | builder.append(actionEventDto.toHashString())
33 | }
34 | return builder.toString()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/testingChecklist/leafNodes/ThrowStatementChecklistNode.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.testingChecklist.leafNodes
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.psi.PsiElement
5 | import com.intellij.psi.PsiMethod
6 | import com.intellij.util.xmlb.annotations.OptionTag
7 | import com.testknight.messageBundleHandlers.TestMethodGenerationMessageBundleHandler
8 | import com.testknight.utilities.PsiConverter
9 |
10 | data class ThrowStatementChecklistNode(
11 | override var description: String = "",
12 | // override val element: PsiThrowStatement,
13 | @OptionTag(converter = PsiConverter::class)
14 | override var element: PsiElement? = null,
15 | val nameOfException: String = ""
16 | ) : TestingChecklistLeafNode(description, element) {
17 |
18 | /**
19 | * Generate a test method for a throw statement.
20 | *
21 | * @param project the project.
22 | * @return the PsiMethod representing the test.
23 | */
24 | override fun generateTestMethod(project: Project): PsiMethod {
25 | val methodName = TestMethodGenerationMessageBundleHandler.message("throwTestCaseName")
26 | return super.generateTestMethod(project, methodName)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/validation/RequestValidator.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.validation;
2 |
3 | import com.testknight.TestKnightTelemetryServer.dataTransferObjects.requests.*;
4 | import com.testknight.TestKnightTelemetryServer.exceptions.*;
5 |
6 | /**
7 | * The request validator interface. To be implemented
8 | * by all classes of the request validation chain.
9 | *
10 | * @param The type of RequestDto being validated.
11 | * The generic here allows to customize the handle
12 | * method as well as ensure that the next validator
13 | * in the chain handles the same type of request.
14 | */
15 | public interface RequestValidator {
16 |
17 | /**
18 | * Sets the next validator in the chain.
19 | *
20 | * @param nextValidator the validator to be used next.
21 | */
22 | public void setNext(RequestValidator nextValidator);
23 |
24 | /**
25 | * Handles the request.
26 | *
27 | * @param requestDto the request to be handled.
28 | * @throws ValidationException thrown iff the request is invalid.
29 | */
30 | public void handle(E requestDto) throws ValidationException;
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/utilities/StringFormatter.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.utilities
2 |
3 | class StringFormatter private constructor() {
4 |
5 | companion object {
6 | /**
7 | * Formats the name of the method to be of the form: "methodName".
8 | *
9 | * @param methodName the full name of the method.
10 | * @return a String representing the formatted method name.
11 | */
12 | fun formatMethodName(methodName: String): String {
13 | return if (methodName.contains(".")) {
14 | methodName.substring(methodName.lastIndexOf(".") + 1)
15 | } else {
16 | methodName
17 | }
18 | }
19 |
20 | /**
21 | * Formats the name of the field that is affected so that
22 | * all fields have the form "this.nameOfField".
23 | *
24 | * @param name the name of the field.
25 | * @return the formatted version of the name.
26 | */
27 | fun formatClassFieldName(name: String): String {
28 | return if (name.startsWith("this.")) {
29 | name.replaceFirst("this.", "")
30 | } else {
31 | name
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/testingChecklist/leafNodes/ParameterChecklistNode.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.testingChecklist.leafNodes
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.psi.PsiElement
5 | import com.intellij.psi.PsiMethod
6 | import com.intellij.util.xmlb.annotations.OptionTag
7 | import com.testknight.messageBundleHandlers.TestMethodGenerationMessageBundleHandler
8 | import com.testknight.utilities.PsiConverter
9 |
10 | data class ParameterChecklistNode(
11 | override var description: String = "",
12 | // override var element: PsiParameter?,
13 | @OptionTag(converter = PsiConverter::class)
14 | override var element: PsiElement? = null,
15 | val parameterName: String = "",
16 | val suggestedValue: String = ""
17 | ) : TestingChecklistLeafNode(description, element) {
18 | /**
19 | * Generate a test method for a parameter.
20 | *
21 | * @param project the project.
22 | * @return the PsiMethod representing the test.
23 | */
24 | override fun generateTestMethod(project: Project): PsiMethod {
25 | val methodName = TestMethodGenerationMessageBundleHandler.message("parameterTestCaseName")
26 | return super.generateTestMethod(project, methodName)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/testingChecklist/leafNodes/loopStatements/ForLoopStatementChecklistNode.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.testingChecklist.leafNodes.loopStatements
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.psi.PsiElement
5 | import com.intellij.psi.PsiMethod
6 | import com.intellij.util.xmlb.annotations.OptionTag
7 | import com.testknight.messageBundleHandlers.TestMethodGenerationMessageBundleHandler
8 | import com.testknight.models.testingChecklist.leafNodes.TestingChecklistLeafNode
9 | import com.testknight.utilities.PsiConverter
10 |
11 | data class ForLoopStatementChecklistNode(
12 | override var description: String = "",
13 | @OptionTag(converter = PsiConverter::class)
14 | override var element: PsiElement? = null
15 | ) : TestingChecklistLeafNode(description, element) {
16 | /**
17 | * Generate a test method for a for loop statement.
18 | *
19 | * @param project the project.
20 | * @return the PsiMethod representing the test.
21 | */
22 | override fun generateTestMethod(project: Project): PsiMethod {
23 | val methodName = TestMethodGenerationMessageBundleHandler.message("forLoopTestCaseName")
24 | return super.generateTestMethod(project, methodName)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/validation/HashValidator.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.validation;
2 |
3 | import com.testknight.TestKnightTelemetryServer.dataTransferObjects.requests.*;
4 | import com.testknight.TestKnightTelemetryServer.exceptions.*;
5 | import com.testknight.TestKnightTelemetryServer.security.*;
6 |
7 | public class HashValidator extends BaseValidator {
8 |
9 | private final Hasher hasher;
10 | private final String magicString = "exampleMagicString";
11 |
12 | public HashValidator(Hasher hasher) {
13 | this.hasher = hasher;
14 | }
15 |
16 | /**
17 | * Handles the request by checking if the hash
18 | * was generated in a verified client.
19 | *
20 | * @param requestDto the request to be handled.
21 | * @throws ValidationException thrown iff the request is invalid.
22 | */
23 | @Override
24 | public void handle(UsageDataDto requestDto) throws ValidationException {
25 | String hashedContents = hasher.hash(requestDto.toHashString() + magicString);
26 | if (!hashedContents.equals(requestDto.getHash())) {
27 | throw new InvalidHashException();
28 | }
29 | super.handle(requestDto);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/settings/DeleteElementAction.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.settings
2 |
3 | import com.intellij.openapi.actionSystem.AnAction
4 | import com.intellij.openapi.actionSystem.AnActionEvent
5 | import com.intellij.ui.treeStructure.Tree
6 | import com.testknight.models.ParameterSuggestionTreeModel
7 |
8 | class DeleteElementAction : AnAction() {
9 | var tree: Tree? = null
10 | /**
11 | * Removes the selected node.
12 | *
13 | * @param e Carries information on the invocation place
14 | */
15 | override fun actionPerformed(e: AnActionEvent) {
16 | (tree!!.model as ParameterSuggestionTreeModel).removePathElement(tree!!.selectionPath)
17 | }
18 |
19 | /**
20 | * Updates the state of the action.
21 | *The action is enabled only if the tree is not null and atleast one node has been selected.
22 | *
23 | * @param e Carries information on the invocation place
24 | */
25 | override fun update(e: AnActionEvent) {
26 | e.presentation.isEnabled = (tree != null && tree!!.selectionCount > 0)
27 | }
28 |
29 | /**
30 | * Sets the tree attribute which is used for accessing tree functions.
31 | */
32 | fun init(tree: Tree) {
33 | this.tree = tree
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/testingChecklist/leafNodes/loopStatements/WhileStatementChecklistNode.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.testingChecklist.leafNodes.loopStatements
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.psi.PsiElement
5 | import com.intellij.psi.PsiMethod
6 | import com.intellij.util.xmlb.annotations.OptionTag
7 | import com.testknight.messageBundleHandlers.TestMethodGenerationMessageBundleHandler
8 | import com.testknight.models.testingChecklist.leafNodes.TestingChecklistLeafNode
9 | import com.testknight.utilities.PsiConverter
10 |
11 | data class WhileStatementChecklistNode(
12 | override var description: String = "",
13 |
14 | @OptionTag(converter = PsiConverter::class)
15 | override var element: PsiElement? = null,
16 | ) : TestingChecklistLeafNode(description, element) {
17 |
18 | /**
19 | * Generate a test method for a while loop statement.
20 | *
21 | * @param project the project.
22 | * @return the PsiMethod representing the test.
23 | */
24 | override fun generateTestMethod(project: Project): PsiMethod {
25 | val methodName = TestMethodGenerationMessageBundleHandler.message("whileLoopTestCaseName")
26 | return super.generateTestMethod(project, methodName)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.run/Run Plugin Verification.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | true
20 | true
21 | false
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/testingChecklist/leafNodes/loopStatements/ForEachStatementChecklistNode.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.testingChecklist.leafNodes.loopStatements
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.psi.PsiElement
5 | import com.intellij.psi.PsiMethod
6 | import com.intellij.util.xmlb.annotations.OptionTag
7 | import com.testknight.messageBundleHandlers.TestMethodGenerationMessageBundleHandler
8 | import com.testknight.models.testingChecklist.leafNodes.TestingChecklistLeafNode
9 | import com.testknight.utilities.PsiConverter
10 |
11 | data class ForEachStatementChecklistNode(
12 | override var description: String = "",
13 | @OptionTag(converter = PsiConverter::class)
14 | override var element: PsiElement? = null,
15 | val iteratedValue: String = ""
16 | ) : TestingChecklistLeafNode(
17 | description, element
18 | ) {
19 | /**
20 | * Generate a test method for a for each statement.
21 | *
22 | * @param project the project.
23 | * @return the PsiMethod representing the test.
24 | */
25 | override fun generateTestMethod(project: Project): PsiMethod {
26 | val methodName = TestMethodGenerationMessageBundleHandler.message("forEachTestCaseName")
27 | return super.generateTestMethod(project, methodName)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/testingChecklist/leafNodes/branchingStatements/TryStatementChecklistNode.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.testingChecklist.leafNodes.branchingStatements
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.psi.PsiElement
5 | import com.intellij.psi.PsiMethod
6 | import com.intellij.util.xmlb.annotations.OptionTag
7 | import com.testknight.messageBundleHandlers.TestMethodGenerationMessageBundleHandler
8 | import com.testknight.models.testingChecklist.leafNodes.TestingChecklistLeafNode
9 | import com.testknight.utilities.PsiConverter
10 |
11 | data class TryStatementChecklistNode(
12 | override var description: String = "",
13 | @OptionTag(converter = PsiConverter::class)
14 | override var element: PsiElement? = null,
15 | val exceptionName: String? = "",
16 | val isExceptionThrown: Boolean = exceptionName != null
17 | ) : TestingChecklistLeafNode(description, element) {
18 |
19 | /**
20 | * Generate a test method for a try statement.
21 | *
22 | * @param project the project.
23 | * @return the PsiMethod representing the test.
24 | */
25 | override fun generateTestMethod(project: Project): PsiMethod {
26 | val methodName = TestMethodGenerationMessageBundleHandler.message("tryTestCaseName")
27 | return super.generateTestMethod(project, methodName)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/testingChecklist/leafNodes/branchingStatements/SwitchStatementChecklistNode.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.testingChecklist.leafNodes.branchingStatements
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.psi.PsiElement
5 | import com.intellij.psi.PsiMethod
6 | import com.intellij.util.xmlb.annotations.OptionTag
7 | import com.testknight.messageBundleHandlers.TestMethodGenerationMessageBundleHandler
8 | import com.testknight.models.testingChecklist.leafNodes.TestingChecklistLeafNode
9 | import com.testknight.utilities.PsiConverter
10 |
11 | data class SwitchStatementChecklistNode(
12 | override var description: String = "",
13 | @OptionTag(converter = PsiConverter::class)
14 | override var element: PsiElement? = null,
15 | val switchVariable: String = "",
16 | val value: String? = "",
17 | val isDefaultCase: Boolean = value == null
18 | ) : TestingChecklistLeafNode(description, element) {
19 | /**
20 | * Generate a test method for a switch statement.
21 | *
22 | * @param project the project.
23 | * @return the PsiMethod representing the test.
24 | */
25 | override fun generateTestMethod(project: Project): PsiMethod {
26 | val methodName = TestMethodGenerationMessageBundleHandler.message("switchTestCaseName")
27 | return super.generateTestMethod(project, methodName)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/models/TruthTableTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import org.junit.Test
4 | import kotlin.test.assertEquals
5 |
6 | internal class TruthTableTest {
7 |
8 | @Test
9 | fun testBasic() {
10 | val table = TruthTable(listOf("a", "b"), "a && b")
11 |
12 | assertEquals(table.value(0), false)
13 | assertEquals(table.value(1), false)
14 | assertEquals(table.value(2), false)
15 | assertEquals(table.value(3), true)
16 | }
17 |
18 | @Test
19 | fun testNonlazy() {
20 | val table = TruthTable(listOf("a", "b"), "a & b")
21 |
22 | assertEquals(table.value(0), false)
23 | assertEquals(table.value(1), false)
24 | assertEquals(table.value(2), false)
25 | assertEquals(table.value(3), true)
26 | }
27 |
28 | @Test
29 | fun testXor() {
30 | val table = TruthTable(listOf("a", "b"), "a ^ b")
31 |
32 | assertEquals(table.value(0), false)
33 | assertEquals(table.value(1), true)
34 | assertEquals(table.value(2), true)
35 | assertEquals(table.value(3), false)
36 | }
37 |
38 | @Test
39 | fun testAssignments() {
40 | val table = TruthTable(listOf("a", "b"), "a && b")
41 |
42 | val assignments = table.assignments(3)
43 |
44 | assertEquals(assignments["a"], true)
45 | assertEquals(assignments["b"], true)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/settings/AddClassAction.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.settings
2 |
3 | import com.intellij.openapi.actionSystem.AnAction
4 | import com.intellij.openapi.actionSystem.AnActionEvent
5 | import com.intellij.ui.treeStructure.Tree
6 | import com.testknight.models.ParameterSuggestionTreeModel
7 | import javax.swing.tree.TreePath
8 |
9 | class AddClassAction : AnAction() {
10 | var tree: Tree? = null
11 |
12 | /**
13 | * Adds a new class node and starts editing the class node.
14 | *
15 | * @param e Carries information on the invocation place
16 | */
17 | override fun actionPerformed(e: AnActionEvent) {
18 |
19 | val path = TreePath((tree!!.model as ParameterSuggestionTreeModel).root).pathByAddingChild("NewClass")
20 | (tree!!.model as ParameterSuggestionTreeModel).addPathElement(path)
21 | tree!!.startEditingAtPath(path)
22 | }
23 |
24 | /**
25 | * Updates the state of the action.
26 | * The action is enabled only if the tree is not null.
27 | *
28 | * @param e Carries information on the invocation place
29 | */
30 | override fun update(e: AnActionEvent) {
31 | e.presentation.isEnabled = (tree != null)
32 | }
33 |
34 | /**
35 | * Sets the tree attribute which is used for accessing tree functions.
36 | */
37 | fun init(tree: Tree) {
38 | this.tree = tree
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/checklist/ClearChecklistAction.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.checklist
2 |
3 | import com.intellij.openapi.actionSystem.AnAction
4 | import com.intellij.openapi.actionSystem.AnActionEvent
5 | import com.intellij.openapi.components.service
6 | import com.intellij.openapi.project.Project
7 | import com.testknight.services.checklist.ChecklistTreeService
8 |
9 | class ClearChecklistAction : AnAction() {
10 |
11 | /**
12 | * Clears the Checklist tree.
13 | *
14 | * @param event Event received when the associated menu item is chosen.
15 | */
16 | override fun actionPerformed(event: AnActionEvent) {
17 | actionPerformed(event.project!!)
18 | }
19 |
20 | /**
21 | * Clears the Checklist tree.
22 | *
23 | * @param project current open project.
24 | */
25 | private fun actionPerformed(project: Project) {
26 | val checklistTreeService = project.service()
27 | checklistTreeService.resetTree()
28 | }
29 |
30 | /**
31 | * Determines whether this action button is available for the current context.
32 | * Requires a project to be open.
33 | *
34 | * @param e Event received when the associated group-id menu is chosen.
35 | */
36 | override fun update(e: AnActionEvent) {
37 | // Set the availability if project is open
38 | e.presentation.isEnabled = e.project != null
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/highlightResolutionStrategies/AssertionArgsStrategy.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.highlightResolutionStrategies
2 |
3 | import com.intellij.psi.PsiMethod
4 | import com.intellij.psi.PsiMethodCallExpression
5 | import com.intellij.psi.util.PsiTreeUtil
6 | import com.testknight.models.HighlightedTextData
7 |
8 | object AssertionArgsStrategy : HighlightResolutionStrategy {
9 |
10 | /**
11 | * A set of recognized assertion names
12 | */
13 | private val assertionNames = setOf(
14 | "assertEquals",
15 | "assertTrue",
16 | "assertFalse",
17 | "assertNotNull",
18 | "assertNull",
19 | "assertSame",
20 | "assertNotSame"
21 | )
22 |
23 | override val priority: Int = 2
24 |
25 | override val settingsName = "Highlight assertion statements"
26 |
27 | override fun getElements(psiMethod: PsiMethod): List {
28 |
29 | val res = arrayListOf()
30 |
31 | PsiTreeUtil.findChildrenOfType(psiMethod, PsiMethodCallExpression::class.java)
32 | .forEach { methodCall ->
33 | if (assertionNames.contains(methodCall.methodExpression.qualifiedName)) {
34 | methodCall.argumentList.expressions.forEach {
35 | res.add(HighlightedTextData(it))
36 | }
37 | }
38 | }
39 |
40 | return res
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/services/TestAnalyzerService.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.services
2 |
3 | import com.intellij.psi.PsiClass
4 | import com.intellij.psi.PsiMethod
5 | import com.intellij.psi.util.PsiTreeUtil
6 |
7 | class TestAnalyzerService {
8 |
9 | /**
10 | * A list of all recognized test annotations
11 | */
12 | private val testAnnotations = setOf(
13 | "Test",
14 | "ParameterizedTest",
15 | "org.junit.jupiter.api.Test",
16 | "org.junit.jupiter.api.ParameterizedTest",
17 | "org.junit.Test",
18 | "org.junit.ParameterizedTest"
19 | )
20 |
21 | /**
22 | * Checks if a Psi class is a test class.
23 | *
24 | * @param psiClass the PsiClass to be examined.
25 | * @return true iff the given class contains a test method.
26 | */
27 | fun isTestClass(psiClass: PsiClass): Boolean {
28 | val methods = PsiTreeUtil.findChildrenOfType(psiClass, PsiMethod::class.java)
29 | methods.forEach { if (isTestMethod(it)) return true }
30 | return false
31 | }
32 |
33 | /**
34 | * Checks if a PSI method is a test method.
35 | *
36 | * @param method the PSI method
37 | * @return true iff the method is a test method
38 | */
39 | fun isTestMethod(method: PsiMethod): Boolean {
40 | val annotations = method.annotations.map { it.qualifiedName }.toSet()
41 | return annotations.intersect(testAnnotations).isNotEmpty()
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/documentation/mockups/CodeCoverage/DetailedCodeCoverage.md:
--------------------------------------------------------------------------------
1 | # Description of the Coverage mockup
2 |
3 | - The Tool window will contain useful information regarding the coverage history. For example, the old and new branch coverage %.
4 | - The image gives a rough idea about the structure of the layout. The user can expand on a test method to see old coverage information as well.
5 | - The main editor which contains the source code will highlight the changes in coverages.
6 | - The new coverage will be highlighted light green.
7 | - The old coverage which didn't change will be highlighted in dark green.
8 | - The old coverage which is no longer covered will be highlighted in red.
9 | - Every other case will not have any highlights.
10 | - This is not the final choice for the colors. The colors can be changed after considering potential issues due to color blindness.
11 | - The user can also request a new diff tab where the old and new coverage will be shown side by side with the usual IntelliJ coverage color scheme. Here any changes in the source code can also be shown.
12 | - The image shows the diff window IntelliJ uses. No modification to their UI has been made there and it already showcases all the necessary diff information.
13 | - The user can request these information by pressing a button in the tool window or by using an action hotkey.
14 |
15 | # Graphical mockup
16 | ###Full Window View
17 | 
18 | ###Diff View
19 | 
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/validation/ContentValidator.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.validation;
2 |
3 | import com.testknight.TestKnightTelemetryServer.dataTransferObjects.requests.*;
4 | import com.testknight.TestKnightTelemetryServer.exceptions.*;
5 | import com.testknight.TestKnightTelemetryServer.repositories.*;
6 |
7 | public class ContentValidator extends BaseValidator {
8 |
9 | private ActionRepository actionRepository;
10 |
11 | public ContentValidator(ActionRepository actionRepository) {
12 | this.actionRepository = actionRepository;
13 | }
14 |
15 | /**
16 | * Handles the request.
17 | *
18 | * @param requestDto the request to be handled.
19 | * @throws ValidationException thrown iff the request is invalid.
20 | */
21 | @Override
22 | public void handle(UsageDataDto requestDto) throws ValidationException {
23 | if (requestDto.getActionsRecorded() == null) {
24 | throw new NullFieldException("actionsRecorded");
25 | }
26 | if (requestDto.getUserId() == null) {
27 | throw new NullFieldException("userId");
28 | }
29 | for (ActionEventDto actionEventDto : requestDto.getActionsRecorded()) {
30 | if (!actionRepository.existsById(actionEventDto.getActionId())) {
31 | throw new InvalidActionIdException(actionEventDto.getActionId());
32 | }
33 | }
34 | super.handle(requestDto);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/views/coverage/CoverageStatsCellRenderer.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.views.coverage
2 |
3 | import com.intellij.ui.ColoredTableCellRenderer
4 | import com.intellij.ui.JBColor
5 | import com.intellij.ui.SimpleTextAttributes
6 | import com.testknight.models.CoverageStatsObject
7 | import javax.swing.JTable
8 |
9 | class CoverageStatsCellRenderer : ColoredTableCellRenderer() {
10 | override fun customizeCellRenderer(
11 | table: JTable,
12 | value: Any?,
13 | selected: Boolean,
14 | hasFocus: Boolean,
15 | row: Int,
16 | column: Int
17 | ) {
18 |
19 | if (value is CoverageStatsObject) {
20 |
21 | append("${value.percentageCovered}%")
22 | val percentChange = value.percentChange
23 | if (percentChange > 0) {
24 | append(
25 | " (+$percentChange)%",
26 | SimpleTextAttributes(
27 | SimpleTextAttributes.STYLE_SMALLER,
28 | JBColor.GREEN
29 | )
30 | )
31 | } else if (percentChange < 0) {
32 | append(
33 | " ($percentChange)%",
34 | SimpleTextAttributes(
35 | SimpleTextAttributes.STYLE_SMALLER,
36 | JBColor.RED
37 | )
38 | )
39 | } else {
40 | append(" (+0)%", SimpleTextAttributes.GRAY_SMALL_ATTRIBUTES)
41 | }
42 |
43 | append(" (${value.coveredLines}/${value.allLines})")
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/plugin/testdata/Dijkstra.java:
--------------------------------------------------------------------------------
1 | public class Dijkstra {
2 |
3 | /**
4 | * Dijkstra implementation
5 | *
6 | * @param s start node index
7 | * @param n number of nodes
8 | * @param edges set of edges
9 | * @return shortest distance from s to each node
10 | */
11 | public static int[] dijkstra(int s, int n, Set edges) {
12 |
13 | PriorityQueue pq = new PriorityQueue<>();
14 |
15 | int[] distances = new int[n];
16 |
17 | // set distances to all nodes to infinity
18 | for (int i=0; i 0 && tree!!.selectionPath!!.pathCount >= 2)
36 | }
37 |
38 | /**
39 | * Sets the tree attribute which is used for accessing tree functions.
40 | */
41 | fun init(tree: Tree) {
42 | this.tree = tree
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/testlist/ClearTestAction.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.testlist
2 |
3 | import com.intellij.openapi.actionSystem.AnAction
4 | import com.intellij.openapi.actionSystem.AnActionEvent
5 | import com.intellij.openapi.components.service
6 | import com.intellij.openapi.project.Project
7 | import com.intellij.ui.treeStructure.Tree
8 | import com.testknight.utilities.UserInterfaceHelper
9 | import javax.swing.tree.DefaultMutableTreeNode
10 | import javax.swing.tree.DefaultTreeModel
11 |
12 | class ClearTestAction : AnAction() {
13 |
14 | /**
15 | * Clears the TestList tree.
16 | *
17 | * @param event Event received when the associated menu item is chosen.
18 | */
19 | override fun actionPerformed(event: AnActionEvent) {
20 | actionPerformed(event.project!!)
21 | }
22 |
23 | /**
24 | * Clears the TestList tree.
25 | *
26 | * @param project current open project.
27 | */
28 | fun actionPerformed(project: Project) {
29 | val viewport = project.service().getTabViewport("Test List") ?: return
30 | val testListTree = viewport.view as Tree
31 | val root = testListTree.model.root as DefaultMutableTreeNode
32 | root.removeAllChildren()
33 | (testListTree.model as DefaultTreeModel).reload()
34 | }
35 |
36 | /**
37 | * Determines whether this action button is available for the current context.
38 | * Requires a project to be open.
39 | *
40 | * @param e Event received when the associated group-id menu is chosen.
41 | */
42 | override fun update(e: AnActionEvent) {
43 | // Set the availability if project is open
44 | e.presentation.isEnabled = e.project != null
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/services/checklist/ChecklistTreePersistent.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.services.checklist
2 |
3 | import com.intellij.openapi.components.PersistentStateComponent
4 | import com.intellij.openapi.components.State
5 | import com.intellij.openapi.components.Storage
6 | import com.intellij.openapi.project.Project
7 | import com.testknight.models.testingChecklist.TestingChecklist
8 |
9 | @State(name = "ChecklistTreePersistent", storages = [Storage("checklistData.xml")])
10 | class ChecklistTreePersistent(val project: Project) : PersistentStateComponent {
11 |
12 | private var testingChecklistTree: TestingChecklist = TestingChecklist(mutableListOf())
13 |
14 | /**
15 | * @return a component state. All properties, public and annotated fields are serialized. Only values, which differ
16 | * from the default (i.e., the value of newly instantiated class) are serialized. `null` value indicates
17 | * that the returned state won't be stored, as a result previously stored state will be used.
18 | * @see com.intellij.util.xmlb.XmlSerializer
19 | */
20 | override fun getState(): TestingChecklist {
21 | return testingChecklistTree
22 | }
23 |
24 | /**
25 | * This method is called when new component state is loaded. The method can and will be called several times, if
26 | * config files were externally changed while IDE was running.
27 | *
28 | *
29 | * State object should be used directly, defensive copying is not required.
30 | *
31 | * @param state loaded component state
32 | * @see com.intellij.util.xmlb.XmlSerializerUtil.copyBean
33 | */
34 | override fun loadState(state: TestingChecklist) {
35 | testingChecklistTree = state
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/plugin/testdata/PointTest.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.BeforeAll;
5 | import org.junit.jupiter.api.AfterAll;
6 | import org.junit.jupiter.params.ParameterizedTest;
7 | import org.junit.jupiter.params.provider.CsvSource;
8 |
9 | import static org.junit.jupiter.api.Assertions.*;
10 |
11 | public class PointTest {
12 |
13 | @BeforeAll
14 | public static void before() throws Exception {
15 | return;
16 | }
17 |
18 | @AfterAll
19 | public static void after() throws Exception {
20 | return;
21 | }
22 |
23 | @Test
24 | void translateTest() {
25 | Point p1 = new Point(0, 0);
26 | Point p2 = new Point(1, 2);
27 | p1.translate(1, 2);
28 | assertEquals(p1, p2);
29 | }
30 |
31 |
32 | @Test
33 | void setXTest() {
34 | Point p = new Point(0, 0);
35 | p.setX(5);
36 | assertEquals(5, p.getX());
37 | }
38 |
39 | @Test
40 | void setYTest() {
41 | Point p = new Point(0, 0);
42 | p.setY(5);
43 | assertEquals(5, p.getY());
44 | }
45 |
46 | @ParameterizedTest(name = "x1={0}, y1={1}, x2={2}, y2={3}, x3={4}, y3={5}")
47 | @CsvSource({
48 | "1,1,0,0,1,1",
49 | "-1,-1,1,1,0,0",
50 | "1,2,0,3,1,5"
51 | })
52 | void parameterizedTest(int x1, int y1, int x2, int y2, int x3, int y3) {
53 | Point p = new Point(x1, y1);
54 | Point expected = new Point(x3, y3);
55 | p.translate(x2, y2);
56 | assertEquals(expected, p);
57 | }
58 |
59 | // this is not a test, should not be returned
60 | void notATest() {
61 | return;
62 | }
63 |
64 | void simple() {
65 | assertTrue(true);
66 | }
67 | }
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/services/ExceptionHandlerService.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.services
2 |
3 | import com.intellij.notification.NotificationGroupManager
4 | import com.intellij.notification.NotificationType
5 | import com.intellij.openapi.project.Project
6 | import com.testknight.exceptions.TestKnightException
7 |
8 | class ExceptionHandlerService(private val project: Project) {
9 |
10 | /**
11 | * Display a notification with the provided title and content.
12 | * The type will represents the way that notification should be displayed: Warning, Error and Information.
13 | *
14 | * @param title String which represents the title of the Notification
15 | * @param content String which represents the content of the Notification
16 | * @param type String which represents the Icon type (WARNING, ERROR and INFORMATION)
17 | */
18 | fun notify(title: String, content: String, type: NotificationType) {
19 |
20 | NotificationGroupManager.getInstance()
21 | .getNotificationGroup("testknight.notifications")
22 | .createNotification(title, content, type)
23 | .notify(project)
24 | }
25 |
26 | /**
27 | * Display a notification with the provided title and content.
28 | * The type will represents the way that notification should be displayed: Warning, Error and Information.
29 | *
30 | * @param exception the TestKnightException for which the user should be notified
31 | */
32 | fun notify(exception: TestKnightException) {
33 | NotificationGroupManager.getInstance()
34 | .getNotificationGroup("testknight.notifications")
35 | .createNotification(exception.title, exception.content, exception.type)
36 | .notify(project)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/extensions/TestKnightTestCase.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.extensions
2 |
3 | import com.intellij.openapi.editor.Editor
4 | import com.intellij.openapi.project.Project
5 | import com.intellij.psi.PsiClass
6 | import com.intellij.psi.PsiFile
7 | import com.intellij.psi.PsiMethod
8 | import com.intellij.psi.util.PsiTreeUtil
9 | import com.intellij.testFramework.fixtures.BasePlatformTestCase
10 | import com.testknight.settings.SettingsService
11 | import org.jetbrains.annotations.NotNull
12 |
13 | open class TestKnightTestCase : BasePlatformTestCase() {
14 |
15 | override fun setUp() {
16 | super.setUp()
17 | SettingsService.instance.resetState()
18 | }
19 |
20 | override fun getTestDataPath() = "testdata"
21 |
22 | fun getBasicTestInfo(filepath: String): Data {
23 | myFixture.configureByFile(filepath)
24 | val project = myFixture.project
25 | val psiClass = PsiTreeUtil.findChildOfType(myFixture.file, PsiClass::class.java)
26 | val psiFile = myFixture.file
27 | val testClasses = PsiTreeUtil.findChildrenOfType(psiFile, PsiClass::class.java)
28 | val editor = myFixture.editor
29 | return Data(filepath, project, psiClass, psiFile, testClasses, editor)
30 | }
31 |
32 | fun getMethodByName(methodName: String): PsiMethod {
33 | val psi = this.myFixture.file
34 | val testClass = PsiTreeUtil.findChildOfType(psi, PsiClass::class.java)
35 | return testClass!!.findMethodsByName(methodName)[0] as PsiMethod
36 | }
37 | }
38 |
39 | data class Data(
40 | var filepath: String,
41 | var project: Project,
42 | var psiClass: PsiClass?,
43 | var psiFile: PsiFile,
44 | val testClasses: @NotNull MutableCollection,
45 | val editor: Editor
46 | )
47 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/extensions/DiffCoverageListener.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.extensions
2 |
3 | import com.intellij.coverage.CoverageDataManager
4 | import com.intellij.coverage.CoverageSuiteListener
5 | import com.intellij.ide.DataManager
6 | import com.intellij.openapi.actionSystem.ActionManager
7 | import com.intellij.openapi.actionSystem.AnActionEvent
8 | import com.intellij.openapi.components.service
9 | import com.intellij.openapi.project.Project
10 | import com.testknight.services.CoverageDataService
11 | import com.testknight.services.CoverageHighlighterService
12 | import com.testknight.settings.SettingsService
13 |
14 | class DiffCoverageListener(val project: Project) : CoverageSuiteListener {
15 |
16 | private val covDataService = project.service()
17 | private val covDataManager = CoverageDataManager.getInstance(project)
18 |
19 | override fun beforeSuiteChosen() {
20 | // pass
21 | }
22 |
23 | override fun afterSuiteChosen() {
24 |
25 | val suite = covDataManager.currentSuitesBundle
26 | val data = suite.coverageData
27 |
28 | covDataService.updateCoverage(suite, data)
29 | DataManager.getInstance().dataContextFromFocusAsync
30 | .onSuccess {
31 | val actionEvent = AnActionEvent.createFromDataContext("DiffCoverageListener", null, it)
32 | val action = ActionManager.getInstance().getAction("LoadCoverageAction")
33 |
34 | if (actionEvent.project != null) {
35 | action.actionPerformed(actionEvent)
36 | }
37 | }
38 |
39 | if (SettingsService.state.coverageSettings.showIntegratedView) {
40 |
41 | project.service().rebuildHighlights()
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/services/TestTracingServiceTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.services
2 |
3 | import com.intellij.testFramework.UsefulTestCase
4 | import com.intellij.util.ThrowableRunnable
5 | import com.testknight.exceptions.CorruptedTraceFileException
6 | import com.testknight.extensions.TestKnightTestCase
7 | import com.testknight.models.TestCoverageData
8 | import org.junit.jupiter.api.Test
9 | import java.io.File
10 |
11 | internal class TestTracingServiceTest : TestKnightTestCase() {
12 |
13 | @Test
14 | fun testTraceFileReading() {
15 | val service = TestTracingService(project)
16 |
17 | val file = File("testdata/traceFiles/PointTest,test.tr")
18 |
19 | val coverageData = service.readTraceFile(file)
20 |
21 | assertContainsElements(coverageData.classes["Point"]!!, 8, 9, 10, 11, 14, 30, 31, 32, 50, 54)
22 | }
23 |
24 | @Test
25 | fun testInvalidTraceFileReading() {
26 | val service = TestTracingService(project)
27 |
28 | val file = File("testdata/traceFiles/BadPointTest,test.tr")
29 |
30 | assertThrows(
31 | CorruptedTraceFileException::class.java,
32 | ThrowableRunnable {
33 | service.readTraceFile(file)
34 | }
35 | )
36 | }
37 |
38 | fun testHighlightEditor() {
39 | val service = TestTracingService(project)
40 |
41 | val covData = TestCoverageData("test")
42 | covData.classes["test.Person"] = mutableListOf(1, 3, 3)
43 | service.activeCovData = covData
44 |
45 | myFixture.configureByFile("Person.java")
46 | val editor = myFixture.editor
47 |
48 | service.highlightEditor(editor)
49 |
50 | UsefulTestCase.assertSize(3, editor.markupModel.allHighlighters)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/domain/factories/UsageRecordFactory.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.domain.factories;
2 |
3 | import com.testknight.TestKnightTelemetryServer.dataTransferObjects.requests.*;
4 | import com.testknight.TestKnightTelemetryServer.exceptions.*;
5 | import com.testknight.TestKnightTelemetryServer.domain.model.*;
6 | import org.springframework.stereotype.*;
7 |
8 | import java.util.*;
9 |
10 | @Component
11 | public class UsageRecordFactory {
12 |
13 | /**
14 | * Creates a list of UsageRecords from a UsageDataDto.
15 | *
16 | * @param usageDataDto the DTO to create from.
17 | * @return a list of UsageRecord objects.
18 | */
19 | public List createUsageRecordFromDto(UsageDataDto usageDataDto) {
20 | validateNonNullFields(usageDataDto);
21 | ArrayList usageRecords = new ArrayList<>();
22 | for (ActionEventDto actionEventDto : usageDataDto.getActionsRecorded()) {
23 | usageRecords.add(
24 | new UsageRecord(
25 | usageDataDto.getUserId(),
26 | new Action(actionEventDto.getActionId()),
27 | actionEventDto.getDateTime()
28 | )
29 | );
30 | }
31 | return usageRecords;
32 | }
33 |
34 | private void validateNonNullFields(UsageDataDto dto) throws NullFieldException {
35 | if (dto.getUserId() == null) {
36 | throw new NullFieldException("userId");
37 | } else if (dto.getHash() == null) {
38 | throw new NullFieldException("hash");
39 | } else if (dto.getActionsRecorded() == null) {
40 | throw new NullFieldException("actionsRecorded");
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/plugin/testdata/expected/Dijkstra.testDijkstraTest.java:
--------------------------------------------------------------------------------
1 | public class Dijkstra {
2 |
3 | /**
4 | * Dijkstra implementation
5 | *
6 | * @param s start node index
7 | * @param n number of nodes
8 | * @param edges set of edges
9 | * @return shortest distance from s to each node
10 | */
11 | public static int[] dijkstra(int s, int n, Set edges) {
12 |
13 | PriorityQueue pq = new PriorityQueue<>();
14 |
15 | int[] distances = new int[n];
16 |
17 | // set distances to all nodes to infinity
18 | for (int i=0; i {
10 |
11 | companion object Factory {
12 | /**
13 | * Creates a new ThrowStatementChecklistGenerationStrategy object.
14 | *
15 | * @return a ThrowStatementChecklistGenerationStrategy object.
16 | */
17 | fun create(): ThrowStatementChecklistGenerationStrategy {
18 | return ThrowStatementChecklistGenerationStrategy()
19 | }
20 | }
21 |
22 | /**
23 | * Generates the checklist for a given throw statement.
24 | *
25 | * @param psiElement the throw statement.
26 | * @return a list of TestingChecklistLeafNode objects corresponding to the required checklist items.
27 | */
28 |
29 | override fun generateChecklist(psiElement: PsiThrowStatement): List {
30 | val newExpression = (psiElement.exception as? PsiNewExpression)
31 | val nameOfException = newExpression?.classReference?.qualifiedName
32 | if (newExpression == null || nameOfException == null) return emptyList()
33 | return listOf(
34 | ThrowStatementChecklistNode(
35 | TestingChecklistMessageBundleHandler.message("throw", nameOfException),
36 | psiElement,
37 | nameOfException
38 | )
39 | )
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/plugin/testdata/PointTestFullAnnotations.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.BeforeAll;
5 | import org.junit.jupiter.api.AfterAll;
6 | import org.junit.jupiter.params.ParameterizedTest;
7 | import org.junit.jupiter.params.provider.CsvSource;
8 |
9 | import static org.junit.jupiter.api.Assertions.*;
10 |
11 | public class PointTest {
12 |
13 | @BeforeAll
14 | public static void before() throws Exception {
15 | return;
16 | }
17 |
18 | @AfterAll
19 | public static void after() throws Exception {
20 | return;
21 | }
22 |
23 | @org.junit.jupiter.api.Test
24 | void translateTest() {
25 | Point p1 = new Point(0, 0);
26 | Point p2 = new Point(1, 2);
27 | p1.translate(1, 2);
28 | assertEquals(p1, p2);
29 | }
30 |
31 |
32 | @org.junit.jupiter.api.Test
33 | void setXTest() {
34 | Point p = new Point(0, 0);
35 | p.setX(5);
36 | assertEquals(5, p.getX());
37 | }
38 |
39 | @org.junit.jupiter.api.Test
40 | void setYTest() {
41 | Point p = new Point(0, 0);
42 | p.setY(5);
43 | assertEquals(5, p.getY());
44 | }
45 |
46 | @org.junit.jupiter.api.ParameterizedTest(name = "x1={0}, y1={1}, x2={2}, y2={3}, x3={4}, y3={5}")
47 | @CsvSource({
48 | "1,1,0,0,1,1",
49 | "-1,-1,1,1,0,0",
50 | "1,2,0,3,1,5"
51 | })
52 | void parameterizedTest(int x1, int y1, int x2, int y2, int x3, int y3) {
53 | Point p = new Point(x1, y1);
54 | Point expected = new Point(x3, y3);
55 | p.translate(x2, y2);
56 | assertEquals(expected, p);
57 | }
58 |
59 | // this is not a test, should not be returned
60 | void notATest() {
61 | return;
62 | }
63 |
64 | void simple() {
65 | assertTrue(true);
66 | }
67 | }
--------------------------------------------------------------------------------
/server/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '2.5.0'
3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE'
4 | id 'java'
5 |
6 | id 'io.freefair.lombok' version "6.0.0-m2"
7 |
8 | id 'jacoco'
9 | id 'checkstyle'
10 | id 'pmd'
11 | }
12 |
13 | group = 'com.testknight'
14 | version = '0.0.1-SNAPSHOT'
15 | sourceCompatibility = '11'
16 |
17 | configurations {
18 | compileOnly {
19 | extendsFrom annotationProcessor
20 | }
21 | }
22 |
23 | repositories {
24 | mavenCentral()
25 | }
26 |
27 | dependencies {
28 | implementation 'org.springframework.boot:spring-boot-starter-web'
29 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
30 | implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
31 | implementation group: 'commons-codec', name: 'commons-codec', version: '1.5'
32 | compileOnly 'org.projectlombok:lombok:1.18.20'
33 | runtimeOnly 'com.h2database:h2'
34 | runtimeOnly 'mysql:mysql-connector-java'
35 | annotationProcessor 'org.projectlombok:lombok:1.18.20'
36 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
37 | }
38 |
39 | jacoco {
40 | toolVersion = "0.8.6"
41 | }
42 |
43 | test {
44 | useJUnitPlatform()
45 | jacoco {
46 | enabled = true
47 | }
48 | }
49 |
50 | jacocoTestCoverageVerification() {
51 | dependsOn test
52 | violationRules {
53 | rule {
54 | enabled = true
55 | element = 'CLASS'
56 | includes = ['com.testknight.server.*']
57 |
58 | limit {
59 | counter = 'BRANCH'
60 | value = 'COVEREDRATIO'
61 | minimum = 0.1
62 | }
63 | }
64 | }
65 | }
66 |
67 |
68 | checkstyle {
69 | toolVersion "8.37"
70 | configFile = file("${rootDir}/server/config/checkstyle/checkstyle.xml")
71 | }
72 |
73 | pmd {
74 | incrementalAnalysis = true
75 | ruleSetFiles = files("${rootDir}/server/config/pmd/pmd.xml")
76 | ruleSets = []
77 | }
78 |
79 |
80 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/icons/pluginIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/checklist/ChecklistClassLineMarkerProvider.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.checklist
2 |
3 | import com.intellij.codeInsight.daemon.LineMarkerInfo
4 | import com.intellij.codeInsight.daemon.LineMarkerProvider
5 | import com.intellij.icons.AllIcons
6 | import com.intellij.openapi.editor.markup.GutterIconRenderer
7 | import com.intellij.psi.PsiClass
8 | import com.intellij.psi.PsiElement
9 | import com.intellij.psi.PsiIdentifier
10 | import com.testknight.listeners.checklist.ClassChecklistIconHandler
11 | import com.testknight.services.TestAnalyzerService
12 | import com.testknight.settings.SettingsService
13 |
14 | /**
15 | * The Line Marker which is on the same line as the class declaration
16 | */
17 | class ChecklistClassLineMarkerProvider : LineMarkerProvider {
18 |
19 | private val testAnalyzerService = TestAnalyzerService()
20 |
21 | private fun settingsState() = SettingsService.instance.state
22 |
23 | private fun isClassDeclaration(element: PsiElement) = (element is PsiIdentifier && element.parent is PsiClass)
24 |
25 | /**
26 | * The method which creates the Line Marker for the class
27 | * @param element PsiElement for which we have to build the Line Marker
28 | */
29 | override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? {
30 |
31 | if (isClassDeclaration(element) &&
32 | !testAnalyzerService.isTestClass(element.parent as PsiClass) &&
33 | settingsState().checklistSettings.showGutterIcons
34 | ) {
35 | return LineMarkerInfo(
36 | element,
37 | element.textRange,
38 | AllIcons.General.Add,
39 | { el: PsiElement -> "Generate checklist" },
40 | ClassChecklistIconHandler(), GutterIconRenderer.Alignment.RIGHT
41 | )
42 | }
43 | return null
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/checklist/ChecklistMethodLineMarkerProvider.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.checklist
2 |
3 | import com.intellij.codeInsight.daemon.LineMarkerInfo
4 | import com.intellij.codeInsight.daemon.LineMarkerProvider
5 | import com.intellij.icons.AllIcons
6 | import com.intellij.openapi.editor.markup.GutterIconRenderer
7 | import com.intellij.psi.PsiElement
8 | import com.intellij.psi.PsiIdentifier
9 | import com.intellij.psi.PsiMethod
10 | import com.testknight.listeners.checklist.MethodChecklistIconHandler
11 | import com.testknight.services.TestAnalyzerService
12 | import com.testknight.settings.SettingsService
13 |
14 | /**
15 | * The Line Marker which is on the same line as the method declaration
16 | */
17 | class ChecklistMethodLineMarkerProvider : LineMarkerProvider {
18 |
19 | private val testAnalyzerService = TestAnalyzerService()
20 |
21 | private fun settingsState() = SettingsService.instance.state
22 |
23 | private fun isMethodDeclaration(element: PsiElement) = (element is PsiIdentifier && element.parent is PsiMethod)
24 |
25 | /**
26 | * The method which creates the Line Marker for the method
27 | * @param element PsiElement for which we have to build the Line Marker
28 | */
29 | override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? {
30 |
31 | if (isMethodDeclaration(element) &&
32 | !testAnalyzerService.isTestMethod(element.parent as PsiMethod) &&
33 | settingsState().checklistSettings.showGutterIcons
34 | ) {
35 | return LineMarkerInfo(
36 | element,
37 | element.textRange,
38 | AllIcons.General.Add,
39 | { el: PsiElement -> "Generate Checklist" },
40 | MethodChecklistIconHandler(), GutterIconRenderer.Alignment.RIGHT
41 | )
42 | }
43 | return null
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/listeners/checklist/CheckListKeyboardListener.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.listeners.checklist
2 |
3 | import com.intellij.openapi.components.service
4 | import com.intellij.openapi.project.Project
5 | import com.intellij.ui.CheckboxTree
6 | import com.intellij.ui.CheckedTreeNode
7 | import com.testknight.models.ChecklistUserObject
8 | import com.testknight.models.testingChecklist.leafNodes.TestingChecklistLeafNode
9 | import com.testknight.services.checklist.ChecklistTreeService
10 | import java.awt.event.KeyAdapter
11 | import java.awt.event.KeyEvent
12 | import javax.swing.tree.TreePath
13 |
14 | class CheckListKeyboardListener(private val tree: CheckboxTree, private val project: Project) : KeyAdapter() {
15 |
16 | /**
17 | * Goes to the test method/class if ENTER has been pressed.
18 | * Duplicates the test method if SHIFT is also held along with pressing enter.
19 | * Delete a node if DELETE has been pressed
20 | *
21 | * @param e The key press event.
22 | */
23 | override fun keyPressed(e: KeyEvent) {
24 | if (e.keyCode == KeyEvent.VK_ENTER) {
25 |
26 | val path: TreePath = tree.selectionPath ?: return
27 |
28 | val node = path.lastPathComponent as CheckedTreeNode
29 | val userObject = ((node.userObject ?: return) as ChecklistUserObject)
30 |
31 | // If its the leaf node
32 | if (userObject.checklistNode is TestingChecklistLeafNode && node.isEnabled) {
33 | tree.setNodeState(node, !node.isChecked)
34 | e.consume()
35 | }
36 | } else if (e.keyCode == KeyEvent.VK_DELETE) {
37 |
38 | val path: TreePath = tree.selectionPath ?: return
39 |
40 | val node = path.lastPathComponent as CheckedTreeNode
41 |
42 | project.service().deleteElement(node)
43 | e.consume()
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/server/src/main/java/com/testknight/TestKnightTelemetryServer/controllers/UsageDataController.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.controllers;
2 |
3 | import com.testknight.TestKnightTelemetryServer.dataTransferObjects.requests.*;
4 | import com.testknight.TestKnightTelemetryServer.dataTransferObjects.responses.*;
5 | import com.testknight.TestKnightTelemetryServer.exceptions.*;
6 | import com.testknight.TestKnightTelemetryServer.services.*;
7 | import org.springframework.beans.factory.annotation.*;
8 | import org.springframework.http.*;
9 | import org.springframework.web.bind.annotation.*;
10 |
11 | @RestController
12 | @RequestMapping("/usagedata")
13 | public class UsageDataController {
14 |
15 | private final UsageRecordService usageDataService;
16 |
17 | @Autowired
18 | public UsageDataController(UsageRecordService usageDataService) {
19 | this.usageDataService = usageDataService;
20 | }
21 |
22 | @PostMapping
23 | @ResponseStatus(HttpStatus.CREATED)
24 | public UsageDataAddedDto addUsageData(@RequestBody UsageDataDto usageData) {
25 | return usageDataService.persistUsageData(usageData);
26 | }
27 |
28 | @ExceptionHandler(value = InvalidActionIdException.class)
29 | @ResponseStatus(HttpStatus.BAD_REQUEST)
30 | public ExceptionDto invalidActionIdHandler(InvalidActionIdException exception) {
31 | return new ExceptionDto(400, exception.getMessage());
32 | }
33 |
34 | @ExceptionHandler(value = NullFieldException.class)
35 | @ResponseStatus(HttpStatus.BAD_REQUEST)
36 | public ExceptionDto nullFieldHandler(NullFieldException exception) {
37 | return new ExceptionDto(400, exception.getMessage());
38 | }
39 |
40 | @ExceptionHandler(value = InvalidHashException.class)
41 | @ResponseStatus(HttpStatus.UNAUTHORIZED)
42 | public ExceptionDto invalidHashHandler(InvalidHashException exception) {
43 | return new ExceptionDto(401, exception.getMessage());
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/testlist/DuplicateTestUnderCaretAction.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.testlist
2 |
3 | import com.intellij.openapi.actionSystem.AnAction
4 | import com.intellij.openapi.actionSystem.AnActionEvent
5 | import com.intellij.openapi.actionSystem.CommonDataKeys
6 | import com.intellij.openapi.components.service
7 | import com.testknight.services.DuplicateTestsService
8 | import com.testknight.services.UsageDataService
9 | import com.testknight.utilities.UserInterfaceHelper
10 |
11 | class DuplicateTestUnderCaretAction : AnAction() {
12 |
13 | /**
14 | * Duplicates the test case under caret.
15 | *
16 | * @param event Event received when the associated menu item is chosen.
17 | */
18 | override fun actionPerformed(event: AnActionEvent) {
19 | val project = event.project!!
20 | val duplicateTestsService = project.service()
21 |
22 | val psiFile = event.getData(CommonDataKeys.PSI_FILE)!!
23 | val editor = event.getData(CommonDataKeys.EDITOR)!!
24 |
25 | if (duplicateTestsService.duplicateMethodUnderCaret(psiFile, editor)) {
26 | project.service().showTab("Test List")
27 | }
28 | UsageDataService.instance.recordDuplicateTest()
29 | }
30 |
31 | /**
32 | * Determines whether this menu item is available for the current context.
33 | * Requires a project to be open and psiFile and Editor to be accessible from the action event.
34 | *
35 | * @param e Event received when the associated group-id menu is chosen.
36 | */
37 | override fun update(e: AnActionEvent) {
38 | // Set the availability based on whether the project, psiFile and editor is not null
39 | e.presentation.isEnabled = (
40 | e.project != null &&
41 | e.getData(CommonDataKeys.PSI_FILE) != null &&
42 | e.getData(CommonDataKeys.EDITOR) != null
43 | )
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/sideEffectAnalysis/Class.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models.sideEffectAnalysis
2 |
3 | import com.intellij.psi.PsiClass
4 | import com.intellij.psi.PsiMethod
5 | import com.intellij.psi.util.PsiTreeUtil
6 |
7 | data class Class(val fields: Map) {
8 |
9 | companion object Factory {
10 | /**
11 | * Creates a new Class object, containing information regarding the
12 | * class and the class fields.
13 | *
14 | * @param method a PsiMethod from which the class information is extracted.
15 | * @return a new Class object.
16 | */
17 | fun createClassFromMethod(method: PsiMethod): Class {
18 | val parentClass =
19 | PsiTreeUtil.getParentOfType(method, PsiClass::class.java) ?: return Class(emptyMap())
20 | val result = mutableMapOf()
21 | parentClass.allFields.forEach {
22 | result[it.name] = it.type.canonicalText
23 | result["this.${it.name}"] = it.type.canonicalText
24 | }
25 | result["this"] = parentClass.name ?: "!UNKNOWN"
26 | return Class(result)
27 | }
28 | }
29 |
30 | /**
31 | * Checks whether an identifier is a class field.
32 | *
33 | * @param identifier the name of the identifier.
34 | * @param identifiersInMethodScope the identifiers in the method scope.
35 | * @return true iff identifier is a class field.
36 | */
37 | fun isClassField(
38 | identifier: String,
39 | identifiersInMethodScope: Map
40 | ): Boolean {
41 | return if (identifier.contains("this.")) {
42 | val newName = identifier.replaceFirst("this.", "")
43 | !identifiersInMethodScope.contains(newName) && this.fields.contains(newName)
44 | } else {
45 | !identifiersInMethodScope.contains(identifier) && this.fields.contains(identifier)
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/TruthTable.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import java.util.Locale
4 | import javax.script.ScriptEngineManager
5 | import javax.script.SimpleBindings
6 | import kotlin.math.pow
7 |
8 | class TruthTable(private val vars: List, expressionString: String) {
9 |
10 | private val n = vars.size
11 | private val table = arrayOfNulls(2.0.pow(n).toInt())
12 | private val engine = ScriptEngineManager().getEngineByName("groovy")
13 |
14 | init {
15 | for (i in table.indices) {
16 |
17 | val bindings = SimpleBindings()
18 |
19 | for ((varI, varStr) in vars.withIndex()) {
20 | bindings[varStr] = getBit(i, varI)
21 | }
22 |
23 | table[i] = engine.eval(expressionString, bindings) as Boolean?
24 | }
25 | }
26 |
27 | /**
28 | * Gets the ith bit in n.
29 | *
30 | * @param num the number
31 | * @param i the index of bit to be read
32 | * @return the ith bit of n
33 | */
34 | private fun getBit(num: Int, i: Int): Boolean {
35 | val binStr = String.format(
36 | Locale.getDefault(),
37 | "%${n}s",
38 | Integer.toBinaryString(num)
39 | ).replace(' ', '0')
40 | return binStr[i] == '1'
41 | }
42 |
43 | /**
44 | * Get the truth table value at a row.
45 | *
46 | * @param row the row
47 | * @return the value at index i
48 | */
49 | fun value(row: Int): Boolean {
50 | return table[row]!!
51 | }
52 |
53 | /**
54 | * Get the variable assignments for row.
55 | *
56 | * @param row the row
57 | * @return a mapping from variable names to truth values
58 | */
59 | fun assignments(row: Int): Map {
60 | val res = HashMap()
61 | for ((varI, variable) in vars.withIndex()) {
62 | res[variable] = getBit(row, varI)
63 | }
64 | return res
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/plugin/testdata/AnimalTest.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.BeforeAll;
5 | import org.junit.jupiter.api.AfterAll;
6 | import org.junit.jupiter.params.ParameterizedTest;
7 | import org.junit.jupiter.params.provider.CsvSource;
8 |
9 | import static org.junit.jupiter.api.Assertions.*;
10 |
11 | public class AnimalTest {
12 |
13 | //contrived example intentionally made large to test scrolling functionality
14 | @BeforeAll
15 | public static void before() throws Exception {
16 | return;
17 | }
18 |
19 | @AfterAll
20 | public static void after() throws Exception {
21 | return;
22 | }
23 |
24 | @org.junit.jupiter.api.Test
25 | void AnimalSoundTest() {
26 | Animal cat = new Animal("cat");
27 | Animal dog = new Animal("dog");
28 | assertEquals(cat.getSound(), "meow");
29 | }
30 |
31 | @org.junit.jupiter.api.Test
32 | void setSoundTest() {
33 | Animal dog = new Animal("dog");
34 | dog.setSound("meow");
35 | assertEquals("meow", dog.getSound());
36 | }
37 |
38 | @Test
39 | void overloadedGetFeetTest() {
40 | Animal chicken = new Animal("chicken",2);
41 | assertEquals(chicken.getFeet(), 2);
42 | }
43 |
44 |
45 | @Test
46 | void overloadedGetFeetTestHorse() {
47 | Animal horse = new Animal("horse",2);
48 | horse.setFeet(4);
49 | assertEquals(horse.getFeet(), 4);
50 | }
51 |
52 | @Test
53 | void overloadedGetFeetTestCow() {
54 | Animal cow = new Animal("cow",2);
55 | horse.setFeet(4);
56 | assertEquals(horse.getFeet(), 4);
57 | }
58 |
59 | @Test
60 | void setSoundTest() {
61 | Animal cat = new Animal("cat");
62 | cat.setSound("woof");
63 | assertEquals("woof", cat.getSound());
64 | }
65 |
66 | void notATest() {
67 | return;
68 | }
69 |
70 | @Test
71 | void simple() {
72 | assertTrue(true);
73 | }
74 |
75 |
76 | }
--------------------------------------------------------------------------------
/plugin/testdata/expected/DuplicateTestsServiceTest.testDuplicateMethod.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.BeforeAll;
5 | import org.junit.jupiter.api.AfterAll;
6 | import org.junit.jupiter.params.ParameterizedTest;
7 | import org.junit.jupiter.params.provider.CsvSource;
8 |
9 | import static org.junit.jupiter.api.Assertions.*;
10 |
11 | public class PointTest {
12 |
13 | @BeforeAll
14 | public static void before() throws Exception {
15 | return;
16 | }
17 |
18 | @AfterAll
19 | public static void after() throws Exception {
20 | return;
21 | }
22 |
23 | @Test
24 | void translateTest() {
25 | Point p1 = new Point(0, 0);
26 | Point p2 = new Point(1, 2);
27 | p1.translate(1, 2);
28 | assertEquals(p1, p2);
29 | }
30 |
31 | @Test
32 | void translateTest() {
33 | Point p1 = new Point(0, 0);
34 | Point p2 = new Point(1, 2);
35 | p1.translate(1, 2);
36 | assertEquals(p1, p2);
37 | }
38 |
39 |
40 | @Test
41 | void setXTest() {
42 | Point p = new Point(0, 0);
43 | p.setX(5);
44 | assertEquals(5, p.getX());
45 | }
46 |
47 | @Test
48 | void setYTest() {
49 | Point p = new Point(0, 0);
50 | p.setY(5);
51 | assertEquals(5, p.getY());
52 | }
53 |
54 | @ParameterizedTest(name = "x1={0}, y1={1}, x2={2}, y2={3}, x3={4}, y3={5}")
55 | @CsvSource({
56 | "1,1,0,0,1,1",
57 | "-1,-1,1,1,0,0",
58 | "1,2,0,3,1,5"
59 | })
60 | void parameterizedTest(int x1, int y1, int x2, int y2, int x3, int y3) {
61 | Point p = new Point(x1, y1);
62 | Point expected = new Point(x3, y3);
63 | p.translate(x2, y2);
64 | assertEquals(expected, p);
65 | }
66 |
67 | // this is not a test, should not be returned
68 | void notATest() {
69 | return;
70 | }
71 |
72 | void simple() {
73 | assertTrue(true);
74 | }
75 | }
--------------------------------------------------------------------------------
/plugin/testdata/expected/DuplicateTestsServiceTest.testDuplicateMethodUnderCaret.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.BeforeAll;
5 | import org.junit.jupiter.api.AfterAll;
6 | import org.junit.jupiter.params.ParameterizedTest;
7 | import org.junit.jupiter.params.provider.CsvSource;
8 |
9 | import static org.junit.jupiter.api.Assertions.*;
10 |
11 | public class PointTest {
12 |
13 | @BeforeAll
14 | public static void before() throws Exception {
15 | return;
16 | }
17 |
18 | @AfterAll
19 | public static void after() throws Exception {
20 | return;
21 | }
22 |
23 | @Test
24 | void translateTest() {
25 | Point p1 = new Point(0, 0);
26 | Point p2 = new Point(1, 2);
27 | p1.translate(1, 2);
28 | assertEquals(p1, p2);
29 | }
30 |
31 | @Test
32 | void translateTest() {
33 | Point p1 = new Point(0, 0);
34 | Point p2 = new Point(1, 2);
35 | p1.translate(1, 2);
36 | assertEquals(p1, p2);
37 | }
38 |
39 |
40 | @Test
41 | void setXTest() {
42 | Point p = new Point(0, 0);
43 | p.setX(5);
44 | assertEquals(5, p.getX());
45 | }
46 |
47 | @Test
48 | void setYTest() {
49 | Point p = new Point(0, 0);
50 | p.setY(5);
51 | assertEquals(5, p.getY());
52 | }
53 |
54 | @ParameterizedTest(name = "x1={0}, y1={1}, x2={2}, y2={3}, x3={4}, y3={5}")
55 | @CsvSource({
56 | "1,1,0,0,1,1",
57 | "-1,-1,1,1,0,0",
58 | "1,2,0,3,1,5"
59 | })
60 | void parameterizedTest(int x1, int y1, int x2, int y2, int x3, int y3) {
61 | Point p = new Point(x1, y1);
62 | Point expected = new Point(x3, y3);
63 | p.translate(x2, y2);
64 | assertEquals(expected, p);
65 | }
66 |
67 | // this is not a test, should not be returned
68 | void notATest() {
69 | return;
70 | }
71 |
72 | void simple() {
73 | assertTrue(true);
74 | }
75 | }
--------------------------------------------------------------------------------
/server/src/test/java/com/testknight/TestKnightTelemetryServer/domain/model/ActionTest.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.domain.model;
2 |
3 | import org.junit.jupiter.api.*;
4 |
5 | import static org.junit.jupiter.api.Assertions.*;
6 |
7 | class ActionTest {
8 |
9 | @Test
10 | void testGetActionId() {
11 | Action action = new Action("actionId");
12 | assertEquals("actionId", action.getActionId());
13 | }
14 |
15 | @Test
16 | void testSetActionId() {
17 | Action action = new Action();
18 | action.setActionId("newActionId");
19 | assertEquals("newActionId", action.getActionId());
20 | }
21 |
22 | @Test
23 | void testEqualsTrue() {
24 | Action actionOne = new Action("actionId");
25 | Action actionTwo = new Action("actionId");
26 | assertEquals(actionTwo, actionOne);
27 | }
28 |
29 | @Test
30 | void testEqualsSelf() {
31 | Action actionOne = new Action("actionId");
32 | assertEquals(actionOne, actionOne);
33 | }
34 |
35 | @Test
36 | void testEqualsFalse() {
37 | Action actionOne = new Action("actionId");
38 | Action actionTwo = new Action("otherActionId");
39 | assertNotEquals(actionTwo, actionOne);
40 | }
41 |
42 | @Test
43 | void testEqualsOtherType() {
44 | Action actionOne = new Action("actionId");
45 | UsageRecord usageRecord = new UsageRecord();
46 | assertNotEquals(actionOne, usageRecord);
47 | }
48 |
49 | @Test
50 | void testEqualsWithNull() {
51 | Action actionOne = new Action("actionId");
52 | assertNotEquals(actionOne, null);
53 | }
54 |
55 | @Test
56 | void testCanEqual() {
57 | Action actionOne = new Action("actionId");
58 | Action actionTwo = new Action("actionId");
59 | assertTrue(actionOne.canEqual(actionTwo));
60 | }
61 |
62 | @Test
63 | void testToString() {
64 | Action action = new Action("actionId");
65 | assertEquals("Action(actionId=actionId)", action.toString());
66 | }
67 | }
--------------------------------------------------------------------------------
/plugin/src/main/resources/META-INF/pluginIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/services/LoadTestsService.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.services
2 |
3 | import com.intellij.openapi.Disposable
4 | import com.intellij.psi.PsiClass
5 | import com.intellij.psi.PsiFile
6 | import com.intellij.psi.PsiMethod
7 | import com.intellij.psi.util.PsiTreeUtil
8 | import com.testknight.models.TestClassData
9 | import com.testknight.models.TestMethodData
10 |
11 | class LoadTestsService : Disposable {
12 |
13 | private val testAnalyzer = TestAnalyzerService()
14 |
15 | /**
16 | * Extracts all the test methods from a PSI file.
17 | *
18 | * @param file the PSI file
19 | * @return a list of TestCaseData elements representing the test cases
20 | */
21 | fun getTests(file: PsiFile): List {
22 | val methods = PsiTreeUtil.findChildrenOfType(file, PsiMethod::class.java)
23 | return methods.filter { testAnalyzer.isTestMethod(it) }.map {
24 | TestMethodData(it.name, (it.parent as PsiClass).name ?: "", it)
25 | }
26 | }
27 |
28 | /**
29 | * Extract all test methods in a PSI file in the form of a tree.
30 | *
31 | * @param file the PSI file
32 | * @return a list of TestClassData corresponding to the classes in the file
33 | */
34 | fun getTestsTree(file: PsiFile): List {
35 | val classes = PsiTreeUtil.findChildrenOfType(file, PsiClass::class.java)
36 |
37 | return classes
38 | .filter { testAnalyzer.isTestClass(it) }
39 | .map { psiClass ->
40 | TestClassData(
41 | psiClass.name ?: "",
42 | psiClass.methods
43 | .filter { testAnalyzer.isTestMethod(it) }
44 | .map { TestMethodData(it.name, psiClass.name ?: "", it) },
45 | psiClass
46 | )
47 | }
48 | }
49 |
50 | /**
51 | * Overridden function for Disposable. Doesn't require anything to be disposed.
52 | */
53 | override fun dispose() {
54 | // No specific dispose function is required.
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/services/TestMethodGenerationService.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.services
2 |
3 | import com.intellij.codeInsight.template.TemplateManager
4 | import com.intellij.openapi.components.service
5 | import com.intellij.openapi.editor.Editor
6 | import com.intellij.openapi.editor.ScrollType
7 | import com.intellij.openapi.fileEditor.FileEditorManager
8 | import com.intellij.openapi.fileEditor.TextEditor
9 | import com.intellij.openapi.project.Project
10 | import com.intellij.psi.PsiManager
11 | import com.intellij.psi.PsiMethod
12 | import com.intellij.psi.util.PsiTreeUtil
13 | import com.intellij.refactoring.suggested.endOffset
14 | import com.testknight.models.testingChecklist.leafNodes.TestingChecklistLeafNode
15 |
16 | class TestMethodGenerationService(val project: Project) {
17 |
18 | /**
19 | * Generates and appends a test method for the given checklist item
20 | * in the current caret position.
21 | *
22 | * @param editor the current editor.
23 | * @param checklistItem the checklist item with to generate the method for.
24 | */
25 | fun generateTestMethod(editor: Editor, checklistItem: TestingChecklistLeafNode) {
26 | val templateCreationService = project.service()
27 | val testMethod = checklistItem.generateTestMethod(project)
28 | val caret = editor.caretModel.primaryCaret
29 | val offset = caret.offset
30 |
31 | val textEditor = FileEditorManager.getInstance(project).selectedEditor as TextEditor? ?: return
32 | val file = PsiManager.getInstance(project).findFile(textEditor.file!!) ?: return
33 |
34 | val element = file.findElementAt(offset)
35 | val parentMethod = PsiTreeUtil.getParentOfType(element, PsiMethod::class.java)
36 | if (parentMethod != null) {
37 | caret.moveToOffset(parentMethod.endOffset)
38 | }
39 | val template = templateCreationService.createBasicTemplate(testMethod)
40 | editor.scrollingModel.scrollToCaret(ScrollType.CENTER)
41 | TemplateManager.getInstance(project).startTemplate(editor, template)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/listeners/checklist/CheckedNodeListener.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.listeners.checklist
2 |
3 | import com.intellij.ui.CheckboxTreeListener
4 | import com.intellij.ui.CheckedTreeNode
5 | import com.testknight.models.ChecklistUserObject
6 | import com.testknight.models.testingChecklist.leafNodes.TestingChecklistLeafNode
7 | import com.testknight.services.UsageDataService
8 |
9 | /**
10 | * Custom CheckboxTreeListener which support counting the selected items.
11 | *
12 | * @param checklistTree CheckboxTree the listener listens to.
13 | */
14 | class CheckedNodeListener : CheckboxTreeListener {
15 |
16 | /**
17 | * This method just makes the changes for the number of checked nodes for the selected node.
18 | *
19 | * @param node the selected CheckedTreeNode for which we have to do the changes in the checked attribute
20 | */
21 | override fun nodeStateChanged(node: CheckedTreeNode) {
22 |
23 | if (node.userObject is ChecklistUserObject) {
24 | val userObject = node.userObject as ChecklistUserObject
25 |
26 | if (userObject.checklistNode is TestingChecklistLeafNode) {
27 | if (node.isChecked) {
28 | userObject.checklistNode.checked = 1
29 | val parent = (node.parent as CheckedTreeNode)
30 | (parent.userObject as ChecklistUserObject).checklistNode.checked += 1
31 |
32 | val grandParent = (parent.parent as CheckedTreeNode)
33 | (grandParent.userObject as ChecklistUserObject).checklistNode.checked += 1
34 | UsageDataService.instance.recordItemMarked()
35 | } else {
36 | userObject.checklistNode.checked = 0
37 | val parent = (node.parent as CheckedTreeNode)
38 | (parent.userObject as ChecklistUserObject).checklistNode.checked -= 1
39 |
40 | val grandParent = (parent.parent as CheckedTreeNode)
41 | (grandParent.userObject as ChecklistUserObject).checklistNode.checked -= 1
42 | }
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/checklistGenerationStrategies/TestKnightJavaPsiVisitorTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.checklistGenerationStrategies
2 |
3 | import com.testknight.extensions.TestKnightTestCase
4 | import com.testknight.models.testingChecklist.parentNodes.TestingChecklistClassNode
5 | import com.testknight.models.testingChecklist.parentNodes.TestingChecklistMethodNode
6 | import org.junit.jupiter.api.Test
7 |
8 | internal class TestKnightJavaPsiVisitorTest : TestKnightTestCase() {
9 |
10 | @Test
11 | fun testClassInitializedProperly() {
12 | val visitor = TestKnightJavaPsiVisitor()
13 | val data = getBasicTestInfo("/EmptyClass.java")
14 | data.psiClass?.accept(visitor)
15 | val expected = TestingChecklistClassNode("EmptyClass", mutableListOf(), data.psiClass)
16 | val actual = visitor.classNode
17 | assertEquals(expected, actual)
18 | }
19 |
20 | @Test
21 | fun testMethodsAreNotAnalyzed() {
22 | val visitor = TestKnightJavaPsiVisitor()
23 | val data = getBasicTestInfo("/Tests.java")
24 | data.psiClass?.accept(visitor)
25 | val expected = TestingChecklistClassNode(
26 | "PointTest",
27 | mutableListOf(
28 | TestingChecklistMethodNode("basic", mutableListOf(), data.psiClass?.methods?.get(0)),
29 | TestingChecklistMethodNode("hasModifiers", mutableListOf(), data.psiClass?.methods?.get(1)),
30 | TestingChecklistMethodNode("hasReturnTy", mutableListOf(), data.psiClass?.methods?.get(2)),
31 | TestingChecklistMethodNode("hasParams", mutableListOf(), data.psiClass?.methods?.get(3)),
32 | TestingChecklistMethodNode("hasTypeParams", mutableListOf(), data.psiClass?.methods?.get(4)),
33 | TestingChecklistMethodNode("throwsException", mutableListOf(), data.psiClass?.methods?.get(5)),
34 | TestingChecklistMethodNode("hasAssertion", mutableListOf(), data.psiClass?.methods?.get(6))
35 | ),
36 | data.psiClass
37 | )
38 | val actual = visitor.classNode
39 | assertEquals(expected, actual)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/services/GlobalHighlighter.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.services
2 |
3 | import com.intellij.openapi.editor.Editor
4 | import com.intellij.openapi.editor.markup.RangeHighlighter
5 | import com.intellij.openapi.fileEditor.FileEditorManager
6 | import com.intellij.openapi.fileEditor.TextEditor
7 | import com.intellij.openapi.project.Project
8 |
9 | /**
10 | * Implements the logic for highlighting all editors given an editor-highlighting function
11 | */
12 | abstract class GlobalHighlighter(project: Project) {
13 |
14 | /**
15 | * Keeps track of the
16 | */
17 | private var highlighters = mutableMapOf>()
18 | private val fileEditorManager = FileEditorManager.getInstance(project)
19 |
20 | /**
21 | * Reconstruct all highlights
22 | */
23 | fun rebuildHighlights() {
24 |
25 | // remove all highlights
26 | removeHighlights()
27 |
28 | // get all open editors
29 | val editors = mutableListOf()
30 | fileEditorManager.allEditors.forEach { if (it is TextEditor) editors.add(it.editor) }
31 |
32 | // highlight them
33 | addHighlights(editors)
34 | }
35 |
36 | /**
37 | * Highlight the provided editors.
38 | *
39 | * @param editors the editors
40 | */
41 | fun addHighlights(editors: List) {
42 | editors.forEach {
43 | val newHls = highlightEditor(it)
44 | if (highlighters[it] == null) highlighters[it] = mutableListOf()
45 | highlighters[it]!!.addAll(newHls)
46 | }
47 | }
48 |
49 | /**
50 | * Remove all Highlights
51 | */
52 | fun removeHighlights() {
53 |
54 | // foreach editor, remove all of its highlights
55 | highlighters.keys.forEach { ed ->
56 | val hls = highlighters[ed]
57 | hls?.forEach { hl -> ed.markupModel.removeHighlighter(hl) }
58 | }
59 | }
60 |
61 | /**
62 | * Highlight the lines covered within an editor.
63 | *
64 | * @param editor the editor
65 | */
66 | abstract fun highlightEditor(editor: Editor): List
67 | }
68 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/checklistGenerationStrategies/leafStrategies/ThrowStatementChecklistGenerationStrategyTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.checklistGenerationStrategies.leafStrategies
2 |
3 | import com.intellij.psi.PsiThrowStatement
4 | import com.intellij.psi.util.PsiTreeUtil
5 | import com.testknight.extensions.TestKnightTestCase
6 | import com.testknight.models.testingChecklist.leafNodes.ThrowStatementChecklistNode
7 | import junit.framework.TestCase
8 | import org.junit.Test
9 |
10 | internal class ThrowStatementChecklistGenerationStrategyTest : TestKnightTestCase() {
11 |
12 | private val generationStrategy = ThrowStatementChecklistGenerationStrategy.create()
13 |
14 | @Test
15 | fun testSimpleThrow() {
16 | getBasicTestInfo("/Person.java")
17 |
18 | val method = getMethodByName("getSpouse")
19 | val throwStatement = PsiTreeUtil.findChildOfType(method, PsiThrowStatement::class.java)
20 |
21 | val expected = listOf(ThrowStatementChecklistNode("Test when NotMarriedException is thrown", throwStatement!!, "NotMarriedException"))
22 | val actual = generationStrategy.generateChecklist(throwStatement)
23 |
24 | TestCase.assertEquals(expected, actual)
25 | }
26 |
27 | @Test
28 | fun testInvalidNewExpressionInThrow() {
29 | getBasicTestInfo("/Person.java")
30 |
31 | val method = getMethodByName("methodWithBrokenThrows")
32 | val throwStatement = PsiTreeUtil.findChildrenOfType(method, PsiThrowStatement::class.java).elementAt(0)
33 |
34 | val expected = emptyList()
35 | val actual = generationStrategy.generateChecklist(throwStatement)
36 |
37 | TestCase.assertEquals(expected, actual)
38 | }
39 |
40 | @Test
41 | fun testMissingExceptionClassInThrow() {
42 | getBasicTestInfo("/Person.java")
43 |
44 | val method = getMethodByName("methodWithBrokenThrows")
45 | val throwStatement = PsiTreeUtil.findChildrenOfType(method, PsiThrowStatement::class.java).elementAt(1)
46 |
47 | val expected = emptyList()
48 | val actual = generationStrategy.generateChecklist(throwStatement)
49 |
50 | TestCase.assertEquals(expected, actual)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/documentation/design/telemetryDesign/ActionIds.md:
--------------------------------------------------------------------------------
1 | This file includes the mapping between action IDs (stored in the telemetry server's DB) and the actions
2 | that the user performed.
3 |
4 | | Action ID | Description |
5 | |----------------------|-------------------------------------------------------------------------------------------------|
6 | | duplicateTest | The user used TestKnight to duplicate a test |
7 | | gotoTest | The user used TestKnight to quickly navigate to a test |
8 | | suggestAssertion | The user used TestKnight to get assertion suggestions |
9 | | generateChecklist | The user used TestKnight to generate a testing checklist |
10 | | splitDiffView | The user used TestKnight to inspect how the coverage changed between two consecutive test runs. |
11 | | integratedDiffView | The user used TestKnight to inspect how the coverage changed between two consecutive test runs. |
12 | | traceTest | The user used TestKnight to inspect what specific lines a test covers |
13 | | generateTest | The user used TestKnight to generate a new test method from a checklist item |
14 | | itemMarked | The user marked one of the items in the generated checklist |
15 | | itemDeleted | The user deleted one of the items in the generated checklist |
16 | | runWithCoverage | The user run a test suite with coverage |
17 | | testRun | The user run a test |
18 | | testFail | A test failed |
19 | | testAdd | The user added a new test | | |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/checklist/DeleteElementChecklistAction.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.checklist
2 |
3 | import com.intellij.notification.NotificationType
4 | import com.intellij.openapi.actionSystem.AnAction
5 | import com.intellij.openapi.actionSystem.AnActionEvent
6 | import com.intellij.openapi.components.service
7 | import com.intellij.ui.CheckedTreeNode
8 | import com.intellij.ui.treeStructure.Tree
9 | import com.testknight.services.ExceptionHandlerService
10 | import com.testknight.services.checklist.ChecklistTreeService
11 | import javax.swing.tree.TreePath
12 |
13 | class DeleteElementChecklistAction : AnAction() {
14 |
15 | private lateinit var tree: Tree
16 |
17 | override fun actionPerformed(e: AnActionEvent) {
18 |
19 | val path: TreePath = tree.selectionPath
20 |
21 | if (path.lastPathComponent !is CheckedTreeNode) {
22 | notifyUser(e)
23 | return
24 | }
25 |
26 | val node = path.lastPathComponent as CheckedTreeNode
27 | if (e.project == null) {
28 | return
29 | } else {
30 | e.project!!.service().deleteElement(node)
31 | }
32 | }
33 |
34 | override fun update(e: AnActionEvent) {
35 | super.update(e)
36 | if (tree.selectionPath == null) {
37 | e.presentation.isEnabled = false
38 | }
39 | }
40 |
41 | /**
42 | * Getter for the tree attribute.
43 | *
44 | * @return the Tree attribute
45 | */
46 | fun getTree(): Tree {
47 | return tree
48 | }
49 |
50 | /**
51 | * Setter for the tree attribute.
52 | *
53 | * @param tree the new value of the Tree attribute
54 | */
55 | fun setTree(tree: Tree) {
56 | this.tree = tree
57 | }
58 |
59 | /**
60 | * Notify the user in case of no path found.
61 | *
62 | * @param e the AnActionEvent for which the user must be notified
63 | */
64 | private fun notifyUser(e: AnActionEvent) {
65 | if (e.project == null) {
66 | return
67 | } else {
68 | e.project!!.service().notify(
69 | "Delete Element not available",
70 | "Not selected element", NotificationType.WARNING
71 | )
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/documentation/mockups/Checklist/DetailedChecklist.md:
--------------------------------------------------------------------------------
1 | # Description of the Checklist mockup
2 |
3 | - We will have the checklist tree following this hierarchy:
4 | ```
5 | > class 1
6 | > method 1
7 | > Item 1
8 | > Item 2
9 | > Item 3
10 | > method 2
11 | > Item 1
12 | > Item 2
13 | > Item 3
14 | > Item 4
15 | > class 2
16 | > method 1
17 | > Item 1
18 | > Item 2
19 | > Item 3
20 | ```
21 |
22 | - For each method we will have a button which will create checklist for that method. Visually, the tree will be updated and the new method checklist items will be expanded (visible without manually pressing the button for expanding the tree).
23 | - For each class we will have a button which will create checklist for that class. Visually, the tree will be updated and the new class and its methods checklist items will be expanded (visible without manually pressing the button for expanding the tree).
24 | - Adding new method and class checklists can also be accessed by right clicking on the method/class on the code editor in addition to the button.
25 | - Near each method, we will have a label which will tell the user how many items of that specific method have been checked (i.e. "6 of 9 items checked")
26 | - Near each class, we will have a label which will tells the user how many methods checklists have been generated for that specific class (i.e. "6 of 9 methods has checklist")
27 | - The number of checked item per method will have a text color to indicate how many checklist items have been checked.
28 | - In the future, the checklist will be dynamically refreshed but it can also be manually refreshed.
29 | - If the user wants to generate the checklist for an existing class/existing method, the Toolwindow UI will not create a new checklist as it already exists. Thus, we will preserve the final version and the "human mistakes" will be avoided.
30 | - The user will be able to modify the checklist item (add/edit/remove) manually by pressing right-click on the toolwindow UI but they can only remove it for the checklist method or checklist class from the Toolwindow UI.
31 | - The stats regarding checklist will be updated after any operation which modifies the tree.
32 |
33 | # Graphical mockup
34 | ###Editor View
35 | 
36 | ###Side Bar View
37 | 
38 |
39 |
40 |
--------------------------------------------------------------------------------
/documentation/design/telemetryDesign/testKnightTelemtryApi.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.0
2 | info:
3 | title: TestKnight Telemetry Server API
4 | description: This the API specification for TestKnight's telemetry server. It contains all the necessary information about how to upload usage data.
5 | contact:
6 | name: TU Delft Software Engineering Research Group
7 | url: https://se.ewi.tudelft.nl/
8 | version: 1.0.0 #API Version
9 | servers:
10 | - url: https://testknight.ewi.tudelft.nl
11 | paths:
12 | /usagedata:
13 | description: Usage Data
14 | post:
15 | description: Operation to upload new usage data. Usage data records are uploaded in an array. This ensures that the endpoint supports both single and multiple entries. A list of all valid actions can be found under `/documentation/design/telemetry`. Furthermore the requests should include an MD5 hash to ensure that the requests are coming from an approved client. The hash is generated by hashing the contents of the request with a secret string appending. Lastly the dateTime field should follow Java's LocalDateTime format.
16 | requestBody:
17 | content:
18 | application/json:
19 | schema:
20 | type: object
21 | properties:
22 | userId:
23 | type: string
24 | format: uuid
25 | example: "123e4567-e89b-12d3-a456-426614174000"
26 | actionsRecorded:
27 | type: array
28 | items:
29 | type: object
30 | properties:
31 | actionId:
32 | type: string
33 | example: "testAdd"
34 | dateTime:
35 | type: string
36 | example: "2021-12-03T10:15:30"
37 | hash:
38 | type: string
39 | example: "79054025255fb1a26e4bc422aef54eb4"
40 | responses:
41 | 201:
42 | description: Successfully added the usage data.
43 | 400:
44 | description: Invalid input. For example an invalid action id or a date-time with the wrong format.
45 | 401:
46 | description: Unauthorized. The hash included in the request does not belong to an authorized client.
47 | tags:
48 | - Data collecting
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/listeners/testlist/TestListSelectionListener.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.listeners.testlist
2 |
3 | import com.intellij.openapi.components.service
4 | import com.intellij.openapi.project.Project
5 | import com.testknight.actions.testlist.TestListTraceabilityAction
6 | import com.testknight.exceptions.CorruptedTraceFileException
7 | import com.testknight.exceptions.NoTestCoverageDataException
8 | import com.testknight.exceptions.TraceFileNotFoundException
9 | import com.testknight.models.TestMethodUserObject
10 | import com.testknight.services.ExceptionHandlerService
11 | import com.testknight.services.TestTracingService
12 | import javax.swing.event.TreeSelectionEvent
13 | import javax.swing.event.TreeSelectionListener
14 | import javax.swing.tree.DefaultMutableTreeNode
15 |
16 | class TestListSelectionListener(
17 | private val project: Project,
18 | private val traceabilityButton: TestListTraceabilityAction
19 | ) : TreeSelectionListener {
20 |
21 | private val tracingService = project.service()
22 |
23 | /**
24 | * Called whenever the value of the selection changes.
25 | * @param e the event that characterizes the change.
26 | */
27 | override fun valueChanged(e: TreeSelectionEvent?) {
28 | tracingService.removeHighlights()
29 | if (!traceabilityButton.getSelected()) {
30 | return
31 | }
32 |
33 | val component = (e?.newLeadSelectionPath ?: return).lastPathComponent ?: return
34 | if (component is DefaultMutableTreeNode && component.userObject is TestMethodUserObject) {
35 | val testUserObject = (component.userObject as TestMethodUserObject)
36 | try {
37 | tracingService
38 | .highlightTest("${testUserObject.reference.testClassName},${testUserObject.reference.name}")
39 | } catch (ex: TraceFileNotFoundException) {
40 | project.service().notify(ex)
41 | return
42 | } catch (ex: CorruptedTraceFileException) {
43 | project.service().notify(ex)
44 | return
45 | } catch (ex: NoTestCoverageDataException) {
46 | project.service().notify(ex)
47 | return
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/checklistGenerationStrategies/leafStrategies/loopStatements/ForEachStatementChecklistGenerationStrategyTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.checklistGenerationStrategies.leafStrategies.loopStatements
2 |
3 | import com.intellij.psi.PsiElement
4 | import com.intellij.psi.PsiForeachStatement
5 | import com.intellij.psi.util.PsiTreeUtil
6 | import com.testknight.extensions.TestKnightTestCase
7 | import com.testknight.models.testingChecklist.leafNodes.loopStatements.ForEachStatementChecklistNode
8 | import junit.framework.TestCase
9 | import org.junit.Test
10 |
11 | internal class ForEachStatementChecklistGenerationStrategyTest : TestKnightTestCase() {
12 |
13 | private val generationStrategy = ForEachStatementChecklistGenerationStrategy.create()
14 |
15 | @Test
16 | fun testMissingIteratedValueReturnsEmptyList() {
17 | getBasicTestInfo("/SimpleArray.java")
18 |
19 | val method = getMethodByName("brokenForEach")
20 | val foreachStatement = PsiTreeUtil.findChildOfType(method, PsiForeachStatement::class.java)
21 |
22 | val expected = emptyList()
23 | val actual = generationStrategy.generateChecklist(foreachStatement!!)
24 |
25 | TestCase.assertEquals(expected, actual)
26 | }
27 |
28 | @Test
29 | fun testForeachChecklistGenerationCorrect() {
30 | getBasicTestInfo("/SimpleArray.java")
31 |
32 | val method = getMethodByName("incrementByOneForEach")
33 | val foreachStatement = PsiTreeUtil.findChildOfType(method, PsiForeachStatement::class.java)
34 |
35 | val expected = listOf(
36 | ForEachStatementChecklistNode(description = "Test where getArrayOfInts() is empty", foreachStatement as PsiElement, "getArrayOfInts()"),
37 | ForEachStatementChecklistNode(description = "Test where getArrayOfInts() has one element", foreachStatement as PsiElement, "getArrayOfInts()"),
38 | ForEachStatementChecklistNode(description = "Test where getArrayOfInts() is null", foreachStatement as PsiElement, "getArrayOfInts()"),
39 | ForEachStatementChecklistNode(description = "Test where foreach loop runs multiple times", foreachStatement as PsiElement, "getArrayOfInts()")
40 | )
41 | val actual = generationStrategy.generateChecklist(foreachStatement!!)
42 |
43 | TestCase.assertEquals(expected, actual)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/models/HighlightedTextData.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.models
2 |
3 | import com.intellij.openapi.application.ApplicationManager
4 | import com.intellij.psi.PsiElement
5 | import com.intellij.psi.PsiLiteralExpression
6 | import com.intellij.refactoring.suggested.endOffset
7 | import com.intellij.refactoring.suggested.startOffset
8 | import com.testknight.settings.SettingsService
9 |
10 | /**
11 | * Represents an element to be highlighted when a test is duplicated
12 | *
13 | * @param startOffset the start position of the element in the source code
14 | * @param endOffset the end position of the element in the source code
15 | * @param text the text
16 | */
17 | class HighlightedTextData(var startOffset: Int, var endOffset: Int, var text: String) {
18 |
19 | /**
20 | * Construct it for a literal. This takes into account strings.
21 | *
22 | * @param element: The PsiLiteralExpression
23 | */
24 | constructor(element: PsiElement) : this(element.startOffset, element.endOffset, element.text) {
25 |
26 | // if literal is string, include only the text inside quotes
27 | if (isQuoteHlEnabled() && (isString(element) || isChar(element))) {
28 | this.startOffset = element.startOffset + 1
29 | this.endOffset = element.endOffset - 1
30 | this.text = element.text.substring(1, element.text.length - 1)
31 | }
32 | }
33 |
34 | /**
35 | * Returns true if the provided PSI element corresponds to a string literal.
36 | *
37 | * @param element the PSI element
38 | */
39 | private fun isString(element: PsiElement) =
40 | element is PsiLiteralExpression && element.text.matches(Regex("\".*\""))
41 |
42 | /**
43 | * Returns true if the provided PSI element corresponds to a string literal.
44 | *
45 | * @param element the PSI element
46 | */
47 | private fun isChar(element: PsiElement) =
48 | element is PsiLiteralExpression && element.text.matches(Regex("'.'"))
49 |
50 | /**
51 | * Reads the settings to determine if in-quote highlighting is enabled.
52 | *
53 | * @return true if in-quote highlighting is enabled
54 | */
55 | private fun isQuoteHlEnabled() = ApplicationManager
56 | .getApplication()
57 | .getService(SettingsService::class.java)
58 | .state.testListSettings.highlightStrategies["Highlight string inside quotes"]!!
59 | }
60 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/listeners/checklist/ChecklistMouseListener.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.listeners.checklist
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.openapi.ui.JBMenuItem
5 | import com.intellij.openapi.ui.JBPopupMenu
6 | import com.intellij.ui.CheckedTreeNode
7 | import com.intellij.ui.treeStructure.Tree
8 | import com.testknight.actions.checklist.ModifyChecklistAction
9 | import com.testknight.models.ChecklistUserObject
10 | import com.testknight.models.testingChecklist.leafNodes.TestingChecklistLeafNode
11 | import com.testknight.models.testingChecklist.parentNodes.TestingChecklistMethodNode
12 | import java.awt.event.MouseAdapter
13 | import java.awt.event.MouseEvent
14 | import javax.swing.SwingUtilities
15 | import javax.swing.tree.DefaultMutableTreeNode
16 | import javax.swing.tree.TreePath
17 |
18 | class ChecklistMouseListener(private val tree: Tree, private val project: Project) : MouseAdapter() {
19 |
20 | override fun mousePressed(event: MouseEvent) {
21 |
22 | if (SwingUtilities.isRightMouseButton(event)) {
23 |
24 | val path: TreePath = tree.selectionPath ?: return
25 |
26 | if (path.lastPathComponent !is DefaultMutableTreeNode) return
27 |
28 | val node = path.lastPathComponent as CheckedTreeNode
29 |
30 | val menu = JBPopupMenu()
31 | val delete = JBMenuItem("Delete")
32 | delete.addActionListener(ModifyChecklistAction(node, tree, path, project))
33 | menu.add(delete)
34 |
35 | if ((node.userObject as ChecklistUserObject).checklistNode is TestingChecklistLeafNode) {
36 | val edit = JBMenuItem("Edit")
37 | val generate = JBMenuItem("Generate Test Method")
38 |
39 | generate.addActionListener(ModifyChecklistAction(node, tree, path, project))
40 |
41 | edit.addActionListener {
42 | it.apply { tree.startEditingAtPath(path) }
43 | }
44 |
45 | menu.add(edit)
46 | menu.add(generate)
47 | } else if ((node.userObject as ChecklistUserObject).checklistNode is TestingChecklistMethodNode) {
48 | val addItem = JBMenuItem("Add item")
49 | addItem.addActionListener(ModifyChecklistAction(node, tree, path, project))
50 | menu.add(addItem)
51 | }
52 |
53 | menu.show(tree, event.x, event.y)
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/server/src/test/java/com/testknight/TestKnightTelemetryServer/validation/ContentValidatorTest.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.validation;
2 |
3 | import com.testknight.TestKnightTelemetryServer.dataTransferObjects.requests.*;
4 | import com.testknight.TestKnightTelemetryServer.exceptions.*;
5 | import com.testknight.TestKnightTelemetryServer.repositories.*;
6 | import org.junit.jupiter.api.*;
7 | import org.mockito.*;
8 | import org.springframework.boot.test.context.*;
9 | import org.springframework.boot.test.mock.mockito.*;
10 |
11 | import java.time.*;
12 | import java.util.*;
13 |
14 | import static org.junit.jupiter.api.Assertions.*;
15 | import static org.mockito.ArgumentMatchers.*;
16 | import static org.mockito.Mockito.*;
17 |
18 | @SpringBootTest
19 | class ContentValidatorTest {
20 |
21 | @MockBean
22 | private ActionRepository actionRepository;
23 |
24 | private UsageDataDto usageDataDto = new UsageDataDto("userId", new ArrayList<>(), "hash");
25 |
26 | private ContentValidator contentValidator;
27 |
28 | @BeforeEach
29 | public void setup() {
30 | MockitoAnnotations.openMocks(this);
31 | when(actionRepository.existsById(anyString())).thenReturn(true);
32 | contentValidator = new ContentValidator(actionRepository);
33 | }
34 |
35 | @Test
36 | public void testGoodWeather() {
37 | assertDoesNotThrow(() -> {
38 | contentValidator.handle(usageDataDto);
39 | });
40 | }
41 |
42 | @Test
43 | public void testNullActionsRecorded() {
44 | usageDataDto.setActionsRecorded(null);
45 | assertThrows(NullFieldException.class, () -> {
46 | contentValidator.handle(usageDataDto);
47 | });
48 | }
49 |
50 | @Test
51 | public void testNullUserId() {
52 | usageDataDto.setUserId(null);
53 | assertThrows(NullFieldException.class, () -> {
54 | contentValidator.handle(usageDataDto);
55 | });
56 | }
57 |
58 | @Test
59 | public void testActionDoesNotExist() {
60 | when(actionRepository.existsById(anyString())).thenReturn(false);
61 | ArrayList actions = new ArrayList<>();
62 | actions.add(new ActionEventDto("invalidActionId", LocalDateTime.now()));
63 | usageDataDto.setActionsRecorded(actions);
64 | assertThrows(InvalidActionIdException.class, () -> {
65 | contentValidator.handle(usageDataDto);
66 | });
67 | }
68 |
69 |
70 | }
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/actions/checklist/GenerateChecklistUnderCaretAction.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.actions.checklist
2 |
3 | import com.intellij.openapi.actionSystem.ActionManager
4 | import com.intellij.openapi.actionSystem.AnAction
5 | import com.intellij.openapi.actionSystem.AnActionEvent
6 | import com.intellij.openapi.actionSystem.CommonDataKeys
7 | import com.intellij.openapi.components.service
8 | import com.intellij.psi.PsiClass
9 | import com.intellij.psi.PsiMethod
10 | import com.intellij.psi.util.PsiTreeUtil
11 | import com.testknight.utilities.UserInterfaceHelper
12 |
13 | class GenerateChecklistUnderCaretAction : AnAction() {
14 |
15 | /**
16 | * Updates the CheckList tab to add new checklist cases.
17 | *
18 | * @param event Event received when the associated menu item is chosen.
19 | */
20 | override fun actionPerformed(event: AnActionEvent) {
21 | val file = event.getData(CommonDataKeys.PSI_FILE)!!
22 | val editor = event.getData(CommonDataKeys.EDITOR)!!
23 | val project = event.project!!
24 |
25 | val caret = editor.caretModel.primaryCaret
26 | val element = file.findElementAt(caret.offset)
27 | val containingMethod = PsiTreeUtil.getParentOfType(element, PsiMethod::class.java)
28 | val containingClass = PsiTreeUtil.getParentOfType(element, PsiClass::class.java)
29 |
30 | val checklistAction = ActionManager.getInstance().getAction("ChecklistAction") as LoadChecklistAction
31 |
32 | if (containingMethod != null && checklistAction.actionPerformed(project, containingMethod)) {
33 | project.service().showTab("Checklist")
34 | } else if (containingClass != null && checklistAction.actionPerformed(project, containingClass)) {
35 | project.service().showTab("Checklist")
36 | }
37 | }
38 |
39 | /**
40 | * Determines whether this menu item is available for the current context.
41 | * Requires a project to be open and psiFile and Editor to be accessible from the action event.
42 | *
43 | * @param e Event received when the associated group-id menu is chosen.
44 | */
45 | override fun update(e: AnActionEvent) {
46 | // Set the availability based on whether psiFile and editor is not null
47 | e.presentation.isEnabled = (
48 | e.project != null &&
49 | e.getData(CommonDataKeys.PSI_FILE) != null &&
50 | e.getData(CommonDataKeys.EDITOR) != null
51 | )
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/services/UsageDataServiceTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.services
2 |
3 | import com.google.gson.Gson
4 | import com.testknight.extensions.TestKnightTestCase
5 | import com.testknight.models.ActionData
6 | import com.testknight.models.UsageData
7 | import com.testknight.settings.SettingsService
8 | import junit.framework.TestCase
9 | import org.junit.Test
10 |
11 | class UsageDataServiceTest : TestKnightTestCase() {
12 |
13 | override fun setUp() {
14 | super.setUp()
15 | UsageDataService.instance.clearRecords()
16 | }
17 |
18 | @Test
19 | private fun recordActions(): List? {
20 | val a0 = UsageDataService.instance.recordDuplicateTest()
21 | val a1 = UsageDataService.instance.recordGotoTest()
22 | val a2 = UsageDataService.instance.recordSuggestAssertion()
23 | val a3 = UsageDataService.instance.recordGenerateChecklist()
24 | val a4 = UsageDataService.instance.recordSplitDiffView()
25 | val a5 = UsageDataService.instance.recordIntegratedDiffView()
26 | val a6 = UsageDataService.instance.recordTraceTest()
27 | val a7 = UsageDataService.instance.recordGenerateTest()
28 | val a8 = UsageDataService.instance.recordItemMarked()
29 | val a9 = UsageDataService.instance.recordItemDeleted()
30 |
31 | val actions = listOf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
32 | actions.forEach { it ?: return null }
33 |
34 | return actions.map { it!! }
35 | }
36 |
37 | @Test
38 | fun testBasic() {
39 | SettingsService.instance.state.telemetrySettings.isEnabled = true
40 |
41 | val actions = recordActions()!!
42 |
43 | val expected = UsageData(actions)
44 | val actual = UsageDataService.instance.usageData()
45 |
46 | TestCase.assertEquals(expected, actual)
47 | }
48 |
49 | @Test
50 | fun testJson() {
51 | SettingsService.instance.state.telemetrySettings.isEnabled = true
52 |
53 | val actions = recordActions()!!
54 |
55 | val expected = Gson().toJson(UsageData(actions))
56 | val actual = UsageDataService.instance.usageDataJson()
57 |
58 | TestCase.assertEquals(expected, actual)
59 | }
60 |
61 | @Test
62 | fun testTelemetryDisabled() {
63 |
64 | // telemetry should be false by default
65 |
66 | recordActions()
67 |
68 | val expected = UsageData(listOf())
69 | val actual = UsageDataService.instance.usageData()
70 |
71 | assertEquals(expected, actual)
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/plugin/src/main/kotlin/com/testknight/checklistGenerationStrategies/parentStrategies/ClassChecklistGenerationStrategy.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.checklistGenerationStrategies.parentStrategies
2 |
3 | import com.intellij.psi.PsiClass
4 | import com.intellij.psi.PsiMethod
5 | import com.intellij.psi.util.PsiTreeUtil
6 | import com.testknight.models.testingChecklist.parentNodes.TestingChecklistClassNode
7 | import com.testknight.models.testingChecklist.parentNodes.TestingChecklistMethodNode
8 |
9 | class ClassChecklistGenerationStrategy private constructor(
10 | private val methodChecklistGenerator: ParentChecklistGeneratorStrategy
11 | ) :
12 | ParentChecklistGeneratorStrategy {
13 |
14 | companion object Factory {
15 | /**
16 | * Creates a new ClassChecklistGenerationStrategy.
17 | *
18 | * @param methodChecklistGenerator the generator to be used to generate the method classes.
19 | * Used mainly to allow dependency injection for testing.
20 | * @return a ClassChecklistGenerationStrategy object.
21 | */
22 | fun create(
23 | methodChecklistGenerator: ParentChecklistGeneratorStrategy
24 | ): ClassChecklistGenerationStrategy {
25 | return ClassChecklistGenerationStrategy(methodChecklistGenerator)
26 | }
27 |
28 | /**
29 | * Creates a new ClassChecklistGenerationStrategy.
30 | *
31 | * @return a ClassChecklistGenerationStrategy object.
32 | */
33 | fun create(): ClassChecklistGenerationStrategy {
34 | return create(MethodChecklistGenerationStrategy.create())
35 | }
36 | }
37 |
38 | /**
39 | * Generates the checklist for a given class.
40 | *
41 | * @param psiElement the PsiClass to generate the checklist on.
42 | * @return a TestingChecklistClassNode object representing the checklist for that class.
43 | */
44 | override fun generateChecklist(psiElement: PsiClass): TestingChecklistClassNode {
45 | val methodName = psiElement.name ?: "No class name found"
46 | val methods = PsiTreeUtil.findChildrenOfType(psiElement, PsiMethod::class.java)
47 | val children = mutableListOf()
48 | // val methodChecklistGenerator = MethodChecklistGenerationStrategy.create()
49 | methods.forEach { children.add(methodChecklistGenerator.generateChecklist(it)) }
50 | return TestingChecklistClassNode(methodName, children, psiElement)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/plugin/src/test/kotlin/com/testknight/checklistGenerationStrategies/leafStrategies/branchingStatements/IfStatementChecklistGenerationStrategyTest.kt:
--------------------------------------------------------------------------------
1 | package com.testknight.checklistGenerationStrategies.leafStrategies.branchingStatements
2 |
3 | import com.intellij.psi.PsiBinaryExpression
4 | import com.intellij.psi.PsiIfStatement
5 | import com.intellij.psi.util.PsiTreeUtil
6 | import com.testknight.checklistGenerationStrategies.leafStrategies.ConditionChecklistGenerationStrategy
7 | import com.testknight.extensions.TestKnightTestCase
8 | import com.testknight.models.testingChecklist.leafNodes.ConditionChecklistNode
9 | import io.mockk.every
10 | import io.mockk.mockk
11 | import io.mockk.verify
12 | import junit.framework.TestCase
13 | import org.junit.Test
14 |
15 | internal class IfStatementChecklistGenerationStrategyTest : TestKnightTestCase() {
16 |
17 | private val conditionGenerationStrategy = mockk()
18 | private val generationStrategy = IfStatementChecklistGenerationStrategy.create(conditionGenerationStrategy)
19 |
20 | @Test
21 | fun testNoCondition() {
22 | getBasicTestInfo("/BrokenClass.java")
23 | val method = getMethodByName("incompleteCondition")
24 |
25 | val ifStatement = PsiTreeUtil.findChildOfType(method, PsiIfStatement::class.java)
26 |
27 | val expected = emptyList()
28 | val actual = generationStrategy.generateChecklist(ifStatement!!)
29 |
30 | TestCase.assertEquals(expected, actual)
31 | }
32 |
33 | @Test
34 | fun testIfStatementGeneration() {
35 |
36 | getBasicTestInfo("/Person.java")
37 |
38 | val method = getMethodByName("setAge")
39 | val conditional = PsiTreeUtil.findChildOfType(method, PsiIfStatement::class.java)
40 | val condition = PsiTreeUtil.getChildOfType(conditional, PsiBinaryExpression::class.java)
41 |
42 | every { conditionGenerationStrategy.generateChecklist(condition!!) } returns emptyList()
43 | generationStrategy.generateChecklist(conditional!!)
44 | verify { conditionGenerationStrategy.generateChecklist(condition!!) }
45 | }
46 |
47 | @Test
48 | fun testLiteralCondition() {
49 |
50 | getBasicTestInfo("/BrokenClass.java")
51 |
52 | val method = getMethodByName("conditionalWithLiteral")
53 | val ifStatement = PsiTreeUtil.findChildOfType(method, PsiIfStatement::class.java)
54 |
55 | val expected = emptyList()
56 | val actual = generationStrategy.generateChecklist(ifStatement!!)
57 |
58 | TestCase.assertEquals(expected, actual)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/server/src/test/java/com/testknight/TestKnightTelemetryServer/domain/factories/UsageRecordFactoryTest.java:
--------------------------------------------------------------------------------
1 | package com.testknight.TestKnightTelemetryServer.domain.factories;
2 |
3 | import com.testknight.TestKnightTelemetryServer.dataTransferObjects.requests.*;
4 | import com.testknight.TestKnightTelemetryServer.domain.model.*;
5 | import com.testknight.TestKnightTelemetryServer.exceptions.*;
6 | import org.junit.jupiter.api.*;
7 |
8 | import java.time.*;
9 | import java.util.*;
10 |
11 | import static org.junit.jupiter.api.Assertions.*;
12 |
13 | class UsageRecordFactoryTest {
14 |
15 | private final UsageRecordFactory usageRecordFactory = new UsageRecordFactory();
16 |
17 | private UsageDataDto usageDataDto;
18 | private final String userId = "userId";
19 | private final LocalDateTime time = LocalDateTime.now();
20 | private final String hash = "hash";
21 | private final String actionId = "testAdd";
22 |
23 | @BeforeEach
24 | public void setup() {
25 | ActionEventDto actionEventDto = new ActionEventDto(actionId, time);
26 | List actionEventDtoList = new ArrayList<>();
27 | actionEventDtoList.add(actionEventDto);
28 | usageDataDto = new UsageDataDto(userId, actionEventDtoList, hash);
29 | }
30 |
31 | @Test
32 | public void testCreateFromDto() {
33 | UsageRecord expected = new UsageRecord(userId, new Action(actionId), time);
34 | UsageRecord actual = usageRecordFactory.createUsageRecordFromDto(usageDataDto).get(0);
35 | assertEquals(expected, actual);
36 | }
37 |
38 | @Test
39 | public void testCreateFromDtoEmptyList() {
40 | usageDataDto.setActionsRecorded(new ArrayList<>());
41 | List actualList = usageRecordFactory.createUsageRecordFromDto(usageDataDto);
42 | assertEquals(0, actualList.size());
43 | }
44 |
45 | @Test
46 | public void testNullUserId() {
47 | usageDataDto.setUserId(null);
48 | assertThrows(NullFieldException.class, () -> {
49 | usageRecordFactory.createUsageRecordFromDto(usageDataDto);
50 | });
51 | }
52 |
53 | @Test
54 | public void testNullHash() {
55 | usageDataDto.setHash(null);
56 | assertThrows(NullFieldException.class, () -> {
57 | usageRecordFactory.createUsageRecordFromDto(usageDataDto);
58 | });
59 | }
60 |
61 | @Test
62 | public void testNullActions() {
63 | usageDataDto.setActionsRecorded(null);
64 | assertThrows(NullFieldException.class, () -> {
65 | usageRecordFactory.createUsageRecordFromDto(usageDataDto);
66 | });
67 | }
68 |
69 | }
--------------------------------------------------------------------------------