Skip to content

Commit

Permalink
added one more section
Browse files Browse the repository at this point in the history
  • Loading branch information
Frosendroska committed Jul 30, 2024
1 parent e80adcb commit dad01cc
Showing 1 changed file with 56 additions and 26 deletions.
82 changes: 56 additions & 26 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,71 +37,91 @@ to include test generation using Grazie in the runIdeForUiTests process, you nee

## Language Support Documentation

# CONTRIBUTORS.md
The TestSpark plugin supports automatic test generation for various programming languages (currently Java and Kotlin)
and aims to support even more programming languages in the future.

## Language Support Documentation

The TestSpark plugin supports automatic test generation for various Java and Kotlin programming languages and aims to support even more programming languages in the future.

This document provides an overview of the existing implementation for Java and Kotlin support and guidelines for adding support for additional programming languages.
This document provides an overview of the existing implementation of Kotlin and Java support and guidelines for adding
more programming languages.

## Key Components

### 1. PSI Parsers

The first step is to enable the collection of the appropriate information for the code under test. This part is responsible for working with the PSI (Program Structure Interface) generated by IntelliJ IDEA. It helps parse the part where the cursor is located, provides a choice of the code elements that are available for testing at its position, and then finds all the needed dependencies to make the prompt complete with all the necessary knowledge about the code under test.
The first step is to enable the collection of the appropriate information for the code under test. This part is
responsible for working with the PSI (Program Structure Interface) generated by IntelliJ IDEA. It helps parse the part
where the cursor is located, provides a choice of the code elements that are available for testing at cursor's position.
Then find all the needed dependencies to make the prompt complete with all the necessary knowledge about the code under
test.

This part is the most granular but complex at the same time.

The main reason for this is to include dependencies only for the languages we need. This avoids errors if the user does not have some languages that our plugin supports. For example, if we work with a Python project, we don't want to depend on Kotlin because it will cause an error if Kotlin isn't present. Additionally, we want to incrementally add dependencies on other languages for faster startup. For example, we do not want to fetch the dependency on Java when we work with TypeScript. Other benefits include better organization, easier maintenance, and clearer separation of concerns. As a side-bonus, the addition of new languages will be easier.
The main reason for this is to include dependencies only for the languages we need. This avoids errors if the user does
not have some languages that our plugin supports. _For example, if we work with a Python project, we don't want to depend
on Kotlin because it will cause an error if Kotlin isn't present._

Additionally, we want to incrementally add dependencies on other languages for faster startup.
_For example, we do not want to fetch the dependency on Java when we work with TypeScript._
Other benefits include better organization, easier maintenance, and clearer separation of
concerns. As a side-bonus, the addition of new languages will be easier.

**Module Dependencies:**

- **langwrappers**: This is a foundational module for language extensions.
- **<Language>**: Depends on the `langwrappers` module to implement the `<Language>`-specific `PsiHelper` and `PsiHelperProvider`.
- **src/**: Depends on `langwrappers` because we want to use `PsiHelper` and other interfaces regardless of the current language. Depends on `<Language>`, to make `plugin.xml` aware of the implementations of the Extension Point.
- **<Language>**: Depends on the `langwrappers` module to implement the `<Language>`-specific `PsiHelper`
and `PsiHelperProvider`.
- **src/**: Depends on `langwrappers` because we want to use `PsiHelper` and other interfaces regardless of the current
language. Depends on `<Language>`, to make `plugin.xml` aware of the implementations of the Extension Point.

**Plugin Dependencies:**

- The main `plugin.xml` file declares the `psiHelperProvider` extension point using the `com.intellij.lang.LanguageExtensionPoint` class.
- The main `plugin.xml` file declares the `psiHelperProvider` extension point using
the `com.intellij.lang.LanguageExtensionPoint` class.
- The language-specific modules extend this extension point to register their implementations.
- When the project is opened, we load the EPs needed to work with the current project. Then, using the `PsiHelperProvider` interface, we can get the appropriate `<language>PsiHelper` class per file.
- When the project is opened, we load the EPs needed to work with the current project. Then, using
the `PsiHelperProvider` interface, we can get the appropriate `<language>PsiHelper` class per file.

**Implementation Details:**

- **Common Module (`langwrappers`)**:
- Contains the `PsiHelper` interface, which provides the necessary methods to interact with `psiFile`.
- The `PsiHelperProvider` class includes a companion object to fetch the appropriate `PsiHelper` implementation based on the file's language.
- The `PsiHelperProvider` class includes a companion object to fetch the appropriate `PsiHelper` implementation
based on the file's language.

- **<Language> Module**:
- Implements the `<Language>PsiHelper` and `<Language>PsiHelperProvider` classes, which provide <Language>-specific logic.
- Implements the `<Language>PsiHelper` and `<Language>PsiHelperProvider` classes, which provide <Language>-specific
logic.
- Declares the extension point in `testspark-<Language>.xml`.

To add new languages, create a separate module for this language and register its implementation as an extension of the `psiHelperProvider` EP. Then follow the template provided above.
To add new languages, create a separate module for this language and register its implementation as an extension of
the `psiHelperProvider` EP. Then follow the template provided above.

### 2. Prompt Generation

When we know how to parse the code, we need to construct the prompt.

For each language, adjust the prompt that goes to the LLM. Ensure that the language, framework platform, and mocking framework are defined correctly in:
For each language, adjust the prompt that goes to the LLM. Ensure that the language, framework platform, and mocking
framework are defined correctly in:

```kotlin
data class PromptConfiguration(
val desiredLanguage: String,
val desiredTestingPlatform: String,
val desiredMockingFramework: String,
data class PromptConfiguration(
val desiredLanguage: String,
val desiredTestingPlatform: String,
val desiredMockingFramework: String,
)
```

Additionally, check that all the dependencies (collected by `PsiHelper` for the current strategy) are passed properly. `PromptGenerator` and `PromptBuilder` are responsible for this job.
Additionally, check that all the dependencies (collected by `PsiHelper` for the current strategy) are passed
properly. `PromptGenerator` and `PromptBuilder` are responsible for this job.

### 3. Parsing LLM Response

When the LLM response to our prompt is received, we have to parse it.

We want to retrieve test functions from the response, collect them separately (and all together) in the tmp folder, and check for compilation.
We want to retrieve test case, all the test functions and additional information like imports or supporting functions
from the response.

The current structure of this part is located in:

- `kotlin/org/jetbrains/research/testspark/core/test`
- `kotlin/org/jetbrains/research/testspark/tools`

Expand All @@ -118,16 +138,26 @@ Before showing the code to the user, it should be checked for compilation.

- `TestCompiler`: Compiles a list of test cases and returns the compilation result.

### 5. UI Representation
Here one should specify the appropriate compilation strategy for each language. With all the dependencies and build paths.

Once we parse the code generated by the LLM and confirm that the code is compilable, it should be presented in the UI.
### 5. UI Representation

There are special interfaces that help to work with already parsed test classes and are specified for each language:
Once the code generated by the LLM is checked for the compilation, it should be presented in the UI.

- `TestCaseDisplayService`: Service responsible for the UI representation.
- `TestCaseDisplayService`: Service responsible for the representation of all the UI components.
- `TestSuiteView`: Interface specific for working with buttons.
- `TestClassCodeAnalyzer`: Interface for retrieving information from test class code.
- `TestClassCodeGenerator`: Interface for generating and formatting test class code.

### 6. Running and saving tests

We should be able to run all the tests in the UI and then save them to the desired folder.

- `TestPersistentStorage`: Interface representing a contract for saving generated tests to a specified file system location.

For Kotlin and Java, the `TestProcessor` implementation also allows saving the JaCoCo report to see the code coverage of
the test that will be saved.

---

## Plugin Configuration File
Expand Down

0 comments on commit dad01cc

Please sign in to comment.