├── .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 | 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 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 | --------------------------------------------------------------------------------