@ai-dev-tools/csharp-copilot-core 0.0.33 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/out/batch/generateCodeTests.js +22 -6
  2. package/out/batch/generateCodeTests.js.map +1 -1
  3. package/out/changedFilesProcessor/processChangedFiles.d.ts +9 -0
  4. package/out/changedFilesProcessor/processChangedFiles.js +156 -0
  5. package/out/changedFilesProcessor/processChangedFiles.js.map +1 -0
  6. package/out/codebk/prompts/buildAfGuidelines_20251226.liquid +15 -0
  7. package/out/codebk/prompts/general/generalUtGuidelines_20251226.liquid +16 -0
  8. package/out/codebk/prompts/xap/xapUtGuideline_20251225.liquid +37 -0
  9. package/out/codebk/prompts/xap/xapUtGuideline_20251226.liquid +49 -0
  10. package/out/command/index.js +32 -0
  11. package/out/command/index.js.map +1 -1
  12. package/out/command/utGenWrapper.js +85 -4
  13. package/out/command/utGenWrapper.js.map +1 -1
  14. package/out/gen/autoFix.d.ts +2 -2
  15. package/out/gen/autoFix.js +88 -31
  16. package/out/gen/autoFix.js.map +1 -1
  17. package/out/gen/csharpUtGen.d.ts +1 -1
  18. package/out/gen/csharpUtGen.js +16 -13
  19. package/out/gen/csharpUtGen.js.map +1 -1
  20. package/out/gen/ensureValidLLMResponse.d.ts +5 -1
  21. package/out/gen/ensureValidLLMResponse.js +15 -3
  22. package/out/gen/ensureValidLLMResponse.js.map +1 -1
  23. package/out/gen/postGen/postGenMoreUTProcess.d.ts +25 -0
  24. package/out/gen/postGen/postGenMoreUTProcess.js +502 -0
  25. package/out/gen/postGen/postGenMoreUTProcess.js.map +1 -0
  26. package/out/gen/postGen/postGenProcess.d.ts +1 -1
  27. package/out/gen/postGen/postGenProcess.js +7 -7
  28. package/out/gen/postGen/postGenProcess.js.map +1 -1
  29. package/out/gen/postGen/repairRequiredNameSpaces.d.ts +17 -0
  30. package/out/gen/postGen/repairRequiredNameSpaces.js +87 -20
  31. package/out/gen/postGen/repairRequiredNameSpaces.js.map +1 -1
  32. package/out/llm/preparePrompt.d.ts +2 -1
  33. package/out/llm/preparePrompt.js +38 -4
  34. package/out/llm/preparePrompt.js.map +1 -1
  35. package/out/llm/prompt/buildAfGuidelines.liquid +8 -8
  36. package/out/llm/prompt/general/generalUtGuidelines.liquid +3 -0
  37. package/out/llm/prompt/moreUT/generateMoreUTSourceCode.liquid +4 -0
  38. package/out/llm/prompt/moreUT/generateMoreUtAutoFix.liquid +45 -0
  39. package/out/llm/prompt/moreUT/generateMoreUtTemplate.liquid +115 -0
  40. package/out/llm/prompt/moreUT/generateMoreUtTestCode.liquid +5 -0
  41. package/out/llm/prompt/moreUT/utGenerationGuidelines.liquid +15 -0
  42. package/out/llm/prompt/xap/xapUtGuideline.liquid +66 -15
  43. package/out/types/changedFilesResult.d.ts +37 -0
  44. package/out/types/changedFilesResult.js +3 -0
  45. package/out/types/changedFilesResult.js.map +1 -0
  46. package/out/types/constants.d.ts +1 -0
  47. package/out/types/constants.js +2 -1
  48. package/out/types/constants.js.map +1 -1
  49. package/out/types/genResult.d.ts +1 -1
  50. package/out/types/genResult.js.map +1 -1
  51. package/out/utils/checkXapCode.d.ts +1 -1
  52. package/out/utils/checkXapCode.js +14 -2
  53. package/out/utils/checkXapCode.js.map +1 -1
  54. package/out/utils/fileUtils.d.ts +1 -0
  55. package/out/utils/fileUtils.js +13 -0
  56. package/out/utils/fileUtils.js.map +1 -1
  57. package/out/utils/getCodeStructurePath.d.ts +4 -0
  58. package/out/utils/getCodeStructurePath.js +30 -0
  59. package/out/utils/getCodeStructurePath.js.map +1 -1
  60. package/out/utils/getTestFile.d.ts +18 -8
  61. package/out/utils/getTestFile.js +735 -62
  62. package/out/utils/getTestFile.js.map +1 -1
  63. package/out/utils/removeFailedTestMethods.d.ts +8 -0
  64. package/out/utils/removeFailedTestMethods.js +363 -0
  65. package/out/utils/removeFailedTestMethods.js.map +1 -1
  66. package/out/utils/verifyGeneratedCode.d.ts +2 -0
  67. package/out/utils/verifyGeneratedCode.js +17 -0
  68. package/out/utils/verifyGeneratedCode.js.map +1 -0
  69. package/package.json +4 -3
@@ -96,6 +96,9 @@ async function generateTestByCodeFiles(paths, enableAutoFix, keepFailedTests, is
96
96
  console.log(`Test code already generated for: ${codeFilePath}`);
97
97
  continue;
98
98
  }
99
+ if (source === constants_1.CSVSPLUGIN && (!stdListener_1.readLineInterface || stdListener_1.readLineInterface.closed)) {
100
+ (0, stdListener_1.startStdListener)();
101
+ }
99
102
  let genResult = await (0, csharpUtGen_1.generateCsharpUtCode)(codeFilePath, "", enableAutoFix, keepFailedTests, source, true);
100
103
  if (!genResult?.success && genResult?.buildResult) {
101
104
  // comment build failed code to unblock following tests
@@ -145,12 +148,25 @@ function getCachedGenResults() {
145
148
  }
146
149
  function commentOutCsFile(testFilePath) {
147
150
  const content = fs.readFileSync(testFilePath, 'utf-8');
148
- const commented = content
149
- .split(constants_1.LINESEPARATOR)
150
- .map(line => `// ${line}`)
151
- .join('\n');
152
- fs.writeFileSync(testFilePath, commented, 'utf-8');
153
- console.log(`Commented out test file: ${testFilePath}`);
151
+ const lines = content.split(constants_1.LINESEPARATOR);
152
+ // Find the first line that doesn't start with // and is not empty
153
+ let firstNonCommentIndex = lines.findIndex(line => {
154
+ const trimmed = line.trimStart();
155
+ return trimmed.length > 0 && !trimmed.startsWith('//');
156
+ });
157
+ // If all lines are comments or empty, return as is
158
+ if (firstNonCommentIndex === -1) {
159
+ return;
160
+ }
161
+ // Insert #if false before the first non-comment line and #endif at the end
162
+ const modifiedLines = [
163
+ ...lines.slice(0, firstNonCommentIndex), // Keep header lines unchanged
164
+ '#if false', // Add #if false directive
165
+ ...lines.slice(firstNonCommentIndex), // All remaining lines
166
+ '#endif' // Add #endif directive
167
+ ];
168
+ fs.writeFileSync(testFilePath, modifiedLines.join('\n'), 'utf-8');
169
+ console.log(`Commented out test file using #if false: ${testFilePath}`);
154
170
  }
155
171
  function isDirectory(path) {
156
172
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"generateCodeTests.js","sourceRoot":"","sources":["../../src/batch/generateCodeTests.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\r\nimport * as path from 'path';\r\n\r\nimport { BenchmarkRow } from '../types/benchmark';\r\nimport { CSHARPUTGEN, CSVSPLUGIN, LINESEPARATOR } from '../types/constants';\r\nimport { generateCodeCoverageReport } from '../codeCoverage/generateCodeCoverageReport';\r\nimport { generateCsharpUtCode } from \"../gen/csharpUtGen\";\r\nimport { getAllCsFiles } from './getFolderCodeFile';\r\nimport { getSourceFilesFromCsproj } from './getProjectCodeFile';\r\nimport { getSourceFilesFromSolution } from './getSolutionCodeFile';\r\nimport { readLineInterface, startStdListener } from \"../vsPlugin/stdListener\";\r\n\r\nexport const CACHEFile = path.join(__dirname, \"../genResultsCache.json\");\r\nexport async function generateBatchTests(\r\n path: any,\r\n enableAutoFix: boolean,\r\n keepFailedTests: boolean,\r\n isBenchmark: boolean = false,\r\n source: string = CSHARPUTGEN): Promise<BenchmarkRow[]> {\r\n let benchmarkRows;\r\n let projectPath;\r\n let solutionPath;\r\n if (Array.isArray(path)) {\r\n benchmarkRows = await generateTestByCodeFiles(path, enableAutoFix, keepFailedTests, isBenchmark, source);\r\n } else if (isFileWithSuffix(path, '.csproj')) {\r\n projectPath = path;\r\n const codeFiles = await getSourceFilesFromCsproj(path);\r\n benchmarkRows = await generateTestByCodeFiles(codeFiles, enableAutoFix, keepFailedTests, isBenchmark, source, projectPath);\r\n } else if (isFileWithSuffix(path, '.sln')) {\r\n solutionPath = path;\r\n const projectToFiles = await getSourceFilesFromSolution(path);\r\n Object.entries(projectToFiles).forEach(async ([project, files]) => {\r\n console.log(`Project: ${project}`);\r\n const projectBenchmarkRows = await generateTestByCodeFiles(files, enableAutoFix, keepFailedTests, isBenchmark, source, project, solutionPath);\r\n benchmarkRows.push(...projectBenchmarkRows);\r\n });\r\n } else {\r\n if (!isDirectory(path)) {\r\n console.error(`Invalid path: ${path}. It should be a directory, .csproj, or .sln file.`);\r\n return [];\r\n }\r\n const codeFiles = await getAllCsFiles(path);\r\n benchmarkRows = await generateTestByCodeFiles(codeFiles, enableAutoFix, keepFailedTests, isBenchmark, source);\r\n }\r\n\r\n return benchmarkRows;\r\n}\r\n\r\nexport async function generateTestByCodeFiles(\r\n paths: string[],\r\n enableAutoFix: boolean,\r\n keepFailedTests: boolean,\r\n isBenchmark: boolean = false,\r\n source: string = CSHARPUTGEN,\r\n projectPath?: string,\r\n solutionPath?: string,\r\n): Promise<BenchmarkRow[]> {\r\n try {\r\n const startTime = Date.now();\r\n\r\n const testProjectPaths = [];\r\n const genResults = getCachedGenResults();\r\n\r\n if (source === CSVSPLUGIN) {\r\n startStdListener();\r\n }\r\n\r\n for (const codeFilePath of paths) {\r\n if (!fs.existsSync(codeFilePath)) {\r\n console.error(`Path does not exist: ${codeFilePath}`);\r\n continue;\r\n }\r\n\r\n if (isBenchmark && genResults.some(result => result.codeFilePath === codeFilePath)) {\r\n console.log(`Test code already generated for: ${codeFilePath}`);\r\n continue;\r\n }\r\n\r\n let genResult = await generateCsharpUtCode(codeFilePath, \"\", enableAutoFix, keepFailedTests, source, true);\r\n\r\n if (!genResult?.success && genResult?.buildResult) {\r\n // comment build failed code to unblock following tests\r\n commentOutCsFile(genResult.testFilePath);\r\n }\r\n genResult.testFilePath && console.log(`Updated gen test to file: ${genResult.testFilePath}`);\r\n\r\n genResult && genResults.push({\r\n solutionPath,\r\n projectPath,\r\n codeFilePath,\r\n testFilePath: genResult.testFilePath, \r\n success: genResult.success,\r\n buildResult: genResult.buildResult,\r\n testResult: genResult.testResult,\r\n });\r\n\r\n if (genResult.testProjectPath && !testProjectPaths.includes(genResult.testProjectPath)) {\r\n testProjectPaths.push(genResult.testProjectPath);\r\n }\r\n\r\n fs.writeFileSync(CACHEFile, JSON.stringify(genResults), 'utf-8');\r\n console.log(`Current progress: ${genResults?.length / paths.length} (${genResults?.length}/${paths.length})`);\r\n }\r\n\r\n await generateCodeCoverageReport(testProjectPaths, paths);\r\n\r\n console.log(`Batch Generate UT time: ${(Date.now() - startTime) / 1000} seconds`);\r\n\r\n return genResults;\r\n } catch (error) {\r\n console.error(\"Error in generateTestByCodeFiles:\", error);\r\n return [];\r\n } finally {\r\n // Close the std listener if it was started in this function\r\n if (source === CSVSPLUGIN && readLineInterface) {\r\n readLineInterface.close();\r\n }\r\n }\r\n}\r\n\r\nfunction isFileWithSuffix(filePath: string, suffix: string): boolean {\r\n return filePath.toLowerCase().endsWith(suffix.toLowerCase());\r\n}\r\n\r\nfunction getCachedGenResults() {\r\n if (!fs.existsSync(CACHEFile)) {\r\n return [];\r\n }\r\n const cachedData = fs.readFileSync(CACHEFile, 'utf-8');\r\n const genResults = JSON.parse(cachedData);\r\n return genResults;\r\n}\r\n\r\nexport function commentOutCsFile(testFilePath: string) {\r\n const content = fs.readFileSync(testFilePath, 'utf-8');\r\n const commented = content\r\n .split(LINESEPARATOR)\r\n .map(line => `// ${line}`)\r\n .join('\\n');\r\n\r\n fs.writeFileSync(testFilePath, commented, 'utf-8');\r\n console.log(`Commented out test file: ${testFilePath}`);\r\n}\r\n\r\nexport function isDirectory(path: string): boolean {\r\n try {\r\n return fs.statSync(path).isDirectory();\r\n } catch (err) {\r\n return false;\r\n }\r\n}"]}
1
+ {"version":3,"file":"generateCodeTests.js","sourceRoot":"","sources":["../../src/batch/generateCodeTests.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAaA,gDAiCC;AAED,0DA0EC;AAeD,4CAyBC;AAED,kCAMC;AA1KD,uCAAyB;AACzB,2CAA6B;AAG7B,kDAA4E;AAC5E,2FAAwF;AACxF,oDAA0D;AAC1D,2DAAoD;AACpD,6DAAgE;AAChE,+DAAmE;AACnE,yDAA8E;AAEjE,QAAA,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,yBAAyB,CAAC,CAAC;AAClE,KAAK,UAAU,kBAAkB,CACpC,IAAS,EACT,aAAsB,EACtB,eAAwB,EACxB,cAAuB,KAAK,EAC5B,SAAiB,uBAAW;IAC5B,IAAI,aAAa,CAAC;IAClB,IAAI,WAAW,CAAC;IAChB,IAAI,YAAY,CAAC;IACjB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,aAAa,GAAG,MAAM,uBAAuB,CAAC,IAAI,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAC7G,CAAC;SAAM,IAAI,gBAAgB,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;QAC3C,WAAW,GAAG,IAAI,CAAC;QACnB,MAAM,SAAS,GAAG,MAAM,IAAA,6CAAwB,EAAC,IAAI,CAAC,CAAC;QACvD,aAAa,GAAG,MAAM,uBAAuB,CAAC,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC/H,CAAC;SAAM,IAAI,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;QACxC,YAAY,GAAG,IAAI,CAAC;QACpB,MAAM,cAAc,GAAG,MAAM,IAAA,gDAA0B,EAAC,IAAI,CAAC,CAAC;QAC9D,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE;YAC9D,OAAO,CAAC,GAAG,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC;YACnC,MAAM,oBAAoB,GAAG,MAAM,uBAAuB,CAAC,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;YAC9I,aAAa,CAAC,IAAI,CAAC,GAAG,oBAAoB,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACP,CAAC;SAAM,CAAC;QACJ,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,KAAK,CAAC,iBAAiB,IAAI,oDAAoD,CAAC,CAAC;YACzF,OAAO,EAAE,CAAC;QACd,CAAC;QACD,MAAM,SAAS,GAAG,MAAM,IAAA,iCAAa,EAAC,IAAI,CAAC,CAAC;QAC5C,aAAa,GAAG,MAAM,uBAAuB,CAAC,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAClH,CAAC;IAED,OAAO,aAAa,CAAC;AACzB,CAAC;AAEM,KAAK,UAAU,uBAAuB,CACzC,KAAe,EACf,aAAsB,EACtB,eAAwB,EACxB,cAAuB,KAAK,EAC5B,SAAiB,uBAAW,EAC5B,WAAoB,EACpB,YAAqB;IAErB,IAAI,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,MAAM,gBAAgB,GAAG,EAAE,CAAC;QAC5B,MAAM,UAAU,GAAG,mBAAmB,EAAE,CAAC;QAEzC,IAAI,MAAM,KAAK,sBAAU,EAAE,CAAC;YACxB,IAAA,8BAAgB,GAAE,CAAC;QACvB,CAAC;QAED,KAAK,MAAM,YAAY,IAAI,KAAK,EAAE,CAAC;YAC/B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC/B,OAAO,CAAC,KAAK,CAAC,wBAAwB,YAAY,EAAE,CAAC,CAAC;gBACtD,SAAS;YACb,CAAC;YAED,IAAI,WAAW,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,YAAY,KAAK,YAAY,CAAC,EAAE,CAAC;gBACjF,OAAO,CAAC,GAAG,CAAC,oCAAoC,YAAY,EAAE,CAAC,CAAC;gBAChE,SAAS;YACb,CAAC;YAED,IAAI,MAAM,KAAK,sBAAU,IAAI,CAAC,CAAC,+BAAiB,IAAI,+BAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5E,IAAA,8BAAgB,GAAE,CAAC;YACvB,CAAC;YAED,IAAI,SAAS,GAAG,MAAM,IAAA,kCAAoB,EAAC,YAAY,EAAE,EAAE,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;YAE3G,IAAI,CAAC,SAAS,EAAE,OAAO,IAAI,SAAS,EAAE,WAAW,EAAE,CAAC;gBAChD,uDAAuD;gBACvD,gBAAgB,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;YAC7C,CAAC;YACD,SAAS,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,6BAA6B,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;YAE7F,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC;gBACzB,YAAY;gBACZ,WAAW;gBACX,YAAY;gBACZ,YAAY,EAAE,SAAS,CAAC,YAAY;gBACpC,OAAO,EAAE,SAAS,CAAC,OAAO;gBAC1B,WAAW,EAAE,SAAS,CAAC,WAAW;gBAClC,UAAU,EAAE,SAAS,CAAC,UAAU;aACnC,CAAC,CAAC;YAEH,IAAI,SAAS,CAAC,eAAe,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,CAAC;gBACrF,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;YACrD,CAAC;YAED,EAAE,CAAC,aAAa,CAAC,iBAAS,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;YACjE,OAAO,CAAC,GAAG,CAAC,qBAAqB,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,UAAU,EAAE,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QAClH,CAAC;QAED,MAAM,IAAA,uDAA0B,EAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QAE1D,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,UAAU,CAAC,CAAC;QAElF,OAAO,UAAU,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;QAC1D,OAAO,EAAE,CAAC;IACd,CAAC;YAAS,CAAC;QACP,4DAA4D;QAC5D,IAAI,MAAM,KAAK,sBAAU,IAAI,+BAAiB,EAAE,CAAC;YAC7C,+BAAiB,CAAC,KAAK,EAAE,CAAC;QAC9B,CAAC;IACL,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAgB,EAAE,MAAc;IACtD,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,mBAAmB;IACxB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,iBAAS,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACd,CAAC;IACD,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,iBAAS,EAAE,OAAO,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1C,OAAO,UAAU,CAAC;AACtB,CAAC;AAED,SAAgB,gBAAgB,CAAC,YAAoB;IACjD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,yBAAa,CAAC,CAAC;IAE3C,kEAAkE;IAClE,IAAI,oBAAoB,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QACjC,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,mDAAmD;IACnD,IAAI,oBAAoB,KAAK,CAAC,CAAC,EAAE,CAAC;QAC9B,OAAO;IACX,CAAC;IAED,2EAA2E;IAC3E,MAAM,aAAa,GAAG;QAClB,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,EAAG,8BAA8B;QACxE,WAAW,EAA+B,0BAA0B;QACpE,GAAG,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAM,sBAAsB;QAChE,QAAQ,CAAkC,uBAAuB;KACpE,CAAC;IAEF,EAAE,CAAC,aAAa,CAAC,YAAY,EAAE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IAClE,OAAO,CAAC,GAAG,CAAC,4CAA4C,YAAY,EAAE,CAAC,CAAC;AAC5E,CAAC;AAED,SAAgB,WAAW,CAAC,IAAY;IACpC,IAAI,CAAC;QACD,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC","sourcesContent":["import * as fs from 'fs';\r\nimport * as path from 'path';\r\n\r\nimport { BenchmarkRow } from '../types/benchmark';\r\nimport { CSHARPUTGEN, CSVSPLUGIN, LINESEPARATOR } from '../types/constants';\r\nimport { generateCodeCoverageReport } from '../codeCoverage/generateCodeCoverageReport';\r\nimport { generateCsharpUtCode } from \"../gen/csharpUtGen\";\r\nimport { getAllCsFiles } from './getFolderCodeFile';\r\nimport { getSourceFilesFromCsproj } from './getProjectCodeFile';\r\nimport { getSourceFilesFromSolution } from './getSolutionCodeFile';\r\nimport { readLineInterface, startStdListener } from \"../vsPlugin/stdListener\";\r\n\r\nexport const CACHEFile = path.join(__dirname, \"../genResultsCache.json\");\r\nexport async function generateBatchTests(\r\n path: any,\r\n enableAutoFix: boolean,\r\n keepFailedTests: boolean,\r\n isBenchmark: boolean = false,\r\n source: string = CSHARPUTGEN): Promise<BenchmarkRow[]> {\r\n let benchmarkRows;\r\n let projectPath;\r\n let solutionPath;\r\n if (Array.isArray(path)) {\r\n benchmarkRows = await generateTestByCodeFiles(path, enableAutoFix, keepFailedTests, isBenchmark, source);\r\n } else if (isFileWithSuffix(path, '.csproj')) {\r\n projectPath = path;\r\n const codeFiles = await getSourceFilesFromCsproj(path);\r\n benchmarkRows = await generateTestByCodeFiles(codeFiles, enableAutoFix, keepFailedTests, isBenchmark, source, projectPath);\r\n } else if (isFileWithSuffix(path, '.sln')) {\r\n solutionPath = path;\r\n const projectToFiles = await getSourceFilesFromSolution(path);\r\n Object.entries(projectToFiles).forEach(async ([project, files]) => {\r\n console.log(`Project: ${project}`);\r\n const projectBenchmarkRows = await generateTestByCodeFiles(files, enableAutoFix, keepFailedTests, isBenchmark, source, project, solutionPath);\r\n benchmarkRows.push(...projectBenchmarkRows);\r\n });\r\n } else {\r\n if (!isDirectory(path)) {\r\n console.error(`Invalid path: ${path}. It should be a directory, .csproj, or .sln file.`);\r\n return [];\r\n }\r\n const codeFiles = await getAllCsFiles(path);\r\n benchmarkRows = await generateTestByCodeFiles(codeFiles, enableAutoFix, keepFailedTests, isBenchmark, source);\r\n }\r\n\r\n return benchmarkRows;\r\n}\r\n\r\nexport async function generateTestByCodeFiles(\r\n paths: string[],\r\n enableAutoFix: boolean,\r\n keepFailedTests: boolean,\r\n isBenchmark: boolean = false,\r\n source: string = CSHARPUTGEN,\r\n projectPath?: string,\r\n solutionPath?: string,\r\n): Promise<BenchmarkRow[]> {\r\n try {\r\n const startTime = Date.now();\r\n\r\n const testProjectPaths = [];\r\n const genResults = getCachedGenResults();\r\n\r\n if (source === CSVSPLUGIN) {\r\n startStdListener();\r\n }\r\n\r\n for (const codeFilePath of paths) {\r\n if (!fs.existsSync(codeFilePath)) {\r\n console.error(`Path does not exist: ${codeFilePath}`);\r\n continue;\r\n }\r\n\r\n if (isBenchmark && genResults.some(result => result.codeFilePath === codeFilePath)) {\r\n console.log(`Test code already generated for: ${codeFilePath}`);\r\n continue;\r\n }\r\n\r\n if (source === CSVSPLUGIN && (!readLineInterface || readLineInterface.closed)) {\r\n startStdListener();\r\n }\r\n\r\n let genResult = await generateCsharpUtCode(codeFilePath, \"\", enableAutoFix, keepFailedTests, source, true);\r\n\r\n if (!genResult?.success && genResult?.buildResult) {\r\n // comment build failed code to unblock following tests\r\n commentOutCsFile(genResult.testFilePath);\r\n }\r\n genResult.testFilePath && console.log(`Updated gen test to file: ${genResult.testFilePath}`);\r\n\r\n genResult && genResults.push({\r\n solutionPath,\r\n projectPath,\r\n codeFilePath,\r\n testFilePath: genResult.testFilePath, \r\n success: genResult.success,\r\n buildResult: genResult.buildResult,\r\n testResult: genResult.testResult,\r\n });\r\n\r\n if (genResult.testProjectPath && !testProjectPaths.includes(genResult.testProjectPath)) {\r\n testProjectPaths.push(genResult.testProjectPath);\r\n }\r\n\r\n fs.writeFileSync(CACHEFile, JSON.stringify(genResults), 'utf-8');\r\n console.log(`Current progress: ${genResults?.length / paths.length} (${genResults?.length}/${paths.length})`);\r\n }\r\n\r\n await generateCodeCoverageReport(testProjectPaths, paths);\r\n\r\n console.log(`Batch Generate UT time: ${(Date.now() - startTime) / 1000} seconds`);\r\n\r\n return genResults;\r\n } catch (error) {\r\n console.error(\"Error in generateTestByCodeFiles:\", error);\r\n return [];\r\n } finally {\r\n // Close the std listener if it was started in this function\r\n if (source === CSVSPLUGIN && readLineInterface) {\r\n readLineInterface.close();\r\n }\r\n }\r\n}\r\n\r\nfunction isFileWithSuffix(filePath: string, suffix: string): boolean {\r\n return filePath.toLowerCase().endsWith(suffix.toLowerCase());\r\n}\r\n\r\nfunction getCachedGenResults() {\r\n if (!fs.existsSync(CACHEFile)) {\r\n return [];\r\n }\r\n const cachedData = fs.readFileSync(CACHEFile, 'utf-8');\r\n const genResults = JSON.parse(cachedData);\r\n return genResults;\r\n}\r\n\r\nexport function commentOutCsFile(testFilePath: string) {\r\n const content = fs.readFileSync(testFilePath, 'utf-8');\r\n const lines = content.split(LINESEPARATOR);\r\n \r\n // Find the first line that doesn't start with // and is not empty\r\n let firstNonCommentIndex = lines.findIndex(line => {\r\n const trimmed = line.trimStart();\r\n return trimmed.length > 0 && !trimmed.startsWith('//');\r\n });\r\n \r\n // If all lines are comments or empty, return as is\r\n if (firstNonCommentIndex === -1) {\r\n return;\r\n }\r\n \r\n // Insert #if false before the first non-comment line and #endif at the end\r\n const modifiedLines = [\r\n ...lines.slice(0, firstNonCommentIndex), // Keep header lines unchanged\r\n '#if false', // Add #if false directive\r\n ...lines.slice(firstNonCommentIndex), // All remaining lines\r\n '#endif' // Add #endif directive\r\n ];\r\n\r\n fs.writeFileSync(testFilePath, modifiedLines.join('\\n'), 'utf-8');\r\n console.log(`Commented out test file using #if false: ${testFilePath}`);\r\n}\r\n\r\nexport function isDirectory(path: string): boolean {\r\n try {\r\n return fs.statSync(path).isDirectory();\r\n } catch (err) {\r\n return false;\r\n }\r\n}"]}
@@ -0,0 +1,9 @@
1
+ import { CategorizedFiles, ProcessedSourceFile, ChangedFileResult, ProcessChangedFilesResult } from '../types/changedFilesResult';
2
+ export type { CategorizedFiles, ProcessedSourceFile, ChangedFileResult, ProcessChangedFilesResult };
3
+ /**
4
+ * Process changed files from a config file path.
5
+ * @param changedFilesConfigPath - Path to the JSON config file containing changed files array
6
+ * @param options - Additional command line options
7
+ * @returns Result with most common solution, its runsettings, and changed files with test paths
8
+ */
9
+ export declare function processChangedFiles(changedFilesConfigPath: string, options: Record<string, unknown>): Promise<ProcessChangedFilesResult>;
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.processChangedFiles = processChangedFiles;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const getTestFile_1 = require("../utils/getTestFile");
40
+ const getCodeStructurePath_1 = require("../utils/getCodeStructurePath");
41
+ const getCodeCoverageSetting_1 = require("../codeCoverage/getCodeCoverageSetting");
42
+ /**
43
+ * Process a single source file to get all related paths.
44
+ * @param sourceFile - The changed file entry (filepath should be absolute path from CI)
45
+ */
46
+ function processSourceFile(sourceFile) {
47
+ const fullSourcePath = path.normalize(sourceFile.filepath);
48
+ // Find solution file
49
+ const solutionFilePath = (0, getCodeStructurePath_1.findSolutionFile)(fullSourcePath);
50
+ // Get test file path
51
+ let testFilePath = null;
52
+ try {
53
+ const result = (0, getTestFile_1.getOrCreatTestFile)(fullSourcePath, undefined);
54
+ testFilePath = result.testFilePath;
55
+ }
56
+ catch (error) {
57
+ console.warn(`Failed to get test file for ${sourceFile.filepath}: ${error}`);
58
+ }
59
+ // Find runsettings file from test project path
60
+ const runsettingsFilePath = testFilePath ? (0, getCodeCoverageSetting_1.getCodeCoverageSetting)(testFilePath) : null;
61
+ return {
62
+ sourceFile,
63
+ testFilePath,
64
+ runsettingsFilePath,
65
+ solutionFilePath
66
+ };
67
+ }
68
+ /**
69
+ * Read and parse the changed files config, filter CS files, and categorize them.
70
+ */
71
+ function readAndCategorizeChangedFiles(configPath) {
72
+ const configContent = fs.readFileSync(configPath, 'utf-8');
73
+ const parsed = JSON.parse(configContent);
74
+ if (!Array.isArray(parsed)) {
75
+ throw new Error(`Changed files config is not an array: ${configPath}`);
76
+ }
77
+ const allFiles = parsed.filter((entry) => entry != null &&
78
+ typeof entry === 'object' &&
79
+ typeof entry.filepath === 'string');
80
+ // Filter only .cs files
81
+ const csFiles = allFiles.filter(entry => entry.filepath.toLowerCase().endsWith('.cs'));
82
+ // Categorize into test files and source files
83
+ const testFiles = [];
84
+ const sourceFiles = [];
85
+ for (const file of csFiles) {
86
+ if ((0, getTestFile_1.isTestFile)(file.filepath)) {
87
+ testFiles.push(file);
88
+ }
89
+ else {
90
+ sourceFiles.push(file);
91
+ }
92
+ }
93
+ console.log(`Found ${csFiles.length} CS files: ${testFiles.length} test files, ${sourceFiles.length} source files`);
94
+ return { testFiles, sourceFiles };
95
+ }
96
+ /**
97
+ * Process changed files from a config file path.
98
+ * @param changedFilesConfigPath - Path to the JSON config file containing changed files array
99
+ * @param options - Additional command line options
100
+ * @returns Result with most common solution, its runsettings, and changed files with test paths
101
+ */
102
+ async function processChangedFiles(changedFilesConfigPath, options) {
103
+ console.log(`Processing changed files from config: ${changedFilesConfigPath}`);
104
+ // Read and categorize files
105
+ const { testFiles, sourceFiles } = readAndCategorizeChangedFiles(changedFilesConfigPath);
106
+ console.log('Test files:', testFiles.map(f => f.filepath));
107
+ console.log('Source files:', sourceFiles.map(f => f.filepath));
108
+ // Process each source file
109
+ const processedFiles = [];
110
+ for (const sourceFile of sourceFiles) {
111
+ const processed = processSourceFile(sourceFile);
112
+ processedFiles.push(processed);
113
+ console.log(`Processed: ${sourceFile.filepath}`);
114
+ console.log(` Solution: ${processed.solutionFilePath || 'not found'}`);
115
+ console.log(` Runsettings: ${processed.runsettingsFilePath || 'not found'}`);
116
+ console.log(` Test file: ${processed.testFilePath || 'not found'}`);
117
+ }
118
+ // solutionPath -> {file count, runsettings}
119
+ const solutionCounts = new Map();
120
+ for (const file of processedFiles) {
121
+ if (file.solutionFilePath) {
122
+ const existing = solutionCounts.get(file.solutionFilePath);
123
+ if (existing) {
124
+ existing.count++;
125
+ }
126
+ else {
127
+ solutionCounts.set(file.solutionFilePath, {
128
+ count: 1,
129
+ runsettingsFilePath: file.runsettingsFilePath
130
+ });
131
+ }
132
+ }
133
+ }
134
+ // Get the solution with the highest count
135
+ let mostCommonSolution = null;
136
+ let mostCommonRunsettings = null;
137
+ if (solutionCounts.size > 0) {
138
+ const [solution, data] = [...solutionCounts.entries()]
139
+ .reduce((a, b) => a[1].count > b[1].count ? a : b);
140
+ mostCommonSolution = solution;
141
+ mostCommonRunsettings = data.runsettingsFilePath;
142
+ }
143
+ // Build the changed file array
144
+ const changedFiles = processedFiles.map(file => ({
145
+ filepath: file.sourceFile.filepath,
146
+ testfilepath: file.testFilePath
147
+ }));
148
+ const result = {
149
+ solutionFilePath: mostCommonSolution,
150
+ runsettingsFilePath: mostCommonRunsettings,
151
+ changedFiles
152
+ };
153
+ console.log(`Result: Solution=${mostCommonSolution}, Runsettings=${mostCommonRunsettings}, ChangedFiles=${changedFiles.length}`);
154
+ return result;
155
+ }
156
+ //# sourceMappingURL=processChangedFiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processChangedFiles.js","sourceRoot":"","sources":["../../src/changedFilesProcessor/processChangedFiles.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport { getOrCreatTestFile, isTestFile } from '../utils/getTestFile';\r\nimport { getCodeRepoRoot, findSolutionFile } from '../utils/getCodeStructurePath';\r\nimport { getCodeCoverageSetting } from '../codeCoverage/getCodeCoverageSetting';\r\nimport { ChangedFileEntry, CategorizedFiles, ProcessedSourceFile, ChangedFileResult, ProcessChangedFilesResult } from '../types/changedFilesResult';\r\n\r\nexport type { CategorizedFiles, ProcessedSourceFile, ChangedFileResult, ProcessChangedFilesResult };\r\n\r\n/**\r\n * Process a single source file to get all related paths.\r\n * @param sourceFile - The changed file entry (filepath should be absolute path from CI)\r\n */\r\nfunction processSourceFile(sourceFile: ChangedFileEntry): ProcessedSourceFile {\r\n const fullSourcePath = path.normalize(sourceFile.filepath);\r\n\r\n // Find solution file\r\n const solutionFilePath = findSolutionFile(fullSourcePath);\r\n\r\n // Get test file path\r\n let testFilePath: string | null = null;\r\n try {\r\n const result = getOrCreatTestFile(fullSourcePath, undefined);\r\n testFilePath = result.testFilePath;\r\n } catch (error) {\r\n console.warn(`Failed to get test file for ${sourceFile.filepath}: ${error}`);\r\n }\r\n\r\n // Find runsettings file from test project path\r\n const runsettingsFilePath = testFilePath ? getCodeCoverageSetting(testFilePath) : null;\r\n\r\n return {\r\n sourceFile,\r\n testFilePath,\r\n runsettingsFilePath,\r\n solutionFilePath\r\n };\r\n}\r\n\r\n/**\r\n * Read and parse the changed files config, filter CS files, and categorize them.\r\n */\r\nfunction readAndCategorizeChangedFiles(configPath: string): CategorizedFiles {\r\n const configContent = fs.readFileSync(configPath, 'utf-8');\r\n const parsed: unknown = JSON.parse(configContent);\r\n\r\n if (!Array.isArray(parsed)) {\r\n throw new Error(`Changed files config is not an array: ${configPath}`);\r\n }\r\n\r\n const allFiles: ChangedFileEntry[] = parsed.filter(\r\n (entry): entry is ChangedFileEntry =>\r\n entry != null &&\r\n typeof entry === 'object' &&\r\n typeof (entry as any).filepath === 'string'\r\n );\r\n\r\n // Filter only .cs files\r\n const csFiles = allFiles.filter(entry => entry.filepath.toLowerCase().endsWith('.cs'));\r\n\r\n // Categorize into test files and source files\r\n const testFiles: ChangedFileEntry[] = [];\r\n const sourceFiles: ChangedFileEntry[] = [];\r\n\r\n for (const file of csFiles) {\r\n if (isTestFile(file.filepath)) {\r\n testFiles.push(file);\r\n } else {\r\n sourceFiles.push(file);\r\n }\r\n }\r\n\r\n console.log(`Found ${csFiles.length} CS files: ${testFiles.length} test files, ${sourceFiles.length} source files`);\r\n\r\n return { testFiles, sourceFiles };\r\n}\r\n\r\n/**\r\n * Process changed files from a config file path.\r\n * @param changedFilesConfigPath - Path to the JSON config file containing changed files array\r\n * @param options - Additional command line options\r\n * @returns Result with most common solution, its runsettings, and changed files with test paths\r\n */\r\nexport async function processChangedFiles(changedFilesConfigPath: string, options: Record<string, unknown>): Promise<ProcessChangedFilesResult> {\r\n console.log(`Processing changed files from config: ${changedFilesConfigPath}`);\r\n\r\n // Read and categorize files\r\n const { testFiles, sourceFiles } = readAndCategorizeChangedFiles(changedFilesConfigPath);\r\n\r\n console.log('Test files:', testFiles.map(f => f.filepath));\r\n console.log('Source files:', sourceFiles.map(f => f.filepath));\r\n\r\n // Process each source file\r\n const processedFiles: ProcessedSourceFile[] = [];\r\n for (const sourceFile of sourceFiles) {\r\n const processed = processSourceFile(sourceFile);\r\n processedFiles.push(processed);\r\n console.log(`Processed: ${sourceFile.filepath}`);\r\n console.log(` Solution: ${processed.solutionFilePath || 'not found'}`);\r\n console.log(` Runsettings: ${processed.runsettingsFilePath || 'not found'}`);\r\n console.log(` Test file: ${processed.testFilePath || 'not found'}`);\r\n }\r\n\r\n // solutionPath -> {file count, runsettings}\r\n const solutionCounts = new Map<string, { count: number; runsettingsFilePath: string | null }>();\r\n for (const file of processedFiles) {\r\n if (file.solutionFilePath) {\r\n const existing = solutionCounts.get(file.solutionFilePath);\r\n if (existing) {\r\n existing.count++;\r\n } else {\r\n solutionCounts.set(file.solutionFilePath, { \r\n count: 1, \r\n runsettingsFilePath: file.runsettingsFilePath \r\n });\r\n }\r\n }\r\n }\r\n\r\n // Get the solution with the highest count\r\n let mostCommonSolution: string | null = null;\r\n let mostCommonRunsettings: string | null = null;\r\n if (solutionCounts.size > 0) {\r\n const [solution, data] = [...solutionCounts.entries()]\r\n .reduce((a, b) => a[1].count > b[1].count ? a : b);\r\n mostCommonSolution = solution;\r\n mostCommonRunsettings = data.runsettingsFilePath;\r\n }\r\n\r\n // Build the changed file array\r\n const changedFiles: ChangedFileResult[] = processedFiles.map(file => ({\r\n filepath: file.sourceFile.filepath,\r\n testfilepath: file.testFilePath\r\n }));\r\n\r\n const result: ProcessChangedFilesResult = {\r\n solutionFilePath: mostCommonSolution,\r\n runsettingsFilePath: mostCommonRunsettings,\r\n changedFiles\r\n };\r\n\r\n console.log(`Result: Solution=${mostCommonSolution}, Runsettings=${mostCommonRunsettings}, ChangedFiles=${changedFiles.length}`);\r\n\r\n return result;\r\n}\r\n"]}
@@ -0,0 +1,15 @@
1
+ # Build Failure Fix Guideline:
2
+ - Case 1: The type or namespace name '{Class/Type}' does not exist in the namespace '{namespace}' (are you missing an assembly reference?)
3
+ Solution: Check the source code, code dependencies. Add the required `using` statements or assembly references.
4
+ - Case 2: No overload for method 'Execute' takes {number} arguments
5
+ Solution: Refer to section [The method parameters of Execute in Xap instance are different from the source code.] of Xap Unit Test Guidelines, Adjust the method parameters to match the correct overload as per the source code.
6
+ - Case 3: [CS0144] Cannot create an instance of the abstract type or interface '{class / bondstucture}'
7
+ Solution: use ExecutionServices.CreateInstance<I{ClassName}>() to create Xap class instance or use ExecutionServices.CreateInstance<{StructName}>() to create Bond struct instance.
8
+ - Case 4: [CS1503] cannot convert from '{Class / Bond type}' to '{Class / Bond type}'
9
+ Solution: Do explicitly type convert.
10
+ - Case 5: [CS0037] Cannot convert null to '{Class / Bond type}' because it is a non-nullable value type
11
+ Solution: MUST assign non-null and valid value.
12
+ - Case 6: [CS0246] The type or namespace name '{class / interface}' could not be found (are you missing a using directive or an assembly reference?)
13
+ Solution: MUST import the **namespace** of {class / interface}
14
+ - Case 7: [CS1501] No overload for method 'Execute' takes 2 arguments
15
+ Solution: - The parameters of **Execute** method with types: **PluginServices**, **PluginOutput<T>**, or decorated with **[ConfigFile("{configFileName}")]** MUST be deleted when using Xap class instance to call it.
@@ -0,0 +1,16 @@
1
+ # General Unit Test Guidelines
2
+
3
+ - MUST use {{testFramework}} test framework to generate tests.
4
+ - **MUST NOT** contain any comments or explanations in test code.
5
+ - Ensure the test code MUST has no syntax errors.
6
+ - Test method names must follow the pattern: [MethodName]_[Scenario]_[ExpectedResult].
7
+ - Return the test code in the format: ```C#\n{Test code}\n```.
8
+ - MUST include Test code in a namespace declaration and place all `using` statements above the namespace declaration.
9
+ - MUST import source code namespace in test code.
10
+ - MUST import all required .Net namespaces if any .Net class or interface are used in the test code. Like:
11
+ - .NET built-in types (e.g., `List<T>`, `IEnumerable<T>`, `Task`, `Assert`, `Moq.Mock`, etc.).
12
+ - Any attribute classes (e.g., `[TestMethod]`, `[TestClass]`, etc.).
13
+ - Any LINQ or threading related classes (e.g., `System.Linq`, `System.Threading.Tasks`).
14
+ - The user will provide the source code directly in their message.
15
+ - **MUST NOT** generate tests for the methods that are attributed with **[ExcludeFromCodeCoverage]**.
16
+ - MUST NOT call private methods in test code.
@@ -0,0 +1,37 @@
1
+ # Xap Unit Test Guidelines
2
+
3
+ - Xap class is a class that inherits one of these interfaces: IPlugin, IConditionPlugin, Workflow, IExperiment, IPluginDataStore, IAsyncPlugin and implements an Execute() method.
4
+ - MUST use ExecutionServices.CreateInstance<I{ClassName}>() to create Xap class instance, where the interface name is the class name prefixed with "I".
5
+ Example: For Xap class SampleWorkflow, create instance MUST be like: **var sampleWorkflow = ExecutionServices.CreateInstance<ISampleWorkflow>();**
6
+ - The parameters of **Execute** method with types: **PluginServices**, **PluginOutput<T>**, or attributed with **[ConfigFile("{configFileName}")]** MUST be deleted when using Xap class instance to call the Execute method.
7
+ - Xap freamwork will take care these deleted parameters, NEVER try to mock or create these deleted types, it's invalid and forbidden.
8
+ - **MUST NOT** mock these deleted types' object, even if its methods or properties are used in the source code.
9
+ - For Remaining parameters,
10
+ - MUST pass in exact type as declared in source code.
11
+ - MUST keep original parameter order.
12
+ - MUST wrap each parameter with Task.FromResult(value).
13
+ Example:
14
+ The Execute method of Xap calss SampleWorkflow is: **PluginResult Execute(PluginServices ps, PluginOutput<Resp> resp, [ConfigFile("config.ini")] EplantConfig eplantConfig, OsResponse osResp = null, EplantRequest req = null)**,
15
+ Base on above declared deleted types, **MUST delete** these three parameters: PluginServices ps, PluginOutput<Resp> resp, [ConfigFile("config.ini")] EplantConfig eplantConfig,
16
+ call the Execute using Xap class instance MUST be like: **var pluginResult = sampleWorkflow.Execute(Task.FromResult(osResp), Task.FromResult(req)).Result;**
17
+ - The Execute method parameters with type PluginOutput<T> MUST be treated as method output, **T** is the type output value.
18
+ - MUST using the parameter name to access the output by the Xap instance
19
+ - MUST using **Result** property to access the output of type **T** in **PluginOutput<T>**.
20
+ - MUST assert PluginOutput<T> parameters' values if there's parameter with **PluginOutput<T>** type.
21
+ Example:
22
+ The Execute method of Xap calss SampleWorkflow is: **PluginResult Execute(PluginServices ps, PluginOutput<Resp> resp, [ConfigFile("config.ini")] EplantConfig eplantConfig, OsResponse osResp = null, EplantRequest req = null)**,
23
+ Get the output value using Xap class instance MUST be like: **sampleWorkflow.resp**.
24
+ Assume there's one field named **Key** in **Resp** type, get **Key** MUST be like: **sampleWorkflow.pluginResp.Result.Key**.
25
+ Assert the PluginOutput<T> value in test code MUST be like: Assert.NotNull(sampleWorkflow.pluginResult.Result.Id); Assert.AreEqual(sampleWorkflow.pluginResult.Result.Id, "id");
26
+ - The Bond structures MUST be treated as regular C# class in test code:
27
+ - **MUST** import **Bond namespace** before using the Bond structure in test code
28
+ Example: The namespace in bond structure is like: **namespace SharedSegments.Weather.OneService**, to use this bond structure, **MUST** add: **using SharedSegments.Weather.OneService** in test code.
29
+ - MUST use ExecutionServices.CreateInstance<{StructName}>() to create Bond struct instances.
30
+ Example: For a Bond structure named **SampleBondStructure<T>**, create bond instance **MUST** be like: ** var subResponse = ExecutionServices.CreateInstance<SampleBondStructure<SampleObject>>();
31
+ - MUST not access the fields that are not defined in bond structure, it's invalid and forbidden.
32
+ - MUST treated the fields as **required** if they **are not** decorated with **optional** in bond structure
33
+ - **MUST** assign a valid, non-null value to **required field** that are **NOT decorated** with **optional** explicitly.
34
+ - MUST assign a valid value to the fields that are used in source code explicitly.
35
+ - MUST correctly handle the null checks for required fields in source code.
36
+ Example: the source code contains **if (sampleRequest?.Category == null)**, the **Category** is require field in sampleRequest bond structure and **MUST be non-null**, just test the case when sampleRequest is null.
37
+ - MUST use AsEnumerable() to convert IList to IEnumerable, e.g. IEnumerable osResponses = osResponseList.AsEnumerable()
@@ -0,0 +1,49 @@
1
+ # Xap Unit Test Guidelines
2
+
3
+ - Xap class is a class that inherits one of these interfaces: IPlugin, IConditionPlugin, Workflow, IExperiment, IPluginDataStore, IAsyncPlugin and implements an Execute() method.
4
+ - MUST use ExecutionServices.CreateInstance<I{ClassName}>() to create Xap class instance, where the interface name is the class name prefixed with "I".
5
+ Example: For Xap class SampleWorkflow, create instance MUST be like: **var sampleWorkflow = ExecutionServices.CreateInstance<ISampleWorkflow>();**
6
+ - The parameters of **Execute** method with types: **PluginServices**, **PluginOutput<T>**, or attributed with **[ConfigFile("{configFileName}")]** MUST be deleted when using Xap class instance to call the Execute method.
7
+ - Xap freamwork will take care these deleted parameters, NEVER try to mock or create these deleted types, it's invalid and forbidden.
8
+ - **MUST NOT** mock these deleted types' object, even if its methods or properties are used in the source code.
9
+ - For Remaining parameters,
10
+ - MUST pass in exact type as declared in source code.
11
+ - MUST keep original parameter order.
12
+ - MUST wrap each parameter with Task.FromResult(value).
13
+ Example:
14
+ The Execute method of Xap calss SampleWorkflow is: **PluginResult Execute(PluginServices ps, PluginOutput<Resp> resp, [ConfigFile("config.ini")] EplantConfig eplantConfig, OsResponse osResp = null, EplantRequest req = null)**,
15
+ Base on above declared deleted types, **MUST delete** these three parameters: PluginServices ps, PluginOutput<Resp> resp, [ConfigFile("config.ini")] EplantConfig eplantConfig,
16
+ call the Execute using Xap class instance MUST be like: **var pluginResult = sampleWorkflow.Execute(Task.FromResult(osResp), Task.FromResult(req)).Result;**
17
+ - The Execute method parameters with type PluginOutput<T> MUST be treated as method output, **T** is the type output value.
18
+ - MUST using the parameter name to access the output by the Xap instance
19
+ - MUST using **Result** property to access the output of type **T** in **PluginOutput<T>**.
20
+ - MUST assert PluginOutput<T> parameters' values if there's parameter with **PluginOutput<T>** type.
21
+ Example:
22
+ The Execute method of Xap calss SampleWorkflow is: **PluginResult Execute(PluginServices ps, PluginOutput<Resp> resp, [ConfigFile("config.ini")] EplantConfig eplantConfig, OsResponse osResp = null, EplantRequest req = null)**,
23
+ Get the output value using Xap class instance MUST be like: **sampleWorkflow.resp**.
24
+ Assume there's one field named **Key** in **Resp** type, get **Key** MUST be like: **sampleWorkflow.pluginResp.Result.Key**.
25
+ Assert the PluginOutput<T> value in test code MUST be like: Assert.NotNull(sampleWorkflow.pluginResult.Result.Id); Assert.AreEqual(sampleWorkflow.pluginResult.Result.Id, "id");
26
+ - For methods **other than Execute method** that have **PluginServices** type parameter, MUST use **ExecutionServices.CreatePluginServices()** to create the PluginServices instance.
27
+ Example:
28
+ For a method declared as: **public static int SomeHowMethod(PluginServices pluginServices, ParameterType1 parameter1)**,
29
+ MUST create PluginServices instance like: **var pluginServices = ExecutionServices.CreatePluginServices();**
30
+ Then call the method like: **var result = SomeHowMethod(pluginServices, parameter1Value);**
31
+ - The Bond structures MUST be treated as regular C# class in test code:
32
+ - **MUST** import **Bond namespace** before using the Bond structure in test code
33
+ Example: The namespace in bond structure is like: **namespace SharedSegments.Weather.OneService**, to use this bond structure, **MUST** add: **using SharedSegments.Weather.OneService** in test code.
34
+ - MUST use ExecutionServices.CreateInstance<{StructName}>() to create Bond struct instances.
35
+ Example: For a Bond structure named **SampleBondStructure<T>**, create bond instance **MUST** be like: ** var subResponse = ExecutionServices.CreateInstance<SampleBondStructure<SampleObject>>();
36
+ - For Bond structure fields with **blob** type, MUST use **ExecutionServices.CreateInstance<XapBlob>()** with a **byte array** parameter to assign value.
37
+ **MUST** add **using Microsoft.Bing.Xap.CodeGenCollections.Generic;** to use XapBlob type.
38
+ Example:
39
+ For a Bond structure: **struct SampleBondStructure { 1: required blob SampleBlobTypeElement; }**
40
+ Assign blob field like: **sampleBondStructure.SampleBlobTypeElement = ExecutionServices.CreateInstance<XapBlob>(Encoding.UTF8.GetBytes("SomeContent"));**
41
+ Where **Encoding.UTF8.GetBytes("SomeContent")** returns a byte array.
42
+ For an empty byte array, **MUST** use **Array.Empty<byte>()**: **sampleBondStructure.SampleBlobTypeElement = ExecutionServices.CreateInstance<XapBlob>(Array.Empty<byte>());**
43
+ - MUST not access the fields that are not defined in bond structure, it's invalid and forbidden.
44
+ - MUST treated the fields as **required** if they **are not** decorated with **optional** in bond structure
45
+ - **MUST** assign a valid, non-null value to **required field** that are **NOT decorated** with **optional** explicitly.
46
+ - MUST assign a valid value to the fields that are used in source code explicitly.
47
+ - MUST correctly handle the null checks for required fields in source code.
48
+ Example: the source code contains **if (sampleRequest?.Category == null)**, the **Category** is require field in sampleRequest bond structure and **MUST be non-null**, just test the case when sampleRequest is null.
49
+ - MUST use AsEnumerable() to convert IList to IEnumerable, e.g. IEnumerable osResponses = osResponseList.AsEnumerable()
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const utGenWrapper_1 = require("./utGenWrapper");
8
+ const processChangedFiles_1 = require("../changedFilesProcessor/processChangedFiles");
8
9
  const yargs_1 = __importDefault(require("yargs/yargs"));
9
10
  const helpers_1 = require("yargs/helpers");
10
11
  const setupProcessSignalHandler_1 = require("../exit/setupProcessSignalHandler");
@@ -68,6 +69,17 @@ function main() {
68
69
  alias: 'np',
69
70
  describe: 'The path to the NuGet packages used by the code analyzer. Used scenario: when the code analyzer is executed in CI pipeline',
70
71
  type: 'string',
72
+ })
73
+ .option('changedTestFiles', {
74
+ alias: 'ctf',
75
+ describe: 'Test files that have changes in the commit. Can be passed via CLI (multiple times) or via config file, then AI will help to refine or improve existing test code to reach high code coverage',
76
+ type: 'array',
77
+ string: true,
78
+ })
79
+ .option('changedFilesConfigPath', {
80
+ alias: 'cfcp',
81
+ describe: 'Path to JSON config file containing changed files array (recommended for large file lists to avoid command line length limits). Example file format: [{"filepath": "Q:\\src\\Project\\Service\\MyService.cs"}, {"filepath": "Q:\\src\\Project\\Tests\\MyServiceTests.cs"}]',
82
+ type: 'string',
71
83
  })
72
84
  .command('utcmd', 'Generate unit test', () => { }, (c) => {
73
85
  console.log('Command executed with options:', c);
@@ -80,6 +92,26 @@ function main() {
80
92
  .command('utbenchmark', 'Generate unit test report for all project/solution files using command line', () => { }, (c) => {
81
93
  console.log('Command executed with options:', c);
82
94
  (0, utGenWrapper_1.generateUtBenchmark)(c);
95
+ })
96
+ .command('utchanged', 'Process changed files in the PR from a config file', () => { }, async (c) => {
97
+ console.log('Command executed with options:', c);
98
+ const configPath = c.changedFilesConfigPath;
99
+ if (!configPath) {
100
+ console.error('Error: changedFilesConfigPath (--cfcp) is required for utchanged command');
101
+ process.exit(1);
102
+ }
103
+ let result;
104
+ try {
105
+ result = await (0, processChangedFiles_1.processChangedFiles)(configPath, c);
106
+ }
107
+ catch (error) {
108
+ console.error('Error processing changed files:', error);
109
+ result = { error: String(error), solutionFilePath: null, runsettingsFilePath: null, changedFiles: [] };
110
+ }
111
+ // Output result as JSON for CI pipeline to consume
112
+ console.log('===RESULT_JSON_START===');
113
+ console.log(JSON.stringify(result, null, 2));
114
+ console.log('===RESULT_JSON_END===');
83
115
  })
84
116
  .help()
85
117
  .alias('help', 'h')
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/command/index.ts"],"names":[],"mappings":";;;;;;AAEA,iDAA0F;AAE1F,wDAAgC;AAChC,2CAAwC;AACxC,iFAAwE;AAExE,mPAAmP;AACnP,sPAAsP;AACtP,0PAA0P;AAC1P,kQAAkQ;AAClQ,qPAAqP;AACrP,SAAS,IAAI;IACT,IAAA,eAAK,EAAC,IAAA,iBAAO,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxB,gCAAgC;SAC/B,UAAU,CAAC,WAAW,CAAC;SACvB,MAAM,CACH,QAAQ,EACR;QACI,QAAQ,EAAE,mCAAmC;QAC7C,OAAO,EAAE,CAAC,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,UAAU,CAAC;QACpE,OAAO,EAAE,aAAa;QACtB,IAAI,EAAE,QAAQ;KACjB,CACJ;SACA,MAAM,CACH,MAAM,EACN;QACI,KAAK,EAAE,GAAG;QACV,QAAQ,EAAE,yBAAyB,EAAE,uCAAuC;QAC5E,OAAO,EAAE,CAAC,UAAU,EAAE,QAAQ,CAAC;QAC/B,OAAO,EAAE,UAAU;QACnB,IAAI,EAAE,QAAQ;KACjB,CACJ;SACA,MAAM,CACH,gBAAgB,EAChB;QACI,QAAQ,EAAE,mCAAmC;QAC7C,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,mEAAmE;QACpF,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,QAAQ;KACjB,CACJ;SACA,MAAM,CACH,WAAW,EACX;QACI,QAAQ,EAAE,yBAAyB;QACnC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,iDAAiD;QAClE,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,QAAQ;KACjB,CACJ;SACA,MAAM,CACH,UAAU,EACV;QACI,QAAQ,EAAE,qBAAqB,EAAE,oCAAoC;QACrE,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE,QAAQ;KACjB,CACJ;SACA,MAAM,CACH,gBAAgB,EAChB;QACI,KAAK,EAAE,GAAG;QACV,QAAQ,EAAE,4EAA4E;QACtF,IAAI,EAAE,QAAQ;KACjB,CACJ;SACA,MAAM,CACH,cAAc,EACd;QACI,KAAK,EAAE,GAAG;QACV,QAAQ,EAAE,0BAA0B;QACpC,IAAI,EAAE,QAAQ;KACjB,CACJ;SACA,MAAM,CACH,SAAS,EACT;QACI,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,yCAAyC;QACnD,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,yCAAyC;QAC1D,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,QAAQ;KACjB,CACJ;SACA,MAAM,CACH,kBAAkB,EAClB;QACI,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,4HAA4H;QACtI,IAAI,EAAE,QAAQ;KACjB,CACJ;SACA,OAAO,CACJ,OAAO,EACP,oBAAoB,EACpB,GAAG,EAAE,GAAG,CAAC,EACT,CAAC,CAAC,EAAE,EAAE;QACF,OAAO,CAAC,GAAG,CAAC,gCAAgC,EAAE,CAAC,CAAC,CAAC;QACjD,IAAA,6BAAc,EAAC,CAAC,CAAC,CAAC;IACtB,CAAC,CACJ;SACA,OAAO,CACJ,SAAS,EACT,6EAA6E,EAC7E,GAAG,EAAE,GAAG,CAAC,EACT,CAAC,CAAC,EAAE,EAAE;QACF,OAAO,CAAC,GAAG,CAAC,gCAAgC,EAAE,CAAC,CAAC,CAAC;QACjD,IAAA,kCAAmB,EAAC,CAAC,CAAC,CAAC;IAC3B,CAAC,CACJ;SACA,OAAO,CACJ,aAAa,EACb,6EAA6E,EAC7E,GAAG,EAAE,GAAG,CAAC,EACT,CAAC,CAAC,EAAE,EAAE;QACF,OAAO,CAAC,GAAG,CAAC,gCAAgC,EAAE,CAAC,CAAC,CAAC;QACjD,IAAA,kCAAmB,EAAC,CAAC,CAAC,CAAC;IAC3B,CAAC,CACJ;SACA,IAAI,EAAE;SACN,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC;SAClB,KAAK,EAAE,CAAC;AACjB,CAAC;AAED,IAAA,+CAAmB,GAAE,CAAC;AACtB,IAAI,EAAE,CAAC","sourcesContent":["#!/usr/bin/env node\r\n\r\nimport { generateBatchUtCode, generateUtBenchmark, generateUtCode } from './utGenWrapper';\r\n\r\nimport Yargs from 'yargs/yargs';\r\nimport { hideBin } from 'yargs/helpers';\r\nimport { setupSignalHandlers } from '../exit/setupProcessSignalHandler';\r\n\r\n// const hardcodedArgs = ['', '', 'utcmd', '--s', 'D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Overview\\\\HttpRequestParser\\\\Utils\\\\SqmidHelper.cs', '--keepFailedTest', \"1\", \"--af\", \"1\"];\r\n// const hardcodedArgs = ['', '', 'utcmd', '--s', 'D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Eplant\\\\Conditions\\\\EplantRequestCategoryCondition.cs', '--keepFailedTest', \"1\", \"--af\", \"1\"];\r\n// const hardcodedArgs = ['', '', 'utcmd', '--s', 'D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Eplant\\\\ResponseBuilders\\\\EplantMigrateResponseBuilder.cs', '--keepFailedTest', \"1\", \"--af\", \"1\"];\r\n// const hardcodedArgs = ['', '', 'utcmd', '--s', 'D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Overview\\\\HttpRequestParser\\\\Utils\\\\WeatherParamsHttpConverter.cs', '--keepFailedTest', \"1\", \"--af\", \"1\"];\r\n// const hardcodedArgs = ['', '', 'utcmd', '--s', 'D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Eplant\\\\RequestBuilders\\\\EplantRankRequestBuilder.cs', '--keepFailedTest', \"1\", \"--af\", \"1\"];\r\nfunction main() {\r\n Yargs(hideBin(process.argv))\r\n // Yargs(hideBin(hardcodedArgs))\r\n .scriptName('csharp-ut')\r\n .option(\r\n 'source',\r\n {\r\n describe: 'where the test copilot is running',\r\n choices: ['csharpUtGen', 'csvsplugin', 'csVsCodePlugin', 'pipeline'],\r\n default: 'csharpUtGen',\r\n type: 'string',\r\n }\r\n )\r\n .option(\r\n 'mode',\r\n {\r\n alias: 'm',\r\n describe: 'test copilot work mode.', // Note: only generate is supported now\r\n choices: ['generate', 'repair'],\r\n default: 'generate',\r\n type: 'string',\r\n }\r\n )\r\n .option(\r\n 'keepFailedTest',\r\n {\r\n describe: 'whether to keep failed test cases',\r\n choices: [0, 1], // 0 means delete failed test cases, 1 means keep failed test cases\r\n default: 1,\r\n type: 'number',\r\n }\r\n )\r\n .option(\r\n 'enableCov',\r\n {\r\n describe: 'whether to run coverage',\r\n choices: [0, 1], // 0 means not run coverage, 1 means run coverage\r\n default: 0,\r\n type: 'number',\r\n }\r\n )\r\n .option(\r\n 'LLMModel',\r\n {\r\n describe: 'use which LLM model', // Note: only gpt4o is supported now\r\n default: \"gpt4o\",\r\n type: 'string',\r\n }\r\n )\r\n .option(\r\n 'sourceCodePath',\r\n {\r\n alias: 's',\r\n describe: 'source code file path / project path or solution path for batch generation',\r\n type: 'string',\r\n }\r\n )\r\n .option(\r\n 'testCodePath',\r\n {\r\n alias: 't',\r\n describe: 'Target file or directory',\r\n type: 'string',\r\n }\r\n )\r\n .option(\r\n 'autofix',\r\n {\r\n alias: 'af',\r\n describe: 'whether to auto fix generated test code',\r\n choices: [0, 1], // 0 means not auto fix, 1 means auto fix\r\n default: 0,\r\n type: 'number',\r\n }\r\n )\r\n .option(\r\n 'nugetPackagePath',\r\n {\r\n alias: 'np',\r\n describe: 'The path to the NuGet packages used by the code analyzer. Used scenario: when the code analyzer is executed in CI pipeline',\r\n type: 'string',\r\n }\r\n )\r\n .command(\r\n 'utcmd',\r\n 'Generate unit test',\r\n () => { },\r\n (c) => {\r\n console.log('Command executed with options:', c);\r\n generateUtCode(c);\r\n }\r\n )\r\n .command(\r\n 'utbatch',\r\n 'Generate unit test report for all project/solution files using command line',\r\n () => { },\r\n (c) => {\r\n console.log('Command executed with options:', c);\r\n generateBatchUtCode(c);\r\n }\r\n )\r\n .command(\r\n 'utbenchmark',\r\n 'Generate unit test report for all project/solution files using command line',\r\n () => { },\r\n (c) => {\r\n console.log('Command executed with options:', c);\r\n generateUtBenchmark(c);\r\n }\r\n )\r\n .help()\r\n .alias('help', 'h')\r\n .parse();\r\n}\r\n\r\nsetupSignalHandlers();\r\nmain();"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/command/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\r\n\r\nimport { generateBatchUtCode, generateUtBenchmark, generateUtCode } from './utGenWrapper';\r\nimport { processChangedFiles } from '../changedFilesProcessor/processChangedFiles';\r\n\r\nimport Yargs from 'yargs/yargs';\r\nimport { hideBin } from 'yargs/helpers';\r\nimport { setupSignalHandlers } from '../exit/setupProcessSignalHandler';\r\n\r\n// const hardcodedArgs = ['', '', 'utcmd', '--s', 'D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Overview\\\\HttpRequestParser\\\\Utils\\\\SqmidHelper.cs', '--keepFailedTest', \"1\", \"--af\", \"1\"];\r\n// const hardcodedArgs = ['', '', 'utcmd', '--s', 'D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Eplant\\\\Conditions\\\\EplantRequestCategoryCondition.cs', '--keepFailedTest', \"1\", \"--af\", \"1\"];\r\n// const hardcodedArgs = ['', '', 'utcmd', '--s', 'D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Eplant\\\\ResponseBuilders\\\\EplantMigrateResponseBuilder.cs', '--keepFailedTest', \"1\", \"--af\", \"1\"];\r\n// const hardcodedArgs = ['', '', 'utcmd', '--s', 'D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Overview\\\\HttpRequestParser\\\\Utils\\\\WeatherParamsHttpConverter.cs', '--keepFailedTest', \"1\", \"--af\", \"1\"];\r\n// const hardcodedArgs = ['', '', 'utcmd', '--s', 'D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Eplant\\\\RequestBuilders\\\\EplantRankRequestBuilder.cs', '--keepFailedTest', \"1\", \"--af\", \"1\"];\r\nfunction main() {\r\n Yargs(hideBin(process.argv))\r\n // Yargs(hideBin(hardcodedArgs))\r\n .scriptName('csharp-ut')\r\n .option(\r\n 'source',\r\n {\r\n describe: 'where the test copilot is running',\r\n choices: ['csharpUtGen', 'csvsplugin', 'csVsCodePlugin', 'pipeline'],\r\n default: 'csharpUtGen',\r\n type: 'string',\r\n }\r\n )\r\n .option(\r\n 'mode',\r\n {\r\n alias: 'm',\r\n describe: 'test copilot work mode.', // Note: only generate is supported now\r\n choices: ['generate', 'repair'],\r\n default: 'generate',\r\n type: 'string',\r\n }\r\n )\r\n .option(\r\n 'keepFailedTest',\r\n {\r\n describe: 'whether to keep failed test cases',\r\n choices: [0, 1], // 0 means delete failed test cases, 1 means keep failed test cases\r\n default: 1,\r\n type: 'number',\r\n }\r\n )\r\n .option(\r\n 'enableCov',\r\n {\r\n describe: 'whether to run coverage',\r\n choices: [0, 1], // 0 means not run coverage, 1 means run coverage\r\n default: 0,\r\n type: 'number',\r\n }\r\n )\r\n .option(\r\n 'LLMModel',\r\n {\r\n describe: 'use which LLM model', // Note: only gpt4o is supported now\r\n default: \"gpt4o\",\r\n type: 'string',\r\n }\r\n )\r\n .option(\r\n 'sourceCodePath',\r\n {\r\n alias: 's',\r\n describe: 'source code file path / project path or solution path for batch generation',\r\n type: 'string',\r\n }\r\n )\r\n .option(\r\n 'testCodePath',\r\n {\r\n alias: 't',\r\n describe: 'Target file or directory',\r\n type: 'string',\r\n }\r\n )\r\n .option(\r\n 'autofix',\r\n {\r\n alias: 'af',\r\n describe: 'whether to auto fix generated test code',\r\n choices: [0, 1], // 0 means not auto fix, 1 means auto fix\r\n default: 0,\r\n type: 'number',\r\n }\r\n )\r\n .option(\r\n 'nugetPackagePath',\r\n {\r\n alias: 'np',\r\n describe: 'The path to the NuGet packages used by the code analyzer. Used scenario: when the code analyzer is executed in CI pipeline',\r\n type: 'string',\r\n }\r\n )\r\n .option(\r\n 'changedTestFiles',\r\n {\r\n alias: 'ctf',\r\n describe: 'Test files that have changes in the commit. Can be passed via CLI (multiple times) or via config file, then AI will help to refine or improve existing test code to reach high code coverage',\r\n type: 'array',\r\n string: true, \r\n }\r\n )\r\n .option(\r\n 'changedFilesConfigPath',\r\n {\r\n alias: 'cfcp',\r\n describe: 'Path to JSON config file containing changed files array (recommended for large file lists to avoid command line length limits). Example file format: [{\"filepath\": \"Q:\\\\src\\\\Project\\\\Service\\\\MyService.cs\"}, {\"filepath\": \"Q:\\\\src\\\\Project\\\\Tests\\\\MyServiceTests.cs\"}]',\r\n type: 'string',\r\n }\r\n )\r\n .command(\r\n 'utcmd',\r\n 'Generate unit test',\r\n () => { },\r\n (c) => {\r\n console.log('Command executed with options:', c);\r\n generateUtCode(c);\r\n }\r\n )\r\n .command(\r\n 'utbatch',\r\n 'Generate unit test report for all project/solution files using command line',\r\n () => { },\r\n (c) => {\r\n console.log('Command executed with options:', c);\r\n generateBatchUtCode(c);\r\n }\r\n )\r\n .command(\r\n 'utbenchmark',\r\n 'Generate unit test report for all project/solution files using command line',\r\n () => { },\r\n (c) => {\r\n console.log('Command executed with options:', c);\r\n generateUtBenchmark(c);\r\n }\r\n )\r\n .command(\r\n 'utchanged',\r\n 'Process changed files in the PR from a config file',\r\n () => { },\r\n async (c) => {\r\n console.log('Command executed with options:', c);\r\n const configPath = c.changedFilesConfigPath as string;\r\n if (!configPath) {\r\n console.error('Error: changedFilesConfigPath (--cfcp) is required for utchanged command');\r\n process.exit(1);\r\n }\r\n let result: object;\r\n try {\r\n result = await processChangedFiles(configPath, c);\r\n } catch (error) {\r\n console.error('Error processing changed files:', error);\r\n result = { error: String(error), solutionFilePath: null, runsettingsFilePath: null, changedFiles: [] };\r\n }\r\n // Output result as JSON for CI pipeline to consume\r\n console.log('===RESULT_JSON_START===');\r\n console.log(JSON.stringify(result, null, 2));\r\n console.log('===RESULT_JSON_END===');\r\n }\r\n )\r\n .help()\r\n .alias('help', 'h')\r\n .parse();\r\n}\r\n\r\nsetupSignalHandlers();\r\nmain();"]}