├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ ├── build.yml
│ └── release.yaml
├── .gitignore
├── .run
└── PickerKt.run.xml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.gradle.kts
├── code-quality
├── README.md
├── build.gradle.kts
└── src
│ ├── main
│ └── kotlin
│ │ └── cc
│ │ └── unitmesh
│ │ └── quality
│ │ ├── CodeQualityType.kt
│ │ ├── QualityAnalyser.kt
│ │ ├── badsmell
│ │ ├── BadSmellGraphCall.kt
│ │ ├── BadSmellModel.kt
│ │ ├── BadsmellAnalyser.kt
│ │ ├── BsThresholds.kt
│ │ └── SmellType.kt
│ │ ├── comment
│ │ ├── CodeComment.kt
│ │ ├── CommentAnalyser.kt
│ │ ├── CommentRuleVisitor.kt
│ │ └── rule
│ │ │ ├── CommentRule.kt
│ │ │ ├── CommentRuleSetProvider.kt
│ │ │ └── MissingParameterDescRule.kt
│ │ ├── extension
│ │ ├── JavaControllerAnalyser.kt
│ │ ├── JavaRepositoryAnalyser.kt
│ │ ├── JavaServiceAnalyser.kt
│ │ └── rule
│ │ │ ├── ServiceRule.kt
│ │ │ └── TooManyRepositoryDependenciesRule.kt
│ │ └── testbadsmell
│ │ ├── TbsResult.kt
│ │ ├── TestBadSmell.kt
│ │ └── TestBadsmellAnalyser.kt
│ └── test
│ ├── kotlin
│ └── cc
│ │ └── unitmesh
│ │ └── quality
│ │ ├── BadsmellAnalyserTest.kt
│ │ ├── JavaControllerAnalyserTest.kt
│ │ ├── JavaServiceAnalyserTest.kt
│ │ ├── TestBadsmellAnalyserTest.kt
│ │ └── comment
│ │ ├── CodeCommentTest.kt
│ │ ├── CommentAnalyserTest.kt
│ │ └── rule
│ │ └── MissingParameterDescRuleTest.kt
│ └── resources
│ ├── bs
│ ├── ComplexIf.java
│ ├── DataClass.java
│ ├── LargeClass.java
│ ├── LazyClass.java
│ ├── LongMethod.java
│ ├── LongParameter.java
│ ├── MultipleIf.java
│ ├── graphcall
│ │ ├── GraphCallA.java
│ │ ├── GraphCallB.java
│ │ └── GraphCallC.java
│ └── interface
│ │ └── BlogRepository.java
│ ├── java
│ ├── ServiceWithSixRepositories.java
│ └── structs_HelloController.json
│ └── tbs
│ ├── AssertionRouletteTest.java
│ ├── ConditionalTest.java
│ ├── ConstructorInitializationTest.java
│ ├── DuplicateAssertTest.java
│ ├── EmptyTest.java
│ ├── IgnoreTest.java
│ ├── MagicNumberTest.java
│ ├── MysteryGuestTest.java
│ ├── RedundantAssertionTest.java
│ ├── RedundantPrintTest.java
│ ├── SleepyTest.java
│ ├── TestersOnly.java
│ ├── UnknownTest.java
│ └── regression
│ ├── CallAssertInClassTests.java
│ ├── CreatorNotUnknownTest.java
│ ├── EnvironmentSystemIntegrationTests.java
│ └── I18NTest.java
├── docs
├── CNAME
├── _config.yml
├── architecture.svg
├── evaluation
│ ├── evaluation.md
│ ├── intent-driven.md
│ └── java.yml
├── finetune
│ ├── datasets.md
│ ├── ext-datasets-builder.md
│ ├── faq.md
│ └── finetune.md
├── home.md
├── instruction
│ ├── instruction.md
│ ├── related-code-completion.md
│ └── similar-code-completion.md
├── logo.svg
├── prompts
│ ├── autodev
│ │ ├── code-completion.vm
│ │ ├── explain-code.vm
│ │ ├── test-generate.vm
│ │ └── test
│ │ │ ├── testgen.request.md
│ │ │ └── testgen.response.md
│ ├── custom
│ │ ├── code-completion.vm
│ │ ├── explain-code.vm
│ │ └── test-completion.vm
│ ├── openai
│ │ ├── code-completion.vm
│ │ ├── explain-code.vm
│ │ └── test-completion.vm
│ └── yiyan
│ │ ├── code-completion.vm
│ │ ├── explain-code.vm
│ │ └── test-completion.vm
├── quality
│ ├── quality.md
│ ├── rule-extension.md
│ └── threshold.md
├── quick-start.md
└── workflow.svg
├── examples
├── config-examples
│ ├── fixtures
│ │ ├── code-completion.jsonl
│ │ └── code-completion.vm
│ ├── mock-connection.yml
│ ├── openai-connection.yml
│ ├── processor.yml
│ └── unit-eval.yml
├── post-processor
│ └── process.py
└── project-example
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── java
│ └── cc
│ └── unitmesh
│ └── example
│ └── App.java
├── finetunes
├── codegeex2
│ └── api-server.py
└── deepseek
│ ├── README.md
│ ├── api-server-python38.py
│ ├── ds_config_zero2.json
│ ├── ds_config_zero3.json
│ ├── finetune.bak.py
│ ├── finetune.ipynb
│ ├── requirements.txt
│ ├── web-ui-python38.py
│ └── web-ui.py
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
├── unit-core
├── build.gradle.kts
└── src
│ ├── main
│ └── kotlin
│ │ └── cc
│ │ └── unitmesh
│ │ └── core
│ │ ├── Instruction.kt
│ │ ├── SupportedLang.kt
│ │ ├── ast
│ │ └── NodeIdentifier.kt
│ │ ├── base
│ │ └── LLMCodeContext.kt
│ │ ├── comment
│ │ ├── CommentBuilder.kt
│ │ ├── CommentBuilderType.kt
│ │ ├── DocCommentToolType.kt
│ │ └── TypedCommentIns.kt
│ │ ├── completion
│ │ ├── CodeCompletionIns.kt
│ │ ├── InstructionBuilderType.kt
│ │ ├── TypedIns.kt
│ │ └── TypedInsBuilder.kt
│ │ ├── intelli
│ │ ├── CommentService.kt
│ │ ├── SimilarChunkContext.kt
│ │ └── SimilarChunker.kt
│ │ └── unittest
│ │ ├── TestCodeBuilder.kt
│ │ ├── TestCodeBuilderType.kt
│ │ └── TypedTestIns.kt
│ └── test
│ └── kotlin
│ └── cc
│ └── unitmesh
│ └── core
│ ├── InstructionTest.kt
│ └── intelli
│ ├── CommentServiceTest.kt
│ └── SimilarChunkerTest.kt
├── unit-distillation
├── README.md
├── build.gradle.kts
└── src
│ └── main
│ └── kotlin
│ └── cc
│ └── unitmesh
│ └── distillate
│ └── Distillate.kt
├── unit-gen
├── .gitignore
├── build.gradle.kts
└── src
│ ├── main
│ └── kotlin
│ │ └── cc
│ │ └── unitmesh
│ │ └── runner
│ │ ├── Picker.kt
│ │ └── cli
│ │ ├── ProcessorResult.kt
│ │ ├── ProcessorUtils.kt
│ │ └── UnitEvalConfig.kt
│ └── test
│ └── kotlin
│ └── cc
│ └── unitmesh
│ └── runner
│ └── cli
│ └── UnitEvalConfigTest.kt
└── unit-picker
├── .gitignore
├── README.md
├── build.gradle.kts
└── src
├── main
└── kotlin
│ └── cc
│ └── unitmesh
│ └── pick
│ ├── PickerPipeline.kt
│ ├── SingleProjectCodePicker.kt
│ ├── builder
│ ├── InsBuilderFactory.kt
│ ├── comment
│ │ ├── CommentsExtractor.kt
│ │ ├── DocumentationTypedInsBuilder.kt
│ │ ├── JvmCommentBuilder.kt
│ │ └── ins
│ │ │ ├── ClassCommentIns.kt
│ │ │ └── MethodCommentIns.kt
│ ├── completion
│ │ ├── AfterBlockCodeTypedInsBuilder.kt
│ │ ├── InBlockCodeTypedInsBuilder.kt
│ │ └── InlineCodeTypedInsBuilder.kt
│ └── unittest
│ │ ├── TestCodeTypedInsBuilder.kt
│ │ ├── base
│ │ ├── BasicTestIns.kt
│ │ └── UnitTestService.kt
│ │ ├── java
│ │ ├── ClassTestCodeBuilder.kt
│ │ ├── JavaMethodTestCodeBuilder.kt
│ │ └── JavaTestCodeService.kt
│ │ ├── kotlin
│ │ ├── KotlinMethodTestCodeBuilder.kt
│ │ └── KotlinTestCodeService.kt
│ │ ├── rust
│ │ └── RustTestCodeService.kt
│ │ └── typescript
│ │ └── TypeScriptTestCodeService.kt
│ ├── ext
│ ├── CodeDataStructUtil.kt
│ ├── CompositionDependencyExt.kt
│ ├── DirectoryWalker.kt
│ └── GitUtil.kt
│ ├── option
│ ├── InsOutputConfig.kt
│ └── InsPickerOption.kt
│ ├── project
│ ├── ProjectContext.kt
│ ├── ProjectLibrary.kt
│ ├── TestStack.kt
│ ├── frameworks
│ │ ├── JavaFrameworkIdentifier.kt
│ │ ├── LangFrameworkIdentifier.kt
│ │ ├── TestFrameworkIdentifier.kt
│ │ └── TypescriptFrameworkIdentifier.kt
│ ├── library
│ │ ├── LibraryDescriptor.kt
│ │ ├── LibrarySet.kt
│ │ └── SpringLibrary.kt
│ └── spec
│ │ └── NamingStyle.kt
│ ├── similar
│ ├── JavaSimilarChunker.kt
│ └── TypeScriptSimilarChunker.kt
│ ├── strategy
│ ├── CodeStrategyType.kt
│ ├── base
│ │ └── CodeStrategyBuilder.kt
│ ├── bizcode
│ │ ├── RelatedCodeStrategyBuilder.kt
│ │ └── SimilarChunksStrategyBuilder.kt
│ └── ins
│ │ ├── RelatedCodeIns.kt
│ │ └── SimilarChunkIns.kt
│ ├── threshold
│ ├── InsQualityThreshold.kt
│ ├── ThresholdChecker.kt
│ ├── filter
│ │ ├── BinaryGeneratedMinifiedFilter.kt
│ │ ├── ComplexityFilter.kt
│ │ ├── ExtensionFilter.kt
│ │ ├── SizeFilter.kt
│ │ └── TokenLengthFilter.kt
│ └── pipeline
│ │ ├── Filter.kt
│ │ ├── FilterResult.kt
│ │ └── Pipeline.kt
│ └── worker
│ ├── WorkerContext.kt
│ ├── WorkerManager.kt
│ ├── base
│ └── LangWorker.kt
│ ├── job
│ ├── InstructionFileJob.kt
│ └── JobContext.kt
│ └── lang
│ ├── JavaWorker.kt
│ ├── KotlinWorker.kt
│ ├── PythonWorker.kt
│ ├── RustWorker.kt
│ └── TypescriptWorker.kt
└── test
├── kotlin
└── cc
│ └── unitmesh
│ └── pick
│ ├── SingleProjectCodePickerTest.kt
│ ├── builder
│ ├── CodeStrategyBuilderTest.kt
│ ├── comment
│ │ └── JvmCommentBuilderTest.kt
│ ├── completion
│ │ ├── AfterBlockCodeTypedInsBuilderTest.kt
│ │ ├── InBlockCodeTypedInsBuilderTest.kt
│ │ └── InlineCodeTypedInsBuilderTest.kt
│ ├── strategy
│ │ ├── RelatedCodeStrategyBuilderTest.kt
│ │ └── SimilarChunksStrategyBuilderTest.kt
│ └── unittest
│ │ ├── java
│ │ ├── JavaTestCodeServiceE2ETest.kt
│ │ └── JavaTestCodeServiceTest.kt
│ │ └── rust
│ │ └── RustTestCodeServiceTest.kt
│ ├── ext
│ ├── CodeDataStructUtilTest.kt
│ └── GitUtilTest.kt
│ ├── option
│ └── InsPickerOptionTest.kt
│ ├── project
│ └── ProjectLibraryTest.kt
│ ├── similar
│ └── JavaSimilarChunkerTest.kt
│ ├── spec
│ └── NamingStyleTest.kt
│ ├── threshold
│ └── ThresholdCheckerTest.kt
│ └── worker
│ ├── TestFrameworkIdentifierTest.kt
│ └── job
│ └── InstructionFileJobTest.kt
└── resources
└── related
├── BlogPost.java
├── BlogRepository.java
└── BlogService.java
/.gitattributes:
--------------------------------------------------------------------------------
1 | **/resources/**/*.java linguist-vendored
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # Dependabot configuration:
2 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates
3 |
4 | version: 2
5 | updates:
6 | # Maintain dependencies for Gradle dependencies
7 | - package-ecosystem: "gradle"
8 | directory: "/"
9 | target-branch: "next"
10 | schedule:
11 | interval: "daily"
12 | # Maintain dependencies for GitHub Actions
13 | - package-ecosystem: "github-actions"
14 | directory: "/"
15 | target-branch: "next"
16 | schedule:
17 | interval: "daily"
18 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [ push, pull_request ]
4 |
5 | jobs:
6 | build:
7 | strategy:
8 | matrix:
9 | # os: [ macos-latest, ubuntu-latest, windows-latest ]
10 | # Windows offers timeouts: https://github.com/unit-mesh/unit-gen/actions/runs/7101623294/job/19331414199
11 | os: [ macos-latest, ubuntu-latest ]
12 | runs-on: ${{ matrix.os }}
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 | with:
17 | fetch-depth: 10
18 | - uses: actions/setup-java@v3
19 | with:
20 | distribution: 'zulu'
21 | java-version: '11'
22 |
23 | - name: Setup Gradle
24 | uses: gradle/gradle-build-action@v2.8.0
25 | with:
26 | arguments: build
27 |
28 | - name: Execute Gradle build
29 | run: ./gradlew build
30 |
31 | - name: Execute Gradle Coverage
32 | run: ./gradlew check
33 |
34 | - name: Upload coverage reports to Codecov
35 | uses: codecov/codecov-action@v3
36 | env:
37 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
38 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 | publish:
10 | runs-on: ubuntu-latest
11 | strategy:
12 | matrix:
13 | assets:
14 | - unit-gen/build/libs/unit-gen-*-all.jar
15 |
16 | permissions:
17 | contents: write
18 | packages: write
19 | steps:
20 | - uses: actions/checkout@v3
21 | with:
22 | fetch-depth: 10
23 | - uses: actions/setup-java@v3
24 | with:
25 | distribution: 'zulu'
26 | java-version: '11'
27 |
28 |
29 | - name: Setup Gradle
30 | run: ./gradlew build --no-daemon -x test
31 |
32 | - name: Upload assets to release
33 | uses: svenstaro/upload-release-action@v2
34 | with:
35 | repo_token: ${{ secrets.GITHUB_TOKEN }}
36 | file: ${{ matrix.assets }}
37 | tag: ${{ github.ref }}
38 | overwrite: true
39 | file_glob: true
40 |
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | build/
3 | !gradle/wrapper/gradle-wrapper.jar
4 | !**/src/main/**/build/
5 | !**/src/test/**/build/
6 |
7 | ### IntelliJ IDEA ###
8 | .idea/modules.xml
9 | .idea/jarRepositories.xml
10 | .idea/compiler.xml
11 | .idea/libraries/
12 | *.iws
13 | *.iml
14 | *.ipr
15 | out/
16 | !**/src/main/**/out/
17 | !**/src/test/**/out/
18 |
19 | ### Eclipse ###
20 | .apt_generated
21 | .classpath
22 | .factorypath
23 | .project
24 | .settings
25 | .springBeans
26 | .sts4-cache
27 | bin/
28 | !**/src/main/**/bin/
29 | !**/src/test/**/bin/
30 |
31 | ### NetBeans ###
32 | /nbproject/private/
33 | /nbbuild/
34 | /dist/
35 | /nbdist/
36 | /.nb-gradle/
37 |
38 | ### VS Code ###
39 | .vscode/
40 |
41 | ### Mac OS ###
42 | .DS_Store
43 | .idea
44 | datasets
45 | prompt-log-*.txt
46 | prompt-evaluate-*.txt
47 | processor.yml
48 | recording.jsonl
49 |
--------------------------------------------------------------------------------
/.run/PickerKt.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/code-quality/README.md:
--------------------------------------------------------------------------------
1 | # 定制高质量数据
2 |
3 | - 代码类型:MVC、DDD、
4 | - 代码实现:Stream API、相近上下文、
5 | - 代码注释:中文文档、英文文档、
6 | - 测试质量:测试类型、测试写法
7 |
--------------------------------------------------------------------------------
/code-quality/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION")
2 | plugins {
3 | alias(libs.plugins.kotlin.jvm)
4 | alias(libs.plugins.serialization)
5 | }
6 |
7 | dependencies {
8 | implementation(libs.serialization.json)
9 |
10 | implementation(libs.chapi.domain)
11 | implementation(libs.chapi.java)
12 | implementation(libs.chapi.kotlin)
13 |
14 | implementation(libs.archguard.scanner.core)
15 | implementation(libs.archguard.analyser.estimate)
16 |
17 | implementation(libs.archguard.rule.sql)
18 | implementation(libs.archguard.rule.webapi)
19 | implementation(libs.archguard.rule.comment)
20 |
21 | implementation(libs.archguard.feat.apicalls)
22 | implementation(libs.archguard.feat.datamap)
23 |
24 | // checkout
25 | implementation(libs.codedb.checkout)
26 |
27 | // Logging
28 | implementation(libs.logging.slf4j.api)
29 | implementation(libs.logging.logback.classic)
30 |
31 | testImplementation(kotlin("test"))
32 |
33 | testImplementation(libs.bundles.test)
34 | }
35 |
36 | tasks.test {
37 | useJUnitPlatform()
38 | }
39 |
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/CodeQualityType.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | enum class CodeQualityType {
7 | BadSmell,
8 | TestBadSmell,
9 | JavaController,
10 | JavaRepository,
11 | JavaService,
12 | DocComment,
13 | }
14 |
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/QualityAnalyser.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality
2 |
3 | import cc.unitmesh.quality.badsmell.BadsmellAnalyser
4 | import cc.unitmesh.quality.comment.CommentAnalyser
5 | import cc.unitmesh.quality.extension.JavaServiceAnalyser
6 | import cc.unitmesh.quality.extension.JavaRepositoryAnalyser
7 | import cc.unitmesh.quality.extension.JavaControllerAnalyser
8 | import cc.unitmesh.quality.testbadsmell.TestBadsmellAnalyser
9 | import chapi.domain.core.CodeContainer
10 |
11 | import chapi.domain.core.CodeDataStruct
12 | import org.archguard.rule.core.Issue
13 |
14 | interface QualityAnalyser {
15 |
16 | /**
17 | * For [CodeQualityType.DocComment], we need to analysis the whole container which contains comments.
18 | */
19 | fun analysis(container: CodeContainer): List {
20 | return listOf()
21 | }
22 |
23 | /**
24 | * Normal analysis, we only need to analysis the given nodes.
25 | *
26 | * - [CodeQualityType.BadSmell], [CodeQualityType.TestBadSmell]
27 | * - [CodeQualityType.JavaController], [CodeQualityType.JavaRepository], [CodeQualityType.JavaService]
28 | *
29 | */
30 | fun analysis(nodes: List): List
31 |
32 | companion object {
33 | /**
34 | * Creates a list of QualityAnalyser objects based on the given list of CodeQualityType.
35 | *
36 | * @param types The list of CodeQualityType to create QualityAnalyser objects for.
37 | * @param thresholds The map of thresholds for each CodeQualityType. Defaults to an empty map if not provided.
38 | * @return A list of QualityAnalyser objects corresponding to the given CodeQualityType.
39 | */
40 | fun create(types: List, thresholds: Map = mapOf()): List {
41 | return types.map { type ->
42 | when (type) {
43 | CodeQualityType.BadSmell -> BadsmellAnalyser(thresholds)
44 | CodeQualityType.TestBadSmell -> TestBadsmellAnalyser(thresholds)
45 | CodeQualityType.JavaController -> JavaControllerAnalyser(thresholds)
46 | CodeQualityType.JavaRepository -> JavaRepositoryAnalyser(thresholds)
47 | CodeQualityType.JavaService -> JavaServiceAnalyser(thresholds)
48 | CodeQualityType.DocComment -> CommentAnalyser(thresholds)
49 | }
50 | }
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/badsmell/BadSmellGraphCall.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.badsmell
2 |
3 | class BadSmellGraphCall {
4 | companion object {
5 | var totalPath: MutableList = mutableListOf()
6 | }
7 |
8 | fun analysisGraphCallPath(nodes: Map>): List {
9 | for (k in nodes.keys) {
10 | getConnectedPath(k, nodes)
11 | }
12 | return totalPath
13 | }
14 |
15 | private fun getConnectedPath(startNode: String, nodes: Map>) {
16 | val relatedNodes = nodes[startNode] ?: emptyList()
17 | val currentPath: MutableList = mutableListOf()
18 | for (i in relatedNodes.indices) {
19 | for (j in i + 1 until relatedNodes.size) {
20 | getPath(startNode, nodes, currentPath, relatedNodes[i], relatedNodes[j])
21 | getPath(startNode, nodes, currentPath, relatedNodes[j], relatedNodes[i])
22 | }
23 | }
24 | }
25 |
26 | private fun getPath(startNode: String, nodes: Map>, currentPath: List, currentNode: String, endNode: String) {
27 | val nextNodes = nodes[currentNode] ?: emptyList()
28 | if (nextNodes.isEmpty() || currentNode == startNode || currentNode == endNode) {
29 | return
30 | }
31 | if (nextNodes.contains(endNode)) {
32 | val path = listOf(startNode) + currentPath + listOf(currentNode, endNode)
33 | totalPath.add(path.joinToString("->") + ";$startNode->$endNode")
34 | }
35 | for (node in nextNodes) {
36 | if (currentPath.contains(node)) {
37 | continue
38 | }
39 | getPath(startNode, nodes, currentPath + currentNode, node, endNode)
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/badsmell/BadSmellModel.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.badsmell
2 |
3 | import org.archguard.rule.core.Issue
4 | import org.archguard.rule.core.IssuePosition
5 | import org.archguard.rule.core.RuleType
6 | import org.archguard.rule.core.Severity
7 |
8 | data class BadSmellModel(
9 | val file: String? = null,
10 | val line: String? = null,
11 | val bs: SmellType? = null,
12 | val description: String? = null,
13 | val size: Int? = null,
14 | ) {
15 | fun toIssue(): Issue {
16 | return Issue(
17 | name = bs?.name ?: "",
18 | fullName = file ?: "",
19 | detail = description ?: "",
20 | position = IssuePosition(),
21 | ruleId = bs?.name ?: "",
22 | severity = Severity.WARN,
23 | source = line ?: "",
24 | ruleType = RuleType.CODE_SMELL,
25 | )
26 | }
27 | }
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/badsmell/BsThresholds.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.badsmell
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class BsThresholds(
7 | val bsLongParasLength: Int = 5,
8 | val bsIfSwitchLength: Int = 8,
9 | val bsLargeLength: Int = 20,
10 | val bsMethodLength: Int = 30,
11 | val bsIfLinesLength: Int = 3,
12 | ) {
13 | fun toThresholds(): Map {
14 | return mapOf(
15 | "bsLongParasLength" to bsLongParasLength,
16 | "bsIfSwitchLength" to bsIfSwitchLength,
17 | "bsLargeLength" to bsLargeLength,
18 | "bsMethodLength" to bsMethodLength,
19 | "bsIfLinesLength" to bsIfLinesLength,
20 | )
21 | }
22 |
23 | fun from(thresholds: Map): BsThresholds {
24 | return BsThresholds(
25 | thresholds["bsLongParasLength"] ?: bsLongParasLength,
26 | thresholds["bsIfSwitchLength"] ?: bsIfSwitchLength,
27 | thresholds["bsLargeLength"] ?: bsLargeLength,
28 | thresholds["bsMethodLength"] ?: bsMethodLength,
29 | thresholds["bsIfLinesLength"] ?: bsIfLinesLength,
30 | )
31 | }
32 | }
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/badsmell/SmellType.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.badsmell
2 |
3 | enum class SmellType {
4 | SMELL_GARPH_CONNECTED_CALL,
5 | SMELL_LAZY_ELEMENT,
6 | SMELL_LONG_METHOD,
7 | SMELL_DATA_CLASS,
8 | SMELL_REFUSED_BEQUEST,
9 | SMELL_LARGE_CLASS,
10 | SMELL_COMPLEX_CONDITION,
11 | SMELL_REPEATED_SWITCHES,
12 | SMELL_LONG_PARAMETER_LIST
13 | }
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/comment/CommentAnalyser.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.comment
2 |
3 | import cc.unitmesh.quality.QualityAnalyser
4 | import cc.unitmesh.quality.comment.rule.CommentRuleSetProvider
5 | import chapi.domain.core.CodeContainer
6 | import chapi.domain.core.CodeDataStruct
7 | import org.archguard.rule.core.Issue
8 |
9 | class CommentAnalyser(thresholds: Map) : QualityAnalyser {
10 | private val ruleSetProvider = CommentRuleSetProvider()
11 | private var comments: List = mutableListOf()
12 |
13 | override fun analysis(nodes: List): List {
14 | return listOf()
15 | }
16 |
17 | override fun analysis(container: CodeContainer): List {
18 | comments = CodeComment.parseComment(container.Content)
19 | return CommentRuleVisitor(comments, container).visitor(listOf(ruleSetProvider.get()))
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/comment/CommentRuleVisitor.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.comment
2 |
3 | import cc.unitmesh.quality.comment.rule.CommentRule
4 | import chapi.domain.core.CodeContainer
5 | import org.archguard.rule.core.*
6 |
7 | class CommentRuleVisitor(val comments: List, val container: CodeContainer) : RuleVisitor(comments) {
8 | private val lineCommentMap = CodeComment.lineCommentMap(comments)
9 |
10 | override fun visitor(ruleSets: Iterable): List {
11 | val results: MutableList = mutableListOf()
12 | val context = RuleContext()
13 |
14 | container.DataStructures.forEach { struct ->
15 | ruleSets.forEach { ruleSet ->
16 | ruleSet.rules.forEach { rule ->
17 | val apiRule = rule as CommentRule
18 | val classComment = lineCommentMap[struct.Position.StartLine - 1]
19 | if (classComment != null) {
20 | apiRule.visitRoot(struct, classComment, context, fun(rule: Rule, position: IssuePosition) {
21 | results += Issue(
22 | position,
23 | ruleId = rule.key,
24 | name = rule.name,
25 | detail = rule.description,
26 | ruleType = RuleType.CODE_SMELL,
27 | fullName = "${struct.Module}:${struct.Package}:${struct.NodeName}",
28 | source = struct.FilePath
29 | )
30 | })
31 | }
32 |
33 | struct.Functions.forEach { method ->
34 | val methodComment = lineCommentMap[method.Position.StartLine - 1] ?: return@forEach
35 | apiRule.visitFunction(method, methodComment, context, fun(rule: Rule, position: IssuePosition) {
36 | results += Issue(
37 | position,
38 | ruleId = rule.key,
39 | name = rule.name,
40 | detail = rule.description,
41 | ruleType = RuleType.CODE_SMELL,
42 | fullName = "${struct.Module}:${struct.Package}:${struct.NodeName}",
43 | source = struct.FilePath
44 | )
45 | })
46 | }
47 | }
48 | }
49 | }
50 |
51 | return results
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/comment/rule/CommentRule.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.comment.rule
2 |
3 | import cc.unitmesh.quality.comment.CodeComment
4 | import chapi.domain.core.CodeDataStruct
5 | import chapi.domain.core.CodeFunction
6 | import org.archguard.rule.core.IssueEmit
7 | import org.archguard.rule.core.Rule
8 | import org.archguard.rule.core.RuleContext
9 |
10 | open class CommentRule : Rule() {
11 | open fun visitRoot(node: CodeDataStruct, comment: CodeComment, context: RuleContext, callback: IssueEmit) {
12 |
13 | }
14 |
15 | open fun visitFunction(node: CodeFunction, comment: CodeComment, context: RuleContext, callback: IssueEmit) {
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/comment/rule/CommentRuleSetProvider.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.comment.rule
2 |
3 | import org.archguard.rule.core.RuleSet
4 | import org.archguard.rule.core.RuleSetProvider
5 | import org.archguard.rule.core.RuleType
6 |
7 | class CommentRuleSetProvider: RuleSetProvider {
8 | override fun get(): RuleSet {
9 | return RuleSet(
10 | RuleType.CODE_SMELL,
11 | "normal",
12 | MissingParameterDescRule()
13 | )
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/comment/rule/MissingParameterDescRule.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.comment.rule
2 |
3 | import cc.unitmesh.quality.comment.CodeComment
4 | import chapi.domain.core.CodeFunction
5 | import org.archguard.rule.core.IssueEmit
6 | import org.archguard.rule.core.IssuePosition
7 | import org.archguard.rule.core.RuleContext
8 | import org.archguard.rule.core.Severity
9 |
10 | /**
11 | * Parse the documentation of the code and check whether the documentation is complete.
12 | *
13 | * For example, the following code is missing the parameter description:
14 | * ```java
15 | * /**
16 | * * Sum a and b, and return the result.
17 | * * @param a the first number
18 | * * @return the result of a + b
19 | * */
20 | * public int calculateSum(int a, int b) {
21 | * return a + b;
22 | * }
23 | * ```
24 | *
25 | * We can use this rule to check whether the documentation is complete.
26 | */
27 | class MissingParameterDescRule : CommentRule() {
28 | init {
29 | this.id = "missing-parameter-desc"
30 | this.name = "MissingParameterDesc"
31 | this.key = this.javaClass.name
32 | this.severity = Severity.WARN
33 | }
34 |
35 | private val pattern = Regex("""@param\s+(\w+)\s+([^@]+)""")
36 |
37 | override fun visitFunction(node: CodeFunction, comment: CodeComment, context: RuleContext, callback: IssueEmit) {
38 | val matches = pattern.findAll(comment.content)
39 |
40 | val nodeSize = node.Parameters.size
41 |
42 | if (matches.count() != nodeSize) {
43 | this.description = "The documentation is un-complete, parameter description is missing"
44 | callback(this, IssuePosition())
45 | return
46 | }
47 |
48 | val matchNames = matches.map { it.groupValues[1] }.toSet()
49 | val nodeNames = node.Parameters.map { it.TypeValue }.toSet()
50 |
51 | if (matchNames != nodeNames) {
52 | this.description = "The documentation is error, parameter name is not match"
53 | callback(this, IssuePosition())
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/extension/JavaControllerAnalyser.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.extension
2 |
3 | import cc.unitmesh.quality.QualityAnalyser
4 | import chapi.domain.core.CodeDataStruct
5 | import org.archguard.linter.rule.webapi.WebApiRule
6 | import org.archguard.linter.rule.webapi.WebApiRuleSetProvider
7 | import org.archguard.linter.rule.webapi.WebApiRuleVisitor
8 | import org.archguard.rule.core.Issue
9 | import org.archguard.scanner.analyser.backend.JavaApiAnalyser
10 |
11 | /**
12 | * The `JavaControllerAnalyser` class is responsible for analyzing Java controller classes
13 | * to identify any quality issues based on a set of predefined rules.
14 | *
15 | * @param thresholds A map of thresholds for different quality metrics. The default value is an empty map.
16 | *
17 | * @constructor Creates a new instance of `JavaControllerAnalyser` with the specified thresholds.
18 | */
19 | class JavaControllerAnalyser(thresholds: Map = mapOf()) : QualityAnalyser {
20 | private val webApiRuleSetProvider = WebApiRuleSetProvider()
21 |
22 | /**
23 | * This method analyzes a list of REST APIs which from code data structures and returns a list of issues.
24 | * It uses the Default ArchGuard [WebApiRule], which includes the following rules:
25 | *
26 | * - SpliceNamingRule
27 | * - NoCrudEndRule
28 | * - NotUppercaseRule
29 | * - StartWithoutCrudRule
30 | * - NoHttpMethodInUrlRule
31 | * - MinFeatureApiRule
32 | * - MultipleParametersRule
33 | *
34 | * @param nodes The list of code data structures to analyze
35 | * @return The list of issues found during the analysis
36 | */
37 | override fun analysis(nodes: List): List {
38 | val apiAnalyser = JavaApiAnalyser()
39 |
40 | nodes.forEach { data ->
41 | apiAnalyser.analysisByNode(data, "")
42 | }
43 | val services = apiAnalyser.toContainerServices()
44 | return WebApiRuleVisitor(services).visitor(listOf(webApiRuleSetProvider.get()))
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/extension/JavaRepositoryAnalyser.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.extension
2 |
3 | import cc.unitmesh.quality.QualityAnalyser
4 | import chapi.domain.core.CodeDataStruct
5 | import org.archguard.linter.rule.sql.DatamapRuleVisitor
6 | import org.archguard.linter.rule.sql.SqlRule
7 | import org.archguard.linter.rule.sql.SqlRuleSetProvider
8 | import org.archguard.linter.rule.webapi.WebApiRule
9 | import org.archguard.rule.core.Issue
10 | import org.archguard.scanner.analyser.database.JvmSqlAnalyser
11 |
12 | class JavaRepositoryAnalyser(thresholds: Map = mapOf()) : QualityAnalyser {
13 | private val sqlRuleSetProvider = SqlRuleSetProvider()
14 | private val sqlAnalyser = JvmSqlAnalyser()
15 |
16 | /**
17 | * This method analyzes a list of SQL statements which from code data structures and returns a list of issues.
18 | * It uses the Default ArchGuard [SqlRule], which includes the following rules:
19 | *
20 | * - [org.archguard.linter.rule.webapi.rules.UnknownColumnSizeRule]
21 | * - [org.archguard.linter.rule.webapi.rules.LikeStartWithoutPercentRule]
22 | * - [org.archguard.linter.rule.webapi.rules.LimitTableNameLengthRule]
23 | * - [org.archguard.linter.rule.webapi.rules.SnakeCaseNamingRule]
24 | * - [org.archguard.linter.rule.webapi.rules.InsertWithoutField]
25 | * - [org.archguard.linter.rule.webapi.rules.LimitJoinsRule]
26 | * - [org.archguard.linter.rule.webapi.rules.AtLeastOnePrimaryKeyRule]
27 | * - [org.archguard.linter.rule.webapi.rules.LimitColumnSizeRule]
28 | *
29 | * @param nodes The list of code data structures to analyze
30 | * @return The list of issues found during the analysis
31 | */
32 | override fun analysis(nodes: List): List {
33 | val relations = nodes.flatMap { data ->
34 | sqlAnalyser.analysisByNode(data, "")
35 | }
36 |
37 | return DatamapRuleVisitor(relations).visitor(listOf(sqlRuleSetProvider.get()))
38 | }
39 | }
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/extension/JavaServiceAnalyser.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.extension
2 |
3 | import cc.unitmesh.quality.QualityAnalyser
4 | import cc.unitmesh.quality.extension.rule.TooManyRepositoryDependenciesRule
5 | import chapi.domain.core.CodeDataStruct
6 | import org.archguard.rule.core.Issue
7 | import org.archguard.rule.core.IssuePosition
8 | import org.archguard.rule.core.Rule
9 | import org.archguard.rule.core.RuleType
10 |
11 | class JavaServiceAnalyser(thresholds: Map = mapOf()) : QualityAnalyser {
12 | override fun analysis(nodes: List): List {
13 | val serviceNodes = nodes.filter { it.filterAnnotations("Service").isNotEmpty() }
14 | if (serviceNodes.isNotEmpty()) {
15 | val results: MutableList = mutableListOf()
16 | TooManyRepositoryDependenciesRule().visitRoot(serviceNodes, fun(rule: Rule, position: IssuePosition) {
17 | results += Issue(
18 | position,
19 | ruleId = rule.key,
20 | name = rule.name,
21 | detail = rule.description,
22 | ruleType = RuleType.SERVICE_SMELL
23 | )
24 | })
25 | return results;
26 | }
27 | //todo rule3: 调用的repository之间的关联度 -- ER关系上,关联度越强越好
28 | //todo rule4: 不依赖controller层相关概念,如:request/response -- 层与层之间依赖关系清晰
29 | //todo rule5: public filed数量 -- service应该都是private filed
30 | //pending rule6: 不被外界访问的public method -- 应归属到bad smell
31 | //pending rule1: service 长度小于x00行 -- bad smell large class
32 |
33 | // 检查 Service 长度,调用的 repository 数量,
34 | return listOf()
35 | }
36 | }
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/extension/rule/ServiceRule.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.extension.rule
2 |
3 | import chapi.domain.core.CodeDataStruct
4 | import org.archguard.rule.core.IssueEmit
5 | import org.archguard.rule.core.Rule
6 |
7 | open class ServiceRule : Rule() {
8 | open fun visitRoot(rootNode: List, callback: IssueEmit) {}
9 | }
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/extension/rule/TooManyRepositoryDependenciesRule.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.extension.rule
2 |
3 | import chapi.domain.core.CodeDataStruct
4 | import org.archguard.rule.core.IssueEmit
5 | import org.archguard.rule.core.IssuePosition
6 | import org.archguard.rule.core.Severity
7 |
8 | /**
9 | * Service should not dependent more than 5 repositories.
10 | */
11 | const val LIMIT = 5
12 |
13 | class TooManyRepositoryDependenciesRule : ServiceRule() {
14 | init {
15 | this.id = "too-many-repository-dependencies"
16 | this.name = "TooManyRepositoryDependencies"
17 | this.key = this.javaClass.name
18 | this.severity = Severity.WARN
19 | this.description = "Service should not dependent more than 5 repositories."
20 | }
21 |
22 | override fun visitRoot(rootNodes: List, callback: IssueEmit) {
23 | rootNodes.forEach {
24 | val repositoryCount = it.Fields.filter { it.TypeType.contains("Repository", true) }.count()
25 | if (repositoryCount > LIMIT) {
26 | callback(this, IssuePosition())
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/testbadsmell/TbsResult.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.testbadsmell
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class TbsResult(var results: List) {
7 |
8 | }
--------------------------------------------------------------------------------
/code-quality/src/main/kotlin/cc/unitmesh/quality/testbadsmell/TestBadSmell.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.testbadsmell
2 |
3 | import kotlinx.serialization.Serializable
4 | import org.archguard.rule.core.Issue
5 | import org.archguard.rule.core.IssuePosition
6 | import org.archguard.rule.core.RuleType
7 | import org.archguard.rule.core.Severity
8 |
9 | @Serializable
10 | data class TestBadSmell(
11 | var fileName: String = "",
12 | var type: String = "",
13 | var description: String = "",
14 | var line: Int = 0,
15 | ) {
16 | fun toIssue(): Issue {
17 | return Issue(
18 | name = type,
19 | fullName = fileName,
20 | detail = description,
21 | position = IssuePosition(),
22 | ruleId = type,
23 | severity = Severity.WARN,
24 | source = line.toString(),
25 | ruleType = RuleType.TEST_CODE_SMELL,
26 | )
27 | }
28 | }
--------------------------------------------------------------------------------
/code-quality/src/test/kotlin/cc/unitmesh/quality/JavaControllerAnalyserTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality;
2 |
3 | import cc.unitmesh.quality.extension.JavaControllerAnalyser
4 | import chapi.domain.core.CodeDataStruct
5 | import kotlinx.serialization.json.Json
6 | import org.junit.jupiter.api.Test
7 | import java.io.File
8 | import kotlin.test.assertEquals
9 |
10 | class JavaControllerAnalyserTest {
11 | private fun loadNodes(source: String): List {
12 | return Json { ignoreUnknownKeys = true }.decodeFromString(
13 | File(this.javaClass.classLoader.getResource(source)!!.file).readText()
14 | )
15 | }
16 | @Test
17 | fun `should return list of issues when checking API`() {
18 | val nodes = loadNodes("java/structs_HelloController.json")
19 | val issues = JavaControllerAnalyser().analysis(nodes)
20 |
21 | assertEquals(2, issues.size)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/code-quality/src/test/kotlin/cc/unitmesh/quality/JavaServiceAnalyserTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality;
2 |
3 | import cc.unitmesh.quality.extension.JavaServiceAnalyser
4 | import chapi.ast.javaast.JavaAnalyser
5 | import chapi.domain.core.CodeDataStruct
6 | import kotlinx.serialization.json.Json
7 | import org.archguard.rule.core.RuleType
8 | import org.junit.jupiter.api.Assertions
9 | import org.junit.jupiter.api.Test
10 | import java.io.File
11 | import java.nio.file.Paths
12 | import kotlin.test.assertEquals
13 |
14 | class JavaServiceAnalyserTest {
15 | private fun loadNodes(source: String): List {
16 | return Json { ignoreUnknownKeys = true }.decodeFromString(
17 | File(this.javaClass.classLoader.getResource(source)!!.file).readText()
18 | )
19 | }
20 |
21 | @Test
22 | fun `should return empty list of issues when node is not a service`() {
23 | val nodes = loadNodes("java/structs_HelloController.json")
24 | val issues = JavaServiceAnalyser().analysis(nodes)
25 |
26 | assertEquals(0, issues.size)
27 | }
28 |
29 | @Test
30 | fun `should identify too many repository dependencies`() {
31 | val path = getAbsolutePath("java/ServiceWithSixRepositories.java")
32 | val data = JavaAnalyser().analysis(File(path).readText(), "ServiceWithSixRepositories.java").DataStructures
33 | val issues = JavaServiceAnalyser().analysis(data)
34 |
35 | Assertions.assertEquals(1, issues.size)
36 | Assertions.assertEquals("TooManyRepositoryDependencies", issues[0].name)
37 | Assertions.assertEquals("Service should not dependent more than 5 repositories.", issues[0].detail)
38 | Assertions.assertEquals(RuleType.SERVICE_SMELL, issues[0].ruleType)
39 | }
40 |
41 | private fun getAbsolutePath(path: String): String {
42 | val resource = this.javaClass.classLoader.getResource(path)
43 | return Paths.get(resource!!.toURI()).toFile().absolutePath
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/code-quality/src/test/kotlin/cc/unitmesh/quality/comment/CodeCommentTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.comment
2 |
3 | import chapi.domain.core.CodePosition
4 | import org.junit.jupiter.api.Assertions.assertEquals
5 | import org.junit.jupiter.api.Test
6 |
7 | class CodeCommentTest {
8 |
9 | @Test
10 | fun should_reIndentComment() {
11 | // Given
12 | val commentContent = """
13 | | This is a comment.
14 | | It has multiple lines.
15 | | And some indented lines.
16 | """.trimMargin()
17 |
18 | // When
19 | val reIndentedComment = CodeComment.reIndentComment(commentContent)
20 |
21 | // Then
22 | val expectedComment = """
23 | |This is a comment.
24 | | It has multiple lines.
25 | | And some indented lines.
26 | """.trimMargin()
27 |
28 | assertEquals(expectedComment, reIndentedComment)
29 | }
30 |
31 | @Test
32 | fun should_extractKotlinComment() {
33 | // Given
34 | val code = """
35 | |fun main() {
36 | | /**
37 | | * This is a comment.
38 | | * It has multiple lines.
39 | | */
40 | | println("Hello, World!")
41 | |}
42 | """.trimMargin()
43 |
44 | // When
45 | val extractedComments = CodeComment.parseComment(code)
46 |
47 | // Then
48 | val expectedComment = CodeComment(
49 | """
50 | |/**
51 | | * This is a comment.
52 | | * It has multiple lines.
53 | | */
54 | """.trimMargin(),
55 | CodePosition(2, 4, 5, 6)
56 | )
57 |
58 | assertEquals(listOf(expectedComment), extractedComments)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/code-quality/src/test/kotlin/cc/unitmesh/quality/comment/CommentAnalyserTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.quality.comment;
2 |
3 | import chapi.ast.javaast.JavaAnalyser
4 | import chapi.domain.core.CodeDataStruct
5 | import org.junit.jupiter.api.Test
6 | import kotlin.test.assertEquals
7 | import kotlin.test.assertFalse
8 | import kotlin.test.assertTrue
9 |
10 | class CommentAnalyserTest {
11 |
12 | @Test
13 | fun `should return empty list when analysis nodes given empty nodes`() {
14 | // given
15 | val nodes = ArrayList()
16 |
17 | // when
18 | val commentAnalyser = CommentAnalyser(mapOf())
19 | val result = commentAnalyser.analysis(nodes)
20 |
21 | // then
22 | assertTrue(result.isEmpty())
23 | }
24 |
25 | @Test
26 | fun `should return list one issue when given lost parameter`() {
27 | // given
28 | val code = """
29 | public class Test {
30 | /**
31 | * Sum a and b, and return the result.
32 | * @param x the first number
33 | * @return the result of x + y
34 | */
35 | public int calculateSum(int x, int y) {
36 | return x + y;
37 | }
38 | }
39 | """.trimIndent()
40 | val container = JavaAnalyser().analysis(code, "Test.java")
41 | container.Content = code
42 | val commentAnalyser = CommentAnalyser(mapOf())
43 |
44 | // when
45 | val result = commentAnalyser.analysis(container)
46 |
47 | // then
48 | assertEquals(1, result.size)
49 | assertEquals("MissingParameterDesc", result[0].name)
50 | }
51 | }
--------------------------------------------------------------------------------
/code-quality/src/test/resources/bs/ComplexIf.java:
--------------------------------------------------------------------------------
1 | package com.zheng.upms.server.controller.manage;
2 |
3 | import com.baidu.unbiz.fluentvalidator.ComplexResult;
4 | import com.baidu.unbiz.fluentvalidator.FluentValidator;
5 | import com.baidu.unbiz.fluentvalidator.ResultCollectors;
6 | import com.zheng.common.base.BaseController;
7 | import com.zheng.common.validator.LengthValidator;
8 | import com.zheng.upms.common.constant.UpmsResult;
9 | import com.zheng.upms.common.constant.UpmsResultConstant;
10 | import com.zheng.upms.dao.model.UpmsSystem;
11 | import com.zheng.upms.dao.model.UpmsSystemExample;
12 | import com.zheng.upms.rpc.api.UpmsSystemService;
13 | import io.swagger.annotations.Api;
14 | import io.swagger.annotations.ApiOperation;
15 | import org.apache.commons.lang.StringUtils;
16 | import org.apache.shiro.authz.annotation.RequiresPermissions;
17 | import org.slf4j.Logger;
18 | import org.slf4j.LoggerFactory;
19 | import org.springframework.beans.factory.annotation.Autowired;
20 | import org.springframework.stereotype.Controller;
21 | import org.springframework.ui.ModelMap;
22 | import org.springframework.web.bind.annotation.*;
23 |
24 | import java.util.HashMap;
25 | import java.util.List;
26 | import java.util.Map;
27 |
28 | /**
29 | * 系统controller
30 | * Created by shuzheng on 2016/12/18.
31 | */
32 | @Controller
33 | @Api(value = "系统管理", description = "系统管理")
34 | @RequestMapping("/manage/system")
35 | public class UpmsSystemController extends BaseController {
36 |
37 | private static final Logger LOGGER = LoggerFactory.getLogger(UpmsSystemController.class);
38 |
39 | @Autowired
40 | private UpmsSystemService upmsSystemService;
41 |
42 | @ApiOperation(value = "系统列表")
43 | @RequiresPermissions("upms:system:read")
44 | @RequestMapping(value = "/list", method = RequestMethod.GET)
45 | @ResponseBody
46 | public Object list(
47 | @RequestParam(required = false, defaultValue = "0", value = "offset") int offset,
48 | @RequestParam(required = false, defaultValue = "10", value = "limit") int limit,
49 | @RequestParam(required = false, defaultValue = "", value = "search") String search,
50 | @RequestParam(required = false, value = "sort") String sort,
51 | @RequestParam(required = false, value = "order") String order) {
52 | UpmsSystemExample upmsSystemExample = new UpmsSystemExample();
53 | if (!StringUtils.isBlank(sort)
54 | && !StringUtils.isBlank(order)
55 |
56 | ) {
57 | upmsSystemExample.setOrderByClause(sort + " " + order);
58 | }
59 | if (StringUtils.isNotBlank(search)) {
60 | upmsSystemExample.or()
61 | .andTitleLike("%" + search + "%");
62 | }
63 | List rows = upmsSystemService.selectByExampleForOffsetPage(upmsSystemExample, offset, limit);
64 | long total = upmsSystemService.countByExample(upmsSystemExample);
65 | Map result = new HashMap<>();
66 | result.put("rows", rows);
67 | result.put("total", total);
68 | return result;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/bs/DataClass.java:
--------------------------------------------------------------------------------
1 | package com.phodal.coca.analysis.identifier.model;
2 |
3 | public class DataClass {
4 | private String date;
5 | private String time;
6 |
7 | public String getDate() {
8 | return date;
9 | }
10 |
11 | public void setDate(String date) {
12 | this.date = date;
13 | }
14 |
15 | public String getTime() {
16 | return time;
17 | }
18 |
19 | public void setTime(String time) {
20 | this.time = time;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/bs/LargeClass.java:
--------------------------------------------------------------------------------
1 | package com.phodal.coca.analysis.identifier.model;
2 |
3 | public class DataClass {
4 | public void method1() {}
5 | public void method2() {}
6 | public void method3() {}
7 | public void method4() {}
8 | public void method5() {}
9 | public void method6() {}
10 | public void method7() {}
11 | public void method8() {}
12 | public void method9() {}
13 | public void method10() {}
14 | public void method11() {}
15 | public void method12() {}
16 | public void method13() {}
17 | public void method14() {}
18 | public void method15() {}
19 | public void method16() {}
20 | public void method17() {}
21 | public void method18() {}
22 | public void method19() {}
23 | public void method20() {}
24 | }
25 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/bs/LazyClass.java:
--------------------------------------------------------------------------------
1 | class LazyClass {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/bs/LongParameter.java:
--------------------------------------------------------------------------------
1 | package com.phodal.coca.analysis.identifier.model;
2 |
3 | public class LongParameter {
4 | public void buildSomeThing(String time, int x, int y, int z, int a, int b) {
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/bs/MultipleIf.java:
--------------------------------------------------------------------------------
1 | class MultipleIf {
2 | public static void main(String args[]) {
3 | int i = 20;
4 |
5 | if (i == 10)
6 | System.out.println("i is 10");
7 | if (i == 15)
8 | System.out.println("i is 15");
9 | if (i == 20)
10 | System.out.println("i is 20");
11 | if (i == 20)
12 | System.out.println("i is 20");
13 | if (i == 20)
14 | System.out.println("i is 20");
15 | if (i == 20)
16 | System.out.println("i is 20");
17 | if (i == 20)
18 | System.out.println("i is 20");
19 | if (i == 20)
20 | System.out.println("i is 20");
21 | else
22 | System.out.println("i is not present");
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/bs/graphcall/GraphCallA.java:
--------------------------------------------------------------------------------
1 |
2 | package graphcall;
3 |
4 | public class GraphCallA {
5 | private GraphCallB graphCallB;
6 | private GraphCallC graphCallC;
7 |
8 | public void sayHi(){
9 | graphCallB.sayHi();
10 | graphCallC.sayHi();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/bs/graphcall/GraphCallB.java:
--------------------------------------------------------------------------------
1 | package graphcall;
2 |
3 | public class GraphCallB {
4 | private GraphCallC graphCallC;
5 |
6 | public void sayHi(){
7 | graphCallC.sayHi();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/bs/graphcall/GraphCallC.java:
--------------------------------------------------------------------------------
1 | package graphcall;
2 |
3 | public class GraphCallC {
4 |
5 | public void sayHi(){
6 | System.out.println("hi");
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/bs/interface/BlogRepository.java:
--------------------------------------------------------------------------------
1 | package study.huhao.demo.domain.models.blog;
2 |
3 | import study.huhao.demo.domain.core.Page;
4 | import study.huhao.demo.domain.core.Repository;
5 |
6 | import java.util.Optional;
7 | import java.util.UUID;
8 |
9 | public interface BlogRepository extends Repository {
10 | void save(Blog blog);
11 |
12 | Optional findById(UUID id);
13 |
14 | boolean existsById(UUID id);
15 |
16 | void deleteById(UUID id);
17 |
18 | Page findAllWithPagination(BlogCriteria criteria);
19 | }
20 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/java/ServiceWithSixRepositories.java:
--------------------------------------------------------------------------------
1 | package com.afs.restapi.service;
2 |
3 | import com.afs.restapi.repository.CompanyRepository;
4 | import com.afs.restapi.repository.EmployeeRepository;
5 | import com.afs.restapi.repository.DepartmentRepository;
6 | import com.afs.restapi.repository.TeamRepository;
7 | import com.afs.restapi.repository.GroupRepository;
8 | import com.afs.restapi.repository.CommunitRepository;
9 | import org.springframework.stereotype.Service;
10 |
11 | @Service
12 | public class Example {
13 | private CompanyRepository companyRepository;
14 | private EmployeeRepository employeeRepository;
15 | private DepartmentRepository departmentRepository;
16 | private TeamRepository teamRepository;
17 | private GroupRepository groupRepository;
18 | private CommunitRepository communitRepository;
19 |
20 | public Example(CompanyRepository companyRepository, EmployeeRepository employeeRepository,
21 | DepartmentRepository departmentRepository, TeamRepository teamRepository,
22 | GroupRepository groupRepository, CommunitRepository communitRepository) {
23 | this.companyRepository = companyRepository;
24 | this.employeeRepository = employeeRepository;
25 | this.departmentRepository = departmentRepository;
26 | this.teamRepository = teamRepository;
27 | this.groupRepository = groupRepository;
28 | this.communitRepository = communitRepository;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/java/structs_HelloController.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "NodeName": "HelloController",
4 | "Type": "CLASS",
5 | "Package": "com.example.springboot",
6 | "FilePath": "HelloController.java",
7 | "Fields": [],
8 | "MultipleExtend": [],
9 | "Implements": [],
10 | "Functions": [
11 | {
12 | "Name": "index",
13 | "ReturnType": "String",
14 | "MultipleReturns": [],
15 | "Parameters": [],
16 | "FunctionCalls": [],
17 | "Annotations": [
18 | {
19 | "Name": "GetMapping",
20 | "KeyValues": [
21 | {
22 | "Key": "\"/blog/get\"",
23 | "Value": "\"/blog/get\""
24 | }
25 | ]
26 | }
27 | ],
28 | "Modifiers": [],
29 | "InnerStructures": [],
30 | "InnerFunctions": [],
31 | "Position": {
32 | "StartLine": 11,
33 | "StartLinePosition": 11,
34 | "StopLine": 13,
35 | "StopLinePosition": 4
36 | },
37 | "LocalVariables": []
38 | }
39 | ],
40 | "InnerStructures": [],
41 | "Annotations": [
42 | {
43 | "Name": "RestController",
44 | "KeyValues": []
45 | },
46 | {
47 | "Name": "RequestMapping",
48 | "KeyValues": []
49 | }
50 | ],
51 | "FunctionCalls": [],
52 | "Parameters": [],
53 | "Imports": [
54 | {
55 | "Source": "org.springframework.web.bind.annotation.GetMapping",
56 | "UsageName": []
57 | },
58 | {
59 | "Source": "org.springframework.web.bind.annotation.RestController",
60 | "UsageName": []
61 | }
62 | ],
63 | "Exports": [],
64 | "Position": {
65 | "StartLine": 8,
66 | "StartLinePosition": 7,
67 | "StopLine": 15
68 | }
69 | }
70 | ]
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/AssertionRouletteTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 | import static org.junit.Assert.assertThat;
7 |
8 | public class AssertionRouletteTest {
9 | @Test
10 | public void testCloneNonBareRepoFromLocalTestServer() throws Exception {
11 | Calculate calculate = new Calculate();
12 | int result = calculate.add(7, 8);
13 | int success = 15;
14 | assertEquals(success, result);
15 |
16 | int subResult = calculate.sub(9, 2);
17 | int subSuccess = 7;
18 | assertEquals(subSuccess, subResult);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/ConditionalTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | public class ConditionalTest {
8 | @Test
9 | public void byGod() {
10 | Calculate calculate = new Calculate();
11 | // just examples
12 | if (calculate.add(7, 9) == 16) {
13 | if (calculate.sub(12, 9) == 3) {
14 | int subSuccess = 7;
15 | assertEquals(subSuccess, calculate.sub(9, 2));
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/ConstructorInitializationTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 |
6 | import static org.junit.Assert.assertEquals;
7 |
8 | public class ConstructorInitializationTest {
9 | @Before
10 | public void init() throws Exception {
11 |
12 | }
13 |
14 | @Test
15 | public void name() {
16 | Calculate calculate = new Calculate();
17 | int result = calculate.add(7, 8);
18 | int success = 15;
19 | assertEquals(success, result);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/DuplicateAssertTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | public class DuplicateAssertTest {
8 | @Test
9 | public void testXmlSanitizer() {
10 | boolean valid = XmlSanitizer.isValid("Fritzbox");
11 | assertEquals("Fritzbox is valid", true, valid);
12 |
13 | valid = XmlSanitizer.isValid("Fritz Box");
14 | assertEquals("Spaces are valid", true, valid);
15 |
16 | valid = XmlSanitizer.isValid("Frützbüx");
17 | assertEquals("Frützbüx is invalid", false, valid);
18 |
19 | valid = XmlSanitizer.isValid("Fritz!box");
20 | assertEquals("Exclamation mark is valid", true, valid);
21 |
22 | valid = XmlSanitizer.isValid("Fritz!box");
23 | assertEquals("Exclamation mark is valid", true, valid);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/EmptyTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Test;
4 |
5 | public class EmptyTest {
6 |
7 | @Test
8 | public void testCredGetFullSampleV1() throws Throwable {
9 | // ScrapedCredentials credentials = innerCredTest(FULL_SAMPLE_v1);
10 | // assertEquals("p4ssw0rd", credentials.pass);
11 | // assertEquals("user@example.com",credentials.user);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/IgnoreTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Ignore;
4 |
5 | public class IgnoreTest {
6 | @Ignore("Oops, Not Time fix it")
7 | public void peerPriority() throws Exception {
8 |
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/MagicNumberTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.Calendar;
6 |
7 | import static org.junit.Assert.assertEquals;
8 |
9 | public class MagicNumberTest {
10 | @Test
11 | public void testGetLocalTimeAsCalendar() {
12 | int result = 7 + 8;
13 | assertEquals(15, result);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/MysteryGuestTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.FileNotFoundException;
6 | import java.io.FileOutputStream;
7 | import java.io.IOException;
8 |
9 | public class MysteryGuestTest {
10 | @Test
11 | public void testPersistence() throws Exception {
12 | try (FileOutputStream out = new FileOutputStream("people.bin");) {
13 | int result = 5;
14 | out.write(result);
15 | } catch (FileNotFoundException e) {
16 | // blabla
17 | } catch (IOException e) {
18 | // blabla
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/RedundantAssertionTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | public class RedundantAssertionTest {
8 | @Test
9 | public void testTrue() {
10 | Calculate calculate = new Calculate();
11 | int result = calculate.add(7, 8);
12 | int success = 15;
13 |
14 | assertEquals(true, true);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/RedundantPrintTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Test;
4 |
5 | public class RedundantPrintTest {
6 | @Test
7 | public void testTransform10mNEUAndBack() {
8 | String result = "a, b, c";
9 | System.out.println("result = " + result);
10 | assertEquals(true, true);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/SleepyTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Test;
4 |
5 | public class SleepyTest {
6 | @Test
7 | public void testEdictExternSearch() throws Exception {
8 | Thread.sleep(500);
9 | assertEquals(true, true);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/TestersOnly.java:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * public for test call only
4 | *
5 | */
6 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/UnknownTest.java:
--------------------------------------------------------------------------------
1 | package tbs;
2 |
3 | import org.junit.Test;
4 |
5 | public class UnknownTest {
6 | @Test
7 | public void hitGetPOICategoriesApi() throws Exception {
8 | String a = "blabla";
9 | String b = "blablac";
10 | String c = a + b;
11 | Show(a, b);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/regression/CallAssertInClassTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2002-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.springframework.context.event;
18 |
19 | import org.junit.jupiter.api.Test;
20 |
21 | import org.springframework.context.ApplicationEvent;
22 | import org.springframework.context.ApplicationListener;
23 | import org.springframework.core.ResolvableType;
24 |
25 | import static org.assertj.core.api.Assertions.assertThat;
26 | import static org.mockito.Mockito.mock;
27 | import static org.mockito.Mockito.times;
28 | import static org.mockito.Mockito.verify;
29 |
30 | /**
31 | * @author Stephane Nicoll
32 | */
33 | @RunWith(PowerMockRunner.class)
34 | public class CallAssertInClassTests extends AbstractApplicationEventListenerTests {
35 |
36 | @Mock
37 | Connection connection = PowerMockito.mock(Connection.class);
38 |
39 | @Test // Demonstrates we cant inject that event because the listener has a raw type
40 | public void genericListenerRawTypeTypeErasure() {
41 | GenericTestEvent stringEvent = createGenericTestEvent("test");
42 | ResolvableType eventType = ResolvableType.forType(stringEvent.getClass());
43 | supportsEventType(true, RawApplicationListener.class, eventType);
44 | }
45 |
46 | @SuppressWarnings("rawtypes")
47 | private void supportsEventType(
48 | boolean match, Class extends ApplicationListener> listenerType, ResolvableType eventType) {
49 |
50 | ApplicationListener> listener = mock(listenerType);
51 | GenericApplicationListenerAdapter adapter = new GenericApplicationListenerAdapter(listener);
52 | assertThat(adapter.supportsEventType(eventType)).as("Wrong match for event '" + eventType + "' on " + listenerType.getClass().getName()).isEqualTo(match);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/code-quality/src/test/resources/tbs/regression/CreatorNotUnknownTest.java:
--------------------------------------------------------------------------------
1 | package cc.arduino.packages.contributions;
2 |
3 | import org.junit.Test;
4 | import processing.app.Platform;
5 |
6 | import static org.junit.Assert.assertFalse;
7 | import static org.junit.Assert.assertTrue;
8 |
9 | public class HostDependentDownloadableContributionTest {
10 | @Test
11 | public void macOsXPositiveTest() {
12 | HostDependentDownloadableContributionStub contribution = new HostDependentDownloadableContributionStub() {
13 | @Override
14 | public String getHost() {
15 | return "x86_64-apple-darwin";
16 | }
17 | };
18 |
19 | Platform platform = new Platform() {
20 | @Override
21 | public String getOsName() {
22 | return "Mac OS X";
23 | }
24 |
25 | @Override
26 | public String getOsArch() {
27 | return "x86_64";
28 | }
29 | };
30 |
31 | assertTrue(contribution.isCompatible(platform));
32 | }
33 | }
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | gen.unitmesh.cc
--------------------------------------------------------------------------------
/docs/evaluation/evaluation.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Evaluation
4 | nav_order: 6
5 | has_children: true
6 | permalink: /evaluation
7 | ---
8 |
9 | # Evaluation in Design
10 |
11 | - Layered Architecture/MVC/DDD [https://github.com/jkazama/ddd-java](https://github.com/jkazama/ddd-java)
12 | - Entity and DTO by file name
13 | - Controller for API design
14 | - Service
15 | - Repository for JPA by specifics
16 | - Inter Infrastructure (Built-in Spec) [Low level design Questions](https://github.com/kumaransg/LLD)
17 | - Redis
18 | - Kafka
19 | - RabbitMQ
20 | - MySQL
21 | - MongoDB
22 | - ...
23 | - OO Design [MarsRover](https://github.com/priyaaank/MarsRover)
24 | - inheritance
25 | - polymorphism
26 | - encapsulation
27 | - abstraction
28 | - ...
29 | - Design Patterns [Java Design Pattern](https://github.com/iluwatar/java-design-patterns), [Design Patterns in Java](https://github.com/RefactoringGuru/design-patterns-java)
30 | - Singleton
31 | - Factory
32 | - Builder
33 | - State
34 | - ...
35 | - Security & Cryptography
36 | - JWT
37 | - Spring Security
38 | - MD5
39 | - Test
40 | - Unit Test - Given-When-Then
41 | - API Test
42 | - Integration Test
43 |
44 | ## Instruct for combined
45 |
46 | ### Trigger by comments
47 |
48 | - [https://github.com/ise-uiuc/magicoder](https://github.com/ise-uiuc/magicoder)
49 |
50 | ### Code completion
51 |
52 | - UnitGen
53 |
54 | ## TypeScript
55 |
56 | - [RefactoringGuru Design Patterns in TypeScript](https://github.com/RefactoringGuru/design-patterns-typescript/tree/main)
57 | - [Revisiting Design Patterns After 20](https://github.com/yanaga/revisiting-design-patterns/tree/main)
--------------------------------------------------------------------------------
/docs/evaluation/intent-driven.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Intent Driven
4 | parent: Evaluation
5 | nav_order: 6
6 | ---
7 |
8 | # Code Generation design
9 |
10 | ## Trigger by Comments
11 |
12 | ```kotlin
13 | /**
14 | * IntentDrivenCompletionIns
15 | *
16 | * IntentDrivenStrategyBuilder was similar to [RelatedCodeStrategyBuilder] and [SimilarChunksStrategyBuilder]
17 | */
18 | ```
19 |
20 | ## Trigger by Path
21 |
22 | when the path is `src/main/java/com/unitmesh/uniteval/IntentDrivenStrategyBuilder.kt` will send similar inheritance code
23 | from `CodeStrategyBuilder`.
24 |
25 | ## Trigger by Class Name and same package paths
26 |
27 | - `class IntentCodeStrategyBuilder` will trigger Test.
28 | - `class IntentCodeStrategyBuilder(private val context: JobContext):` will be working.
29 |
30 | # How we implement
31 |
32 |
--------------------------------------------------------------------------------
/docs/evaluation/java.yml:
--------------------------------------------------------------------------------
1 | projects:
2 | # Functional Programming
3 | - repository: https://github.com/fpinjava/fpinjava
4 | branch: master
5 | language: java
6 | - repository: https://github.com/functionaljava/functionaljava
7 | branch: series/5.x
8 | language: java
9 | # OO Design
10 | - repository: https://github.com/RefactoringGuru/design-patterns-java
11 | branch: main
12 | language: java
13 | - repository: https://github.com/iluwatar/java-design-patterns
14 | branch: master
15 | language: java
16 | - repository: https://github.com/priyaaank/MarsRover
17 | branch: master
18 | language: java
19 |
--------------------------------------------------------------------------------
/docs/finetune/datasets.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Datasets
4 | parent: FineTune
5 | nav_order: 98
6 | ---
7 |
8 | ## 开源数据集
9 |
10 |
11 | ### 中文代码数据集
12 |
13 | - https://github.com/Denilah/CoLLaMA
14 | - https://github.com/sahil280114/codealpaca
15 |
16 | ### Math
17 |
18 | - [OpenOrca](https://huggingface.co/datasets/Open-Orca/OpenOrca)
19 | - [zetavg/ShareGPT-Processed](https://huggingface.co/datasets/zetavg/ShareGPT-Processed)
20 | - [MathInstruct](https://huggingface.co/datasets/TIGER-Lab/MathInstruct)
21 |
22 | ## 内部数据集
23 |
24 | 应用类:根据组织内部选择
25 |
26 | 架构类:
27 |
28 | ```yml
29 | - repository: https://github.com/ttulka/ddd-example-ecommerce
30 | branch: main
31 | language: java
32 | - repository: https://github.com/TNG/ArchUnit-Examples
33 | branch: main
34 | language: java
35 | - repository: https://github.com/iluwatar/java-design-patterns
36 | branch: master
37 | language: java
38 | ```
39 |
40 | 框架类:
41 |
42 | ```yml
43 | - repository: https://github.com/spring-projects-experimental/spring-ai
44 | branch: main
45 | language: java
46 | - repository: https://github.com/spring-projects/spring-data-jpa
47 | branch: main
48 | language: java
49 | ```
50 |
51 | 框架示例:获取最新的 API 使用(需要结合版本)
52 |
53 | ```yml
54 | - repository: https://github.com/spring-projects/spring-data-examples
55 | branch: main
56 | language: java
57 | - repository: https://github.com/spring-projects/spring-security-samples
58 | branch: main
59 | language: java
60 | ```
61 |
--------------------------------------------------------------------------------
/docs/finetune/ext-datasets-builder.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Ext Datasets Builder
4 | parent: FineTune
5 | nav_order: 100
6 | ---
7 |
8 | ## Refactoring
9 |
10 | [RefactoringMiner](https://github.com/tsantalis/RefactoringMiner) is a library/API written in Java that can detect
11 | refactorings applied in the history of a Java project.
12 |
13 | - [kotlinRMiner](https://github.com/JetBrains-Research/kotlinRMiner) is A library that detects performed refactorings in
14 | changes in Kotlin code.
15 | - [PyRef](https://github.com/PyRef/PyRef) is a tool that automatically detects mainly method-level refactoring
16 | operations in Python projects.
17 | - [RefactoringMiner](https://github.com/maldil/RefactoringMiner) s a Java library that can detect refactorings applied
18 | in the commit history of a Java project.
19 |
20 | ## Merge
21 |
22 | - [MergeScenarioMiner](https://github.com/Symbolk/MergeScenarioMiner) is a mining tool to collect merge scenarios from
23 | Git repositories.
--------------------------------------------------------------------------------
/docs/finetune/faq.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: FAQ
4 | parent: FineTune
5 | nav_order: 99
6 | ---
7 |
8 | # FAQ
9 |
10 |
11 | ## NotImplementedError
12 |
13 | NotImplementedError: Using RTX 3090 or 4000 series doesn't support faster communication broadband via P2P or IB. Please
14 | set `NCCL_P2P_DISABLE="1"` and `NCCL_IB_DISABLE="1" or use `accelerate launch` which will do this automatically.
15 |
16 | ```
17 | export NCCL_P2P_DISABLE=1
18 | export NCCL_IB_DISABLE=1
19 | ```
20 |
21 | ## exits with return code = -9
22 |
23 | https://github.com/deepseek-ai/DeepSeek-Coder/issues/55
24 |
25 | https://github.com/deepseek-ai/DeepSeek-Coder/issues/54
26 |
27 | ```bash
28 | [2023-11-28 10:00:34,590] [ERROR] [launch.py:321:sigkill_handler] ['/home/.python_libs/conda_env/deepseek/bin/python', '-u', 'finetune_deepseekcoder.py', '--local_rank=0', '--model_name_or_path', '/home/project/deepseek/DeepSeek-Coder-main/models/deepseek-coder-6.7b-instruct', '--data_path', '/home/project/deepseek/DeepSeek-Coder-main/data/test.json', '--output_dir', '/home/project/deepseek/DeepSeek-Coder-main/deepseek_finetune', '--num_train_epochs', '1', '--model_max_length', '1024', '--per_device_train_batch_size', '1', '--per_device_eval_batch_size', '1', '--gradient_accumulation_steps', '4', '--evaluation_strategy', 'no', '--save_strategy', 'steps', '--save_steps', '100', '--save_total_limit', '100', '--learning_rate', '2e-5', '--warmup_steps', '10', '--logging_steps', '1', '--lr_scheduler_type', 'cosine', '--gradient_checkpointing', 'True', '--report_to', 'tensorboard', '--deepspeed', 'configs/ds_config_zero3.json', '--bf16', 'True'] exits with return code = -9
29 | ```
30 |
31 | modify deepspeed config `configs/ds_config_zero3.json`, set `pin_memory` to `false`:
32 |
33 | ```
34 | "zero_optimization": {
35 | "stage": 3,
36 | "offload_optimizer": {
37 | "device": "cpu",
38 | "pin_memory": false
39 | },
40 | "offload_param": {
41 | "device": "cpu",
42 | "pin_memory": false
43 | },
44 | ...
45 | }
46 | ```
--------------------------------------------------------------------------------
/docs/finetune/finetune.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: FineTune
4 | nav_order: 7
5 | has_children: true
6 | ---
7 |
8 | - CodeGeeX 6b
9 | - [DeepSeek 6.6b](https://github.com/unit-mesh/unit-gen/tree/master/finetunes/deepseek)
10 |
--------------------------------------------------------------------------------
/docs/instruction/instruction.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Instruction Builder
4 | nav_order: 4
5 | has_children: true
6 | permalink: /instruction
7 | ---
8 |
9 | UnitGen with combined with CodeContextStrategy and CompletionBuilderType to build the instruction.
10 |
11 | The instructions will be generated by the following steps:
12 |
13 | | CodeContextStrategy | Description |
14 | |---------------------|--------------------------------------------------------------------------|
15 | | SimilarChunks | tokenize path to find similar path, then calculate similar chunk by path |
16 | | RelatedCode | use static analysis by imports to calculate related code |
17 |
18 | for example:
19 |
20 | | | SimilarChunks | RelatedCode |
21 | |----------------------|------------------|-------------|
22 | | InlineCompletion | afterCursor > 10 | |
23 | | InBlockCompletion | | |
24 | | AfterBlockCompletion | | |
25 | | TestCode | | |
26 | | Documentation | lines >= 5 | |
27 |
28 | If you want to add a new instruction, you need to add a new CodeContextStrategy and CompletionBuilderType.
29 |
30 | # Code Context Strategy
31 |
32 | ```kotlin
33 | enum class BizCodeContextStrategy {
34 | /**
35 | * the AutoDev with pre-build context for un-support language
36 | */
37 | SIMILAR_CHUNKS,
38 |
39 | /**
40 | * the AutoDev with pre-build context
41 | */
42 | RELATED_CODE;
43 |
44 | fun builder(context: JobContext): CodeContextBuilder {
45 | return mapOf(
46 | SIMILAR_CHUNKS to SimilarChunksCompletionBuilder(context),
47 | RELATED_CODE to RelatedCodeCompletionBuilder(context),
48 | )[this] ?: throw SerializationException("Unknown message type: $this")
49 | }
50 | }
51 | ```
52 |
53 | # Completion Type
54 |
55 | ```kotlin
56 | enum class CompletionBuilderType {
57 | INLINE_COMPLETION,
58 | IN_BLOCK_COMPLETION,
59 | AFTER_BLOCK_COMPLETION,
60 | TEST_CODE_GEN,
61 | DOCUMENTATION
62 | }
63 | ```
64 |
65 |
--------------------------------------------------------------------------------
/docs/prompts/autodev/code-completion.vm:
--------------------------------------------------------------------------------
1 | Complete ${context.lang} code, return rest code, no explaining
2 | ## check by controler,service, model
3 | #set($isController = $!{ArchUtil.isController(${context.fileName}, ${context.lang})})
4 | #set($isService = $!{ArchUtil.isService(${context.fileName}, ${context.lang})})
5 |
6 | #if ($isController)
7 | #set($spec = $!{PromptConfig.load().spec["controller"]})
8 | #if (!$spec.isEmpty())
9 | #set($additionContext = "requirements: \n$spec")
10 | #end
11 | #set($additionContext = "$additionContext$mvcContextService.controllerPrompt($file)")
12 | ## get service and model code by imports ?
13 | #elseif ($isService)
14 | #set($spec = $!{PromptConfig.load().spec["service"]})
15 | #if (!$spec.isEmpty())
16 | #set($additionContext = "requirements: \n$spec")
17 | #end
18 | #set($additionContext = "${context.servicePrompt($file)}")
19 | ## get service and model code by imports ?
20 | #else
21 | #set($additionContext = $!{Smart.simliarChunk($file)})
22 | #end
23 |
--------------------------------------------------------------------------------
/docs/prompts/autodev/explain-code.vm:
--------------------------------------------------------------------------------
1 | ${context.instruction}
2 |
3 | ${context.code}
4 |
--------------------------------------------------------------------------------
/docs/prompts/autodev/test-generate.vm:
--------------------------------------------------------------------------------
1 | Write unit test for following code.
2 |
3 | ${context.testFramework}
4 | ${context.coreFramework}
5 | ${context.testSpec}
6 |
7 | ${context.related_model}
8 |
9 | ```${context.language}
10 | ${context.selection}
11 | ```
12 |
--------------------------------------------------------------------------------
/docs/prompts/autodev/test/testgen.request.md:
--------------------------------------------------------------------------------
1 | Write unit test for following code.
2 | You MUST return method code only, no explain.
3 | You MUST Use English to reply me!
4 | You MUST return start with @Test annotation.
5 | You are working on a project that uses Spring MVC,Spring WebFlux,JDBC to build RESTful APIs.
6 | You MUST use should_xx_xx style for test method name.
7 | You MUST use given-when-then style.
8 | - Test file should be complete and compilable, without need for further actions.
9 | - Ensure that each test focuses on a single use case to maintain clarity and readability.
10 | - Instead of using `@BeforeEach` methods for setup, include all necessary code initialization within each individual test method, do not write parameterized tests.
11 | - This project uses JUnit 5, you should import `org.junit.jupiter.api.Test` and use `@Test` annotation.
12 | - You MUST use MockMvc and test API only.
13 | - Use appropriate Spring test annotations such as `@MockBean`, `@Autowired`, `@WebMvcTest`, `@DataJpaTest`, `@AutoConfigureTestDatabase`, `@AutoConfigureMockMvc`, `@SpringBootTest` etc.
14 |
15 | // here are related classes:
16 | // 'filePath: /Users/phodal/IdeaProjects/untitled/src/main/java/cc/unitmesh/untitled/demo/service/BlogService.java
17 | // class BlogService {
18 | // blogRepository
19 | // + public BlogPost createBlog(BlogPost blogDto)
20 | // + public BlogPost getBlogById(Long id)
21 | // + public BlogPost updateBlog(Long id, BlogPost blogDto)
22 | // + public void deleteBlog(Long id)
23 | // }
24 | // 'filePath: /Users/phodal/IdeaProjects/untitled/src/main/java/cc/unitmesh/untitled/demo/dto/CreateBlogRequest.java
25 | // class CreateBlogRequest {
26 | // title
27 | // content
28 | // user
29 | //
30 | // }
31 | // 'filePath: /Users/phodal/IdeaProjects/untitled/src/main/java/cc/unitmesh/untitled/demo/entity/BlogPost.java
32 | // class BlogPost {
33 | // id
34 | // title
35 | // content
36 | // author
37 | // + public BlogPost(String title, String content, String author)
38 | // + public BlogPost()
39 | // + public void setId(Long id)
40 | // + public Long getId()
41 | // + public String getTitle()
42 | // + public void setTitle(String title)
43 | // + public String getContent()
44 | // + public void setContent(String content)
45 | // + public String getAuthor()
46 | // + public void setAuthor(String author)
47 | // }
48 | ```java
49 | @ApiOperation(value = "Create a new blog")
50 | @PostMapping("/")
51 | public BlogPost createBlog(@RequestBody CreateBlogRequest request) {
52 | CreateBlogResponse response = new CreateBlogResponse();
53 | BlogPost blogPost = new BlogPost();
54 | BeanUtils.copyProperties(request, blogPost);
55 | BlogPost createdBlog = blogService.createBlog(blogPost);
56 | BeanUtils.copyProperties(createdBlog, response);
57 | return createdBlog;
58 | }
59 | ```
60 | Start test code with `@Test` syntax here:
61 |
--------------------------------------------------------------------------------
/docs/prompts/autodev/test/testgen.response.md:
--------------------------------------------------------------------------------
1 | @Test
2 | void shouldCreateBlog() throws Exception {
3 | // given
4 | String title = "Test Title";
5 | String content = "Test Content";
6 | String author = "Test Author";
7 |
8 | CreateBlogRequest request = new CreateBlogRequest();
9 | request.setTitle(title);
10 | request.setContent(content);
11 | request.setUser(author);
12 |
13 | BlogPost blogPost = new BlogPost();
14 | blogPost.setTitle(title);
15 | blogPost.setContent(content);
16 | blogPost.setAuthor(author);
17 |
18 | BlogPost createdBlogPost = new BlogPost();
19 | // set expected values for createdBlogPost
20 |
21 | given(blogService.createBlog(any(BlogPost.class))).willReturn(createdBlogPost);
22 |
23 | // when
24 | mockMvc.perform(post("/")
25 | .contentType(MediaType.APPLICATION_JSON)
26 | .content(objectMapper.writeValueAsString(request)))
27 | .andExpect(status().isOk())
28 | .andExpect(jsonPath("$.title", is(title)))
29 | .andExpect(jsonPath("$.content", is(content)))
30 | .andExpect(jsonPath("$.author", is(author)));
31 |
32 |
33 | // then
34 | verify(blogService).createBlog(any(BlogPost.class));
35 | }
--------------------------------------------------------------------------------
/docs/prompts/custom/code-completion.vm:
--------------------------------------------------------------------------------
1 | ```user```
2 |
3 |
--------------------------------------------------------------------------------
/docs/prompts/custom/explain-code.vm:
--------------------------------------------------------------------------------
1 | ${context.instruction}
2 |
3 | ${context.code}
4 |
--------------------------------------------------------------------------------
/docs/prompts/custom/test-completion.vm:
--------------------------------------------------------------------------------
1 | ```user```
2 |
3 |
--------------------------------------------------------------------------------
/docs/prompts/openai/code-completion.vm:
--------------------------------------------------------------------------------
1 | ```user```
2 |
3 |
--------------------------------------------------------------------------------
/docs/prompts/openai/explain-code.vm:
--------------------------------------------------------------------------------
1 | ```user```
2 |
3 |
4 | ${context.before_cursor}
5 |
--------------------------------------------------------------------------------
/docs/prompts/openai/test-completion.vm:
--------------------------------------------------------------------------------
1 | ```user```
2 |
3 |
--------------------------------------------------------------------------------
/docs/prompts/yiyan/code-completion.vm:
--------------------------------------------------------------------------------
1 | ```user```
2 |
3 |
--------------------------------------------------------------------------------
/docs/prompts/yiyan/explain-code.vm:
--------------------------------------------------------------------------------
1 | ```user```
2 |
3 |
4 | ${context.before_cursor}
5 |
--------------------------------------------------------------------------------
/docs/prompts/yiyan/test-completion.vm:
--------------------------------------------------------------------------------
1 | ```user```
2 |
3 |
--------------------------------------------------------------------------------
/docs/quality/rule-extension.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Rule Extension
4 | parent: Quality
5 | nav_order: 1
6 | permalink: /quality/rule
7 | ---
8 |
9 | ## Rule Example
10 |
11 | ```kotlin
12 | import org.archguard.rule.core.IssueEmit
13 | import org.archguard.rule.core.IssuePosition
14 | import org.archguard.rule.core.RuleContext
15 | import org.archguard.rule.core.Severity
16 | import org.archguard.linter.rule.webapi.WebApiRule
17 | import org.archguard.scanner.core.sourcecode.ContainerSupply
18 |
19 | class NoCrudEndRule: WebApiRule() {
20 | init {
21 | this.id = "no-crud-end"
22 | this.name = "NoCrudEndRule"
23 | this.key = this.javaClass.name
24 | this.description = "URLs should not end with action. Incorrect: `/api/book/get`, correct: `GET /api/book`."
25 | this.severity = Severity.WARN
26 | }
27 |
28 | override fun visitResource(resource: ContainerSupply, context: RuleContext, callback: IssueEmit) {
29 | val split = resource.sourceUrl.split("/")
30 | if(CRUD.contains(split.last().lowercase())) {
31 | callback(this, IssuePosition())
32 | }
33 | }
34 | }
35 | ```
36 |
37 |
38 | Refs:
39 |
40 | - [https://archguard.org/custom/custom-rule](https://archguard.org/custom/custom-rule)
41 | - [https://archguard.org/governance](https://archguard.org/governance)
42 |
43 |
44 |
--------------------------------------------------------------------------------
/docs/quick-start.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Quick Start
4 | nav_order: 2
5 | permalink: /quick-start
6 | ---
7 |
8 | # Quick Start
9 |
10 | for examples, see: [examples](https://github.com/unit-mesh/unit-gen/tree/master/examples) folder
11 |
12 | ## use in IDE
13 |
14 | 1. config project by `processor.yml`
15 | 2. run `PickerKt`
16 |
17 | or run in `unit-cli/src/main/kotlin/cc/unitmesh/runner/Picker.kt`
18 |
19 | ## use CLI
20 |
21 | ### use CLI
22 |
23 | see in [config-examples](https://github.com/unit-mesh/unit-gen/tree/master/examples/config-examples/)
24 |
25 | download the latest version from [GitHub Release](https://github.com/unit-mesh/unit-gen/releases)
26 |
27 | #### Generate Instructions
28 |
29 | 1. config project by `processor.yml`
30 | 2. run picker: `java -jar unit-gen.jar`
31 |
32 | Processor.yml examples:
33 |
34 | ```yaml
35 | projects:
36 | - repository: https://github.com/domain-driven-design/ddd-lite-example
37 | branch: main
38 | language: java
39 | - repository: https://github.com/unit-mesh/unit-gen-testing
40 | branch: main
41 | language: java
42 |
43 | instructionConfig:
44 | mergeInput: true # if the LLM don't support separate input, you can set it to true, will merge input to instruction.
45 | ```
46 |
47 | ## use Java API
48 |
49 | see in [config-example](examples/project-example/)
50 |
51 | 1.add dependency
52 |
53 | ```groovy
54 | dependencies {
55 | implementation("cc.unitmesh:unit-picker:0.1.7")
56 | implementation("cc.unitmesh:code-quality:0.1.7")
57 | }
58 | ```
59 |
60 | 2.write code
61 | ```java
62 | public class App {
63 | public static void main(String[] args) {
64 | List instructionTypes = new ArrayList<>();
65 | instructionTypes.add(CodeContextStrategy.RELATED_CODE);
66 |
67 | // config your code quality types
68 | List codeQualityTypes = new ArrayList<>();
69 | codeQualityTypes.add(CodeQualityType.BadSmell);
70 | codeQualityTypes.add(CodeQualityType.JavaService);
71 |
72 | BuilderConfig builderConfig = new BuilderConfig();
73 |
74 | List completionTypes = new ArrayList<>();
75 | completionTypes.add(CompletionBuilderType.IN_BLOCK_COMPLETION);
76 | completionTypes.add(CompletionBuilderType.AFTER_BLOCK_COMPLETION);
77 |
78 | PickerOption pickerOption = new PickerOption(
79 | "https://github.com/unit-mesh/unit-gen-testing", "master", "java",
80 | ".", instructionTypes, completionTypes, codeQualityTypes, builderConfig
81 | );
82 |
83 | SimpleCodePicker simpleCodePicker = new SimpleCodePicker(pickerOption);
84 | List output = simpleCodePicker.blockingExecute();
85 |
86 | // handle output
87 | System.out.println(output);
88 | }
89 | }
90 | ```
91 |
--------------------------------------------------------------------------------
/examples/config-examples/fixtures/code-completion.vm:
--------------------------------------------------------------------------------
1 | Complete ${language} code, return rest code, no explaining
2 |
3 | ```${language}
4 | ${relatedCode}
5 | ```
6 |
7 | Code:
8 | ```${language}
9 | ${beforeCursor}
10 | ```
11 |
--------------------------------------------------------------------------------
/examples/config-examples/mock-connection.yml:
--------------------------------------------------------------------------------
1 | name: mock_response
2 | type: MockLlm
3 | configs:
4 | api-response: "{\"text\": \"this is a mock resource\"}"
5 |
--------------------------------------------------------------------------------
/examples/config-examples/openai-connection.yml:
--------------------------------------------------------------------------------
1 | name: open_ai_connection
2 | type: OpenAI
3 | configs:
4 | api-host: https://api.aios.chat/ # if you are using aios.chat
5 | secrets:
6 | api-key: sk-xxx # your api key
7 |
--------------------------------------------------------------------------------
/examples/config-examples/processor.yml:
--------------------------------------------------------------------------------
1 | projects:
2 | - repository: https://github.com/domain-driven-design/ddd-lite-example
3 | branch: main
4 | language: java
5 |
--------------------------------------------------------------------------------
/examples/config-examples/unit-eval.yml:
--------------------------------------------------------------------------------
1 | name: "Eval Units"
2 | description: "A sample of UnitGen"
3 |
4 | jobs:
5 | prompt-evaluate:
6 | description: "Evaluate prompt with different parameters"
7 | connection: mock-connection.yml # 根据你的实际情况修改
8 | template: fixtures/code-builder.vm
9 | log-path: logs/
10 | template-datasource:
11 | - type: file
12 | value: fixtures/code-builder.jsonl
13 |
14 | strategy:
15 | - type: datasource-collection
16 | value:
17 | - temperature: 0.3
18 | max_tokens: 1000
19 |
--------------------------------------------------------------------------------
/examples/post-processor/process.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 |
4 | def process_jsonl(input_file, output_file):
5 | with open(input_file, 'r') as f:
6 | lines = f.readlines()
7 |
8 | filtered_records = []
9 |
10 | for line in lines:
11 | data = json.loads(line)
12 | if data["lang"] == "java":
13 | filtered_record = {
14 | "instruction": data["problem"],
15 | "output": data["solution"]
16 | }
17 | filtered_records.append(filtered_record)
18 |
19 | # write down 1000 records to output file
20 | filtered_records = filtered_records
21 | with open(output_file, 'w') as f:
22 | for record in filtered_records:
23 | f.write(json.dumps(record) + '\n')
24 |
25 |
26 |
27 | def merge_jsonl(input_file1, input_file2, input_file3, output_file):
28 | with open(input_file1, 'r') as f1, open(input_file2, 'r') as f2, open(input_file3, 'r') as f3:
29 | lines1 = f1.readlines()
30 | lines2 = f2.readlines()
31 | lines3 = f3.readlines()
32 |
33 | records = []
34 |
35 | # Process the records from the first file
36 | for line in lines1[:4000]:
37 | data = json.loads(line)
38 | records.append(data)
39 |
40 |
41 | random.shuffle(lines2)
42 | # Process the records from the second file
43 | for line in lines2[:1000]:
44 | data = json.loads(line)
45 | records.append(data)
46 |
47 | random.shuffle(lines3)
48 | # Process the records from the third file
49 | for line in lines3[:1000]:
50 | data = json.loads(line)
51 | records.append(data)
52 |
53 | random.shuffle(records)
54 | # Write the merged records to the output file
55 | with open(output_file, 'w') as f:
56 | for record in records:
57 | f.write(json.dumps(record) + '\n')
58 |
59 | # 用法示例
60 | input_file = 'data-oss_instruct-decontaminated.jsonl'
61 | oss_instruction = 'java-oss_instruction.jsonl'
62 | summary_file = 'summary.jsonl'
63 | merged_file = 'output.jsonl'
64 | test_summary_file = 'test-summary.jsonl'
65 |
66 | # 处理输入文件,过滤Java记录
67 | process_jsonl(input_file, oss_instruction)
68 |
69 | # 合并输出文件和结果文件
70 | merge_jsonl(oss_instruction, summary_file, test_summary_file, merged_file)
71 |
--------------------------------------------------------------------------------
/examples/project-example/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | application
3 | java
4 | }
5 |
6 | repositories {
7 | mavenCentral()
8 | }
9 |
10 | dependencies {
11 | implementation("cc.unitmesh:unit-picker:0.1.7")
12 | implementation("cc.unitmesh:code-quality:0.1.7")
13 | }
14 |
15 | application {
16 | mainClass.set("cc.unitmesh.example.App")
17 | }
18 |
--------------------------------------------------------------------------------
/examples/project-example/src/main/java/cc/unitmesh/example/App.java:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.example;
2 |
3 | import cc.unitmesh.pick.SimpleCodePicker;
4 | import cc.unitmesh.pick.builder.BuilderConfig;
5 | import cc.unitmesh.pick.builder.PickerOption;
6 | import cc.unitmesh.pick.prompt.CodeContextStrategy;
7 | import cc.unitmesh.pick.prompt.CompletionBuilderType;
8 | import cc.unitmesh.pick.prompt.Instruction;
9 | import cc.unitmesh.quality.CodeQualityType;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | public class App {
15 | public static void main(String[] args) {
16 | List instructionTypes = new ArrayList<>();
17 | instructionTypes.add(CodeContextStrategy.RELATED_CODE);
18 |
19 | // config your code quality types
20 | List codeQualityTypes = new ArrayList<>();
21 | codeQualityTypes.add(CodeQualityType.BadSmell);
22 | codeQualityTypes.add(CodeQualityType.JavaService);
23 |
24 | BuilderConfig builderConfig = new BuilderConfig();
25 |
26 | List completionTypes = new ArrayList<>();
27 | completionTypes.add(CompletionBuilderType.IN_BLOCK_COMPLETION);
28 | completionTypes.add(CompletionBuilderType.AFTER_BLOCK_COMPLETION);
29 |
30 | PickerOption pickerOption = new PickerOption(
31 | "https://github.com/unit-mesh/unit-gen-testing", "master", "java",
32 | ".", instructionTypes, completionTypes, codeQualityTypes, builderConfig
33 | );
34 |
35 | SimpleCodePicker simpleCodePicker = new SimpleCodePicker(pickerOption);
36 | List output = simpleCodePicker.blockingExecute();
37 |
38 | System.out.println(output);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/finetunes/deepseek/ds_config_zero2.json:
--------------------------------------------------------------------------------
1 | {
2 | "fp16": {
3 | "enabled": "auto",
4 | "loss_scale": 0,
5 | "loss_scale_window": 1000,
6 | "initial_scale_power": 16,
7 | "hysteresis": 2,
8 | "min_loss_scale": 1
9 | },
10 |
11 | "optimizer": {
12 | "type": "AdamW",
13 | "params": {
14 | "lr": "auto",
15 | "betas": "auto",
16 | "eps": "auto",
17 | "weight_decay": "auto"
18 | }
19 | },
20 |
21 | "scheduler": {
22 | "type": "WarmupLR",
23 | "params": {
24 | "warmup_min_lr": "auto",
25 | "warmup_max_lr": "auto",
26 | "warmup_num_steps": "auto"
27 | }
28 | },
29 |
30 | "zero_optimization": {
31 | "stage": 2,
32 | "offload_optimizer": {
33 | "device": "cpu",
34 | "pin_memory": true
35 | },
36 | "allgather_partitions": true,
37 | "allgather_bucket_size": 2e8,
38 | "overlap_comm": true,
39 | "reduce_scatter": true,
40 | "reduce_bucket_size": 2e8,
41 | "contiguous_gradients": true
42 | },
43 |
44 | "gradient_accumulation_steps": "auto",
45 | "gradient_clipping": "auto",
46 | "steps_per_print": 2000,
47 | "train_batch_size": "auto",
48 | "train_micro_batch_size_per_gpu": "auto",
49 | "wall_clock_breakdown": false
50 | }
--------------------------------------------------------------------------------
/finetunes/deepseek/ds_config_zero3.json:
--------------------------------------------------------------------------------
1 | {
2 | "bf16": {
3 | "enabled": "auto"
4 | },
5 | "optimizer": {
6 | "type": "AdamW",
7 | "params": {
8 | "lr": "auto",
9 | "betas": "auto",
10 | "eps": "auto",
11 | "weight_decay": "auto"
12 | }
13 | },
14 |
15 | "scheduler": {
16 | "type": "WarmupLR",
17 | "params": {
18 | "warmup_min_lr": "auto",
19 | "warmup_max_lr": "auto",
20 | "warmup_num_steps": "auto"
21 | }
22 | },
23 |
24 | "zero_optimization": {
25 | "stage": 3,
26 | "offload_optimizer": {
27 | "device": "cpu",
28 | "pin_memory": false
29 | },
30 | "offload_param": {
31 | "device": "cpu",
32 | "pin_memory": false
33 | },
34 | "overlap_comm": true,
35 | "contiguous_gradients": true,
36 | "sub_group_size": 1e9,
37 | "reduce_bucket_size": "auto",
38 | "stage3_prefetch_bucket_size": "auto",
39 | "stage3_param_persistence_threshold": "auto",
40 | "stage3_max_live_parameters": 1e9,
41 | "stage3_max_reuse_distance": 1e9,
42 | "stage3_gather_16bit_weights_on_model_save": true
43 | },
44 |
45 | "gradient_accumulation_steps": "auto",
46 | "gradient_clipping": "auto",
47 | "steps_per_print": 20,
48 | "train_batch_size": "auto",
49 | "train_micro_batch_size_per_gpu": "auto",
50 | "wall_clock_breakdown": false
51 | }
--------------------------------------------------------------------------------
/finetunes/deepseek/requirements.txt:
--------------------------------------------------------------------------------
1 | accelerate==0.23.0
2 | bitsandbytes==0.41.1
3 | gradio==3.48.0
4 | protobuf==3.20.3
5 | # scipy==1.11.2
6 | sentencepiece==0.1.99
7 | spaces==0.16.1
8 | torch==2.0.0
9 | transformers==4.34.0
10 | fastapi
11 | uvicorn
12 | asyncio
13 | async_timeout
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.code.style=official
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unit-mesh/unit-gen/14753b78da7c83d229cde7f22c2ab1d7886e5c8e/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Dec 01 18:00:13 CST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | @file:Suppress("UnstableApiUsage")
2 |
3 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
4 |
5 | rootProject.name = "AutoDevUnitGen"
6 |
7 | plugins {
8 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0"
9 | }
10 |
11 | include(
12 | "unit-core",
13 | "unit-picker",
14 | "unit-gen",
15 | "unit-distillation",
16 |
17 | "code-quality",
18 |
19 | "examples:project-example",
20 | )
21 |
--------------------------------------------------------------------------------
/unit-core/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION")
2 | plugins {
3 | alias(libs.plugins.kotlin.jvm)
4 | alias(libs.plugins.serialization)
5 | }
6 |
7 | dependencies {
8 | implementation(libs.clikt)
9 | implementation(libs.clikt)
10 | implementation(libs.serialization.json)
11 |
12 | implementation(libs.chapi.domain)
13 | implementation(libs.cf.language)
14 | implementation(libs.kaml)
15 |
16 | // Logging
17 | implementation(libs.logging.slf4j.api)
18 | implementation(libs.logging.logback.classic)
19 |
20 | testImplementation(kotlin("test"))
21 |
22 | testImplementation(libs.bundles.test)
23 | }
24 |
25 | tasks.test {
26 | useJUnitPlatform()
27 | }
28 |
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/Instruction.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlinx.serialization.json.Json
5 |
6 | private val prettyJson = Json {
7 | prettyPrint = true
8 | }
9 |
10 | @Serializable
11 | data class SimpleInstruction(
12 | val instruction: String,
13 | val output: String,
14 | )
15 |
16 | @Serializable
17 | data class Instruction(
18 | var instruction: String,
19 | var input: String,
20 | var output: String,
21 | ) {
22 | override fun toString(): String {
23 | throw Exception("we don't support toString() for Instruction, please call render()")
24 | }
25 |
26 | fun render(
27 | pretty: Boolean = false,
28 | mergeInput: Boolean = false,
29 | ): String {
30 | if (mergeInput) {
31 | val simpleInstruction = SimpleInstruction(
32 | instruction = this.instruction.trim() + "\n" + this.input.trim(),
33 | output = this.output.trim(),
34 | )
35 |
36 | return simpleInstruction.let {
37 | if (pretty) {
38 | prettyJson.encodeToString(SimpleInstruction.serializer(), it)
39 | } else {
40 | Json.encodeToString(SimpleInstruction.serializer(), it)
41 | }
42 | }
43 | }
44 |
45 | return if (pretty) {
46 | prettyJson.encodeToString(serializer(), this)
47 | } else {
48 | Json.encodeToString(serializer(), this)
49 | }
50 | }
51 |
52 | companion object {
53 | /**
54 | * Takes a list of instructions and applies a strategy to determine which instructions to return.
55 | *
56 | * @param instructions the list of instructions to be processed
57 | * @return a new list of instructions based on the applied strategy
58 | */
59 | fun takeStrategy(instructions: List, maxCompletionInOneFile: Int): List {
60 | // if size is less than maxCompletionInOneFile, return all
61 | if (instructions.size <= maxCompletionInOneFile) {
62 | return instructions
63 | }
64 |
65 | // if maxCompletionInOneFile == 1, return the last one, the first one almost `constructor` function
66 | if (maxCompletionInOneFile == 1) {
67 | return listOf(instructions.last())
68 | }
69 |
70 | // if more than maxCompletionInOneFile, return the first maxCompletionInOneFile, and random the rest
71 | val first: List = listOf(instructions.last())
72 | val rest = instructions.drop(maxCompletionInOneFile)
73 | val randomRest = rest.shuffled().take(maxCompletionInOneFile - 1)
74 | return first + randomRest
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/SupportedLang.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core
2 |
3 | enum class SupportedLang(val extension: String) {
4 | JAVA("java"),
5 | TYPESCRIPT("ts"),
6 | KOTLIN("kt"),
7 | RUST("rs")
8 | ;
9 |
10 | companion object {
11 | fun from(value: String): SupportedLang? {
12 | return when (value.lowercase()) {
13 | "java" -> JAVA
14 | "typescript" -> TYPESCRIPT
15 | "kotlin" -> KOTLIN
16 | "rust" -> RUST
17 | else -> null
18 | }
19 | }
20 |
21 | fun all(): List {
22 | return listOf(JAVA, TYPESCRIPT, KOTLIN, RUST)
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/ast/NodeIdentifier.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.ast
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | enum class NodeType {
6 | CLASS,
7 | METHOD,
8 | ;
9 | }
10 |
11 | /**
12 | * 用于标识一个 [CodeDataStruct] 中的 Function 或者 Class,以在生成指令时更有标志性。
13 | */
14 | @Serializable
15 | data class NodeIdentifier(
16 | val type: NodeType,
17 | val name: String,
18 | )
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/base/LLMCodeContext.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.base
2 |
3 | interface LLMCodeContext {
4 | fun format(): String
5 | }
6 |
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/comment/CommentBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.comment
2 |
3 | import cc.unitmesh.core.completion.TypedIns
4 | import chapi.domain.core.CodeContainer
5 |
6 | /**
7 | * The CommentBuilder interface represents a builder for generating comments in code.
8 | *
9 | * The CommentBuilder interface provides properties and methods to configure and build comments.
10 | *
11 | * Properties:
12 | * - commentStart: A string representing the starting characters of a comment.
13 | * - commentEnd: A string representing the ending characters of a comment.
14 | * - docInstruction: An instance of the DocInstruction enum representing the type of documentation instruction.
15 | *
16 | * Methods:
17 | * - build(container: CodeContainer): Generates a list of TypedIns objects based on the provided CodeContainer.
18 | *
19 | * Example usage:
20 | *
21 | * Note: This interface does not provide any implementation details for the methods.
22 | *
23 | * @see CodeContainer
24 | * @see TypedIns
25 | * @see DocCommentToolType
26 | */
27 | interface CommentBuilder {
28 | /// for generate instruction
29 | val docCommentToolType: DocCommentToolType
30 | fun build(code: String, container: CodeContainer): List
31 | }
32 |
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/comment/CommentBuilderType.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.comment
2 |
3 | enum class CommentBuilderType {
4 | CLASS_LEVEL,
5 | METHOD_LEVEL,
6 | LINE_LEVEL,
7 | }
8 |
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/comment/DocCommentToolType.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.comment
2 |
3 | enum class DocCommentToolType(val value: String) {
4 | CPP("doxygen"),
5 | JAVA("javadoc"),
6 | JAVASCRIPT("JSDoc"),
7 | PHP("PHPDoc"),
8 | GO("Go Doc"),
9 | RUBY("YARD documentation"),
10 | KOTLIN("KDoc")
11 | }
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/comment/TypedCommentIns.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.comment
2 |
3 | import cc.unitmesh.core.completion.InstructionBuilderType
4 | import cc.unitmesh.core.completion.TypedIns
5 |
6 | abstract class TypedCommentIns : TypedIns {
7 | override val type: InstructionBuilderType = InstructionBuilderType.DOCUMENTATION
8 | abstract val builderLevel: CommentBuilderType
9 | }
10 |
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/completion/CodeCompletionIns.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.completion
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlinx.serialization.json.Json
5 |
6 | @Serializable
7 | data class CodeCompletionIns(
8 | val beforeCursor: String,
9 | val afterCursor: String,
10 | val instructionBuilderType: InstructionBuilderType,
11 | ) {
12 | override fun toString(): String {
13 | return Json.encodeToString(serializer(), this)
14 | }
15 | }
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/completion/InstructionBuilderType.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.completion
2 |
3 | /**
4 | * The `CompletionBuilderType` enum represents the different types of code completion that can be generated by the completion builder.
5 | *
6 | * - `INLINE_COMPLETION`: This type generates code after the cursor position. For example, if the text after `Blog blog = ` is `new Blog();`, this type will generate `new Blog();`.
7 | *
8 | * - `IN_BLOCK_COMPLETION`: This type generates the rest of the code for a correct block. It is typically used for generating code within a function or method.
9 | *
10 | * - `AFTER_BLOCK_COMPLETION`: This type generates code after a block. It is commonly used for generating multiple functions or methods.
11 | *
12 | * - `TEST_CODE_GEN`: This type generates the full code for a file. It is often used for generating test code, class code, or API code.
13 | *
14 | * - `DOCUMENTATION`: This type generates documentation for code. It is used to generate comments and documentation for classes, methods, and variables.
15 | */
16 | enum class InstructionBuilderType {
17 | /**
18 | * generate code after cursor, like text after `Blog blog = `, will be `new Blog();`
19 | */
20 | INLINE_COMPLETION,
21 |
22 | /**
23 | * generate the rest of code of correct block, like function inner code
24 | */
25 | IN_BLOCK_COMPLETION,
26 |
27 | /**
28 | * generate code after block, like multiple functions
29 | */
30 | AFTER_BLOCK_COMPLETION,
31 |
32 | /**
33 | * generate full file code, like test code, class code, api code
34 | */
35 | TEST_CODE_GEN,
36 |
37 | /**
38 | * generate documentation for code
39 | */
40 | DOCUMENTATION
41 | }
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/completion/TypedIns.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.completion
2 |
3 | import cc.unitmesh.core.Instruction
4 |
5 | interface TypedIns {
6 | val type: InstructionBuilderType
7 |
8 | /**
9 | * Build final instruction.
10 | */
11 | fun toInstruction(): Instruction
12 | }
13 |
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/intelli/CommentService.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.intelli
2 |
3 | import cc.unitmesh.language.LanguageService;
4 |
5 | class CommentService {
6 | private val langService = LanguageService();
7 |
8 | fun lineComment(lang: String): String {
9 | return langService.guessLineComment(lang) ?: "//"
10 | }
11 |
12 | companion object {
13 | private var instance: CommentService? = null
14 | fun getInstance(): CommentService {
15 | if (instance == null) {
16 | instance = CommentService()
17 | }
18 | return instance!!
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/intelli/SimilarChunkContext.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.intelli
2 |
3 | import cc.unitmesh.core.base.LLMCodeContext
4 |
5 | class SimilarChunkContext(val language: String, private val paths: List?, private val chunks: List?) : LLMCodeContext {
6 | override fun format(): String {
7 | val commentPrefix = CommentService.getInstance().lineComment(language)
8 |
9 | if (paths == null || chunks == null) return ""
10 |
11 | val filteredPairs = paths.zip(chunks).filter { it.second.isNotEmpty() }
12 |
13 | val queryBuilder = StringBuilder()
14 | for ((path, chunk) in filteredPairs) {
15 | val commentedCode = commentCode(chunk, commentPrefix)
16 | queryBuilder.append("$commentPrefix Compare this snippet from $path\n")
17 | queryBuilder.append(commentedCode).append("\n")
18 | }
19 |
20 | return queryBuilder.toString().trim()
21 | }
22 |
23 | private fun commentCode(code: String, commentSymbol: String?): String {
24 | if (commentSymbol == null) return code
25 |
26 | return code.split("\n").joinToString("\n") { "$commentSymbol $it" }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/unittest/TestCodeBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.unittest
2 |
3 | import chapi.domain.core.CodeDataStruct
4 |
5 | interface TestCodeBuilder {
6 | fun build(dataStruct: CodeDataStruct, underTestFile: CodeDataStruct, relevantClasses: List): List
7 | }
8 |
9 |
10 |
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/unittest/TestCodeBuilderType.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.unittest
2 |
3 | enum class TestCodeBuilderType {
4 | METHOD_UNIT,
5 | CLASS_UNIT,
6 | }
--------------------------------------------------------------------------------
/unit-core/src/main/kotlin/cc/unitmesh/core/unittest/TypedTestIns.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.unittest
2 |
3 | import cc.unitmesh.core.completion.InstructionBuilderType
4 | import cc.unitmesh.core.completion.TypedIns
5 |
6 | abstract class TypedTestIns : TypedIns {
7 | override val type: InstructionBuilderType = InstructionBuilderType.TEST_CODE_GEN
8 | abstract val testType: TestCodeBuilderType
9 | }
10 |
--------------------------------------------------------------------------------
/unit-core/src/test/kotlin/cc/unitmesh/core/intelli/CommentServiceTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.core.intelli;
2 |
3 | import org.junit.jupiter.api.Assertions.assertEquals
4 | import org.junit.jupiter.api.Test
5 |
6 | class CommentServiceTest {
7 | @Test
8 | fun givenLang_whenLineComment_thenReturnLineComment() {
9 | assertEquals("//", CommentService.getInstance().lineComment("Java"))
10 | assertEquals("#", CommentService.getInstance().lineComment("Python"))
11 | assertEquals("--", CommentService.getInstance().lineComment("SQL"))
12 | assertEquals("--", CommentService.getInstance().lineComment("PL/SQL"))
13 | }
14 |
15 | @Test
16 | fun givenUnknownLang_whenLineComment_thenReturnDefaultLineComment() {
17 | assertEquals("//", CommentService.getInstance().lineComment("Unknown"))
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/unit-distillation/README.md:
--------------------------------------------------------------------------------
1 | # Unit Distillation
2 |
3 | filter not working
4 |
5 | - code smell for refactor
6 |
7 | Support case:
8 |
9 | - explain code
10 | - find bug
11 | - trace exception
12 | - refactor code
13 | - code review
14 |
--------------------------------------------------------------------------------
/unit-distillation/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION")
2 | plugins {
3 | alias(libs.plugins.kotlin.jvm)
4 | alias(libs.plugins.shadow)
5 | alias(libs.plugins.serialization)
6 | application
7 | }
8 |
9 | dependencies {
10 | implementation(projects.unitCore)
11 | implementation(libs.clikt)
12 | implementation(libs.serialization.json)
13 |
14 | implementation(libs.cf.language)
15 | implementation(libs.cf.connection)
16 | implementation(libs.cf.openai)
17 |
18 | implementation(libs.kaml)
19 |
20 | // Logging
21 | implementation(libs.logging.slf4j.api)
22 | implementation(libs.logging.logback.classic)
23 |
24 | testImplementation(kotlin("test"))
25 |
26 | testImplementation(libs.bundles.test)
27 | }
28 |
29 | tasks.test {
30 | useJUnitPlatform()
31 | }
32 |
33 | application {
34 | mainClass.set("cc.unitmesh.runner.DistillateKt")
35 | }
36 |
37 | tasks {
38 | shadowJar {
39 | manifest {
40 | attributes(Pair("Main-Class", "cc.unitmesh.runner.DistillateKt"))
41 | }
42 | dependencies {
43 | exclude(dependency("org.junit.jupiter:.*:.*"))
44 | exclude(dependency("org.junit:.*:.*"))
45 | exclude(dependency("junit:.*:.*"))
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/unit-distillation/src/main/kotlin/cc/unitmesh/distillate/Distillate.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.distillate
2 |
3 | import com.github.ajalt.clikt.core.CliktCommand
4 |
5 | fun main(args: Array) = DistillateCommand().main(args)
6 |
7 | class DistillateCommand : CliktCommand() {
8 | override fun run() {
9 | // TODO: find some code smell and do refactor
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/unit-gen/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unit-mesh/unit-gen/14753b78da7c83d229cde7f22c2ab1d7886e5c8e/unit-gen/.gitignore
--------------------------------------------------------------------------------
/unit-gen/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION")
2 | plugins {
3 | alias(libs.plugins.kotlin.jvm)
4 | alias(libs.plugins.shadow)
5 | alias(libs.plugins.serialization)
6 | application
7 | }
8 |
9 | dependencies {
10 | implementation(projects.unitCore)
11 | implementation(projects.unitPicker)
12 |
13 | implementation(libs.clikt)
14 | implementation(libs.kaml)
15 | implementation(libs.serialization.json)
16 | implementation(libs.coroutines.core)
17 |
18 | // Logging
19 | implementation(libs.logging.slf4j.api)
20 | implementation(libs.logging.logback.classic)
21 |
22 | testImplementation(kotlin("test"))
23 |
24 | testImplementation(libs.bundles.test)
25 | }
26 |
27 | tasks.test {
28 | useJUnitPlatform()
29 | }
30 |
31 | application {
32 | mainClass.set("cc.unitmesh.runner.PickerKt")
33 | }
34 |
35 | tasks {
36 | shadowJar {
37 | manifest {
38 | attributes(Pair("Main-Class", "cc.unitmesh.runner.PickerKt"))
39 | }
40 | // minimize()
41 | dependencies {
42 | exclude(dependency("org.junit.jupiter:.*:.*"))
43 | exclude(dependency("org.junit:.*:.*"))
44 | exclude(dependency("junit:.*:.*"))
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/unit-gen/src/main/kotlin/cc/unitmesh/runner/cli/ProcessorResult.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.runner.cli
2 |
3 | import cc.unitmesh.core.Instruction
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class ProcessorResult(
8 | val repository: String,
9 | val content: List,
10 | val outputName: String,
11 | )
12 |
--------------------------------------------------------------------------------
/unit-gen/src/main/kotlin/cc/unitmesh/runner/cli/ProcessorUtils.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.runner.cli
2 |
3 | import com.charleskorn.kaml.Yaml
4 | import org.slf4j.Logger
5 | import java.io.File
6 | import kotlin.system.exitProcess
7 |
8 | object ProcessorUtils {
9 | private val logger: Logger = org.slf4j.LoggerFactory.getLogger(ProcessorUtils::class.java)
10 |
11 | fun loadConfig(): UnitEvalConfig {
12 | val file = File("processor.yml").let {
13 | if (!it.exists()) {
14 | logger.error("Config file not found: ${it.absolutePath}")
15 | exitProcess(1)
16 | }
17 |
18 | it
19 | }
20 |
21 | val content = file.readText()
22 | return Yaml.default.decodeFromString(deserializer = UnitEvalConfig.serializer(), content)
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/unit-gen/src/main/kotlin/cc/unitmesh/runner/cli/UnitEvalConfig.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.runner.cli
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class UnitEvalConfig(
7 | val projects: List,
8 | val instructionConfig: InstructionConfig = InstructionConfig(),
9 | )
10 |
11 | @Serializable
12 | data class InstructionConfig(
13 | /**
14 | * the Default instruction will be like:
15 | * ```json
16 | * {
17 | * "instruction": "some command",
18 | * "input": "some-input",
19 | * "output": "xxx"
20 | * }
21 | *
22 | * if mergeInstructionInput is true, the instruction will be like:
23 | * ```json
24 | * {
25 | * "instruction": "some command\nsome-input",
26 | * "input": "",
27 | * "output": "xxx"
28 | * }
29 | * ```
30 | */
31 | val mergeInput: Boolean = false,
32 | )
33 |
34 | @Serializable
35 | data class SourceCode(
36 | val repository: String,
37 | val branch: String,
38 | val language: String,
39 | )
--------------------------------------------------------------------------------
/unit-gen/src/test/kotlin/cc/unitmesh/runner/cli/UnitEvalConfigTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.runner.cli;
2 |
3 | import com.charleskorn.kaml.Yaml
4 | import io.kotest.matchers.shouldBe
5 | import org.junit.jupiter.api.Test
6 |
7 | class UnitEvalConfigTest {
8 | @Test
9 | fun shouldSerialize() {
10 | val config = """
11 | projects:
12 | # Functional Programming
13 | - repository: https://github.com/fpinjava/fpinjava
14 | branch: master
15 | language: java
16 | """
17 | val unitEvalConfig = Yaml.default.decodeFromString(deserializer = UnitEvalConfig.serializer(), config)
18 | unitEvalConfig.projects.size shouldBe 1
19 |
20 | val sourceCode = unitEvalConfig.projects[0]
21 | sourceCode.repository shouldBe "https://github.com/fpinjava/fpinjava"
22 |
23 | unitEvalConfig.instructionConfig.mergeInput shouldBe false
24 | }
25 |
26 | @Test
27 | fun shouldSerializeWithInstructionConfig() {
28 | val config = """
29 | projects:
30 | # Functional Programming
31 | - repository: https://github.com/fpinjava/fpinjava
32 | branch: master
33 | language: java
34 |
35 | instructionConfig:
36 | mergeInput: true
37 | """
38 | val unitEvalConfig = Yaml.default.decodeFromString(deserializer = UnitEvalConfig.serializer(), config)
39 | unitEvalConfig.projects.size shouldBe 1
40 |
41 | unitEvalConfig.instructionConfig.mergeInput shouldBe true
42 | }
43 | }
--------------------------------------------------------------------------------
/unit-picker/.gitignore:
--------------------------------------------------------------------------------
1 | unit-gen-testing
2 | github.com
3 | gitlab.com
4 | test.jsonl
5 | debug.json
6 |
--------------------------------------------------------------------------------
/unit-picker/README.md:
--------------------------------------------------------------------------------
1 | # Unit Picker
2 |
3 | components:
4 |
5 | - [ArchGuard](https://github.com/archguard/archguard)
6 | - [CodeDB](https://github.com/archguard/codedb)
7 |
8 | ## by Code Scene
9 |
10 | - Inline Completion
11 | - Test by framework
12 | - History Code
13 |
14 | AI Assistant Type
15 |
16 | - In-Line Completion by Git History
17 | - In-Block Completion by Git History
18 | - After-Block Completion by Git History
19 |
20 | ## Context related
21 |
22 | Context related
23 |
24 | - Inbound & Outbound
25 | - Test Code
26 |
27 | ## What is good code?
28 |
29 | Good Code
30 |
31 | - TDD
32 | - Short (< 20)
33 | - Short Parameters
34 | - Good Naming (which English name or Domain name?)
35 | - Good Structure (Layered Architecture)
36 |
37 | Code Unit
38 |
39 | - [ ] Layered Architecture's Code
40 | - [ ] Tested Code (Assertion Code)
41 | - [ ] Infrastructure Code
42 | - [ ] Code by Naming
43 |
--------------------------------------------------------------------------------
/unit-picker/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION")
2 | plugins {
3 | alias(libs.plugins.kotlin.jvm)
4 | alias(libs.plugins.serialization)
5 | }
6 |
7 | dependencies {
8 | implementation(projects.unitCore)
9 | implementation(projects.codeQuality)
10 |
11 | implementation(libs.clikt)
12 | implementation(libs.serialization.json)
13 | implementation(libs.coroutines.core)
14 | implementation(libs.kotlin.reflect)
15 |
16 | implementation(libs.chapi.domain)
17 | implementation(libs.chapi.java)
18 | implementation(libs.chapi.kotlin)
19 | implementation(libs.chapi.typescript)
20 | implementation(libs.chapi.rust)
21 |
22 | implementation(libs.archguard.scanner.core)
23 | implementation(libs.archguard.analyser.estimate)
24 | implementation(libs.archguard.analyser.sca)
25 |
26 | // checkout
27 | implementation(libs.codedb.checkout)
28 | implementation(libs.codedb.action.toolkit)
29 |
30 | // tokenization
31 | implementation(libs.jtokkit)
32 |
33 | // Logging
34 | implementation(libs.logging.slf4j.api)
35 | implementation(libs.logging.logback.classic)
36 |
37 | testImplementation(kotlin("test"))
38 |
39 | testImplementation(libs.bundles.test)
40 | }
41 |
42 | tasks.test {
43 | useJUnitPlatform()
44 | }
45 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/PickerPipeline.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick
2 |
3 | /**
4 | * Pipeline is a DSL for building a pipeline of operations for picking code.
5 | */
6 | class PickerPipeline {
7 | // repo -> filter -> threshold -> instruction
8 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/InsBuilderFactory.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder
2 |
3 | import cc.unitmesh.core.completion.TypedInsBuilder
4 | import cc.unitmesh.core.completion.InstructionBuilderType
5 | import cc.unitmesh.pick.builder.comment.DocumentationTypedInsBuilder
6 | import cc.unitmesh.pick.builder.completion.AfterBlockCodeTypedInsBuilder
7 | import cc.unitmesh.pick.builder.completion.InBlockCodeTypedInsBuilder
8 | import cc.unitmesh.pick.builder.completion.InlineCodeTypedInsBuilder
9 | import cc.unitmesh.pick.builder.unittest.TestCodeTypedInsBuilder
10 | import cc.unitmesh.pick.worker.job.JobContext
11 | import kotlinx.serialization.SerializationException
12 |
13 | fun instructionBuilders(types: List, context: JobContext) : List {
14 | return types.map { instructionBuilder(it, context) }
15 | }
16 |
17 | fun instructionBuilder(instructionBuilderType: InstructionBuilderType, context: JobContext): TypedInsBuilder {
18 | return mapOf(
19 | InstructionBuilderType.INLINE_COMPLETION to InlineCodeTypedInsBuilder(context),
20 | InstructionBuilderType.IN_BLOCK_COMPLETION to InBlockCodeTypedInsBuilder(context),
21 | InstructionBuilderType.AFTER_BLOCK_COMPLETION to AfterBlockCodeTypedInsBuilder(context),
22 | InstructionBuilderType.TEST_CODE_GEN to TestCodeTypedInsBuilder(context),
23 | InstructionBuilderType.DOCUMENTATION to DocumentationTypedInsBuilder(context),
24 | )[instructionBuilderType] ?: throw SerializationException("Unknown message type: $instructionBuilderType")
25 | }
26 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/comment/CommentsExtractor.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.comment
2 |
3 | import cc.unitmesh.core.SupportedLang
4 | import cc.unitmesh.quality.comment.CodeComment
5 |
6 | /**
7 | * Extracts the documentation comments (KDoc) from the given code.
8 | *
9 | * @param code the Kotlin code from which to extract the KDoc comments
10 | * @return a list of pairs, where each pair contains the line number and the extracted KDoc comment
11 | */
12 | fun extractComments(code: String, language: SupportedLang): List {
13 | return when (language) {
14 | SupportedLang.KOTLIN -> CodeComment.parseComment(code)
15 | SupportedLang.JAVA -> CodeComment.parseComment(code)
16 | else -> {
17 | println("Unsupported language: $language")
18 | emptyList()
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/comment/DocumentationTypedInsBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.comment
2 |
3 | import cc.unitmesh.core.SupportedLang
4 | import cc.unitmesh.core.comment.DocCommentToolType
5 | import cc.unitmesh.core.completion.TypedIns
6 | import cc.unitmesh.core.completion.TypedInsBuilder
7 | import cc.unitmesh.pick.worker.job.JobContext
8 | import chapi.domain.core.CodeContainer
9 |
10 | class DocumentationTypedInsBuilder(val context: JobContext) : TypedInsBuilder {
11 | private val kotlinCommentBuilder = JvmCommentBuilder(SupportedLang.KOTLIN, DocCommentToolType.KOTLIN)
12 | private val javaCommentBuilder = JvmCommentBuilder(SupportedLang.JAVA, DocCommentToolType.JAVA)
13 |
14 | override fun build(container: CodeContainer): List {
15 | val language = context.project.language
16 | return when (language) {
17 | SupportedLang.JAVA -> javaCommentBuilder.build(context.job.code, container)
18 | SupportedLang.KOTLIN -> {
19 | kotlinCommentBuilder.build(context.job.code, container)
20 | }
21 |
22 | SupportedLang.TYPESCRIPT -> TODO()
23 | SupportedLang.RUST -> TODO()
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/comment/JvmCommentBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.comment
2 |
3 | import cc.unitmesh.core.SupportedLang
4 | import cc.unitmesh.quality.comment.CodeComment
5 | import cc.unitmesh.core.comment.CommentBuilder
6 | import cc.unitmesh.core.comment.DocCommentToolType
7 | import cc.unitmesh.core.comment.TypedCommentIns
8 | import cc.unitmesh.pick.builder.comment.ins.ClassCommentIns
9 | import cc.unitmesh.pick.builder.comment.ins.MethodCommentIns
10 | import chapi.domain.core.CodeContainer
11 |
12 | class JvmCommentBuilder(
13 | val language: SupportedLang,
14 | override val docCommentToolType: DocCommentToolType,
15 | ) :
16 | CommentBuilder {
17 |
18 | /**
19 | * Builds a list of TypedCommentIns objects based on the provided code and CodeContainer.
20 | *
21 | * @param code The code from which to extract comments.
22 | * @param container The CodeContainer containing the data structures and functions.
23 | * @return A list of TypedCommentIns objects representing the extracted comments.
24 | */
25 | override fun build(code: String, container: CodeContainer): List {
26 | val posComments = try {
27 | extractComments(code, language)
28 | } catch (e: Exception) {
29 | emptyList()
30 | }
31 |
32 | if (posComments.isEmpty()) return emptyList()
33 |
34 | val startLineCommentMap: Map = CodeComment.lineCommentMap(posComments)
35 |
36 | val comments = mutableListOf()
37 | container.DataStructures.forEach { dataStruct ->
38 | val classComment = startLineCommentMap[dataStruct.Position.StartLine - 1]
39 | classComment?.let {
40 | val commentIns = ClassCommentIns(docCommentToolType, dataStruct, it, language = language.name)
41 | comments.add(commentIns)
42 | }
43 |
44 | val methodCommentIns =
45 | dataStruct.Functions.filter { it.Name != "constructor" && it.Name != "PrimaryConstructor" }
46 | .map { function ->
47 | val functionComment = startLineCommentMap[function.Position.StartLine - 1] ?: return@map null
48 | MethodCommentIns(
49 | docCommentToolType, function, functionComment, dataStruct, language = language.name
50 | )
51 | }
52 |
53 | comments.addAll(methodCommentIns.filterNotNull())
54 | }
55 |
56 | return comments
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/comment/ins/ClassCommentIns.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.comment.ins
2 |
3 | import cc.unitmesh.core.Instruction
4 | import cc.unitmesh.quality.comment.CodeComment
5 | import cc.unitmesh.core.comment.CommentBuilderType
6 | import cc.unitmesh.core.comment.DocCommentToolType
7 | import cc.unitmesh.core.comment.TypedCommentIns
8 | import chapi.domain.core.CodeDataStruct
9 | import kotlinx.serialization.Serializable
10 |
11 | @Serializable
12 | data class ClassCommentIns(
13 | val docInstruction: DocCommentToolType,
14 | val dataStructure: CodeDataStruct,
15 | val comment: CodeComment,
16 | val language: String,
17 | ) : TypedCommentIns() {
18 | override val builderLevel: CommentBuilderType = CommentBuilderType.CLASS_LEVEL
19 |
20 | override fun toInstruction(): Instruction {
21 | val instruction = "Write ${docInstruction.value} for given class " + dataStructure.NodeName + " .\n"
22 | val input = "Code:\n```$language\n" + dataStructure.Content + "\n```"
23 | val output = comment.content
24 |
25 | return Instruction(instruction, input, output)
26 | }
27 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/comment/ins/MethodCommentIns.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.comment.ins
2 |
3 | import cc.unitmesh.core.Instruction
4 | import cc.unitmesh.quality.comment.CodeComment
5 | import cc.unitmesh.core.comment.CommentBuilderType
6 | import cc.unitmesh.core.comment.DocCommentToolType
7 | import cc.unitmesh.core.comment.TypedCommentIns
8 | import chapi.domain.core.CodeDataStruct
9 | import chapi.domain.core.CodeFunction
10 | import kotlinx.serialization.Serializable
11 |
12 | @Serializable
13 | data class MethodCommentIns(
14 | val docInstruction: DocCommentToolType,
15 | val function: CodeFunction,
16 | val comment: CodeComment,
17 | val currentDataStruct: CodeDataStruct,
18 | val language: String,
19 | ) : TypedCommentIns() {
20 | override val builderLevel: CommentBuilderType = CommentBuilderType.METHOD_LEVEL
21 |
22 | override fun toInstruction(): Instruction {
23 | val instruction = "Write ${docInstruction.value} for given method " + function.Name + " .\n"
24 | val input =
25 | "### Current class:\n${currentDataStruct.toUml()}\n###\nCode:\n```$language\n${currentDataStruct.Content}\n```\n"
26 | val output = comment.content
27 |
28 | return Instruction(instruction, input, output)
29 | }
30 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/completion/AfterBlockCodeTypedInsBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.completion
2 |
3 | import cc.unitmesh.core.completion.CodeCompletionIns
4 | import cc.unitmesh.core.completion.TypedInsBuilder
5 | import cc.unitmesh.core.completion.InstructionBuilderType
6 | import cc.unitmesh.pick.worker.job.JobContext
7 | import chapi.domain.core.CodeFunction
8 |
9 |
10 | /**
11 | * The `AfterBlockCodeTypedInsBuilder` class is responsible for building a list of code completion instructions
12 | * after a block of code has been typed.
13 | *
14 | * @property context The job context associated with the code completion.
15 | */
16 | class AfterBlockCodeTypedInsBuilder(val context: JobContext) : TypedInsBuilder {
17 |
18 | /**
19 | * Builds a list of code completion instructions based on the provided code function.
20 | *
21 | * @param function The code function for which the code completion instructions are to be built.
22 | * @return A list of code completion instructions.
23 | */
24 | override fun build(function: CodeFunction): List {
25 | val position = function.Position
26 | val codeLines = context.job.codeLines
27 | val beforeCursor = codeLines.subList(0, position.StopLine).joinToString("\n")
28 | var afterCursor = codeLines.subList(position.StopLine, codeLines.size).joinToString("\n")
29 |
30 | if (position.StopLine == 0) {
31 | afterCursor = codeLines.joinToString("\n")
32 | }
33 |
34 | // create line break by os
35 | val lineBreak = System.lineSeparator()
36 | if (afterCursor == "}$lineBreak") {
37 | return emptyList()
38 | }
39 |
40 | return listOf(CodeCompletionIns(beforeCursor, afterCursor, InstructionBuilderType.AFTER_BLOCK_COMPLETION))
41 | }
42 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/completion/InBlockCodeTypedInsBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.completion
2 |
3 | import cc.unitmesh.core.completion.CodeCompletionIns
4 | import cc.unitmesh.core.completion.TypedInsBuilder
5 | import cc.unitmesh.core.completion.InstructionBuilderType
6 | import cc.unitmesh.pick.worker.job.JobContext
7 | import chapi.domain.core.CodeFunction
8 |
9 | class InBlockCodeTypedInsBuilder(val context: JobContext) : TypedInsBuilder {
10 | override fun build(function: CodeFunction): List {
11 | val position = function.Position
12 | val beforeCursor = context.job.codeLines.subList(0, position.StartLine).joinToString("\n")
13 |
14 | val stopLine = if (position.StopLine == 0) {
15 | context.job.codeLines.size
16 | } else {
17 | position.StopLine
18 | }
19 |
20 | val afterCursor = context.job.codeLines.subList(position.StartLine, stopLine).joinToString("\n")
21 |
22 | return listOf(CodeCompletionIns(beforeCursor, afterCursor, InstructionBuilderType.IN_BLOCK_COMPLETION))
23 | }
24 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/completion/InlineCodeTypedInsBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.completion
2 |
3 | import cc.unitmesh.core.completion.CodeCompletionIns
4 | import cc.unitmesh.core.completion.TypedInsBuilder
5 | import cc.unitmesh.core.completion.InstructionBuilderType
6 | import cc.unitmesh.pick.worker.job.JobContext
7 | import chapi.domain.core.CodeFunction
8 |
9 | class InlineCodeTypedInsBuilder(val context: JobContext) : TypedInsBuilder {
10 | private fun isValidTypeOver(char: Char): Boolean {
11 | return char == ')' || char == ']' || char == '}' || char == '"' || char == '\'' || char == '>' || char == ';' || char == ','
12 | }
13 |
14 | private val GOOD_FOR_SUGGEST_LENGTH = 10
15 |
16 | /**
17 | * Builds a list of code completion instructions by splitting the code lines and analyzing each character.
18 | * For example, given the input: `println("Hello, world!")`
19 | * The output will be a list of CodeCompletionIns instances representing different cursor positions:
20 | *
21 | * - `CodeCompletionIns(beforeCursor = "println(", afterCursor = "Hello, world!")`
22 | * - `CodeCompletionIns(beforeCursor = "println(\"Hello,", afterCursor = " world!\")"`
23 | * - `CodeCompletionIns(beforeCursor = "println(\"Hello, world!", afterCursor = "\")"`
24 | * - `CodeCompletionIns(beforeCursor = "println(\"Hello, world!\")", afterCursor = "")`
25 | *
26 | * @param function The CodeFunction object containing information about the code.
27 | * @return A list of CodeCompletionIns instances representing different cursor positions.
28 | */
29 | override fun build(function: CodeFunction): List {
30 | val position = function.Position
31 | val functionCode: List =
32 | context.job.codeLines.subList(position.StartLine, position.StopLine).joinToString("\n")
33 | .lines()
34 |
35 | val completions = mutableListOf()
36 |
37 | functionCode.forEach { line ->
38 | for (i in line.indices) {
39 | if (isValidTypeOver(line[i])) {
40 | val beforeCursor = line.substring(0, i + 1).trim()
41 | val afterCursor = line.substring(i + 1).trimEnd()
42 |
43 | if (beforeCursor.isBlank() || afterCursor.isBlank()) {
44 | continue
45 | }
46 |
47 | // if afterCursor is a ';', skip it
48 | if (afterCursor.length < GOOD_FOR_SUGGEST_LENGTH) {
49 | continue
50 | }
51 |
52 | completions.add(CodeCompletionIns(beforeCursor, afterCursor, InstructionBuilderType.INLINE_COMPLETION))
53 | }
54 | }
55 | }
56 |
57 | return completions
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/unittest/TestCodeTypedInsBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.unittest
2 |
3 | import cc.unitmesh.core.completion.CodeCompletionIns
4 | import cc.unitmesh.core.completion.TypedInsBuilder
5 | import cc.unitmesh.core.completion.TypedIns
6 | import cc.unitmesh.pick.builder.unittest.base.UnitTestService
7 | import cc.unitmesh.pick.worker.job.JobContext
8 | import chapi.domain.core.CodeContainer
9 | import chapi.domain.core.CodeDataStruct
10 | import chapi.domain.core.CodeFunction
11 |
12 | class TestCodeTypedInsBuilder(val context: JobContext) : TypedInsBuilder {
13 | override fun build(dataStruct: CodeDataStruct): List {
14 | val testIns = UnitTestService.lookup(dataStruct, context).map {
15 | it.build(dataStruct)
16 | }.flatten()
17 |
18 | return testIns
19 | }
20 |
21 | override fun build(container: CodeContainer): List {
22 | val testIns = UnitTestService.lookup(container, context).map {
23 | it.build(container)
24 | }.flatten()
25 |
26 | return testIns
27 | }
28 |
29 | override fun build(function: CodeFunction): List {
30 | return listOf()
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/unittest/base/BasicTestIns.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.unittest.base
2 |
3 | import cc.unitmesh.core.Instruction
4 | import cc.unitmesh.core.SupportedLang
5 | import cc.unitmesh.core.ast.NodeIdentifier
6 | import cc.unitmesh.core.unittest.TestCodeBuilderType
7 | import cc.unitmesh.core.unittest.TypedTestIns
8 | import kotlinx.serialization.Serializable
9 | import kotlinx.serialization.json.Json
10 |
11 |
12 | @Serializable
13 | data class BasicTestIns(
14 | val identifier: NodeIdentifier,
15 | val language: SupportedLang,
16 | val underTestCode: String,
17 | val generatedCode: String,
18 | val coreFrameworks: List = listOf(),
19 | val testFrameworks: List = listOf(),
20 | /**
21 | * the Specification of the test
22 | */
23 | val specs: List = listOf(),
24 | private val relatedCode: List = listOf(),
25 | override val testType: TestCodeBuilderType,
26 | ) : TypedTestIns() {
27 | override fun toString(): String {
28 | return Json.encodeToString(serializer(), this)
29 | }
30 |
31 | override fun toInstruction(): Instruction {
32 | val input = StringBuilder()
33 |
34 | input.append("\n###")
35 | input.append(specs.joinToString("\n") { "- $it" })
36 | input.append("\n")
37 |
38 | if (coreFrameworks.isNotEmpty()) {
39 | input.append("- You are working on a project that uses ${coreFrameworks.joinToString(", ")}\n")
40 | }
41 |
42 | if (testFrameworks.isNotEmpty()) {
43 | input.append("- This project uses ${testFrameworks.joinToString(", ")} to test code.\n")
44 | }
45 |
46 | if (relatedCode.isNotEmpty()) {
47 | input.append("Related code:\n")
48 | input.append(relatedCode.joinToString("\n"))
49 | }
50 | input.append("\n###")
51 |
52 | input.append("Code under test:\n")
53 | val languageName = language.name.lowercase()
54 | input.append("```$languageName\n")
55 | input.append(underTestCode)
56 | input.append("\n```")
57 |
58 | when (testType) {
59 | TestCodeBuilderType.CLASS_UNIT -> {
60 | input.append("\nStart test code with `package` syntax here: \n")
61 | }
62 |
63 | TestCodeBuilderType.METHOD_UNIT -> {
64 | input.append("\nStart test code with `@Test` syntax here: \n")
65 | }
66 | }
67 |
68 | val id = identifier.name
69 | val typeName = identifier.type.name.lowercase()
70 |
71 | return Instruction(
72 | instruction = """Write $languageName language unit test for $id's $typeName.""",
73 | input = input.toString(),
74 | output = generatedCode,
75 | )
76 | }
77 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/unittest/java/ClassTestCodeBuilder.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.unittest.java
2 |
3 | import cc.unitmesh.core.ast.NodeIdentifier
4 | import cc.unitmesh.core.ast.NodeType
5 | import cc.unitmesh.core.unittest.TestCodeBuilder
6 | import cc.unitmesh.core.unittest.TestCodeBuilderType
7 | import cc.unitmesh.pick.builder.unittest.base.BasicTestIns
8 | import cc.unitmesh.pick.project.spec.checkNamingStyle
9 | import cc.unitmesh.pick.ext.toSourceCode
10 | import cc.unitmesh.pick.worker.job.JobContext
11 | import chapi.domain.core.CodeDataStruct
12 |
13 | /**
14 | * 为给定的 CodeDataStruct 生成对应类级别的测试 Instruction。
15 | *
16 | * @property context the job context for the test code generation
17 | * @constructor Creates a `ClassTestCodeBuilder` with the specified job context.
18 | * @param context the job context for the test code generation
19 | */
20 | class ClassTestCodeBuilder(private val context: JobContext) : TestCodeBuilder {
21 | /**
22 | * 为给定的 CodeDataStruct 生成测试指令。
23 | *
24 | * @param dataStruct is the test file
25 | * @param underTestFile is the file under test
26 | * @param relevantClasses are the classes relevant to the test file
27 | * @return 返回生成的 [BasicTestIns]
28 | *
29 | * 主要处理逻辑:
30 | * 1. 判断给定的 [CodeDataStruct] 是否符合质量阈值,如果不符合则返回空列表。
31 | * 2. 直接生成 [BasicTestIns]。
32 | * 额外逻辑:
33 | * 1. 生成命名风格,以便于后续生成测试代码时使用。
34 | */
35 | override fun build(
36 | dataStruct: CodeDataStruct,
37 | underTestFile: CodeDataStruct,
38 | relevantClasses: List,
39 | ): List {
40 | val generatedCode = dataStruct.toSourceCode()
41 | if (generatedCode.lines().size > context.insQualityThreshold.maxLineInCode) {
42 | return emptyList()
43 | }
44 |
45 | val namingStyle = dataStruct.checkNamingStyle()
46 |
47 | return listOf(
48 | BasicTestIns(
49 | identifier = NodeIdentifier(
50 | type = NodeType.CLASS,
51 | name = underTestFile.NodeName,
52 | ),
53 | language = context.project.language,
54 | underTestCode = underTestFile.Content,
55 | generatedCode = generatedCode,
56 | coreFrameworks = context.project.coreFrameworks,
57 | testFrameworks = context.project.testFrameworks,
58 | testType = TestCodeBuilderType.CLASS_UNIT,
59 | specs = listOf(
60 | "Test class should be named `${namingStyle}`."
61 | )
62 | )
63 | )
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/unittest/kotlin/KotlinTestCodeService.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.unittest.kotlin
2 |
3 | import cc.unitmesh.core.SupportedLang
4 | import cc.unitmesh.core.unittest.TypedTestIns
5 | import cc.unitmesh.pick.builder.unittest.java.ClassTestCodeBuilder
6 | import cc.unitmesh.pick.builder.unittest.java.JavaTestCodeService
7 | import cc.unitmesh.pick.worker.job.JobContext
8 | import chapi.domain.core.CodeDataStruct
9 |
10 | /**
11 | * 为给定的 CodeDataStruct 的每个 CodeFunction 生成测试指令。
12 | */
13 | class KotlinTestCodeService(override val context: JobContext) : JavaTestCodeService(context) {
14 | override fun isApplicable(dataStruct: CodeDataStruct): Boolean {
15 | return context.project.language == SupportedLang.KOTLIN && dataStruct.NodeName.endsWith("Test") || dataStruct.NodeName.endsWith("Tests")
16 | }
17 |
18 | override fun build(dataStruct: CodeDataStruct): List {
19 | val underTestFile = this.findUnderTestFile(dataStruct).firstOrNull() ?: return emptyList()
20 | val relevantClasses = this.lookupRelevantClass(dataStruct)
21 |
22 | val classTestIns = ClassTestCodeBuilder(context)
23 | .build(dataStruct, underTestFile, relevantClasses)
24 |
25 | val methodTests = KotlinMethodTestCodeBuilder(context)
26 | .build(dataStruct, underTestFile, relevantClasses)
27 |
28 | return classTestIns + methodTests
29 | }
30 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/builder/unittest/typescript/TypeScriptTestCodeService.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.unittest.typescript
2 |
3 | import cc.unitmesh.core.unittest.TypedTestIns
4 | import cc.unitmesh.pick.builder.unittest.base.UnitTestService
5 | import cc.unitmesh.pick.worker.job.JobContext
6 | import chapi.domain.core.CodeDataStruct
7 |
8 | class TypeScriptTestCodeService(val job: JobContext): UnitTestService {
9 | override fun isApplicable(dataStruct: CodeDataStruct): Boolean {
10 | return false
11 | }
12 |
13 | override fun build(dataStruct: CodeDataStruct): List {
14 | TODO()
15 | }
16 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/ext/CompositionDependencyExt.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.ext
2 |
3 | import org.archguard.scanner.core.sca.CompositionDependency
4 |
5 | fun CompositionDependency.Companion.from(
6 | name: String,
7 | group: String,
8 | artifactId: String,
9 | ): CompositionDependency {
10 | return CompositionDependency(
11 | name = name,
12 | depName = name,
13 | depGroup = group,
14 | depArtifact = artifactId,
15 | packageManager = "",
16 | depVersion = "",
17 | version = "",
18 | path = "",
19 | depScope = "",
20 | id = "",
21 | parentId = "",
22 | )
23 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/ext/GitUtil.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.ext
2 |
3 | import org.archguard.action.checkout.GitSourceSettings
4 | import org.archguard.action.checkout.executeGitCheckout
5 | import java.nio.file.Files
6 | import java.nio.file.Path
7 | import kotlin.io.path.absolutePathString
8 |
9 | object GitUtil {
10 | private val logger = org.slf4j.LoggerFactory.getLogger(javaClass)
11 | private val GIT_URL_REGEX =
12 | """(git@|http://|https://)((?[\w\.@]+)(/|:))(?[\w,\-\_]+)/(?[\w,\-,._]+)"""
13 | .toRegex()
14 |
15 | /**
16 | * Convert git url to path
17 | *
18 | * for example:
19 | *
20 | * - `https://github.com/unit-mesh/unit-pick` tobe `github.com/unit-mesh/unit-pick`
21 | * - `git://github.com/unit-mesh/unit-pick` tobe `github.com/unit-mesh/unit-pick`
22 | * - `git://github.com/unit-mesh/unit-pick.git` tobe `github.com/unit-mesh/unit-pick`
23 | * - `http://github.com/unit-mesh/unit-pick` tobe `github.com/unit-mesh/unit-pick`
24 | *
25 | */
26 | fun gitUrlToPath(url: String): String {
27 | val trimmedUrl = url.trim()
28 | .removeSuffix("/")
29 | .removeSuffix(".git")
30 |
31 | val matchResult = GIT_URL_REGEX.find(trimmedUrl) ?: throw IllegalArgumentException("invalid git url: $url")
32 |
33 | val host = matchResult.groups["host"]!!.value
34 | val owner = matchResult.groups["owner"]!!.value
35 | val repo = matchResult.groups["repo"]!!.value
36 |
37 | return "$host/$owner/$repo"
38 | }
39 |
40 | fun checkoutCode(url: String, branch: String, baseDir: Path, depth: Int): Path {
41 | if (!GIT_URL_REGEX.matches(url)) {
42 | return Path.of(url)
43 | }
44 |
45 | val gitDir = gitUrlToPath(url)
46 | val targetDir = baseDir.resolve(gitDir)
47 |
48 | if (targetDir.toFile().exists()) {
49 | logger.info("targetDir exists: $targetDir")
50 | return targetDir
51 | }
52 |
53 | logger.info("targetDir: $targetDir")
54 | val settings = GitSourceSettings(
55 | repository = url,
56 | branch = branch,
57 | workdir = targetDir.parent.absolutePathString(),
58 | fetchDepth = depth
59 | )
60 |
61 | try {
62 | Files.createDirectories(targetDir.parent)
63 | } catch (e: Exception) {
64 | logger.info("create dir failed: ${targetDir.parent}")
65 | }
66 |
67 | executeGitCheckout(settings)
68 | return targetDir
69 | }
70 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/option/InsOutputConfig.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.option
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 |
6 | @Serializable
7 | data class InsOutputConfig(
8 | /**
9 | * For different generic data in [cc.unitmesh.pick.prompt.CodeStrategyBuilder]
10 | */
11 | val withGenPureData: Boolean = true,
12 | val mergeFinalOutput: Boolean = true,
13 | )
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/project/ProjectContext.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.project
2 |
3 | import cc.unitmesh.core.SupportedLang
4 | import kotlinx.serialization.Serializable
5 | import org.archguard.scanner.core.sca.CompositionDependency
6 |
7 | @Serializable
8 | data class ProjectContext(
9 | var language: SupportedLang = SupportedLang.JAVA,
10 | var compositionDependency: List = listOf(),
11 | val codeDir: String = ""
12 | ) {
13 | var testFrameworks: List
14 | var coreFrameworks: List
15 |
16 | private val testStack: TestStack by lazy {
17 | ProjectLibrary.prepare(language, compositionDependency)
18 | }
19 |
20 | init {
21 | coreFrameworks = testStack.coreFrameworks()
22 | testFrameworks = testStack.testFrameworks()
23 | }
24 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/project/ProjectLibrary.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.project
2 |
3 | import cc.unitmesh.core.SupportedLang
4 | import cc.unitmesh.pick.project.library.SpringLibrary
5 | import cc.unitmesh.pick.project.frameworks.TestFrameworkIdentifier
6 | import org.archguard.scanner.core.sca.CompositionDependency
7 |
8 | object ProjectLibrary {
9 | /**
10 | * Prepare test stack from dependencies.
11 | *
12 | * This method takes a list of dependencies and prepares a test stack based on those dependencies.
13 | *
14 | * @param language The language of the project.
15 | * @param deps The list of dependencies to be used to prepare the test stack.
16 | * @return The test stack containing the core and test frameworks.
17 | *
18 | * Example usage:
19 | * ```kotlin
20 | * val dependencies = listOf(
21 | * CompositionDependency.from("org.springframework.boot:spring-boot-starter-test", "org.springframework.boot", "spring-boot-starter-test")
22 | * )
23 | * val testStack = ProjectLibrary.prepare(dependencies)
24 | * println(testStack)
25 | * ```
26 | *
27 | * @see TestStack
28 | * @see CompositionDependency
29 | */
30 | fun prepare(language: SupportedLang = SupportedLang.JAVA, deps: List): TestStack {
31 | val testStack = TestStack()
32 |
33 | deps.forEach { dep ->
34 | val name = dep.depName
35 | SpringLibrary.SPRING_MVC.forEach {
36 | if (name.contains(it.coords)) {
37 | testStack.coreFrameworks.putIfAbsent(it.shortText, true)
38 | }
39 | }
40 |
41 | SpringLibrary.SPRING_DATA.forEach {
42 | it.coords.forEach { coord ->
43 | if (name.contains(coord)) {
44 | testStack.coreFrameworks.putIfAbsent(it.shortText, true)
45 | }
46 | }
47 | }
48 |
49 | TestFrameworkIdentifier(language, deps).identify().forEach {
50 | testStack.testFrameworks.putIfAbsent(it, true)
51 | }
52 | }
53 |
54 | return testStack
55 | }
56 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/project/TestStack.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.project
2 |
3 | /**
4 | * This class represents a stack of technologies and dependencies for testing purposes.
5 | * It contains information about various core frameworks, test frameworks, dependencies,
6 | * and development dependencies.
7 | *
8 | * The stack is defined using four mutable maps: coreFrameworks, testFrameworks, deps,
9 | * and devDeps.
10 | *
11 | * - The coreFrameworks map stores the core frameworks used in the stack. Each framework
12 | * is identified by its name (a String) and associated with a boolean value indicating
13 | * whether it is enabled or not.
14 | *
15 | * - The testFrameworks map stores the test frameworks used in the stack. Similar to the
16 | * coreFrameworks map, each framework is identified by its name and associated with a
17 | * boolean value indicating its enabled status.
18 | *
19 | * - The deps map represents the external dependencies in the stack. Each dependency is
20 | * identified by its name (a String) and associated with the version of that dependency
21 | * (another String).
22 | *
23 | * - The devDeps map represents the development dependencies in the stack. Similar to the
24 | * deps map, each dependency is identified by its name and associated with the version.
25 | *
26 | * Additionally, the TestStack class provides two utility methods:
27 | *
28 | * - The coreFrameworks() method returns a list of all the core frameworks used in the stack.
29 | * It extracts the keys (framework names) from the coreFrameworks map and converts them to
30 | * a list.
31 | *
32 | * - The testFrameworks() method returns a list of all the test frameworks used in the stack.
33 | * It works similarly to the coreFrameworks() method, extracting the keys (test framework
34 | * names) from the testFrameworks map and returning them as a list.
35 | *
36 | */
37 | data class TestStack(
38 | val coreFrameworks: MutableMap = mutableMapOf(),
39 | val testFrameworks: MutableMap = mutableMapOf(),
40 | val deps: MutableMap = mutableMapOf(),
41 | val devDeps: MutableMap = mutableMapOf()
42 | ) {
43 | fun coreFrameworks(): List {
44 | return coreFrameworks.keys.toList()
45 | }
46 |
47 | fun testFrameworks(): List {
48 | return testFrameworks.keys.toList()
49 | }
50 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/project/frameworks/JavaFrameworkIdentifier.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.project.frameworks
2 |
3 | import org.archguard.scanner.core.sca.CompositionDependency
4 |
5 | /**
6 | * The `JavaFrameworkIdentifier` class is a Kotlin implementation of the `LangFrameworkIdentifier` interface.
7 | * It is responsible for identifying the test frameworks used in a Java project based on the project's dependencies.
8 | *
9 | * @param dependencies A list of `CompositionDependency` objects representing the project's dependencies.
10 | */
11 | class JavaFrameworkIdentifier(private val dependencies: List) : LangFrameworkIdentifier {
12 |
13 | /**
14 | * A map that associates Java test framework dependencies with their corresponding test framework names.
15 | */
16 | private val javaTestFrameworkMap: Map> = mapOf(
17 | "junit:junit" to listOf("JUnit"),
18 | "org.testng:testng" to listOf("testng"),
19 | "org.spockframework:spock-core" to listOf("spock"),
20 | "io.cucumber:cucumber-java" to listOf("cucumber"),
21 | "com.intuit.karate:karate-junit5" to listOf("Karate JUnit5"),
22 | "org.jbehave:jbehave-core" to listOf("jbehave"),
23 | "org.jgiven:jgiven-junit5" to listOf("JGiven JUnit5"),
24 | "org.concordion:concordion" to listOf("concordion"),
25 | "org.junit.jupiter:junit-jupiter" to listOf("JUnit5"),
26 | "org.assertj:assertj-core" to listOf("assertj"),
27 | "org.hamcrest:hamcrest" to listOf("hamcrest"),
28 | "com.google.truth:truth" to listOf("truth"),
29 | "org.easytesting:fest-assert" to listOf("fest"),
30 | "org.easetech:easytest-core" to listOf("easytest"),
31 | "org.jmockit:jmockit" to listOf("jmockit"),
32 | "org.mockito:mockito-core" to listOf("mockito"),
33 | "org.powermock:powermock-core" to listOf("powermock"),
34 | "io.mockk:mockk" to listOf("mockk"),
35 | "com.github.tomakehurst:wiremock" to listOf("wiremock"),
36 | "org.springframework.boot:spring-boot-starter-test" to listOf(
37 | "Spring Boot Test", "Spring Test"
38 | )
39 | )
40 |
41 | /**
42 | * A list of Java framework dependencies.
43 | */
44 | private val javaFrameworkList = javaTestFrameworkMap.keys.toList()
45 |
46 | /**
47 | * Returns a list of test frameworks used in the Java project.
48 | *
49 | * @return A list of test framework names.
50 | */
51 | override fun testFramework(): List {
52 | val testFrameworks = mutableListOf()
53 |
54 | for (dep in dependencies) {
55 | val element = dep.depGroup + ":" + dep.depArtifact
56 | javaFrameworkList.contains(element).let {
57 | if (it) {
58 | testFrameworks.addAll(javaTestFrameworkMap[element]!!)
59 | }
60 | }
61 | }
62 |
63 | return testFrameworks
64 | }
65 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/project/frameworks/LangFrameworkIdentifier.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.project.frameworks
2 |
3 | interface LangFrameworkIdentifier {
4 | fun testFramework(): List
5 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/project/frameworks/TypescriptFrameworkIdentifier.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.project.frameworks
2 |
3 | import org.archguard.scanner.core.sca.CompositionDependency
4 |
5 | class TypescriptFrameworkIdentifier(private val dependencies: List) : LangFrameworkIdentifier {
6 | // 在类中定义 testFrameworkList,使其可复用
7 | companion object {
8 | private val testFrameworkList = listOf(
9 | "jest",
10 | "mocha",
11 | "jasmine",
12 | "ava",
13 | "tape",
14 | "qunit",
15 | "web-component-tester",
16 | "testem",
17 | "webdriverio",
18 | "nightwatch",
19 | "puppeteer",
20 | "protractor",
21 | "cypress",
22 | "test"
23 | )
24 | }
25 |
26 | override fun testFramework(): List {
27 | val testFrameworks = dependencies
28 | .filter { it.depName in testFrameworkList }
29 | .map { it.depName }
30 |
31 | return testFrameworks
32 | }
33 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/project/library/LibraryDescriptor.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.project.library
2 |
3 | data class LibraryDescriptor(val shortText: String, val coords: String)
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/project/library/LibrarySet.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.project.library
2 |
3 | data class LibrarySet(val shortText: String, val coords: List)
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/project/library/SpringLibrary.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.project.library
2 |
3 | object SpringLibrary {
4 | // Spring
5 | private val SPRING_MVC_MAVEN = "org.springframework:spring-webmvc"
6 | private val SPRING_WEBFLUX_MAVEN = "org.springframework:spring-webflux"
7 | private val SPRING_BOOT_WEB = "org.springframework.boot:spring-boot-starter-web"
8 | private val SPRING_BOOT = "org.springframework.boot:spring-boot-starter"
9 |
10 | // Spring Data
11 | private val REACTOR_MAVEN = "io.projectreactor:reactor-core"
12 | private val MONGO_REACTIVE_STREAMS_MAVEN = "org.mongodb:mongodb-driver-reactivestreams"
13 | private val SPRING_DATA_COMMONS_MAVEN = "org.springframework.data:spring-data-commons"
14 | private val JPA_MAVEN = "org.springframework.data:spring-data-jpa"
15 | private val JPA_BOOT_MAVEN = "org.springframework.boot:spring-boot-starter-data-jpa"
16 |
17 | private val CASSANDRA_MAVEN = "org.springframework.data:spring-data-cassandra"
18 | private val COUCHBASE_MAVEN = "org.springframework.data:spring-data-couchbase"
19 |
20 | private val JDBC_MAVEN = "org.springframework.data:spring-data-jdbc"
21 | private val JDBC_BOOT_MAVEN = "org.springframework.data:spring-data-mongodb"
22 | private val MONGO_MAVEN = "org.springframework.data:spring-data-mongodb"
23 | private val MONGO_BOOT_MAVEN = "org.springframework.boot:spring-boot-starter-data-mongodb"
24 |
25 | private val NEO4J_MAVEN = "org.springframework.data:spring-data-neo4j"
26 | private val R2DBC_MAVEN = "org.springframework.data:spring-data-r2dbc"
27 | private val REDIS_MAVEN = "org.springframework.data:spring-data-redis"
28 |
29 | val SPRING_DATA = listOf(
30 | LibrarySet("JPA ", listOf(JPA_MAVEN)),
31 | LibrarySet("JPA SPRING BOOT", listOf(JPA_MAVEN, REACTOR_MAVEN)),
32 | LibrarySet("CASSANDRA", listOf(CASSANDRA_MAVEN)),
33 | LibrarySet("REACTIVE CASSANDRA", listOf(CASSANDRA_MAVEN, REACTOR_MAVEN)),
34 | LibrarySet("COUCHBASE", listOf(COUCHBASE_MAVEN)),
35 | LibrarySet("REACTIVE COUCHBASE", listOf(COUCHBASE_MAVEN, REACTOR_MAVEN)),
36 | LibrarySet("JDBC", listOf(JDBC_MAVEN)),
37 | LibrarySet("JDBC SPRING BOOT", listOf(JDBC_BOOT_MAVEN)),
38 | LibrarySet("MONGO", listOf(MONGO_MAVEN)),
39 | LibrarySet("MONGO SPRING BOOT", listOf(MONGO_BOOT_MAVEN)),
40 | LibrarySet(
41 | "REACTIVE MONGO",
42 | listOf(MONGO_MAVEN, REACTOR_MAVEN, MONGO_REACTIVE_STREAMS_MAVEN)
43 | ),
44 | LibrarySet("NEO4J", listOf(NEO4J_MAVEN)),
45 | LibrarySet("R2DBC", listOf(R2DBC_MAVEN)),
46 | LibrarySet("REDIS", listOf(REDIS_MAVEN))
47 | )
48 |
49 | val SPRING_MVC = listOf(
50 | LibraryDescriptor("Spring MVC", SPRING_MVC_MAVEN),
51 | LibraryDescriptor("Spring WebFlux", SPRING_WEBFLUX_MAVEN),
52 | LibraryDescriptor("Spring Boot", SPRING_BOOT),
53 | LibraryDescriptor("Spring Boot Web", SPRING_BOOT)
54 | )
55 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/project/spec/NamingStyle.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.project.spec
2 |
3 | import chapi.domain.core.CodeDataStruct
4 |
5 | enum class NamingStyle(val value: String) {
6 | CAMEL_CASE("CamelCase"),
7 | SNAKE_CASE("snake_case"),
8 | KEBAB_CASE("kebab-case"),
9 | ;
10 | }
11 |
12 | /**
13 | * This method checks the naming style used in the CodeDataStruct object.
14 | *
15 | * It counts the occurrences of different naming styles (camel case, snake case, kebab case) in the names of the functions
16 | * within the CodeDataStruct object. The naming style with the highest count is considered the predominant naming style.
17 | *
18 | * @return The predominant naming style as a string. Possible values are [NamingStyle.SNAKE_CASE], [NamingStyle.KEBAB_CASE], and [NamingStyle.CAMEL_CASE].
19 | */
20 | fun CodeDataStruct.checkNamingStyle(): String {
21 | val countByNaming: MutableMap = mutableMapOf(
22 | NamingStyle.CAMEL_CASE to 0,
23 | NamingStyle.SNAKE_CASE to 0,
24 | NamingStyle.KEBAB_CASE to 0,
25 | )
26 |
27 | this.Functions.map {
28 | val name = it.Name
29 | val nameStyle = when {
30 | checkSnakeCase(name) -> NamingStyle.SNAKE_CASE
31 | name.contains("-") -> NamingStyle.KEBAB_CASE
32 | checkCamelCase(name) -> NamingStyle.CAMEL_CASE
33 | else -> NamingStyle.CAMEL_CASE
34 | }
35 |
36 | countByNaming[nameStyle] = countByNaming[nameStyle]!! + 1
37 | }
38 |
39 | val maxCount = countByNaming.values.maxOrNull()!!
40 | val maxNamingStyle = countByNaming.filter { it.value == maxCount }.keys.first()
41 |
42 | return maxNamingStyle.value
43 | }
44 |
45 | /**
46 | * Checks if a given string follows the snake_case naming style.
47 | *
48 | * @param name The string to be checked.
49 | * @return `true` if the string follows the snake_case naming style, `false` otherwise.
50 | */
51 | fun checkSnakeCase(name: String): Boolean {
52 | if(!name.contains("_")) return false
53 |
54 | // check _ is not the first or last character
55 | if(name.startsWith("_") || name.endsWith("_")) return false
56 |
57 | // check _ is not consecutive
58 | if(name.contains("__")) return false
59 |
60 | // check _ is not followed by a number
61 | if(name.contains(Regex("_\\d"))) return false
62 |
63 | // check _ is not followed by a capital letter
64 | if(name.contains(Regex("_[A-Z]"))) return false
65 |
66 | return true
67 | }
68 |
69 | /**
70 | * Checks if a given name follows the camel case naming style.
71 | *
72 | * @param name The name to be checked.
73 | * @return `true` if the name follows the camel case naming style, `false` otherwise.
74 | */
75 | fun checkCamelCase(name: String): Boolean {
76 | return name.matches(Regex("^[a-z][a-zA-Z0-9]*$"))
77 | }
78 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/similar/TypeScriptSimilarChunker.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.similar
2 |
3 | import cc.unitmesh.core.intelli.SimilarChunkContext
4 | import cc.unitmesh.core.intelli.SimilarChunker
5 | import cc.unitmesh.pick.worker.job.InstructionFileJob
6 | import java.util.HashMap
7 |
8 | class TypeScriptSimilarChunker(fileTree: HashMap): SimilarChunker() {
9 | override fun calculate(text: String, canonicalName: String): SimilarChunkContext {
10 | TODO("Not yet implemented")
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/strategy/CodeStrategyType.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.strategy
2 |
3 | import cc.unitmesh.pick.strategy.base.CodeStrategyBuilder
4 | import cc.unitmesh.pick.strategy.bizcode.RelatedCodeStrategyBuilder
5 | import cc.unitmesh.pick.strategy.bizcode.SimilarChunksStrategyBuilder
6 | import cc.unitmesh.pick.worker.job.JobContext
7 | import kotlinx.serialization.SerializationException
8 |
9 | /**
10 | * The `BuildPlanType` enum class represents different strategies for generating code context in AutoDev.
11 | *
12 | * There are two available strategies:
13 | * 1. [CodeStrategyType.SIMILAR_CHUNKS]: This prompt is used with pre-built context for unsupported languages.
14 | * It allows AutoDev to generate code context with similar code chunks builder.
15 | * 2. [CodeStrategyType.RELATED_CODE]: This prompt is used with pre-built context. It allows AutoDev to
16 | * generate code context with similar code builder.
17 | *
18 | * The strategies are used through the `builder` function, which takes an `InstructionContext` parameter and returns an `InstructionBuilder` object.
19 | *
20 | * Note that the `builder` function throws a `SerializationException` if the prompt is unknown.
21 | */
22 | enum class CodeStrategyType {
23 | /**
24 | * the AutoDev with pre-build context for un-support language
25 | */
26 | SIMILAR_CHUNKS,
27 |
28 | /**
29 | * the AutoDev with pre-build context
30 | */
31 | RELATED_CODE
32 | ;
33 |
34 | fun builder(context: JobContext): CodeStrategyBuilder {
35 | return mapOf(
36 | SIMILAR_CHUNKS to SimilarChunksStrategyBuilder(context),
37 | RELATED_CODE to RelatedCodeStrategyBuilder(context),
38 | )[this] ?: throw SerializationException("Unknown message type: $this")
39 | }
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/strategy/ins/RelatedCodeIns.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.strategy.ins
2 |
3 | import cc.unitmesh.pick.ext.toUml
4 | import cc.unitmesh.core.completion.InstructionBuilderType
5 | import cc.unitmesh.core.Instruction
6 | import cc.unitmesh.core.completion.TypedIns
7 | import cc.unitmesh.pick.threshold.InsQualityThreshold
8 | import chapi.domain.core.CodeDataStruct
9 | import kotlinx.serialization.Serializable
10 | import kotlinx.serialization.json.Json
11 |
12 | @Serializable
13 | data class RelatedCodeIns(
14 | val language: String,
15 | val beforeCursor: String,
16 | val relatedCode: List,
17 | val output: String,
18 | override val type: InstructionBuilderType,
19 | ) : TypedIns {
20 | override fun toString(): String {
21 | return Json.encodeToString(serializer(), this)
22 | }
23 |
24 | override fun toInstruction(): Instruction {
25 | // Related code strategy
26 | val relatedCode = if (relatedCode.isNotEmpty()) {
27 | // todo: count be similar
28 | val relatedCode = relatedCode.take(3).joinToString("\n") {
29 | it.toUml()
30 | }
31 |
32 | val relatedCodeLines = relatedCode.lines()
33 | val maxLine = InsQualityThreshold.MAX_RELATED_CODE_LINE
34 | if (relatedCodeLines.size > maxLine) {
35 | relatedCodeLines.take(maxLine).joinToString("\n")
36 | }
37 |
38 | "\n// Compare this snippets: \n ```${language}\n${relatedCodeLines.joinToString("\n")}\n```"
39 | } else {
40 | ""
41 | }
42 |
43 | // Count strategy
44 | val maxLine = InsQualityThreshold.MAX_LINE_IN_CODE
45 | val beforeCursorLine = beforeCursor.count { it == '\n' }
46 | val afterCursorLine = output.count { it == '\n' }
47 |
48 | // drop from the start of beforeCursor
49 | val beforeCursor = if (beforeCursorLine + afterCursorLine > maxLine) {
50 | val dropLine = beforeCursorLine + afterCursorLine - maxLine
51 | beforeCursor.lines().drop(dropLine).joinToString("\n")
52 | } else {
53 | beforeCursor
54 | }
55 |
56 | val input = "$relatedCode\n\nCode:\n```${language}\n$beforeCursor\n```"
57 | return Instruction(
58 | instruction = "Complete $language code, return rest code, no explaining",
59 | output = output,
60 | input = input
61 | )
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/strategy/ins/SimilarChunkIns.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.strategy.ins
2 |
3 | import cc.unitmesh.core.completion.InstructionBuilderType
4 | import cc.unitmesh.core.Instruction
5 | import cc.unitmesh.core.completion.TypedIns
6 | import cc.unitmesh.pick.threshold.InsQualityThreshold
7 | import kotlinx.serialization.Serializable
8 | import kotlinx.serialization.json.Json
9 |
10 | @Serializable
11 | data class SimilarChunkIns(
12 | val language: String,
13 | val beforeCursor: String,
14 | val afterCursor: String,
15 | val similarChunks: String,
16 | val output: String,
17 | override val type: InstructionBuilderType,
18 | ) : TypedIns {
19 | override fun toString(): String {
20 | return Json.encodeToString(serializer(), this)
21 | }
22 |
23 | override fun toInstruction(): Instruction {
24 | // Similar chunk strategy
25 | val similarChunks = if (similarChunks.isNotBlank() && similarChunks.isNotEmpty()) {
26 | // limit similarChunks to 30 lines
27 | val similarChunksLines = similarChunks.lines()
28 | val maxLine = InsQualityThreshold.MAX_RELATED_CODE_LINE
29 | if (similarChunksLines.size > maxLine) {
30 | similarChunksLines.take(maxLine).joinToString("\n")
31 | } else {
32 | similarChunks
33 | }
34 | "\n// Similar chunk:\n```${language}\n${similarChunksLines.joinToString("\n")}\n```"
35 | } else {
36 | ""
37 | }
38 |
39 | // Count strategy
40 | val maxLine = InsQualityThreshold.MAX_LINE_IN_CODE
41 | val beforeCursorLine = beforeCursor.count { it == '\n' }
42 | val afterCursorLine = output.count { it == '\n' }
43 | // drop from the start of beforeCursor
44 | val beforeCursor = if (beforeCursorLine + afterCursorLine > maxLine) {
45 | val dropLine = beforeCursorLine + afterCursorLine - maxLine
46 | beforeCursor.lines().drop(dropLine).joinToString("\n")
47 | } else {
48 | beforeCursor
49 | }
50 |
51 | val input = "$similarChunks\n\nCode:\n```${language}\n$beforeCursor\n```"
52 |
53 | return Instruction(
54 | instruction = "Complete $language code, return rest code, no explaining",
55 | output = output,
56 | input = input
57 | )
58 | }
59 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/threshold/InsQualityThreshold.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.threshold
2 |
3 | import cc.unitmesh.quality.badsmell.BsThresholds
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class InsQualityThreshold(
8 | val complexity: Int = MAX_COMPLEXITY,
9 | val fileSize: Int = MAX_FILE_SIZE,
10 | /**
11 | * https://docs.sweep.dev/blogs/chunking-2m-files
12 | * This is because the average token to a character ratio for code is ~1:5(300 tokens), and embedding models are
13 | * capped at 512 tokens. Further, 1500 characters correspond approximately to 40 lines, roughly equivalent to a
14 | * small to medium-sized function or class.
15 | *
16 | * Our token length is 1024, so we can use 1500 * 1024 / 512 = 1500
17 | */
18 | val maxCharInCode: Int = MAX_CHAR_IN_CODE,
19 | /**
20 | * Our token length is 1024, so we can use 40 * 2048 / 512 = 320
21 | */
22 | val maxLineInCode: Int = MAX_LINE_IN_CODE,
23 | val badsmellThreshold: Map = BsThresholds().toThresholds(),
24 | val maxTokenLength: Int = MAX_TOKEN_LENGTH,
25 | ) {
26 | companion object {
27 | const val MAX_TOKEN_LENGTH: Int = 2048
28 | const val MAX_COMPLEXITY: Int = 1000
29 | const val MAX_PROJECT_TYPED_COMPLETION_SIZE: Int = 1000
30 | const val MAX_FILE_SIZE: Int = 1024 * 64
31 | const val MAX_LINE_IN_CODE: Int = 320
32 | const val MAX_CHAR_IN_CODE: Int = 1500
33 | const val MAX_RELATED_CODE_LINE: Int = 30
34 | }
35 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/threshold/ThresholdChecker.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.threshold
2 |
3 | import cc.unitmesh.core.Instruction
4 | import cc.unitmesh.pick.threshold.filter.*
5 | import cc.unitmesh.pick.threshold.pipeline.Pipeline
6 | import cc.unitmesh.pick.worker.WorkerContext
7 | import cc.unitmesh.pick.worker.job.FileSummary
8 | import cc.unitmesh.pick.worker.job.InstructionFileJob
9 | import com.knuddels.jtokkit.Encodings
10 | import com.knuddels.jtokkit.api.Encoding
11 | import com.knuddels.jtokkit.api.EncodingRegistry
12 | import com.knuddels.jtokkit.api.EncodingType
13 |
14 | /**
15 | * The `ThresholdChecker` class is responsible for determining whether a given job or instruction
16 | * meets the threshold criteria for processing. It utilizes various criteria, including file type,
17 | * code complexity, file size, and token length, to make these determinations.
18 | *
19 | * @property context The worker context providing configuration settings for the threshold checks.
20 | */
21 | class ThresholdChecker(private val context: WorkerContext) {
22 | private var registry: EncodingRegistry = Encodings.newDefaultEncodingRegistry()
23 | private var enc: Encoding = registry.getEncoding(EncodingType.CL100K_BASE)
24 |
25 | private val pipeline: Pipeline
26 | get() {
27 | return Pipeline()
28 | .addFilter(ExtensionFilter(context.qualityThreshold))
29 | .addFilter(ComplexityFilter(context.qualityThreshold))
30 | .addFilter(BinaryGeneratedMinifiedFilter(context.qualityThreshold))
31 | .addFilter(SizeFilter(context.qualityThreshold))
32 | .addFilter(TokenLengthFilter(context.qualityThreshold))
33 | }
34 |
35 | /**
36 | * Checks if the given job meets the threshold criteria for processing.
37 | *
38 | * @param job The instruction file job to be checked.
39 | * @return Returns true if the job meets the threshold criteria, false otherwise.
40 | */
41 | fun isMetThreshold(job: InstructionFileJob): Boolean {
42 | return pipeline.process(job.fileSummary)
43 | }
44 |
45 | /**
46 | * Determines whether the given instruction meets the threshold criteria.
47 | *
48 | * @param ins the instruction to be evaluated
49 | * @return true if the instruction meets the threshold criteria, false otherwise
50 | */
51 | fun isMetThreshold(ins: Instruction): Boolean {
52 | // skip empty instruction
53 | if (ins.input.isEmpty() || ins.output.isEmpty()) {
54 | return false
55 | }
56 |
57 | // limit by token length
58 | val totalToken = enc.encode(ins.instruction + ins.input + ins.output).size
59 | return totalToken <= context.qualityThreshold.maxTokenLength
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/threshold/filter/BinaryGeneratedMinifiedFilter.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.threshold.filter
2 |
3 | import cc.unitmesh.pick.threshold.pipeline.Filter
4 | import cc.unitmesh.pick.threshold.pipeline.FilterResult
5 | import cc.unitmesh.pick.threshold.InsQualityThreshold
6 | import cc.unitmesh.pick.worker.job.FileSummary
7 |
8 | class BinaryGeneratedMinifiedFilter(qualityThreshold: InsQualityThreshold) : Filter {
9 | override fun filter(data: FileSummary): FilterResult {
10 | return FilterResult(!(data.binary || data.generated || data.minified), "binary or generated or minified", true)
11 | }
12 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/threshold/filter/ComplexityFilter.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.threshold.filter
2 |
3 | import cc.unitmesh.pick.threshold.pipeline.Filter
4 | import cc.unitmesh.pick.threshold.pipeline.FilterResult
5 | import cc.unitmesh.pick.threshold.InsQualityThreshold
6 | import cc.unitmesh.pick.worker.job.FileSummary
7 |
8 | /**
9 | * The `ComplexityFilter` class is responsible for filtering `FileSummary` objects based on their complexity.
10 | * It implements the `Filter` interface and provides the logic to determine if a file's complexity meets the threshold criteria.
11 | *
12 | * @param qualityThreshold The quality threshold object that contains the maximum allowed complexity.
13 | */
14 | class ComplexityFilter(private val qualityThreshold: InsQualityThreshold) : Filter {
15 | override fun filter(data: FileSummary): FilterResult {
16 | return FilterResult(
17 | data.complexity <= qualityThreshold.complexity,
18 | "complexity too high: ${data.complexity}",
19 | true
20 | )
21 | }
22 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/threshold/filter/ExtensionFilter.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.threshold.filter
2 |
3 | import cc.unitmesh.core.SupportedLang
4 | import cc.unitmesh.pick.threshold.pipeline.Filter
5 | import cc.unitmesh.pick.threshold.pipeline.FilterResult
6 | import cc.unitmesh.pick.threshold.InsQualityThreshold
7 | import cc.unitmesh.pick.worker.job.FileSummary
8 | import org.archguard.scanner.analyser.count.LanguageService
9 |
10 | class ExtensionFilter(private val qualityThreshold: InsQualityThreshold) : Filter {
11 | private val language: LanguageService = LanguageService()
12 |
13 | private val supportedExtensions: Set = SupportedLang.all().map {
14 | language.getExtension(it.extension)
15 | }.toSet()
16 |
17 | override fun filter(data: FileSummary): FilterResult {
18 | val ext = data.extension
19 | return FilterResult(supportedExtensions.contains(ext), "extension not supported: $ext")
20 | }
21 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/threshold/filter/SizeFilter.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.threshold.filter
2 |
3 | import cc.unitmesh.pick.threshold.pipeline.Filter
4 | import cc.unitmesh.pick.threshold.pipeline.FilterResult
5 | import cc.unitmesh.pick.threshold.InsQualityThreshold
6 | import cc.unitmesh.pick.worker.job.FileSummary
7 |
8 | class SizeFilter(private val qualityThreshold: InsQualityThreshold) : Filter {
9 | override fun filter(data: FileSummary): FilterResult {
10 | return FilterResult(data.bytes <= qualityThreshold.fileSize, "file size too large: ${data.bytes}", true)
11 | }
12 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/threshold/filter/TokenLengthFilter.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.threshold.filter
2 |
3 | import cc.unitmesh.pick.threshold.pipeline.Filter
4 | import cc.unitmesh.pick.threshold.pipeline.FilterResult
5 | import cc.unitmesh.pick.threshold.InsQualityThreshold
6 | import cc.unitmesh.pick.worker.job.FileSummary
7 | import com.knuddels.jtokkit.Encodings
8 | import com.knuddels.jtokkit.api.Encoding
9 | import com.knuddels.jtokkit.api.EncodingRegistry
10 | import com.knuddels.jtokkit.api.EncodingType
11 |
12 | /**
13 | * A filter that checks if the token length of a file meets the threshold criteria.
14 | *
15 | * @param qualityThreshold The quality threshold for token length.
16 | */
17 | class TokenLengthFilter(private val qualityThreshold: InsQualityThreshold) : Filter {
18 | private var registry: EncodingRegistry = Encodings.newDefaultEncodingRegistry()
19 | private var enc: Encoding = registry.getEncoding(EncodingType.CL100K_BASE)
20 |
21 | override fun filter(data: FileSummary): FilterResult {
22 | val encoded = enc.encode(data.content.toString())
23 | val length = encoded.size
24 | val codeWithBuffer = 1.25
25 | return FilterResult(
26 | length <= qualityThreshold.maxTokenLength * codeWithBuffer,
27 | "token length too long: $length",
28 | true
29 | )
30 | }
31 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/threshold/pipeline/Filter.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.threshold.pipeline
2 |
3 | interface Filter {
4 | fun filter(data: T): FilterResult
5 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/threshold/pipeline/FilterResult.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.threshold.pipeline
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class FilterResult(
7 | val result: Boolean,
8 | val reason: String = "",
9 | val isCritical: Boolean = false,
10 | )
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/threshold/pipeline/Pipeline.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.threshold.pipeline
2 |
3 | import cc.unitmesh.pick.threshold.ThresholdChecker
4 | import org.slf4j.Logger
5 |
6 | class Pipeline {
7 | private val logger: Logger = org.slf4j.LoggerFactory.getLogger(ThresholdChecker::class.java)
8 |
9 | private val filters = mutableListOf>()
10 |
11 | fun addFilter(filter: Filter): Pipeline {
12 | filters.add(filter)
13 | return this
14 | }
15 |
16 | fun process(data: T): Boolean {
17 | return filters.all {
18 | val result = it.filter(data)
19 | if (!result.result) {
20 | if (result.isCritical) {
21 | logger.warn("not met filter: ${it.javaClass.simpleName}, reason: ${result.reason}")
22 | }
23 | }
24 |
25 | result.result
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/worker/job/InstructionFileJob.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.worker.job
2 |
3 | import chapi.domain.core.CodeContainer
4 | import kotlinx.serialization.Serializable
5 | import org.archguard.scanner.analyser.count.FileJob
6 | import java.util.regex.Pattern
7 |
8 | typealias FileSummary = FileJob
9 |
10 | @Serializable
11 | class InstructionFileJob(
12 | var fileSummary: FileJob,
13 | var code: String = "",
14 | var container: CodeContainer? = null,
15 | var codeLines: List = listOf(),
16 | ) {
17 | companion object {
18 | fun from(fileJob: FileJob, canRemoveComment: Boolean): InstructionFileJob {
19 | val contentString = fileJob.content.decodeToString()
20 | val content = if (canRemoveComment) {
21 | removeMultipleComments(fileJob.language, contentString)
22 | } else {
23 | contentString
24 | }
25 |
26 | return InstructionFileJob(
27 | code = content,
28 | fileSummary = fileJob
29 | )
30 | }
31 |
32 | }
33 | }
34 |
35 | val pattern: Pattern = Pattern.compile("/\\*.*?\\*/", Pattern.DOTALL)
36 |
37 | /**
38 | * Removes multiple comments from the given code.
39 | *
40 | * @param language the programming language of the code (e.g., "java")
41 | * @param code the code from which comments should be removed
42 | * @return the code without multiple comments
43 | */
44 | fun removeMultipleComments(language: String, code: String): String {
45 | return when (language.lowercase()) {
46 | "java" -> {
47 | val matcher = pattern.matcher(code)
48 | return matcher.replaceAll("")
49 | }
50 |
51 | else -> {
52 | code
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/worker/job/JobContext.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.worker.job
2 |
3 | import cc.unitmesh.core.completion.InstructionBuilderType
4 | import cc.unitmesh.pick.option.InsOutputConfig
5 | import cc.unitmesh.pick.project.ProjectContext
6 | import cc.unitmesh.pick.threshold.InsQualityThreshold
7 | import cc.unitmesh.quality.CodeQualityType
8 | import kotlinx.serialization.Serializable
9 | import org.archguard.scanner.analyser.count.FileJob
10 | import org.jetbrains.annotations.TestOnly
11 |
12 | @Serializable
13 | data class JobContext(
14 | val job: InstructionFileJob,
15 | val qualityTypes: List,
16 | val fileTree: HashMap,
17 | val insOutputConfig: InsOutputConfig = InsOutputConfig(),
18 | val instructionBuilderTypes: List,
19 | val maxTypedCompletionSize: Int,
20 | val project: ProjectContext = ProjectContext(),
21 | val insQualityThreshold: InsQualityThreshold,
22 | ) {
23 | companion object {
24 | @TestOnly
25 | fun default(fileTree: HashMap = hashMapOf()): JobContext {
26 | return JobContext(
27 | job = InstructionFileJob(
28 | fileSummary = FileJob()
29 | ),
30 | qualityTypes = listOf(),
31 | fileTree = fileTree,
32 | instructionBuilderTypes = listOf(),
33 | maxTypedCompletionSize = InsQualityThreshold.MAX_PROJECT_TYPED_COMPLETION_SIZE,
34 | insQualityThreshold = InsQualityThreshold()
35 | )
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/worker/lang/JavaWorker.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.worker.lang
2 |
3 | import cc.unitmesh.pick.worker.job.InstructionFileJob
4 | import cc.unitmesh.pick.ext.buildSourceCode
5 | import cc.unitmesh.pick.worker.WorkerContext
6 | import cc.unitmesh.pick.worker.base.LangWorker
7 | import chapi.ast.javaast.JavaAnalyser
8 | import org.slf4j.Logger
9 |
10 | /**
11 | * The JavaWorker class is an implementation of the LangWorker interface.
12 | * It provides functionality for handling Java based instruction file jobs.
13 | */
14 | open class JavaWorker(override val workerContext: WorkerContext) : LangWorker {
15 | override val jobs: MutableList = mutableListOf()
16 | override val fileTree: HashMap = hashMapOf()
17 | override val logger: Logger = org.slf4j.LoggerFactory.getLogger(JavaWorker::class.java)
18 |
19 | protected open val packageRegex = Regex("package\\s+([a-zA-Z0-9_.]+);")
20 | protected open val extLength = ".java".length
21 |
22 | /**
23 | * Adds a job to the list of instruction file jobs.
24 | *
25 | * @param job The InstructionFileJob object to be added.
26 | */
27 | override fun prepareJob(job: InstructionFileJob) {
28 | this.jobs.add(job)
29 |
30 | try {
31 | tryAddClassToTree(job)
32 | // since the Java Analyser imports will be in data structures
33 | val container = JavaAnalyser().analysis(job.code, job.fileSummary.location)
34 | job.codeLines = job.code.lines()
35 | container.buildSourceCode(job.codeLines)
36 |
37 | job.container = container
38 | } catch (e: Exception) {
39 | logger.error("failed to prepare job: ${job.fileSummary.location}")
40 | e.printStackTrace()
41 | }
42 | }
43 |
44 | /**
45 | * Tries to add a class to the file tree.
46 | *
47 | * This method takes an InstructionFileJob object as a parameter and attempts to add the class represented by the job to the file tree.
48 | * It extracts the package name from the code of the job, and if a package name is found, it constructs the full class name using the package name and the filename of the job.
49 | * The method then adds the full class name as a key to the file tree, with the job as its corresponding value.
50 | *
51 | * @param job the InstructionFileJob object representing the class to be added to the file tree
52 | */
53 | override fun tryAddClassToTree(job: InstructionFileJob) {
54 | val packageMatch = packageRegex.find(job.code)
55 | if (packageMatch != null) {
56 | val packageName = packageMatch.groupValues[1]
57 | // in Java the filename is the class name
58 | val className = job.fileSummary.filename.substring(0, job.fileSummary.filename.length - extLength)
59 | val fullClassName = "$packageName.$className"
60 | fileTree[fullClassName] = job
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/worker/lang/KotlinWorker.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.worker.lang
2 |
3 | import cc.unitmesh.core.completion.InstructionBuilderType
4 | import cc.unitmesh.pick.ext.buildSourceCode
5 | import cc.unitmesh.pick.worker.WorkerContext
6 | import cc.unitmesh.pick.worker.base.LangWorker
7 | import cc.unitmesh.pick.worker.job.InstructionFileJob
8 | import chapi.ast.kotlinast.KotlinAnalyser
9 | import chapi.parser.ParseMode
10 | import org.slf4j.Logger
11 |
12 | class KotlinWorker(override val workerContext: WorkerContext) : JavaWorker(workerContext), LangWorker {
13 | override val jobs: MutableList = mutableListOf()
14 |
15 | override val fileTree: HashMap = hashMapOf()
16 | override val logger: Logger = org.slf4j.LoggerFactory.getLogger(KotlinWorker::class.java)
17 |
18 | override val packageRegex = Regex("package\\s+([a-zA-Z0-9_.]+)")
19 | override val extLength = ".kt".length
20 |
21 | /**
22 | *
23 | * 对[InstructionFileJob]进行预处理,主要包括以下几个步骤:
24 | * 1. 添加到fileTree中,方便后续查找
25 | * 2. 解析代码,生成抽象语法树
26 | *
27 | * Note: 在 Chapi 中,Kotlin 的解析模式分为两种,一种是 Full,一种是 Basic,Full 模式会解析出更多的信息,但是会消耗更多的时间。
28 | * 在测试代码生成的过程中,我们只需要解析出基本的信息即可,所以使用 FULL 模式即可。
29 | *
30 | * @param job 待解析的文件
31 | * @return 返回解析后的文件
32 | */
33 | override fun prepareJob(job: InstructionFileJob) {
34 | this.jobs.add(job)
35 |
36 | try {
37 | tryAddClassToTree(job)
38 | val container = if (workerContext.completionTypes.contains(InstructionBuilderType.TEST_CODE_GEN)) {
39 | KotlinAnalyser().analysis(job.code, job.fileSummary.location, ParseMode.Full)
40 | } else {
41 | KotlinAnalyser().analysis(job.code, job.fileSummary.location)
42 | }
43 |
44 | job.codeLines = job.code.lines()
45 | container.buildSourceCode(job.codeLines)
46 |
47 | job.container = container
48 | } catch (e: Exception) {
49 | logger.error("failed to prepare job: ${job.fileSummary.location}")
50 | e.printStackTrace()
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/worker/lang/PythonWorker.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.worker.lang
2 |
3 | import cc.unitmesh.pick.ext.CodeDataStructUtil
4 | import cc.unitmesh.pick.ext.buildSourceCode
5 | import cc.unitmesh.pick.worker.WorkerContext
6 | import cc.unitmesh.pick.worker.base.LangWorker
7 | import cc.unitmesh.pick.worker.job.InstructionFileJob
8 | import chapi.ast.kotlinast.KotlinAnalyser
9 | import chapi.ast.typescriptast.TypeScriptAnalyser
10 | import org.slf4j.Logger
11 |
12 | class PythonWorker(override val workerContext: WorkerContext) : LangWorker {
13 | override val jobs: MutableList = mutableListOf()
14 |
15 | override val fileTree: HashMap = hashMapOf()
16 | override val logger: Logger = org.slf4j.LoggerFactory.getLogger(PythonWorker::class.java)
17 |
18 | val extLength = ".pyt".length
19 |
20 | override fun prepareJob(job: InstructionFileJob) {
21 | this.jobs.add(job)
22 |
23 | try {
24 | val container = TypeScriptAnalyser().analysis(job.code, job.fileSummary.location)
25 | job.codeLines = job.code.lines()
26 | container.DataStructures.map { ds ->
27 | ds.Imports = container.Imports
28 |
29 | ds.Content = CodeDataStructUtil.contentByPosition(job.codeLines, ds.Position)
30 | ds.Functions.map {
31 | it.apply {
32 | it.Content = CodeDataStructUtil.contentByPosition(job.codeLines, it.Position)
33 | }
34 | }
35 | }
36 |
37 | job.container = container
38 | } catch (e: Exception) {
39 | e.printStackTrace()
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/unit-picker/src/main/kotlin/cc/unitmesh/pick/worker/lang/TypescriptWorker.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.worker.lang
2 |
3 | import cc.unitmesh.pick.ext.CodeDataStructUtil
4 | import cc.unitmesh.pick.worker.WorkerContext
5 | import cc.unitmesh.pick.worker.base.LangWorker
6 | import cc.unitmesh.pick.worker.job.InstructionFileJob
7 | import chapi.ast.typescriptast.TypeScriptAnalyser
8 | import org.slf4j.Logger
9 |
10 | class TypescriptWorker(override val workerContext: WorkerContext) : LangWorker {
11 | override val jobs: MutableList = mutableListOf()
12 | override val fileTree: HashMap = hashMapOf()
13 | override val logger: Logger = org.slf4j.LoggerFactory.getLogger(TypescriptWorker::class.java)
14 |
15 | override fun prepareJob(job: InstructionFileJob) {
16 | this.jobs.add(job)
17 | try {
18 | val container = TypeScriptAnalyser().analysis(job.code, job.fileSummary.location)
19 | job.codeLines = job.code.lines()
20 | container.DataStructures.map { ds ->
21 | ds.Imports = container.Imports
22 |
23 | ds.Content = CodeDataStructUtil.contentByPosition(job.codeLines, ds.Position)
24 | ds.Functions.map {
25 | it.apply {
26 | it.Content = CodeDataStructUtil.contentByPosition(job.codeLines, it.Position)
27 | }
28 | }
29 | }
30 |
31 | job.container = container
32 | } catch (e: Exception) {
33 | e.printStackTrace()
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/SingleProjectCodePickerTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick
2 |
3 | import cc.unitmesh.core.Instruction
4 | import cc.unitmesh.core.completion.InstructionBuilderType
5 | import cc.unitmesh.pick.option.InsPickerOption
6 | import cc.unitmesh.pick.strategy.CodeStrategyType
7 | import kotlinx.coroutines.runBlocking
8 | import kotlinx.serialization.encodeToString
9 | import kotlinx.serialization.json.Json
10 | import org.junit.jupiter.api.Test
11 | import java.io.File
12 |
13 | class SingleProjectCodePickerTest {
14 |
15 | @Test
16 | fun shouldCheckoutTestCode() {
17 | val picker = SingleProjectCodePicker(
18 | InsPickerOption(
19 | url = "https://github.com/unit-mesh/unit-eval-testing",
20 | completionTypeSize = 10,
21 | maxCharInCode = 100,
22 | )
23 | )
24 |
25 | val outputFile = File("test.jsonl")
26 | runBlocking {
27 | val output: MutableList = picker.execute()
28 | outputFile.writeText(output.joinToString("\n") {
29 | Json.encodeToString(it)
30 | })
31 | }
32 | }
33 |
34 | @Test
35 | fun should_handle_for_kotlin_test_gen() {
36 | val root = File(".").canonicalPath
37 | val picker = SingleProjectCodePicker(
38 | InsPickerOption(
39 | language = "kotlin",
40 | url = root,
41 | maxTokenLength = 8192,
42 | codeStrategyTypes = listOf(CodeStrategyType.RELATED_CODE),
43 | instructionTypes = listOf(
44 | InstructionBuilderType.DOCUMENTATION, InstructionBuilderType.TEST_CODE_GEN
45 | ),
46 | )
47 | )
48 |
49 | val outputFile = File("test.jsonl")
50 | runBlocking {
51 | val output: MutableList = picker.execute()
52 | outputFile.writeText(output.joinToString("\n") {
53 | Json.encodeToString(it)
54 | })
55 | }
56 | }
57 |
58 | @Test
59 | fun should_handle_for_rust_test_gen() {
60 | val picker = SingleProjectCodePicker(
61 | InsPickerOption(
62 | language = "rust",
63 | url = "https://github.com/unit-mesh/edge-infer",
64 | maxTokenLength = 8192,
65 | codeStrategyTypes = listOf(CodeStrategyType.RELATED_CODE),
66 | instructionTypes = listOf(
67 | InstructionBuilderType.TEST_CODE_GEN
68 | ),
69 | )
70 | )
71 |
72 | val outputFile = File("test.jsonl")
73 | runBlocking {
74 | val output: MutableList = picker.execute()
75 | outputFile.writeText(output.joinToString("\n") {
76 | Json.encodeToString(it)
77 | })
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/builder/CodeStrategyBuilderTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder;
2 |
3 |
4 | import org.junit.jupiter.api.Test
5 |
6 | class CodeStrategyBuilderTest {
7 | @Test
8 | fun shouldConvertInstructionToJson() {
9 |
10 | }
11 | }
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/builder/completion/AfterBlockCodeTypedInsBuilderTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.completion;
2 |
3 | import cc.unitmesh.pick.option.InsOutputConfig
4 | import cc.unitmesh.pick.worker.job.InstructionFileJob
5 | import cc.unitmesh.core.completion.CodeCompletionIns
6 | import cc.unitmesh.core.completion.InstructionBuilderType
7 | import cc.unitmesh.pick.threshold.InsQualityThreshold
8 | import cc.unitmesh.pick.worker.job.JobContext
9 | import chapi.domain.core.CodeFunction
10 | import chapi.domain.core.CodePosition
11 | import io.kotest.matchers.shouldBe
12 | import org.archguard.scanner.analyser.count.FileJob
13 | import org.junit.jupiter.api.Test
14 |
15 | class AfterBlockCodeTypedInsBuilderTest {
16 |
17 | @Test
18 | fun should_success_split_after_block_code() {
19 | val codeFunction = CodeFunction(
20 | Position = CodePosition(1, 0, 3)
21 | )
22 | val codeLines = """
23 | fun myFunction() {
24 | println("Hello, world!")
25 | }
26 | // other code here
27 | """.trimIndent().lines()
28 | val job = InstructionFileJob(
29 | fileSummary = FileJob(),
30 | codeLines = codeLines,
31 | code = codeLines.joinToString("\n")
32 | )
33 | val jobContext = JobContext(
34 | job,
35 | emptyList(),
36 | hashMapOf("" to job),
37 | InsOutputConfig(),
38 | emptyList(),
39 | 3,
40 | insQualityThreshold = InsQualityThreshold()
41 | )
42 | val builder = AfterBlockCodeTypedInsBuilder(jobContext)
43 |
44 | val result = builder.build(codeFunction)
45 |
46 | result shouldBe listOf(
47 | CodeCompletionIns(
48 | beforeCursor = codeLines.subList(0, 3).joinToString("\n"),
49 | afterCursor = "// other code here",
50 | instructionBuilderType = InstructionBuilderType.AFTER_BLOCK_COMPLETION
51 | )
52 | )
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/builder/completion/InBlockCodeTypedInsBuilderTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.completion;
2 |
3 | import cc.unitmesh.pick.option.InsOutputConfig
4 | import cc.unitmesh.pick.worker.job.InstructionFileJob
5 | import cc.unitmesh.core.completion.CodeCompletionIns
6 | import cc.unitmesh.core.completion.InstructionBuilderType
7 | import cc.unitmesh.pick.threshold.InsQualityThreshold
8 | import cc.unitmesh.pick.worker.job.JobContext
9 | import chapi.domain.core.CodeFunction
10 | import chapi.domain.core.CodePosition
11 | import io.kotest.matchers.shouldBe
12 | import org.archguard.scanner.analyser.count.FileJob
13 | import org.junit.jupiter.api.Test
14 |
15 | class InBlockCodeTypedInsBuilderTest {
16 |
17 | @Test
18 | fun should_return_single_CodeCompletionIns() {
19 | val codeFunction = CodeFunction(
20 | Position = CodePosition(1, 0, 3)
21 | )
22 | val codeLines = """
23 | fun myFunction() {
24 | println("Hello, world!")
25 | }
26 | // other code here
27 | """.trimIndent().lines()
28 | val job = InstructionFileJob(
29 | fileSummary = FileJob(),
30 | codeLines = codeLines,
31 | code = codeLines.joinToString("\n")
32 | )
33 | val jobContext = JobContext(
34 | job,
35 | emptyList(),
36 | hashMapOf("" to job),
37 | InsOutputConfig(),
38 | emptyList(),
39 | 3,
40 | insQualityThreshold = InsQualityThreshold()
41 | )
42 | val builder = InBlockCodeTypedInsBuilder(jobContext)
43 |
44 | // when
45 | val result = builder.build(codeFunction)
46 |
47 | // then
48 | result.size shouldBe 1
49 | result[0] shouldBe CodeCompletionIns(
50 | beforeCursor = codeLines.subList(0, 1).joinToString("\n"),
51 | afterCursor = codeLines.subList(1, 3).joinToString("\n"),
52 | instructionBuilderType = InstructionBuilderType.IN_BLOCK_COMPLETION
53 | )
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/builder/completion/InlineCodeTypedInsBuilderTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.builder.completion;
2 |
3 | import cc.unitmesh.pick.option.InsOutputConfig
4 | import cc.unitmesh.pick.worker.job.InstructionFileJob
5 | import cc.unitmesh.core.completion.CodeCompletionIns
6 | import cc.unitmesh.core.completion.InstructionBuilderType
7 | import cc.unitmesh.pick.threshold.InsQualityThreshold
8 | import cc.unitmesh.pick.worker.job.JobContext
9 | import chapi.domain.core.CodeFunction
10 | import chapi.domain.core.CodePosition
11 | import io.kotest.matchers.shouldBe
12 | import org.archguard.scanner.analyser.count.FileJob
13 | import org.junit.jupiter.api.Test
14 |
15 | class InlineCodeTypedInsBuilderTest {
16 | @Test
17 | fun should_return_list_of_code_completion_ins() {
18 | // Given
19 | val codeFunction = CodeFunction(
20 | Position = CodePosition(1, 0, 3)
21 | )
22 | val codeLines = """
23 | fun myFunction() {
24 | println("Hello, world!")
25 | }
26 | // other code here
27 | """.trimIndent().lines()
28 | val job = InstructionFileJob(
29 | fileSummary = FileJob(),
30 | codeLines = codeLines,
31 | code = codeLines.joinToString("\n")
32 | )
33 | val jobContext = JobContext(
34 | job,
35 | emptyList(),
36 | hashMapOf("" to job),
37 | InsOutputConfig(),
38 | emptyList(),
39 | 3,
40 | insQualityThreshold = InsQualityThreshold()
41 | )
42 | val builder = InlineCodeTypedInsBuilder(jobContext)
43 |
44 | // When
45 | val result = builder.build(codeFunction)
46 |
47 | // Then
48 | result.size shouldBe 1
49 | result shouldBe listOf(
50 | CodeCompletionIns(
51 | beforeCursor = "println(\"",
52 | afterCursor = "Hello, world!\")",
53 | instructionBuilderType = InstructionBuilderType.INLINE_COMPLETION
54 | )
55 | )
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/ext/GitUtilTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.ext
2 |
3 | import org.junit.jupiter.api.Test
4 | import kotlin.test.assertEquals
5 |
6 | class GitUtilTest {
7 | @Test
8 | fun should_return_unique_path() {
9 | assertEquals(
10 | "github.com/unit-mesh/unit-gen",
11 | GitUtil.gitUrlToPath("https://github.com/unit-mesh/unit-gen")
12 | )
13 | assertEquals(
14 | "bitbucket.org/moylop260/odoo-mexico",
15 | GitUtil.gitUrlToPath("https://bitbucket.org/moylop260/odoo-mexico.git")
16 | )
17 | assertEquals(
18 | "bitbucket.org/moylop260/odoo-mexico",
19 | GitUtil.gitUrlToPath("git@bitbucket.org:moylop260/odoo-mexico.git")
20 | )
21 | assertEquals(
22 | "github.com/moylop_260/odoo_mexico",
23 | GitUtil.gitUrlToPath("git@github.com:moylop_260/odoo_mexico.git/")
24 | )
25 | assertEquals(
26 | "github.com/odoo-mexico/odoo_mexico",
27 | GitUtil.gitUrlToPath("git@github.com:odoo-mexico/odoo_mexico.git/")
28 | )
29 | assertEquals(
30 | "github.com/odoo-mexico/odoo-mexico",
31 | GitUtil.gitUrlToPath("git@github.com:odoo-mexico/odoo-mexico.git/")
32 | )
33 | assertEquals("github.com/odoo/odoo", GitUtil.gitUrlToPath("https://github.com/odoo/odoo"))
34 | assertEquals(
35 | "github.com/odoo/odoo",
36 | GitUtil.gitUrlToPath("https://github.com/odoo/odoo.git")
37 | )
38 | assertEquals("github.com/odoo/odoo", GitUtil.gitUrlToPath("git@github.com:odoo/odoo.git"))
39 | assertEquals("github.com/odoo/odoo", GitUtil.gitUrlToPath("https://github.com/odoo/odoo"))
40 | assertEquals(
41 | "bitbucket.org/jespern/django-piston",
42 | GitUtil.gitUrlToPath("https://bitbucket.org/jespern/django-piston")
43 | )
44 | assertEquals(
45 | "bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc",
46 | GitUtil.gitUrlToPath("https://bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc")
47 | )
48 | assertEquals(
49 | "bitbucket.org/dhellmann/virtualenvwrapper-hg",
50 | GitUtil.gitUrlToPath("https://bitbucket.org/dhellmann/virtualenvwrapper-hg")
51 | )
52 | assertEquals(
53 | "bitbucket.org/zzzeek/alembic_moved_from_hg_to_git",
54 | GitUtil.gitUrlToPath("https://bitbucket.org/zzzeek/alembic_moved_from_hg_to_git")
55 | )
56 | }
57 |
58 | @Test
59 | fun shouldHandleDotInUrl() {
60 | val url = "https://github.com/openmrs/openmrs-module-webservices.rest"
61 | val gitUrlToPath = GitUtil.gitUrlToPath(url)
62 | assertEquals("github.com/openmrs/openmrs-module-webservices.rest", gitUrlToPath)
63 | }
64 | }
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/option/InsPickerOptionTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.option
2 |
3 | import org.junit.jupiter.api.Assertions.assertEquals
4 | import org.junit.jupiter.api.Test
5 |
6 | class InsPickerOptionTest {
7 | @Test
8 | fun should_return_correct_pure_data_file_name() {
9 |
10 | // When
11 | val result = InsPickerOption(
12 | url = "https://github.com/functionaljava/functionaljava",
13 | branch = "series/5.x",
14 | completionTypeSize = 10,
15 | maxCharInCode = 100,
16 | ).pureDataFileName()
17 |
18 | // Then
19 | assertEquals("datasets/https___github.com_functionaljava_functionaljava_series_5.x_java.jsonl", result)
20 | }
21 |
22 | @Test
23 | fun should_return_correct_repo_file_name() {
24 | // Given
25 | val insPickerOption = InsPickerOption(
26 | url = "https://github.com/example/repo.git",
27 | branch = "main",
28 | completionTypeSize = 10,
29 | maxCharInCode = 100,
30 | )
31 |
32 | // When
33 | val result = insPickerOption.repoFileName()
34 |
35 | // Then
36 | assertEquals("https___github.com_example_repo.git_main_java", result)
37 | }
38 |
39 | @Test
40 | fun should_encode_file_name_with_special_characters() {
41 | // Given
42 | val insPickerOption = InsPickerOption(
43 | url = "https://github.com/example/repo*:?<>.|",
44 | completionTypeSize = 10,
45 | maxCharInCode = 100,
46 | )
47 |
48 | // When
49 | val result = insPickerOption.encodeFileName(insPickerOption.url)
50 |
51 | // Then
52 | assertEquals("https___github.com_example_repo_____._", result)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/project/ProjectLibraryTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.project;
2 |
3 | import cc.unitmesh.core.SupportedLang
4 | import cc.unitmesh.pick.ext.from
5 | import org.archguard.scanner.core.sca.CompositionDependency
6 | import org.junit.jupiter.api.Test
7 | import kotlin.test.assertEquals
8 |
9 | class ProjectLibraryTest {
10 |
11 | @Test
12 | fun should_prepareTestStack_when_dependenciesGiven() {
13 | // given
14 | val language = SupportedLang.JAVA
15 | val deps = listOf(
16 | CompositionDependency.from(
17 | "org.springframework.boot:spring-boot-starter-test",
18 | "org.springframework.boot",
19 | "spring-boot-starter-test"
20 | )
21 | )
22 |
23 | // when
24 | val testStack = ProjectLibrary.prepare(language, deps)
25 |
26 | // then
27 | assertEquals("Spring Boot, Spring Boot Web", testStack.coreFrameworks().joinToString(", "))
28 | assertEquals("Spring Boot Test, Spring Test", testStack.testFrameworks().joinToString(", "))
29 | }
30 | }
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/similar/JavaSimilarChunkerTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.similar;
2 |
3 | import org.junit.jupiter.api.Test
4 | import kotlin.test.assertEquals
5 |
6 | class JavaSimilarChunkerTest {
7 | private val similarChunks = JavaSimilarChunker(hashMapOf())
8 |
9 | @Test
10 | fun should_return_empty_string_for_empty_input() {
11 | // Given
12 | val paths = emptyList()
13 |
14 | // When
15 | val result = similarChunks.calculateCommonPath(paths)
16 |
17 | // Then
18 | assertEquals("", result)
19 | }
20 |
21 | @Test
22 | fun should_return_single_element_for_single_element_input() {
23 | // Given
24 | val paths = listOf("org.unitmesh.controller.BlogController")
25 |
26 | // When
27 | val result = similarChunks.calculateCommonPath(paths)
28 |
29 | // Then
30 | assertEquals("org.unitmesh.controller.BlogController", result)
31 | }
32 |
33 | @Test
34 | fun should_return_common_path_for_multiple_elements_with_common_path() {
35 | // Given
36 | val paths = listOf(
37 | "org.unitmesh.controller.BlogController",
38 | "org.unitmesh.service.BlogService",
39 | "org.unitmesh.model.BlogModel"
40 | )
41 |
42 | // When
43 | val result = similarChunks.calculateCommonPath(paths)
44 |
45 | // Then
46 | assertEquals("org.unitmesh", result)
47 | }
48 |
49 | @Test
50 | fun should_return_empty_string_for_multiple_elements_with_different_common_paths() {
51 | // Given
52 | val paths = listOf(
53 | "org.unitmesh.controller.BlogController",
54 | "org.example.service.BlogService",
55 | "cc.unitmesh.model.BlogModel"
56 | )
57 |
58 | // When
59 | val result = similarChunks.calculateCommonPath(paths)
60 |
61 | // Then
62 | assertEquals("", result)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/spec/NamingStyleTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.spec;
2 |
3 | import cc.unitmesh.pick.project.spec.checkSnakeCase
4 | import org.junit.jupiter.api.Test
5 | import kotlin.test.assertFalse
6 | import kotlin.test.assertTrue
7 |
8 | class NamingStyleTest {
9 |
10 | @Test
11 | fun should_return_true_when_string_follows_snake_case_naming_style() {
12 | // given
13 | val name = "snake_case"
14 |
15 | // when
16 | val result = checkSnakeCase(name)
17 |
18 | // then
19 | assertTrue(result)
20 | }
21 |
22 | @Test
23 | fun should_return_false_when_string_does_not_contain_underscore() {
24 | // given
25 | val name = "camelCase"
26 |
27 | // when
28 | val result = checkSnakeCase(name)
29 |
30 | // then
31 | assertFalse(result)
32 | }
33 |
34 | @Test
35 | fun should_return_false_when_underscore_is_the_first_character() {
36 | // given
37 | val name = "_snake_case"
38 |
39 | // when
40 | val result = checkSnakeCase(name)
41 |
42 | // then
43 | assertFalse(result)
44 | }
45 |
46 | @Test
47 | fun should_return_false_when_underscore_is_the_last_character() {
48 | // given
49 | val name = "snake_case_"
50 |
51 | // when
52 | val result = checkSnakeCase(name)
53 |
54 | // then
55 | assertFalse(result)
56 | }
57 |
58 | @Test
59 | fun should_return_false_when_underscore_is_consecutive() {
60 | // given
61 | val name = "snake__case"
62 |
63 | // when
64 | val result = checkSnakeCase(name)
65 |
66 | // then
67 | assertFalse(result)
68 | }
69 |
70 | @Test
71 | fun should_return_false_when_underscore_is_followed_by_a_number() {
72 | // given
73 | val name = "snake_123_case"
74 |
75 | // when
76 | val result = checkSnakeCase(name)
77 |
78 | // then
79 | assertFalse(result)
80 | }
81 |
82 | @Test
83 | fun should_return_false_when_underscore_is_followed_by_a_capital_letter() {
84 | // given
85 | val name = "snake_Case"
86 |
87 | // when
88 | val result = checkSnakeCase(name)
89 |
90 | // then
91 | assertFalse(result)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/worker/TestFrameworkIdentifierTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.worker;
2 |
3 | import cc.unitmesh.core.SupportedLang
4 | import cc.unitmesh.pick.ext.from
5 | import cc.unitmesh.pick.project.frameworks.TestFrameworkIdentifier
6 | import io.kotest.matchers.collections.shouldContain
7 | import io.kotest.matchers.shouldBe
8 | import org.archguard.scanner.core.sca.CompositionDependency
9 | import org.junit.jupiter.api.Test
10 |
11 | class TestFrameworkIdentifierTest {
12 |
13 | @Test
14 | fun shouldIdentifyJavaTestFrameworks() {
15 | // given
16 | val language = SupportedLang.JAVA
17 | val dependencies = listOf(
18 | CompositionDependency.from("junit:junit", "org.junit.jupiter", "junit-jupiter"),
19 | CompositionDependency.from("org.mockito:mockito-core", "org.mockito", "mockito-core"),
20 | CompositionDependency.from("com.intuit.karate:karate-junit5", "com.intuit.karate", "karate-junit5")
21 | )
22 | val testFrameworkIdentifier = TestFrameworkIdentifier(language, dependencies)
23 |
24 | // when
25 | val identifiedFrameworks = testFrameworkIdentifier.identify()
26 |
27 | identifiedFrameworks shouldBe listOf("JUnit5", "mockito", "Karate JUnit5")
28 | }
29 |
30 | @Test
31 | fun shouldIdentifyTypescriptTestFrameworks() {
32 | // given
33 | val language = SupportedLang.TYPESCRIPT
34 | val dependencies = listOf(
35 | CompositionDependency.from("jest", "some.group", "jest"),
36 | CompositionDependency.from("mocha", "another.group", "mocha"),
37 | CompositionDependency.from("jasmine", "third.group", "jasmine")
38 | )
39 | val testFrameworkIdentifier = TestFrameworkIdentifier(language, dependencies)
40 |
41 | // when
42 | val identifiedFrameworks = testFrameworkIdentifier.identify()
43 |
44 | // then
45 | val expectedFrameworks = listOf("jest", "mocha", "jasmine")
46 | assert(identifiedFrameworks == expectedFrameworks)
47 | }
48 |
49 | @Test
50 | fun shouldReturnMultipleForSpringTest() {
51 | // given
52 | val language = SupportedLang.JAVA
53 | val dependencies = listOf(
54 | CompositionDependency.from("org.springframework.boot:spring-boot-starter-test", "org.springframework.boot", "spring-boot-starter-test"),
55 | )
56 | val testFrameworkIdentifier = TestFrameworkIdentifier(language, dependencies)
57 |
58 | // when
59 | val identifiedFrameworks = testFrameworkIdentifier.identify()
60 |
61 | // then
62 | identifiedFrameworks shouldContain "Spring Test"
63 | identifiedFrameworks shouldContain "Spring Boot Test"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/unit-picker/src/test/kotlin/cc/unitmesh/pick/worker/job/InstructionFileJobTest.kt:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.pick.worker.job
2 |
3 | import io.kotest.matchers.shouldBe
4 | import org.junit.jupiter.api.Test;
5 |
6 | class InstructionFileJobTest {
7 |
8 | @Test
9 | fun shouldRemoveCommentsInJava() {
10 | val javaCode = """
11 | // This is a single-line comment
12 | /* This is a
13 | * multi-line comment
14 | */
15 | public class MyClass {
16 | // This is another comment
17 | public void myMethod() {
18 | // This is an inline comment
19 | System.out.println("Hello, World!");
20 | }
21 | }
22 | """.trimIndent()
23 |
24 | val codeWithoutComments = removeMultipleComments("Java", javaCode)
25 |
26 | codeWithoutComments shouldBe """// This is a single-line comment
27 |
28 | public class MyClass {
29 | // This is another comment
30 | public void myMethod() {
31 | // This is an inline comment
32 | System.out.println("Hello, World!");
33 | }
34 | }"""
35 | }
36 | }
--------------------------------------------------------------------------------
/unit-picker/src/test/resources/related/BlogPost.java:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.testng.entity;
2 |
3 | import javax.persistence.Entity;
4 | import javax.persistence.GeneratedValue;
5 | import javax.persistence.GenerationType;
6 | import javax.persistence.Id;
7 |
8 | @Entity
9 | public class BlogPost {
10 | @Id
11 | @GeneratedValue(strategy = GenerationType.AUTO)
12 | private Long id;
13 |
14 | private String title;
15 | private String content;
16 | private String author;
17 |
18 | public BlogPost(String title, String content, String author) {
19 | this.title = title;
20 | this.content = content;
21 | this.author = author;
22 | }
23 |
24 | public BlogPost() {
25 |
26 | }
27 |
28 | public void setId(Long id) {
29 | this.id = id;
30 | }
31 |
32 | public Long getId() {
33 | return this.id;
34 | }
35 |
36 | public String getTitle() {
37 | return title;
38 | }
39 |
40 | public void setTitle(String title) {
41 | this.title = title;
42 | }
43 |
44 | public String getContent() {
45 | return content;
46 | }
47 |
48 | public void setContent(String content) {
49 | this.content = content;
50 | }
51 |
52 | public String getAuthor() {
53 | return author;
54 | }
55 |
56 | public void setAuthor(String author) {
57 | this.author = author;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/unit-picker/src/test/resources/related/BlogRepository.java:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.testng.repository;
2 |
3 | import cc.unitmesh.testng.entity.BlogPost;
4 | import org.springframework.data.repository.CrudRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | @Repository
8 | public interface BlogRepository extends CrudRepository {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/unit-picker/src/test/resources/related/BlogService.java:
--------------------------------------------------------------------------------
1 | package cc.unitmesh.testng.service;
2 |
3 | import cc.unitmesh.testng.entity.BlogPost;
4 | import cc.unitmesh.testng.repository.BlogRepository;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.stereotype.Service;
7 |
8 | @Service
9 | public class BlogService {
10 | @Autowired
11 | BlogRepository blogRepository;
12 |
13 | public BlogPost createBlog(BlogPost blogDto) {
14 | return blogRepository.save(blogDto);
15 | }
16 |
17 | public BlogPost getBlogById(Long id) {
18 | return blogRepository.findById(id).orElse(null);
19 | }
20 |
21 | public BlogPost updateBlog(Long id, BlogPost blogDto) {
22 | return blogRepository.findById(id).map(blog -> {
23 | blog.setTitle(blogDto.getTitle());
24 | blog.setContent(blogDto.getContent());
25 | return blogRepository.save(blog);
26 | }).orElse(null);
27 | }
28 |
29 | public void deleteBlog(Long id) {
30 | blogRepository.deleteById(id);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------