@ai-dev-tools/csharp-copilot-core 0.0.33 → 0.0.34
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.
- package/out/batch/generateCodeTests.js +22 -6
- package/out/batch/generateCodeTests.js.map +1 -1
- package/out/changedFilesProcessor/processChangedFiles.d.ts +9 -0
- package/out/changedFilesProcessor/processChangedFiles.js +156 -0
- package/out/changedFilesProcessor/processChangedFiles.js.map +1 -0
- package/out/codebk/prompts/buildAfGuidelines_20251226.liquid +15 -0
- package/out/codebk/prompts/general/generalUtGuidelines_20251226.liquid +16 -0
- package/out/codebk/prompts/xap/xapUtGuideline_20251225.liquid +37 -0
- package/out/codebk/prompts/xap/xapUtGuideline_20251226.liquid +49 -0
- package/out/command/index.js +32 -0
- package/out/command/index.js.map +1 -1
- package/out/command/utGenWrapper.js +85 -4
- package/out/command/utGenWrapper.js.map +1 -1
- package/out/gen/autoFix.d.ts +2 -2
- package/out/gen/autoFix.js +88 -31
- package/out/gen/autoFix.js.map +1 -1
- package/out/gen/csharpUtGen.d.ts +1 -1
- package/out/gen/csharpUtGen.js +16 -13
- package/out/gen/csharpUtGen.js.map +1 -1
- package/out/gen/ensureValidLLMResponse.d.ts +5 -1
- package/out/gen/ensureValidLLMResponse.js +15 -3
- package/out/gen/ensureValidLLMResponse.js.map +1 -1
- package/out/gen/postGen/postGenMoreUTProcess.d.ts +25 -0
- package/out/gen/postGen/postGenMoreUTProcess.js +502 -0
- package/out/gen/postGen/postGenMoreUTProcess.js.map +1 -0
- package/out/gen/postGen/postGenProcess.d.ts +1 -1
- package/out/gen/postGen/postGenProcess.js +7 -7
- package/out/gen/postGen/postGenProcess.js.map +1 -1
- package/out/gen/postGen/repairRequiredNameSpaces.d.ts +17 -0
- package/out/gen/postGen/repairRequiredNameSpaces.js +87 -20
- package/out/gen/postGen/repairRequiredNameSpaces.js.map +1 -1
- package/out/llm/preparePrompt.d.ts +2 -1
- package/out/llm/preparePrompt.js +38 -4
- package/out/llm/preparePrompt.js.map +1 -1
- package/out/llm/prompt/buildAfGuidelines.liquid +8 -8
- package/out/llm/prompt/general/generalUtGuidelines.liquid +3 -0
- package/out/llm/prompt/moreUT/generateMoreUTSourceCode.liquid +4 -0
- package/out/llm/prompt/moreUT/generateMoreUtAutoFix.liquid +45 -0
- package/out/llm/prompt/moreUT/generateMoreUtTemplate.liquid +115 -0
- package/out/llm/prompt/moreUT/generateMoreUtTestCode.liquid +5 -0
- package/out/llm/prompt/moreUT/utGenerationGuidelines.liquid +15 -0
- package/out/llm/prompt/xap/xapUtGuideline.liquid +66 -15
- package/out/types/changedFilesResult.d.ts +37 -0
- package/out/types/changedFilesResult.js +3 -0
- package/out/types/changedFilesResult.js.map +1 -0
- package/out/types/constants.d.ts +1 -0
- package/out/types/constants.js +2 -1
- package/out/types/constants.js.map +1 -1
- package/out/types/genResult.d.ts +1 -1
- package/out/types/genResult.js.map +1 -1
- package/out/utils/checkXapCode.d.ts +1 -1
- package/out/utils/checkXapCode.js +14 -2
- package/out/utils/checkXapCode.js.map +1 -1
- package/out/utils/fileUtils.d.ts +1 -0
- package/out/utils/fileUtils.js +13 -0
- package/out/utils/fileUtils.js.map +1 -1
- package/out/utils/getCodeStructurePath.d.ts +4 -0
- package/out/utils/getCodeStructurePath.js +30 -0
- package/out/utils/getCodeStructurePath.js.map +1 -1
- package/out/utils/getTestFile.d.ts +18 -8
- package/out/utils/getTestFile.js +734 -59
- package/out/utils/getTestFile.js.map +1 -1
- package/out/utils/removeFailedTestMethods.d.ts +8 -0
- package/out/utils/removeFailedTestMethods.js +363 -0
- package/out/utils/removeFailedTestMethods.js.map +1 -1
- package/out/utils/verifyGeneratedCode.d.ts +2 -0
- package/out/utils/verifyGeneratedCode.js +17 -0
- package/out/utils/verifyGeneratedCode.js.map +1 -0
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getTestFile.js","sourceRoot":"","sources":["../../src/utils/getTestFile.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\r\nimport * as path from 'path';\r\n\r\nimport { getAssemblyName, getCodeRepoRoot, getSourceFileCsprojFilePath, getSourceFileProjectDir } from './getCodeStructurePath';\r\n\r\nimport { XMLParser } from 'fast-xml-parser';\r\n\r\nconst TEST_NAME_REGEX = /(Test|Tests)/i;\r\nconst TEST_ATTR_REGEX = /\\[(Fact|Theory|TestClass|TestMethod|Test)\\]/;\r\n\r\nfunction findAllCsprojFiles(dir: string, excludedPaths: string[]): string[] {\r\n const results: string[] = [];\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n if (entry.name === \".vs\" || excludedPaths.includes(fullPath)) {\r\n continue; // Skip excluded paths\r\n }\r\n\r\n if (entry.isDirectory()) {\r\n results.push(...findAllCsprojFiles(fullPath, excludedPaths));\r\n } else if (entry.isFile() && entry.name.endsWith('.csproj')) {\r\n results.push(fullPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\nfunction isXapTestProject(csprojPath: string): boolean {\r\n try {\r\n const content = fs.readFileSync(csprojPath, 'utf-8');\r\n const parser = new XMLParser({ ignoreAttributes: false, ignoreDeclaration: true });\r\n const json = parser.parse(content);\r\n const project = json.Project;\r\n\r\n const propertyGroups = Array.isArray(project.PropertyGroup)\r\n ? project.PropertyGroup\r\n : [project.PropertyGroup];\r\n\r\n for (const group of propertyGroups) {\r\n if (group.XapProjectType === 'TestProject') {\r\n return true;\r\n }\r\n }\r\n } catch (err: any) {\r\n console.error(`Failed to parse ${csprojPath}: ${err.message}`);\r\n }\r\n return false;\r\n}\r\n\r\nfunction isGeneralTestProject(csprojPath: string): boolean {\r\n try {\r\n const content = fs.readFileSync(csprojPath, 'utf-8');\r\n\r\n // 1. package reference (test framework)\r\n const referencesTestFramework = /<PackageReference\\s+Include=\"(xunit|nunit|MSTest\\.TestFramework|Microsoft\\.NET\\.Test\\.Sdk)\"/i.test(content);\r\n\r\n // 2. file name *.Tests.csproj\r\n const fileNameHasTests = (/\\.Tests\\.csproj$/i.test(path.basename(csprojPath))) || (/\\.Test\\.csproj$/i.test(path.basename(csprojPath)));\r\n\r\n // 3. path contains Tests\r\n const pathHasTests = /[\\\\/](Tests?|UnitTests?|IntegrationTests?)[\\\\/]/i.test(csprojPath);\r\n\r\n // 4. Project SDK is test type (optional)\r\n const sdkIsTestSdk = /<Project\\s+Sdk=\"Microsoft\\.NET\\.Test\\.Sdk\"/i.test(content);\r\n\r\n // 组合判断\r\n return referencesTestFramework || sdkIsTestSdk || fileNameHasTests || pathHasTests;\r\n } catch (err) {\r\n console.error(`can't read csproj file: ${csprojPath}`);\r\n return false;\r\n }\r\n}\r\n\r\nfunction isFalconUnitTestProject(csprojPath: string): boolean {\r\n const xmlContent = fs.readFileSync(csprojPath, 'utf-8');\r\n\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n attributeNamePrefix: ''\r\n });\r\n const parsed = parser.parse(xmlContent);\r\n\r\n // get PackageReference node\r\n const project = parsed.Project;\r\n const itemGroups = Array.isArray(project.ItemGroup) ? project.ItemGroup : [project.ItemGroup];\r\n\r\n let isFalconUnitTestProject = false;\r\n\r\n for (const group of itemGroups) {\r\n if(!group) continue;\r\n const refs = group.PackageReference;\r\n if (!refs) continue;\r\n\r\n // PackageReference\r\n const refList = Array.isArray(refs) ? refs : [refs];\r\n\r\n for (const ref of refList) {\r\n if (ref.Include === 'Microsoft.Falcon.CodeAnalyzers') {\r\n isFalconUnitTestProject = true;\r\n console.log('Found Microsoft.Falcon.CodeAnalyzers');\r\n break;\r\n }\r\n }\r\n\r\n if (isFalconUnitTestProject) break;\r\n }\r\n\r\n // if (!isFalconUnitTestProject) {\r\n // console.log('Microsoft.Falcon.CodeAnalyzers not found');\r\n // }\r\n\r\n return isFalconUnitTestProject;\r\n}\r\n\r\nfunction isProjectNameMatchedTestProject(csprojPath: string, projectAssemblyName: string, projectName: string) {\r\n const testProjectName = path.basename(csprojPath, '.csproj').toLowerCase();\r\n const fileNameEndWithTests = testProjectName.endsWith('.tests') || testProjectName.endsWith('.test') || testProjectName.endsWith('.unittest') || testProjectName.endsWith('.unittests');\r\n if (testProjectName.startsWith(projectName.toLowerCase()) && fileNameEndWithTests) {\r\n return true;\r\n }\r\n const testProjectAssemblyName = getAssemblyName(csprojPath);\r\n return testProjectAssemblyName?.toLowerCase()?.startsWith(projectAssemblyName.toLowerCase()) && fileNameEndWithTests;\r\n}\r\n\r\nfunction findTestProjectFromCsFile(csFilePath: string): string | null {\r\n let currentDir = path.dirname(csFilePath);\r\n const repoRoot = getCodeRepoRoot(csFilePath);\r\n const projectFilePath = getSourceFileCsprojFilePath(csFilePath);\r\n const projectName = path.basename(projectFilePath, '.csproj');\r\n const projectAssemblyName = getAssemblyName(projectFilePath);\r\n\r\n const excludedPaths: string[] = [currentDir];\r\n const repoRootParentPath = path.dirname(repoRoot);\r\n // Start from the parent directory of the source file, because test project is usually in the parent directory\r\n currentDir = path.dirname(currentDir);\r\n while (currentDir !== repoRootParentPath) {\r\n const csprojFiles = findAllCsprojFiles(currentDir, excludedPaths);\r\n for (const csproj of csprojFiles) {\r\n if (isProjectNameMatchedTestProject(csproj, projectAssemblyName, projectName)) {\r\n return csproj;\r\n } else if (isXapTestProject(csproj)) {\r\n return csproj;\r\n } else if (isFalconUnitTestProject(csproj)) {\r\n const filename = path.basename(csproj, '.csproj').toLowerCase();\r\n if (filename.endsWith('unittest') || filename.endsWith('unittests')) {\r\n return csproj;\r\n }\r\n } else if (isGeneralTestProject(csproj)) {\r\n return csproj;\r\n }\r\n }\r\n // Add current directory to excluded paths to avoid re-checking\r\n excludedPaths.push(currentDir);\r\n currentDir = path.dirname(currentDir);\r\n }\r\n\r\n return null;\r\n}\r\n\r\nfunction findPossibleTestFiles(testProjectDir: string, className: string): string[] {\r\n const results: string[] = [];\r\n const stack: string[] = [testProjectDir];\r\n\r\n while (stack.length > 0) {\r\n const currentDir = stack.pop()!;\r\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n if (entry.name === \"bin\" || entry.name === \"obj\") {\r\n continue;\r\n }\r\n const fullPath = path.join(currentDir, entry.name);\r\n if (entry.isDirectory()) {\r\n stack.push(fullPath);\r\n } else if (entry.isFile() && entry.name.endsWith('.cs') && entry.name.includes(className)) {\r\n results.push(fullPath);\r\n }\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\nfunction fileContainsTestAttributes(filePath: string): boolean {\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n return TEST_ATTR_REGEX.test(content);\r\n}\r\n\r\nexport function getExistingTestFile(sourceCodePath: string) {\r\n const absPath = path.resolve(sourceCodePath);\r\n const testProjectPath = findTestProjectFromCsFile(absPath);\r\n\r\n if (!testProjectPath) {\r\n console.error(`No test project found for ${sourceCodePath}`);\r\n return { testFilePath: null, testProjectPath: null };\r\n }\r\n\r\n const testProjectDir = path.dirname(testProjectPath);\r\n const className = path.basename(sourceCodePath, '.cs');\r\n const candidateFiles = findPossibleTestFiles(testProjectDir, className);\r\n\r\n for (const testFilePath of candidateFiles) {\r\n if (TEST_NAME_REGEX.test(path.basename(testFilePath))) {\r\n return { testFilePath, testProjectPath };\r\n }\r\n }\r\n\r\n return { testFilePath: null, testProjectPath: testProjectPath };\r\n}\r\n\r\nfunction createXapTestProject(sourceCodePath: string) {\r\n}\r\n\r\nexport function createTestFile(sourceCodePath: string) {\r\n const testProjectPath = findTestProjectFromCsFile(sourceCodePath);\r\n if (!testProjectPath) {\r\n console.error(`No test project found for ${sourceCodePath}`);\r\n return { testFilePath: null, testProjectPath: null };\r\n }\r\n const sourceFileName = path.basename(sourceCodePath, '.cs');\r\n const testFileName = `${sourceFileName}Tests.cs`;\r\n const sourceFileProjectPath = getSourceFileProjectDir(sourceCodePath);\r\n const sourceFileDir = path.dirname(sourceCodePath);\r\n const testFileRelativePath = sourceFileDir.replace(sourceFileProjectPath, \"\");\r\n const testProjecDir = path.dirname(testProjectPath);\r\n const testFilePath = path.join(testProjecDir, testFileRelativePath, testFileName);\r\n const testFileDir = path.dirname(testFilePath);\r\n // Create all missing directories recursively\r\n fs.mkdirSync(testFileDir, { recursive: true });\r\n console.log(`Created test file dir at ${testFilePath}`);\r\n return { testFilePath, testProjectPath };\r\n}\r\n\r\nexport function getOrCreatTestFile(sourceCodePath: string, testFilePath: string, isXapCode: boolean) {\r\n let finalTestFilePath = testFilePath;\r\n let finalTestProjectPath;\r\n let testFileExist = !!testFilePath && fs.existsSync(testFilePath);\r\n // if outputPath param is not provided, try to find an existing test file\r\n // if no existing test file found, create a new one in test project\r\n if (!testFilePath || testFilePath.trim() === '') {\r\n console.log(' Output path is missing. outputPath:', testFilePath, ', start looking for existing test project');\r\n let result = getExistingTestFile(sourceCodePath);\r\n if (result.testFilePath) {\r\n testFileExist = true;\r\n console.log(`Found existing test file: ${result.testFilePath}`);\r\n } else {\r\n console.log('No existing test file found, creating a new one.');\r\n result = createTestFile(sourceCodePath);\r\n }\r\n finalTestFilePath = result.testFilePath;\r\n finalTestProjectPath = result.testProjectPath;\r\n }\r\n\r\n return { testFilePath: finalTestFilePath, testProjectPath: finalTestProjectPath, testFileExist };\r\n\r\n}\r\n\r\n\r\n\r\n// Example usage\r\n// const csFilePath = \"D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\LocationDetection\\\\AudienceResponseParser.cs\";\r\n// const csFilePath = \"D:\\\\code\\\\msnews-experiences\\\\experiences\\\\weather-right-rail\\\\src\\\\WeatherRightRail.connector.ts\";\r\n// const testProjectPath = \"D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Tests\\\\SharedSegments.Tests.csproj\";\r\n// if (!csFilePath) {\r\n// console.error('Usage: ts-node findTestFile.ts path/to/your/file.cs');\r\n// process.exit(1);\r\n// }\r\n\r\n// const start = Date.now();\r\n// const testFilePath = getExistingTestFile(csFilePath);\r\n// console.log(`Test file for ${csFilePath}:`, testFilePath ?? 'No test file found');\r\n// console.log(`Search completed in ${(Date.now() - start) / 1000} seconds`);\r\n// createXapTestFile(csFilePath);\r\n\r\n// const repoDir = getCodeRepoRoot(\"D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\LocationDetection\\\\AudienceResponseParser.cs\");\r\n// console.log(`Code repository root directory: ${repoDir}`);\r\n\r\n// const result = getExistingTestFile(\"D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Eplant\\\\EplantConfigManager.cs\");\r\n// console.log(`Test file path: ${result.testFilePath}`);\r\n// console.log(`Test project path: ${result.testProjectPath}`);\r\n\r\n\r\n// falcon test project\r\n// const isFlaconUt = isFalconUnitTestProject(\"D:\\\\code\\\\WeatherNotification\\\\services\\\\WeatherUserInterest\\\\tests\\\\UnitTests\\\\Microsoft.Falcon.WeatherUserInterest.UnitTests.csproj\");\r\n// console.log(`Is Falcon Unit Test Project: ${isFlaconUt}`);\r\n// const testProject = findTestProjectFromCsFile(\"D:\\\\code\\\\WeatherNotification\\\\services\\\\WeatherUserInterest\\\\src\\\\Service\\\\Microsoft.Falcon.WeatherUserInterest.csproj\");\r\n// console.log(`Test project path: ${testProject}`);\r\n// const testFilePath = getOrCreatTestFile(\"D:\\\\code\\\\WeatherNotification\\\\services\\\\WeatherUserInterest\\\\src\\\\Service\\\\Components\\\\IUserInterestTable.cs\", undefined, false);\r\n// console.log(`Test file path: ${testFilePath.testFilePath}`);\r\n\r\n// ads projects\r\n// const testProjectPath = findTestProjectFromCsFile(\"D:\\\\code\\\\UCMDynamics\\\\private\\\\DynamicCode\\\\Plugin\\\\UCMDynamics.AppointmentPlugin\\\\AppointmentOperation.cs\");\r\n// console.log(`Test project path: ${testProjectPath}`);\r\n// const isValid = isGeneralTestProject(\"D:\\\\code\\\\UCMDynamics\\\\private\\\\DynamicCode\\\\Plugin\\\\UcmDynamics.AppointmentPlugin\\\\UcmDynamics.AppointmentPlugin.csproj\");\r\n// // const isValid = isGeneralTestProject(\"D:\\\\code\\\\UCMDynamics\\\\private\\\\DynamicCode\\\\Plugin\\\\UcmDynamics.AppointmentPlugin.Tests\\\\UcmDynamics.AppointmentPlugin.Tests.csproj\");\r\n// console.log(`Is General Test Project: ${isValid}`);\r\n\r\n// const testProjectPath = findTestProjectFromCsFile(\"D:\\\\code\\\\AIUnitTestExample\\\\src\\\\ToListinator.Analyzers\\\\ToListCountAnalyzer.cs\");\r\n// console.log(`Test project path: ${testProjectPath}`);"]}
|
|
1
|
+
{"version":3,"file":"getTestFile.js","sourceRoot":"","sources":["../../src/utils/getTestFile.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,gCAQC;AA4sBD,sDAqDC;AAwHD,gDAiEC;AAj9BD,uCAAyB;AACzB,2CAA6B;AAE7B,iEAAgI;AAChI,qDAAkD;AAElD,qDAA4C;AAE5C,MAAM,eAAe,GAAG,eAAe,CAAC;AACxC,MAAM,eAAe,GAAG,6CAA6C,CAAC;AAEtE;;;GAGG;AACH,SAAgB,UAAU,CAAC,QAAgB;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACvD,MAAM,kBAAkB,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAEzD,OAAO,kBAAkB,CAAC,QAAQ,CAAC,MAAM,CAAC;QACnC,kBAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC;QACpC,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC;QACvC,kBAAkB,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACpD,CAAC;AAED,gFAAgF;AAChF,MAAM,gBAAgB,GAAG,yCAAyC,CAAC;AAEnE,mDAAmD;AACnD,SAAS,sBAAsB,CAAC,cAAsB;IAClD,OAAO,IAAI,MAAM,CAAC,IAAI,cAAc,4BAA4B,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;GAMG;AACH,SAAS,oBAAoB,CAAC,IAAY;IACtC,OAAO,IAAI;SACN,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;SACtC,KAAK,CAAC,eAAe,CAAC;SACtB,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,uBAAuB,CAAC,KAAa,EAAE,KAAa;IACzD,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAE3C,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AACvD,CAAC;AAED;;;;;;GAMG;AACH,SAAS,0BAA0B,CAAC,OAAe;IAC/C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;gBAC9F,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;gBACzC,6BAA6B;gBAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC/C,MAAM,cAAc,GAAG,0BAA0B,CAAC,OAAO,CAAC,CAAC;gBAC3D,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACnD,CAAC;QACL,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,6CAA6C;IACjD,CAAC;IACD,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,qBAAqB,CAAC,OAAe;IAC1C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;gBACvE,SAAS;YACb,CAAC;YACD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACtB,iDAAiD;gBACjD,MAAM,YAAY,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;gBACrD,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAChD,CAAC;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtD,sCAAsC;gBACtC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YACzD,CAAC;QACL,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,6CAA6C;IACjD,CAAC;IACD,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW,EAAE,aAAuB;IAC5D,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3D,SAAS,CAAC,sBAAsB;QACpC,CAAC;QAED,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;QACjE,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1D,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,SAAS,gBAAgB,CAAC,UAAkB;IACxC,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,IAAI,2BAAS,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;QACnF,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAE7B,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;YACvD,CAAC,CAAC,OAAO,CAAC,aAAa;YACvB,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAE9B,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,cAAc,KAAK,aAAa,EAAE,CAAC;gBACzC,OAAO,IAAI,CAAC;YAChB,CAAC;QACL,CAAC;IACL,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,mBAAmB,UAAU,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,oBAAoB,CAAC,UAAkB;IAC5C,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAErD,wCAAwC;QACxC,MAAM,uBAAuB,GAAG,8FAA8F,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAE7I,8BAA8B;QAC9B,MAAM,gBAAgB,GAAG,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAEvI,yBAAyB;QACzB,MAAM,YAAY,GAAG,kDAAkD,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAEzF,yCAAyC;QACzC,MAAM,YAAY,GAAG,6CAA6C,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEjF,OAAO;QACP,OAAO,uBAAuB,IAAI,YAAY,IAAI,gBAAgB,IAAI,YAAY,CAAC;IACvF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,2BAA2B,UAAU,EAAE,CAAC,CAAC;QACvD,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AAED,SAAS,uBAAuB,CAAC,UAAkB;IAC/C,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAExD,MAAM,MAAM,GAAG,IAAI,2BAAS,CAAC;QACzB,gBAAgB,EAAE,KAAK;QACvB,mBAAmB,EAAE,EAAE;KAC1B,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAExC,4BAA4B;IAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAE9F,IAAI,uBAAuB,GAAG,KAAK,CAAC;IAEpC,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC7B,IAAG,CAAC,KAAK;YAAE,SAAS;QACpB,MAAM,IAAI,GAAG,KAAK,CAAC,gBAAgB,CAAC;QACpC,IAAI,CAAC,IAAI;YAAE,SAAS;QAEpB,mBAAmB;QACnB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAEpD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YACxB,IAAI,GAAG,CAAC,OAAO,KAAK,gCAAgC,EAAE,CAAC;gBACnD,uBAAuB,GAAG,IAAI,CAAC;gBAC/B,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;gBACpD,MAAM;YACV,CAAC;QACL,CAAC;QAED,IAAI,uBAAuB;YAAE,MAAM;IACvC,CAAC;IAED,kCAAkC;IAClC,+DAA+D;IAC/D,IAAI;IAEJ,OAAO,uBAAuB,CAAC;AACnC,CAAC;AAED,SAAS,+BAA+B,CAAC,UAAkB,EAAE,mBAA2B,EAAE,WAAmB;IACzG,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3E,MAAM,gBAAgB,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC;IAEnD,mEAAmE;IACnE,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACjF,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,eAAe,KAAK,gBAAgB,GAAG,MAAM,CAAC,CAAC;IAE1F,IAAI,UAAU,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,iDAAiD;IACjD,MAAM,uBAAuB,GAAG,IAAA,sCAAe,EAAC,UAAU,CAAC,CAAC;IAC5D,IAAI,uBAAuB,EAAE,CAAC;QAC1B,MAAM,iBAAiB,GAAG,uBAAuB,CAAC,WAAW,EAAE,CAAC;QAChE,MAAM,wBAAwB,GAAG,mBAAmB,CAAC,WAAW,EAAE,CAAC;QACnE,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,iBAAiB,KAAK,wBAAwB,GAAG,MAAM,CAAC,CAAC;IAC5F,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,gCAAgC,CACrC,UAAkB,EAClB,QAAgB,EAChB,eAAuB,EACvB,qBAA6B;IAE7B,IAAI,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,oBAAoB,GAAa,EAAE,CAAC;IAC1C,MAAM,aAAa,GAAa,CAAC,UAAU,CAAC,CAAC;IAC7C,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAElD,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACtC,OAAO,UAAU,KAAK,kBAAkB,EAAE,CAAC;QACvC,MAAM,WAAW,GAAG,kBAAkB,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;QAElE,+DAA+D;QAC/D,MAAM,qBAAqB,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;YACtD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YACjD,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;YACzC,uFAAuF;YACvF,IACI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAC7B,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAC5B,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC;gBACjC,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC;gBAChC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAC5B,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,EAC7B,CAAC;gBACC,OAAO,IAAI,CAAC;YAChB,CAAC;YAED,yDAAyD;YACzD,MAAM,KAAK,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;YAC5C,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;QAEH,KAAK,MAAM,MAAM,IAAI,qBAAqB,EAAE,CAAC;YACzC,qDAAqD;YACrD,MAAM,iBAAiB,GAAG,8BAA8B,CAAC,MAAM,CAAC,CAAC;YACjE,MAAM,uBAAuB,GAAG,uCAAuC,CAAC,MAAM,EAAE,qBAAqB,EAAE,iBAAiB,CAAC,CAAC;YAE1H,IAAI,CAAC,uBAAuB,EAAE,CAAC;gBAC3B,SAAS;YACb,CAAC;YAED,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;YAC9D,MAAM,mBAAmB,GAAG,IAAA,sCAAe,EAAC,eAAe,CAAC,CAAC;YAC7D,IAAI,+BAA+B,CAAC,MAAM,EAAE,mBAAmB,EAAE,WAAW,CAAC,EAAE,CAAC;gBAC5E,OAAO,CAAC,MAAM,CAAC,CAAC;YACpB,CAAC;YAED,8DAA8D;YAC9D,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;QAED,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/B,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,oBAAoB,CAAC;AAChC,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,yBAAyB,CAC9B,oBAA8B,EAC9B,eAAuB;IAEvB,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IACvD,MAAM,gBAAgB,GAAG,0BAA0B,CAAC,gBAAgB,CAAC,CAAC;IAEtE,MAAM,kBAAkB,GAAG,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;QACzD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,cAAc,GAAG,0BAA0B,CAAC,cAAc,CAAC,CAAC;QAElE,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE;YAChC,IAAI,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;gBACnC,UAAU,EAAE,CAAC;YACjB,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;IAE/D,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC;IAClE,OAAO,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC;AAC/C,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,wBAAwB,CAC7B,kBAAiE,EACjE,eAAuB;IAEvB,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IACvD,MAAM,eAAe,GAAG,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;IAChE,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAErF,MAAM,0BAA0B,GAAG,kBAAkB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE;QACxE,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,aAAa,GAAG,qBAAqB,CAAC,cAAc,CAAC,CAAC;QAE5D,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;YAC3C,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;gBAChC,MAAM,oBAAoB,GAAG,CAAC,cAAc,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;gBACrE,IAAI,aAAa,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,CAAC;oBAC1C,OAAO,IAAI,CAAC;gBAChB,CAAC;YACL,CAAC;QACL,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,OAAO,0BAA0B,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC;AACrF,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,yBAAyB,CAC9B,kBAAiE,EACjE,WAAmB;IAEnB,MAAM,sBAAsB,GAAG,kBAAkB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE;QACjE,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACtE,MAAM,cAAc,GAAG,uBAAuB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QAC7E,OAAO,EAAE,GAAG,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,MAAM,mBAAmB,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC;IAErF,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,EAAE,CAAC;IACd,CAAC;IAED,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC;IAExE,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC9D,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC1E,CAAC;AAED;;;;;;GAMG;AACH,SAAS,wBAAwB,CAC7B,kBAAiE,EACjE,WAAmB,CAAC;IAEpB,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC;IACrF,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AACzE,CAAC;AAED;;;;;;GAMG;AACH,SAAS,kCAAkC,CAAC,UAAkB;IAC1D,MAAM,QAAQ,GAAG,IAAA,sCAAe,EAAC,UAAU,CAAC,CAAC;IAC7C,MAAM,eAAe,GAAG,IAAA,kDAA2B,EAAC,UAAU,CAAC,CAAC;IAChE,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;IAC9D,MAAM,qBAAqB,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAE9D,yCAAyC;IACzC,MAAM,oBAAoB,GAAG,gCAAgC,CACzD,UAAU,EACV,QAAQ,EACR,eAAe,EACf,qBAAqB,CACxB,CAAC;IAEF,IAAI,oBAAoB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,CAAC;IACd,CAAC;IAED,IAAI,oBAAoB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,oBAAoB,CAAC;IAChC,CAAC;IAED,+BAA+B;IAC/B,MAAM,qBAAqB,GAAG,yBAAyB,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAC;IAC/F,IAAI,qBAAqB,KAAK,IAAI,EAAE,CAAC;QACjC,OAAO,wBAAwB,CAAC,qBAAqB,CAAC,CAAC;IAC3D,CAAC;IAED,4DAA4D;IAC5D,MAAM,kBAAkB,GAAG,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3F,MAAM,oBAAoB,GAAG,wBAAwB,CAAC,kBAAkB,EAAE,eAAe,CAAC,CAAC;IAC3F,IAAI,oBAAoB,KAAK,IAAI,EAAE,CAAC;QAChC,OAAO,wBAAwB,CAAC,oBAAoB,CAAC,CAAC;IAC1D,CAAC;IAED,uDAAuD;IACvD,MAAM,gBAAgB,GAAG,yBAAyB,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;IACpF,OAAO,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/D,CAAC;AAED,SAAS,qBAAqB,CAAC,cAAsB,EAAE,SAAiB;IACpE,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAa,CAAC,cAAc,CAAC,CAAC;IAEzC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;QAChC,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAEpE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;gBAC/C,SAAS;YACb,CAAC;YACD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACnD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACtB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBACxF,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3B,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,SAAS,0BAA0B,CAAC,QAAgB;IAChD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,OAAO,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,uCAAuC,CAC5C,cAAsB,EACtB,gBAAwB,EACxB,iBAA2B;IAE3B,MAAM,0BAA0B,GAAG,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,WAAW,EAAE,CAAC;IAClF,OAAO,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QAChC,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACtG,OAAO,eAAe,KAAK,0BAA0B,CAAC;IAC1D,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,8BAA8B,CAAC,UAAkB;IACtD,MAAM,iBAAiB,GAAa,EAAE,CAAC;IACvC,IAAI,CAAC;QACD,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACxD,MAAM,MAAM,GAAG,IAAI,2BAAS,CAAC;YACzB,gBAAgB,EAAE,KAAK;YACvB,mBAAmB,EAAE,EAAE;SAC1B,CAAC,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAE7C,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;QACpC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAE9F,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;YAC7B,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,MAAM,WAAW,GAAG,KAAK,CAAC,gBAAgB,CAAC;YAC3C,IAAI,CAAC,WAAW;gBAAE,SAAS;YAE3B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;YACzE,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;gBACxB,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;oBACd,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACxC,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,2BAA2B,UAAU,EAAE,EAAE,GAAG,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,iBAAiB,CAAC;AAC7B,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,sBAAsB,CAAC,gBAAwB,EAAE,kBAA0B;IAChF,8CAA8C;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAEnD,iFAAiF;IACjF,IAAI,CAAC,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,EAAE,CAAC,EAAE,CAAC;QACjF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC7C,CAAC;IAED,oDAAoD;IACpD,MAAM,WAAW,GAAG,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/G,MAAM,aAAa,GAAG,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAEvH,oEAAoE;IACpE,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAErE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,aAAa,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;YAClF,UAAU,EAAE,CAAC;QACjB,CAAC;aAAM,CAAC;YACJ,yBAAyB;YACzB,MAAM;QACV,CAAC;IACL,CAAC;IAED,sEAAsE;IACtE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAE/D,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,uBAAuB,CAAC,YAAoB,EAAE,cAAsB;IACzE,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAExD,gEAAgE;IAChE,uDAAuD;IACvD,0HAA0H;IAC1H,MAAM,WAAW,GAAG,sBAAsB,CAAC,cAAc,CAAC,CAAC;IAC3D,MAAM,cAAc,GAAG,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACtD,IAAI,CAAC,cAAc,EAAE,CAAC;QAClB,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,IAAI,CAAC;QACD,0BAA0B;QAC1B,MAAM,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAC3D,MAAM,aAAa,GAAG,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;QAE/D,qFAAqF;QACrF,MAAM,oBAAoB,GAAG,aAAa,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACzE,IAAI,CAAC,oBAAoB,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,sEAAsE;YACtE,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,eAAe,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;QAChD,8DAA8D;QAC9D,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,YAAY,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QACpF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,+CAA+C;YAC/C,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,iFAAiF;QACjF,sFAAsF;QACtF,8EAA8E;QAC9E,uDAAuD;QACvD,MAAM,eAAe,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,gBAAgB,gBAAgB,6CAA6C,CAAC,CAAC,CAAC;QACvI,MAAM,iBAAiB,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,kBAAkB,gBAAgB,6CAA6C,CAAC,CAAC,CAAC;QAE3I,MAAM,eAAe,GAAG,eAAe,IAAI,iBAAiB,CAAC;QAC7D,IAAI,CAAC,eAAe,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1C,8EAA8E;YAC9E,6DAA6D;YAC7D,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,MAAM,cAAc,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;QAC1C,sEAAsE;QACtE,6EAA6E;QAC7E,uEAAuE;QACvE,MAAM,iBAAiB,GAAG,IAAI,MAAM,CAAC,6BAA6B,gBAAgB,uBAAuB,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC/H,IAAI,4BAAoC,CAAC;QACzC,IAAI,iBAAiB,EAAE,CAAC;YACpB,4CAA4C;YAC5C,4BAA4B,GAAG,IAAI,MAAM,CAAC,MAAM,cAAc,KAAK,EAAE,GAAG,CAAC,CAAC;QAC9E,CAAC;aAAM,CAAC;YACJ,qEAAqE;YACrE,4BAA4B,GAAG,IAAI,MAAM,CAAC,UAAU,cAAc,KAAK,EAAE,GAAG,CAAC,CAAC;QAClF,CAAC;QAED,IAAI,4BAA4B,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YACjD,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,0DAA0D;QAC1D,OAAO,KAAK,CAAC;IAEjB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,6CAA6C,GAAG,EAAE,CAAC,CAAC;QAClE,qEAAqE;QACrE,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,mBAAmB,CACxB,cAAsB,EACtB,cAAsB,EACtB,gBAAwB;IAExB,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACxD,IAAI,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC9C,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,0DAA0D;IAC1D,OAAO,UAAU,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,IAAI,UAAU,CAAC,MAAM,IAAI,gBAAgB,CAAC,MAAM,EAAE,CAAC;QACzH,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACpE,MAAM,OAAO,GAAG,OAAO;iBAClB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,cAAc,CAAC;iBACrG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YAE7C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC3B,qDAAqD;gBACrD,MAAM,MAAM,GAAG,6BAA6B,CAAC,cAAc,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC;gBACvF,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;oBACtB,OAAO,EAAE,mBAAmB,EAAE,MAAM,CAAC,YAAY,EAAE,mBAAmB,EAAE,KAAK,EAAE,CAAC;gBACpF,CAAC;YACL,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,6CAA6C;QACjD,CAAC;QAED,2BAA2B;QAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;YAC3B,MAAM;QACV,CAAC;QACD,UAAU,GAAG,SAAS,CAAC;QACvB,KAAK,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,qBAAqB,CAAC,cAAsB,EAAE,cAAuB;IACjF,IAAI,eAAe,GAAG,cAAc,CAAC;IACrC,IAAI,CAAC,eAAe,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,6BAA6B,cAAc,EAAE,CAAC,CAAC;QAC7D,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;IACzD,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,GAAG,cAAc,UAAU,CAAC;IACjD,MAAM,qBAAqB,GAAG,IAAA,8CAAuB,EAAC,cAAc,CAAC,CAAC;IACtE,MAAM,gBAAgB,GAAG,IAAA,kDAA2B,EAAC,cAAc,CAAC,CAAC;IAErE,+DAA+D;IAC/D,MAAM,aAAa,GAAG,mBAAmB,CAAC,cAAc,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAC;IAE7F,IAAI,YAAoB,CAAC;IACzB,IAAI,aAAa,EAAE,CAAC;QAChB,oEAAoE;QACpE,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC;QACvE,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QAEnD,IAAI,aAAa,CAAC,mBAAmB,KAAK,CAAC,EAAE,CAAC;YAC1C,6EAA6E;YAC7E,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QAC3D,CAAC;aAAM,CAAC;YACJ,sGAAsG;YACtG,IAAI,aAAa,GAAa,EAAE,CAAC;YACjC,IAAI,OAAO,GAAG,aAAa,CAAC;YAE5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,mBAAmB,EAAE,CAAC,EAAE,EAAE,CAAC;gBACzD,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;gBAC9C,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACpC,CAAC;YAED,yEAAyE;YACzE,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClD,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;QACzE,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,iDAAiD;QACjD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACnD,MAAM,oBAAoB,GAAG,aAAa,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;QAC9E,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QACpD,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,oBAAoB,EAAE,YAAY,CAAC,CAAC;IAChF,CAAC;IAED,wCAAwC;IACxC,MAAM,aAAa,GAAG,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAClD,IAAI,aAAa,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,+BAA+B,YAAY,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,6BAA6B,CAClC,cAAsB,EACtB,cAAsB,EACtB,gBAAwB;IAExB,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAEpD,qCAAqC;IACrC,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvD,mEAAmE;IACnE,MAAM,kBAAkB,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;IAE1E,qCAAqC;IACrC,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IAE5D,iFAAiF;IACjF,MAAM,YAAY,GAAG,qBAAqB,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;IAE3E,qEAAqE;IACrE,MAAM,WAAW,GAAG,sBAAsB,CAAC,cAAc,CAAC,CAAC;IAE3D,uDAAuD;IACvD,MAAM,oBAAoB,GAAG,YAAY;SACpC,MAAM,CAAC,IAAI,CAAC,EAAE;QACX,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAChD,OAAO,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1C,CAAC,CAAC;SACD,GAAG,CAAC,IAAI,CAAC,EAAE;QACR,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,sBAAsB,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;QAC3E,OAAO;YACH,QAAQ,EAAE,IAAI;YACd,YAAY,EAAE,gBAAgB;YAC9B,KAAK,EAAE,KAAK;SACf,CAAC;IACN,CAAC,CAAC;QACF,sDAAsD;SACrD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACX,IAAI,CAAC,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;YAC5C,OAAO,CAAC,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC;QACnD,CAAC;QACD,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IACzC,CAAC,CAAC,CAAC;IAEP,KAAK,MAAM,SAAS,IAAI,oBAAoB,EAAE,CAAC;QAC3C,IAAI,uBAAuB,CAAC,SAAS,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,CAAC;YAC9D,OAAO,EAAE,YAAY,EAAE,SAAS,CAAC,QAAQ,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC;QACjF,CAAC;IACL,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;AACzD,CAAC;AAED,SAAS,2BAA2B,CAAC,cAAsB,EAAE,gBAA2B;IACpF,IAAI,CAAC,gBAAgB,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrD,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;IACzD,CAAC;IAED,oCAAoC;IACpC,MAAM,gBAAgB,GAAG,IAAA,kDAA2B,EAAC,cAAc,CAAC,CAAC;IACrE,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpB,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;IACzD,CAAC;IAED,oEAAoE;IACpE,MAAM,WAAW,GAAG,IAAI,GAAG,EAAoB,CAAC;IAChD,wDAAwD;IACxD,MAAM,uBAAuB,GAAG,IAAI,GAAG,EAAU,CAAC;IAElD,KAAK,MAAM,YAAY,IAAI,gBAAgB,EAAE,CAAC;QAC1C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAC/B,SAAS;QACb,CAAC;QAED,kCAAkC;QAClC,MAAM,cAAc,GAAG,IAAA,kDAA2B,EAAC,YAAY,CAAC,CAAC;QACjE,IAAI,CAAC,cAAc,EAAE,CAAC;YAClB,SAAS;QACb,CAAC;QAED,oBAAoB;QACpB,IAAI,iBAAiB,GAAG,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACxD,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACrB,iBAAiB,GAAG,8BAA8B,CAAC,cAAc,CAAC,CAAC;YACnE,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,iBAAiB,CAAC,CAAC;QACvD,CAAC;QAED,sDAAsD;QACtD,MAAM,0BAA0B,GAAG,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACpE,IAAI,uCAAuC,CAAC,cAAc,EAAE,0BAA0B,EAAE,iBAAiB,CAAC,EAAE,CAAC;YACzG,uBAAuB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAC5C,iDAAiD;YACjD,IAAI,uBAAuB,CAAC,YAAY,EAAE,cAAc,CAAC,EAAE,CAAC;gBACxD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC;YAC7D,CAAC;QACL,CAAC;IACL,CAAC;IAED,gFAAgF;IAChF,KAAK,MAAM,cAAc,IAAI,uBAAuB,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,6BAA6B,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,CAAC,CAAC;QAC/F,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACtB,OAAO,MAAM,CAAC;QAClB,CAAC;IACL,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;AACzD,CAAC;AAED,SAAgB,kBAAkB,CAAC,cAAsB,EAAE,YAAoB,EAAE,gBAA2B;IACxG,IAAI,iBAAiB,GAAG,YAAY,CAAC;IACrC,IAAI,oBAAoB,CAAC;IACzB,IAAI,aAAa,GAAG,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAElE,gEAAgE;IAChE,IAAI,aAAa,EAAE,CAAC;QAChB,oBAAoB,GAAG,IAAA,kDAA2B,EAAC,YAAY,CAAC,CAAC;QACjE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,oBAAoB,EAAE,aAAa,EAAE,CAAC;IACrG,CAAC;IAED,yEAAyE;IACzE,mEAAmE;IACnE,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,sCAAsC,EAAE,YAAY,EAAE,2CAA2C,CAAC,CAAC;QAE/G,iDAAiD;QACjD,IAAI,sBAAsB,GAAG,2BAA2B,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;QAC3F,IAAI,sBAAsB,CAAC,YAAY,EAAE,CAAC;YACtC,OAAO,EAAE,YAAY,EAAE,sBAAsB,CAAC,YAAY,EAAE,eAAe,EAAE,sBAAsB,CAAC,eAAe,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;QAC/I,CAAC;QAED,2CAA2C;QAC3C,MAAM,mBAAmB,GAAG,kCAAkC,CAAC,cAAc,CAAC,CAAC;QAC/E,IAAI,CAAC,mBAAmB,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3D,OAAO,CAAC,KAAK,CAAC,2CAA2C,cAAc,EAAE,CAAC,CAAC;YAC3E,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;QAC/E,CAAC;QAED,0CAA0C;QAC1C,MAAM,gBAAgB,GAAG,IAAA,kDAA2B,EAAC,cAAc,CAAC,CAAC;QACrE,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,mCAAmC,cAAc,EAAE,CAAC,CAAC;YACnE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;QAC/E,CAAC;QAED,+DAA+D;QAC/D,KAAK,MAAM,cAAc,IAAI,mBAAmB,EAAE,CAAC;YAC/C,MAAM,MAAM,GAAG,6BAA6B,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,CAAC,CAAC;YAC/F,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;gBACtB,aAAa,GAAG,IAAI,CAAC;gBACrB,iBAAiB,GAAG,MAAM,CAAC,YAAY,CAAC;gBACxC,oBAAoB,GAAG,MAAM,CAAC,eAAe,CAAC;gBAC9C,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,oBAAoB,EAAE,aAAa,EAAE,CAAC;YACrG,CAAC;QACL,CAAC;QAED,4EAA4E;QAC5E,MAAM,MAAM,GAAG,qBAAqB,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7E,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAClD,OAAO,CAAC,KAAK,CAAC,0CAA0C,cAAc,EAAE,CAAC,CAAC;YAC1E,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;QAC/E,CAAC;QACD,iBAAiB,GAAG,MAAM,CAAC,YAAY,CAAC;QACxC,oBAAoB,GAAG,MAAM,CAAC,eAAe,CAAC;QAC9C,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,KAAK,CAAC;QAE9C,8CAA8C;QAC9C,IAAI,CAAC,aAAa,EAAE,CAAC;YACjB,IAAA,+BAAc,EAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC;QACpD,CAAC;IACL,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,oBAAoB,EAAE,aAAa,EAAE,CAAC;AAErG,CAAC;AAED,6CAA6C;AAC7C,yCAAyC;AACzC,+DAA+D;AAC/D,gEAAgE;AAChE,oCAAoC;AAEpC,YAAY;AACZ,0DAA0D;AAC1D,iEAAiE;AACjE,gDAAgD;AAChD,oBAAoB;AACpB,gFAAgF;AAChF,iDAAiD;AACjD,mEAAmE;AACnE,yHAAyH;AACzH,4EAA4E;AAC5E,qFAAqF;AACrF,oEAAoE;AACpE,8EAA8E;AAC9E,gDAAgD;AAChD,sEAAsE;AACtE,sDAAsD;AACtD,+CAA+C;AAC/C,0DAA0D;AAE1D,+FAA+F;AAC/F,0DAA0D;AAC1D,4BAA4B;AAC5B,wBAAwB;AACxB,oBAAoB;AACpB,8BAA8B;AAC9B,mDAAmD;AACnD,gBAAgB;AAChB,kCAAkC;AAClC,aAAa;AAEb,kEAAkE;AAClE,mEAAmE;AACnE,oBAAoB;AACpB,gFAAgF;AAChF,yEAAyE;AACzE,iDAAiD;AACjD,0EAA0E;AAC1E,6DAA6D;AAC7D,wBAAwB;AACxB,oBAAoB;AACpB,8DAA8D;AAC9D,iDAAiD;AACjD,yHAAyH;AACzH,uEAAuE;AACvE,mEAAmE;AACnE,wCAAwC;AACxC,6CAA6C;AAC7C,4BAA4B;AAC5B,wBAAwB;AACxB,oBAAoB;AACpB,8BAA8B;AAC9B,qDAAqD;AACrD,gBAAgB;AAChB,2BAA2B;AAC3B,aAAa;AAEb,mCAAmC;AACnC,6DAA6D;AAE7D,iCAAiC;AACjC,kDAAkD;AAClD,0DAA0D;AAC1D,wDAAwD;AAExD,4BAA4B;AAC5B,wEAAwE;AACxE,2EAA2E;AAC3E,iFAAiF;AACjF,0HAA0H;AAC1H,2DAA2D;AAC3D,gBAAgB;AAChB,YAAY;AAEZ,mCAAmC;AACnC,qEAAqE;AACrE,2DAA2D;AAC3D,sBAAsB;AACtB,8DAA8D;AAC9D,QAAQ;AACR,IAAI;AAEJ,mCAAmC;AACnC,yBAAyB;AAEzB,+HAA+H;AAG/H,gBAAgB;AAChB,0KAA0K;AAC1K,0HAA0H;AAC1H,iJAAiJ;AACjJ,qBAAqB;AACrB,4EAA4E;AAC5E,uBAAuB;AACvB,IAAI;AAEJ,4BAA4B;AAC5B,wDAAwD;AACxD,qFAAqF;AACrF,6EAA6E;AAC7E,iCAAiC;AAEjC,wLAAwL;AACxL,6DAA6D;AAE7D,6KAA6K;AAC7K,yDAAyD;AACzD,+DAA+D;AAG/D,sBAAsB;AACtB,uLAAuL;AACvL,6DAA6D;AAC7D,4KAA4K;AAC5K,oDAAoD;AACpD,8KAA8K;AAC9K,+DAA+D;AAE/D,eAAe;AACf,oKAAoK;AACpK,wDAAwD;AACxD,oKAAoK;AACpK,mLAAmL;AACnL,sDAAsD;AAEtD,yIAAyI;AACzI,wDAAwD","sourcesContent":["import * as fs from 'fs';\r\nimport * as path from 'path';\r\n\r\nimport { getAssemblyName, getCodeRepoRoot, getSourceFileCsprojFilePath, getSourceFileProjectDir } from './getCodeStructurePath';\r\nimport { getOrCreateDir } from './getOrCreateDir';\r\n\r\nimport { XMLParser } from 'fast-xml-parser';\r\n\r\nconst TEST_NAME_REGEX = /(Test|Tests)/i;\r\nconst TEST_ATTR_REGEX = /\\[(Fact|Theory|TestClass|TestMethod|Test)\\]/;\r\n\r\n/**\r\n * Check if a file path is a test file based on common naming conventions.\r\n * Checks if the file name (without .cs extension) ends with test, tests, unittest, or unittests.\r\n */\r\nexport function isTestFile(filepath: string): boolean {\r\n const fileName = path.basename(filepath).toLowerCase();\r\n const fileNameWithoutExt = fileName.replace(/\\.cs$/, '');\r\n \r\n return fileNameWithoutExt.endsWith('test') || \r\n fileNameWithoutExt.endsWith('tests') ||\r\n fileNameWithoutExt.endsWith('unittest') ||\r\n fileNameWithoutExt.endsWith('unittests');\r\n}\r\n\r\n// Modifier pattern for C# type declarations (sealed, abstract, static, partial)\r\nconst MODIFIER_PATTERN = `(?:sealed|abstract|static|partial)?\\\\s*`;\r\n\r\n// Helper function to create test file name pattern\r\nfunction getTestFileNamePattern(sourceFileName: string): RegExp {\r\n return new RegExp(`^${sourceFileName}(Unit)?(\\\\.)?(Test|Tests)$`);\r\n}\r\n\r\n/**\r\n * Extract words from a name string (e.g., project name, file name)\r\n * Splits by camelCase and special characters, returns lowercase words\r\n * \r\n * @param name - The name string to extract words from\r\n * @returns Array of lowercase words\r\n */\r\nfunction extractWordsFromName(name: string): string[] {\r\n return name\r\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\r\n .split(/[^a-zA-Z0-9]+/)\r\n .filter(Boolean)\r\n .map(w => w.toLowerCase());\r\n}\r\n\r\n/**\r\n * Calculate word-level matching score between two names\r\n * Returns the count of matching words\r\n * \r\n * @param name1 - First name to compare\r\n * @param name2 - Second name to compare\r\n * @returns Number of matching words\r\n */\r\nfunction calculateWordMatchCount(name1: string, name2: string): number {\r\n const words1 = extractWordsFromName(name1);\r\n const words2 = extractWordsFromName(name2);\r\n \r\n const words1Set = new Set(words1);\r\n return words2.filter(w => words1Set.has(w)).length;\r\n}\r\n\r\n/**\r\n * Get all subfolder names under a directory (recursive)\r\n * Skips: bin, obj, .vs directories\r\n * \r\n * @param dirPath - Path to directory\r\n * @returns Set of lowercase folder names\r\n */\r\nfunction getSubfolderNamesRecursive(dirPath: string): Set<string> {\r\n const subfolders = new Set<string>();\r\n try {\r\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\r\n for (const entry of entries) {\r\n if (entry.isDirectory() && entry.name !== 'bin' && entry.name !== 'obj' && entry.name !== '.vs') {\r\n subfolders.add(entry.name.toLowerCase());\r\n // Recursively get subfolders\r\n const subPath = path.join(dirPath, entry.name);\r\n const deepSubfolders = getSubfolderNamesRecursive(subPath);\r\n deepSubfolders.forEach(f => subfolders.add(f));\r\n }\r\n }\r\n } catch (err) {\r\n // Ignore errors for inaccessible directories\r\n }\r\n return subfolders;\r\n}\r\n\r\n/**\r\n * Get all .cs file names under a directory (recursive)\r\n * Skips: bin, obj, .vs directories\r\n * \r\n * @param dirPath - Path to directory\r\n * @returns Set of lowercase file names (without .cs extension)\r\n */\r\nfunction getFileNamesRecursive(dirPath: string): Set<string> {\r\n const fileNames = new Set<string>();\r\n try {\r\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\r\n for (const entry of entries) {\r\n if (entry.name === 'bin' || entry.name === 'obj' || entry.name === '.vs') {\r\n continue;\r\n }\r\n const fullPath = path.join(dirPath, entry.name);\r\n if (entry.isDirectory()) {\r\n // Recursively get file names from subdirectories\r\n const subFileNames = getFileNamesRecursive(fullPath);\r\n subFileNames.forEach(f => fileNames.add(f));\r\n } else if (entry.isFile() && entry.name.endsWith('.cs')) {\r\n // Add file name without .cs extension\r\n fileNames.add(entry.name.slice(0, -3).toLowerCase());\r\n }\r\n }\r\n } catch (err) {\r\n // Ignore errors for inaccessible directories\r\n }\r\n return fileNames;\r\n}\r\n\r\nfunction findAllCsprojFiles(dir: string, excludedPaths: string[]): string[] {\r\n const results: string[] = [];\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n if (entry.name === \".vs\" || excludedPaths.includes(fullPath)) {\r\n continue; // Skip excluded paths\r\n }\r\n\r\n if (entry.isDirectory()) {\r\n results.push(...findAllCsprojFiles(fullPath, excludedPaths));\r\n } else if (entry.isFile() && entry.name.endsWith('.csproj')) {\r\n results.push(fullPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\nfunction isXapTestProject(csprojPath: string): boolean {\r\n try {\r\n const content = fs.readFileSync(csprojPath, 'utf-8');\r\n const parser = new XMLParser({ ignoreAttributes: false, ignoreDeclaration: true });\r\n const json = parser.parse(content);\r\n const project = json.Project;\r\n\r\n const propertyGroups = Array.isArray(project.PropertyGroup)\r\n ? project.PropertyGroup\r\n : [project.PropertyGroup];\r\n\r\n for (const group of propertyGroups) {\r\n if (group.XapProjectType === 'TestProject') {\r\n return true;\r\n }\r\n }\r\n } catch (err: any) {\r\n console.error(`Failed to parse ${csprojPath}: ${err.message}`);\r\n }\r\n return false;\r\n}\r\n\r\nfunction isGeneralTestProject(csprojPath: string): boolean {\r\n try {\r\n const content = fs.readFileSync(csprojPath, 'utf-8');\r\n\r\n // 1. package reference (test framework)\r\n const referencesTestFramework = /<PackageReference\\s+Include=\"(xunit|nunit|MSTest\\.TestFramework|Microsoft\\.NET\\.Test\\.Sdk)\"/i.test(content);\r\n\r\n // 2. file name *.Tests.csproj\r\n const fileNameHasTests = (/\\.Tests\\.csproj$/i.test(path.basename(csprojPath))) || (/\\.Test\\.csproj$/i.test(path.basename(csprojPath)));\r\n\r\n // 3. path contains Tests\r\n const pathHasTests = /[\\\\/](Tests?|UnitTests?|IntegrationTests?)[\\\\/]/i.test(csprojPath);\r\n\r\n // 4. Project SDK is test type (optional)\r\n const sdkIsTestSdk = /<Project\\s+Sdk=\"Microsoft\\.NET\\.Test\\.Sdk\"/i.test(content);\r\n\r\n // 组合判断\r\n return referencesTestFramework || sdkIsTestSdk || fileNameHasTests || pathHasTests;\r\n } catch (err) {\r\n console.error(`can't read csproj file: ${csprojPath}`);\r\n return false;\r\n }\r\n}\r\n\r\nfunction isFalconUnitTestProject(csprojPath: string): boolean {\r\n const xmlContent = fs.readFileSync(csprojPath, 'utf-8');\r\n\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n attributeNamePrefix: ''\r\n });\r\n const parsed = parser.parse(xmlContent);\r\n\r\n // get PackageReference node\r\n const project = parsed.Project;\r\n const itemGroups = Array.isArray(project.ItemGroup) ? project.ItemGroup : [project.ItemGroup];\r\n\r\n let isFalconUnitTestProject = false;\r\n\r\n for (const group of itemGroups) {\r\n if(!group) continue;\r\n const refs = group.PackageReference;\r\n if (!refs) continue;\r\n\r\n // PackageReference\r\n const refList = Array.isArray(refs) ? refs : [refs];\r\n\r\n for (const ref of refList) {\r\n if (ref.Include === 'Microsoft.Falcon.CodeAnalyzers') {\r\n isFalconUnitTestProject = true;\r\n console.log('Found Microsoft.Falcon.CodeAnalyzers');\r\n break;\r\n }\r\n }\r\n\r\n if (isFalconUnitTestProject) break;\r\n }\r\n\r\n // if (!isFalconUnitTestProject) {\r\n // console.log('Microsoft.Falcon.CodeAnalyzers not found');\r\n // }\r\n\r\n return isFalconUnitTestProject;\r\n}\r\n\r\nfunction isProjectNameMatchedTestProject(csprojPath: string, projectAssemblyName: string, projectName: string) {\r\n const testProjectName = path.basename(csprojPath, '.csproj').toLowerCase();\r\n const projectNameLower = projectName.toLowerCase();\r\n \r\n // Check if test project name exactly matches: projectName + suffix\r\n const suffixes = ['.tests', '.test', '.unittest', '.unittests', 'tests', 'test'];\r\n const exactMatch = suffixes.some(suffix => testProjectName === projectNameLower + suffix);\r\n \r\n if (exactMatch) {\r\n return true;\r\n }\r\n \r\n // Fallback: check assembly name with exact match\r\n const testProjectAssemblyName = getAssemblyName(csprojPath);\r\n if (testProjectAssemblyName) {\r\n const assemblyNameLower = testProjectAssemblyName.toLowerCase();\r\n const projectAssemblyNameLower = projectAssemblyName.toLowerCase();\r\n return suffixes.some(suffix => assemblyNameLower === projectAssemblyNameLower + suffix);\r\n }\r\n \r\n return false;\r\n}\r\n\r\n/**\r\n * Search for test projects in repository directories\r\n * Walks up from source file directory and collects all test projects that reference the source project\r\n * \r\n * @param csFilePath - Path to the source .cs file\r\n * @param repoRoot - Repository root directory\r\n * @param projectFilePath - Path to source project's .csproj\r\n * @param normalizedProjectPath - Normalized path to source project\r\n * @returns Array of test project paths that reference the source project\r\n */\r\nfunction searchTestProjectsInRepositories(\r\n csFilePath: string,\r\n repoRoot: string,\r\n projectFilePath: string,\r\n normalizedProjectPath: string\r\n): string[] {\r\n let currentDir = path.dirname(csFilePath);\r\n const possibleTestProjects: string[] = [];\r\n const excludedPaths: string[] = [currentDir];\r\n const repoRootParentPath = path.dirname(repoRoot);\r\n \r\n currentDir = path.dirname(currentDir);\r\n while (currentDir !== repoRootParentPath) {\r\n const csprojFiles = findAllCsprojFiles(currentDir, excludedPaths);\r\n \r\n // Filter csproj files by standard test project naming patterns\r\n const testProjectCandidates = csprojFiles.filter(csproj => {\r\n const rawName = path.basename(csproj, '.csproj');\r\n const csprojName = rawName.toLowerCase();\r\n // Standard test naming: ends with .tests, .test, .unittests, .unittest, Tests, or Test\r\n if (\r\n csprojName.endsWith('.tests') || \r\n csprojName.endsWith('.test') || \r\n csprojName.endsWith('.unittests') || \r\n csprojName.endsWith('.unittest') ||\r\n csprojName.endsWith('tests') ||\r\n csprojName.endsWith('test')\r\n ) {\r\n return true;\r\n }\r\n\r\n // Word-level match: any word ends with \"test\" or \"tests\"\r\n const words = extractWordsFromName(rawName);\r\n return words.some(w => w.endsWith('test') || w.endsWith('tests'));\r\n });\r\n \r\n for (const csproj of testProjectCandidates) {\r\n // Check if this csproj references the source project\r\n const projectReferences = getProjectReferencesFromCsproj(csproj);\r\n const referencesSourceProject = checkTestProjectReferencesSourceProject(csproj, normalizedProjectPath, projectReferences);\r\n \r\n if (!referencesSourceProject) {\r\n continue;\r\n }\r\n \r\n const projectName = path.basename(projectFilePath, '.csproj');\r\n const projectAssemblyName = getAssemblyName(projectFilePath);\r\n if (isProjectNameMatchedTestProject(csproj, projectAssemblyName, projectName)) {\r\n return [csproj];\r\n }\r\n \r\n // Collect all test projects that reference the source project\r\n possibleTestProjects.push(csproj);\r\n }\r\n \r\n excludedPaths.push(currentDir);\r\n currentDir = path.dirname(currentDir);\r\n }\r\n \r\n return possibleTestProjects;\r\n}\r\n\r\n/**\r\n * Filter test projects by subfolder matching\r\n * Returns projects sorted by match count, or proceeds to file name matching if all zero\r\n * \r\n * @param possibleTestProjects - List of test project paths\r\n * @param projectFilePath - Path to source project's .csproj\r\n * @returns Filtered projects with scores, or null if needs file name matching\r\n */\r\nfunction filterBySubfolderMatching(\r\n possibleTestProjects: string[],\r\n projectFilePath: string\r\n): Array<{ csproj: string, matchCount: number }> | null {\r\n const sourceProjectDir = path.dirname(projectFilePath);\r\n const sourceSubfolders = getSubfolderNamesRecursive(sourceProjectDir);\r\n\r\n const projectsWithScores = possibleTestProjects.map(csproj => {\r\n const testProjectDir = path.dirname(csproj);\r\n const testSubfolders = getSubfolderNamesRecursive(testProjectDir);\r\n \r\n let matchCount = 0;\r\n testSubfolders.forEach(folderName => {\r\n if (sourceSubfolders.has(folderName)) {\r\n matchCount++;\r\n }\r\n });\r\n \r\n return { csproj, matchCount };\r\n });\r\n\r\n projectsWithScores.sort((a, b) => b.matchCount - a.matchCount);\r\n\r\n const allZero = projectsWithScores.every(p => p.matchCount === 0);\r\n return allZero ? null : projectsWithScores;\r\n}\r\n\r\n/**\r\n * Filter test projects by file name matching with test suffixes\r\n * Returns filtered projects if any matches found\r\n * \r\n * @param projectsWithScores - Projects with scores\r\n * @param projectFilePath - Path to source project's .csproj\r\n * @returns Filtered projects by file name, or null if no matches\r\n */\r\nfunction filterByFileNameMatching(\r\n projectsWithScores: Array<{ csproj: string, matchCount: number }>,\r\n projectFilePath: string\r\n): Array<{ csproj: string, matchCount: number }> | null {\r\n const sourceProjectDir = path.dirname(projectFilePath);\r\n const sourceFileNames = getFileNamesRecursive(sourceProjectDir);\r\n const testSuffixes = ['.tests', '.test', '.unittests', '.unittest', 'tests', 'test'];\r\n\r\n const filteredProjectsWithScores = projectsWithScores.filter(projectScore => {\r\n const testProjectDir = path.dirname(projectScore.csproj);\r\n const testFileNames = getFileNamesRecursive(testProjectDir);\r\n\r\n for (const sourceFileName of sourceFileNames) {\r\n for (const suffix of testSuffixes) {\r\n const expectedTestFileName = (sourceFileName + suffix).toLowerCase();\r\n if (testFileNames.has(expectedTestFileName)) {\r\n return true;\r\n }\r\n }\r\n }\r\n return false;\r\n });\r\n\r\n return filteredProjectsWithScores.length > 0 ? filteredProjectsWithScores : null;\r\n}\r\n\r\n/**\r\n * Filter test projects by word-level matching\r\n * Returns top 2 projects with highest word match count\r\n * \r\n * @param projectsWithScores - Projects with scores\r\n * @param projectName - Name of source project\r\n * @returns Top 2 projects by word match count, or empty array if no matches\r\n */\r\nfunction filterByWordLevelMatching(\r\n projectsWithScores: Array<{ csproj: string, matchCount: number }>,\r\n projectName: string\r\n): string[] {\r\n const projectsWithWordScores = projectsWithScores.map(projectScore => {\r\n const testProjectName = path.basename(projectScore.csproj, '.csproj');\r\n const wordMatchCount = calculateWordMatchCount(projectName, testProjectName);\r\n return { ...projectScore, wordMatchCount };\r\n });\r\n\r\n const projectsWithMatches = projectsWithWordScores.filter(p => p.wordMatchCount > 0);\r\n\r\n if (projectsWithMatches.length === 0) {\r\n return [];\r\n }\r\n\r\n projectsWithMatches.sort((a, b) => b.wordMatchCount - a.wordMatchCount);\r\n\r\n const countToReturn = Math.min(2, projectsWithMatches.length);\r\n return projectsWithMatches.slice(0, countToReturn).map(p => p.csproj);\r\n}\r\n\r\n/**\r\n * Select top projects by score percentage or minimum count\r\n * \r\n * @param projectsWithScores - Projects with scores\r\n * @param minCount - Minimum projects to return (default 2)\r\n * @returns Array of selected project paths\r\n */\r\nfunction selectTopProjectsByScore(\r\n projectsWithScores: Array<{ csproj: string, [key: string]: any }>,\r\n minCount: number = 2\r\n): string[] {\r\n const countToReturn = Math.max(minCount, Math.ceil(projectsWithScores.length * 0.3));\r\n return projectsWithScores.slice(0, countToReturn).map(p => p.csproj);\r\n}\r\n\r\n/**\r\n * Find possible test projects for a C# source file\r\n * Uses multi-level matching: folder names -> file names -> word-level matching\r\n * \r\n * @param csFilePath - Path to the source .cs file\r\n * @returns Array of possible test project paths\r\n */\r\nfunction findPossibleTestProjectsFromCsFile(csFilePath: string): string[] {\r\n const repoRoot = getCodeRepoRoot(csFilePath);\r\n const projectFilePath = getSourceFileCsprojFilePath(csFilePath);\r\n const projectName = path.basename(projectFilePath, '.csproj');\r\n const normalizedProjectPath = path.normalize(projectFilePath);\r\n\r\n // Search for test projects in repository\r\n const possibleTestProjects = searchTestProjectsInRepositories(\r\n csFilePath,\r\n repoRoot,\r\n projectFilePath,\r\n normalizedProjectPath\r\n );\r\n\r\n if (possibleTestProjects.length === 0) {\r\n return [];\r\n }\r\n\r\n if (possibleTestProjects.length === 1) {\r\n return possibleTestProjects;\r\n }\r\n\r\n // Try subfolder-level matching\r\n const subfolderMatchResults = filterBySubfolderMatching(possibleTestProjects, projectFilePath);\r\n if (subfolderMatchResults !== null) {\r\n return selectTopProjectsByScore(subfolderMatchResults);\r\n }\r\n\r\n // If subfolder matching is all zero, try file name matching\r\n const projectsWithScores = possibleTestProjects.map(csproj => ({ csproj, matchCount: 0 }));\r\n const fileNameMatchResults = filterByFileNameMatching(projectsWithScores, projectFilePath);\r\n if (fileNameMatchResults !== null) {\r\n return selectTopProjectsByScore(fileNameMatchResults);\r\n }\r\n\r\n // If file name matching fails, try word-level matching\r\n const wordLevelResults = filterByWordLevelMatching(projectsWithScores, projectName);\r\n return wordLevelResults.length > 0 ? wordLevelResults : [];\r\n}\r\n\r\nfunction findPossibleTestFiles(testProjectDir: string, className: string): string[] {\r\n const results: string[] = [];\r\n const stack: string[] = [testProjectDir];\r\n\r\n while (stack.length > 0) {\r\n const currentDir = stack.pop()!;\r\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n if (entry.name === \"bin\" || entry.name === \"obj\") {\r\n continue;\r\n }\r\n const fullPath = path.join(currentDir, entry.name);\r\n if (entry.isDirectory()) {\r\n stack.push(fullPath);\r\n } else if (entry.isFile() && entry.name.endsWith('.cs') && entry.name.includes(className)) {\r\n results.push(fullPath);\r\n }\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\nfunction fileContainsTestAttributes(filePath: string): boolean {\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n return TEST_ATTR_REGEX.test(content);\r\n}\r\n\r\n/**\r\n * Check if a test project references a source project.\r\n * \r\n * @param testCsprojPath - Path to the test project's csproj file\r\n * @param sourceCsprojPath - Path to the source project's csproj file\r\n * @param projectReferences - Project references from the test csproj\r\n * @returns True if test project references the source project, false otherwise\r\n */\r\nfunction checkTestProjectReferencesSourceProject(\r\n testCsprojPath: string,\r\n sourceCsprojPath: string,\r\n projectReferences: string[]\r\n): boolean {\r\n const normalizedSourceCsprojPath = path.normalize(sourceCsprojPath).toLowerCase();\r\n return projectReferences.some(ref => {\r\n const resolvedRefPath = path.normalize(path.resolve(path.dirname(testCsprojPath), ref)).toLowerCase();\r\n return resolvedRefPath === normalizedSourceCsprojPath;\r\n });\r\n}\r\n\r\nfunction getProjectReferencesFromCsproj(csprojPath: string): string[] {\r\n const projectReferences: string[] = [];\r\n try {\r\n const xmlContent = fs.readFileSync(csprojPath, 'utf-8');\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n attributeNamePrefix: ''\r\n });\r\n const projectData = parser.parse(xmlContent);\r\n \r\n const project = projectData.Project;\r\n const itemGroups = Array.isArray(project.ItemGroup) ? project.ItemGroup : [project.ItemGroup];\r\n \r\n for (const group of itemGroups) {\r\n if (!group) continue;\r\n const projectRefs = group.ProjectReference;\r\n if (!projectRefs) continue;\r\n\r\n const refList = Array.isArray(projectRefs) ? projectRefs : [projectRefs];\r\n for (const ref of refList) {\r\n if (ref.Include) {\r\n projectReferences.push(ref.Include);\r\n }\r\n }\r\n }\r\n } catch (err) {\r\n console.error(`Failed to parse csproj: ${csprojPath}`, err);\r\n }\r\n return projectReferences;\r\n}\r\n\r\n/**\r\n * Calculate matching score between test file path and source file path based on directory structure.\r\n * Returns two scores: continuous matching folders from end, and total matching folders.\r\n * \r\n * @param testRelativePath - Relative path of test file from test project root\r\n * @param sourceRelativePath - Relative path of source file from source project root\r\n * @returns Object with 'continuous' (right-to-left consecutive matches) and 'total' (all matches)\r\n */\r\nfunction calculateMatchingScore(testRelativePath: string, sourceRelativePath: string): { continuous: number, total: number } {\r\n // Extract directory paths (remove file names)\r\n const testDir = path.dirname(testRelativePath);\r\n const sourceDir = path.dirname(sourceRelativePath);\r\n \r\n // If both files are in root directory (no subdirectories), give highest priority\r\n if ((testDir === '.' || testDir === '') && (sourceDir === '.' || sourceDir === '')) {\r\n return { continuous: 1000, total: 1000 };\r\n }\r\n \r\n // Split paths into folder arrays (case-insensitive)\r\n const testFolders = testDir === '.' || testDir === '' ? [] : testDir.split(path.sep).map(f => f.toLowerCase());\r\n const sourceFolders = sourceDir === '.' || sourceDir === '' ? [] : sourceDir.split(path.sep).map(f => f.toLowerCase());\r\n \r\n // 1. Calculate continuous matching folders from end (right to left)\r\n let continuous = 0;\r\n const minLength = Math.min(testFolders.length, sourceFolders.length);\r\n \r\n for (let i = 1; i <= minLength; i++) {\r\n if (testFolders[testFolders.length - i] === sourceFolders[sourceFolders.length - i]) {\r\n continuous++;\r\n } else {\r\n // Stop at first mismatch\r\n break;\r\n }\r\n }\r\n \r\n // 2. Calculate total matching folders (not required to be continuous)\r\n const sourceSet = new Set(sourceFolders);\r\n const total = testFolders.filter(f => sourceSet.has(f)).length;\r\n \r\n return { continuous, total };\r\n}\r\n\r\nfunction isTestFileForSourceFile(testFilePath: string, sourceCodePath: string): boolean {\r\n const sourceFileName = path.basename(sourceCodePath, '.cs');\r\n const testFileName = path.basename(testFilePath, '.cs');\r\n \r\n // First check: test file name should match common test patterns\r\n // Patterns: SourceFile + (Unit)? + (.)? + (Test|Tests)\r\n // Examples: SourceFileTests, SourceFileTest, SourceFile.Tests, SourceFile.Test, SourceFileUnitTests, SourceFileUnit.Tests\r\n const testPattern = getTestFileNamePattern(sourceFileName);\r\n const matchesPattern = testPattern.test(testFileName);\r\n if (!matchesPattern) {\r\n return false;\r\n }\r\n \r\n try {\r\n // Read both files content\r\n const testContent = fs.readFileSync(testFilePath, 'utf-8');\r\n const sourceContent = fs.readFileSync(sourceCodePath, 'utf-8');\r\n \r\n // First check: Extract namespace from source file and verify test file references it\r\n const sourceNamespaceMatch = sourceContent.match(/namespace\\s+([\\w.]+)/);\r\n if (!sourceNamespaceMatch || !sourceNamespaceMatch[1]) {\r\n // No namespace found in source file, fall back to name-based matching\r\n return true;\r\n }\r\n \r\n const sourceNamespace = sourceNamespaceMatch[1];\r\n // Check if test file has using statement for source namespace\r\n const usingRegex = new RegExp(`using\\\\s+${sourceNamespace.replace(/\\./g, '\\\\.')};`);\r\n if (!usingRegex.test(testContent)) {\r\n // Test file doesn't reference source namespace\r\n return false;\r\n }\r\n \r\n // Second check: Extract the main type name from source file (public or internal)\r\n // Supports: class, interface, struct, record (but not nested private/protected types)\r\n // Priority: public > internal; match first public, fallback to first internal\r\n // Handles modifiers: sealed, abstract, static, partial\r\n const publicTypeMatch = sourceContent.match(new RegExp(`\\\\bpublic\\\\s+${MODIFIER_PATTERN}(?:class|interface|struct|record)\\\\s+(\\\\w+)`));\r\n const internalTypeMatch = sourceContent.match(new RegExp(`\\\\binternal\\\\s+${MODIFIER_PATTERN}(?:class|interface|struct|record)\\\\s+(\\\\w+)`));\r\n \r\n const sourceTypeMatch = publicTypeMatch || internalTypeMatch;\r\n if (!sourceTypeMatch || !sourceTypeMatch[1]) {\r\n // No public or internal type found - this file might be a utility/helper file\r\n // without testable classes, so it shouldn't have a test file\r\n return false;\r\n }\r\n \r\n const sourceTypeName = sourceTypeMatch[1];\r\n // Check if test file references the source type name or its interface\r\n // If source is an interface (direct interface definition), only match itself\r\n // If source is a class/struct/record, also match interface (ITypeName)\r\n const isSourceInterface = new RegExp(`\\\\b(?:public|internal)\\\\s+${MODIFIER_PATTERN}interface\\\\s+(?:\\\\w+)`).test(sourceContent);\r\n let sourceCodeTypeReferenceRegex: RegExp;\r\n if (isSourceInterface) {\r\n // Source is an interface, match only itself\r\n sourceCodeTypeReferenceRegex = new RegExp(`\\\\b${sourceTypeName}\\\\b`, 'g');\r\n } else {\r\n // Source is a class/struct/record, match both TypeName and ITypeName\r\n sourceCodeTypeReferenceRegex = new RegExp(`\\\\b(I)?${sourceTypeName}\\\\b`, 'g');\r\n }\r\n \r\n if (sourceCodeTypeReferenceRegex.test(testContent)) {\r\n return true;\r\n }\r\n \r\n // Namespace matches but class name not found in test file\r\n return false;\r\n \r\n } catch (err) {\r\n console.error(`Failed to read file content for matching: ${err}`);\r\n // Strict mode: if we can't verify the content, treat as non-matching\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Find an existing test file from sibling source files in the same or parent directories.\r\n * This helps determine the best location for a new test file by looking at where\r\n * existing test files are located for nearby source files.\r\n * \r\n * @param sourceCodePath - Path to the source file\r\n * @param testCsprojPath - Path to the test project's csproj file\r\n * @param sourceCsprojPath - Path to the source project's csproj file\r\n * @returns Object with siblingTestFilePath and sourceRelativeDepth if found, otherwise null\r\n */\r\nfunction findSiblingTestFile(\r\n sourceCodePath: string,\r\n testCsprojPath: string,\r\n sourceCsprojPath: string\r\n): { siblingTestFilePath: string, sourceRelativeDepth: number } | null {\r\n const sourceProjectDir = path.dirname(sourceCsprojPath);\r\n let currentDir = path.dirname(sourceCodePath);\r\n let depth = 0;\r\n \r\n // Search from current directory up to source project root\r\n while (currentDir.toLowerCase().startsWith(sourceProjectDir.toLowerCase()) && currentDir.length >= sourceProjectDir.length) {\r\n try {\r\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\r\n const csFiles = entries\r\n .filter(e => e.isFile() && e.name.endsWith('.cs') && path.join(currentDir, e.name) !== sourceCodePath)\r\n .map(e => path.join(currentDir, e.name));\r\n \r\n for (const csFile of csFiles) {\r\n // Try to find test file for this sibling source file\r\n const result = findMatchingTestFileInProject(testCsprojPath, csFile, sourceCsprojPath);\r\n if (result.testFilePath) {\r\n return { siblingTestFilePath: result.testFilePath, sourceRelativeDepth: depth };\r\n }\r\n }\r\n } catch (err) {\r\n // Ignore errors for inaccessible directories\r\n }\r\n \r\n // Move to parent directory\r\n const parentDir = path.dirname(currentDir);\r\n if (parentDir === currentDir) {\r\n break;\r\n }\r\n currentDir = parentDir;\r\n depth++;\r\n }\r\n \r\n return null;\r\n}\r\n\r\n/**\r\n * Determine the best test file path for a source file.\r\n * This function only calculates the path, it does not create any directories.\r\n * \r\n * @param sourceCodePath - Path to the source file\r\n * @param testCsprojPath - Path to the test project's csproj file (optional)\r\n * @returns Object with testFilePath, testProjectPath, and testFileExist flag\r\n */\r\nexport function determineTestFilePath(sourceCodePath: string, testCsprojPath?: string): { testFilePath: string | null, testProjectPath: string | null, testFileExist?: boolean } {\r\n let testProjectPath = testCsprojPath;\r\n if (!testProjectPath) {\r\n console.error(`No test project found for ${sourceCodePath}`);\r\n return { testFilePath: null, testProjectPath: null };\r\n }\r\n \r\n const sourceFileName = path.basename(sourceCodePath, '.cs');\r\n const testFileName = `${sourceFileName}Tests.cs`;\r\n const sourceFileProjectPath = getSourceFileProjectDir(sourceCodePath);\r\n const sourceCsprojPath = getSourceFileCsprojFilePath(sourceCodePath);\r\n \r\n // Try to find sibling test file to determine the best location\r\n const siblingResult = findSiblingTestFile(sourceCodePath, testProjectPath, sourceCsprojPath);\r\n \r\n let testFilePath: string;\r\n if (siblingResult) {\r\n // Found sibling test file, calculate new test file path based on it\r\n const siblingTestDir = path.dirname(siblingResult.siblingTestFilePath);\r\n const sourceFileDir = path.dirname(sourceCodePath);\r\n \r\n if (siblingResult.sourceRelativeDepth === 0) {\r\n // Sibling is in the same directory, put test file in the same test directory\r\n testFilePath = path.join(siblingTestDir, testFileName);\r\n } else {\r\n // Sibling is in a parent directory, calculate the relative path from sibling source to current source\r\n let relativeParts: string[] = [];\r\n let tempDir = sourceFileDir;\r\n \r\n for (let i = 0; i < siblingResult.sourceRelativeDepth; i++) {\r\n relativeParts.unshift(path.basename(tempDir));\r\n tempDir = path.dirname(tempDir);\r\n }\r\n \r\n // The relative path from the found sibling source dir to our source file\r\n const relativePath = relativeParts.join(path.sep);\r\n testFilePath = path.join(siblingTestDir, relativePath, testFileName);\r\n }\r\n } else {\r\n // No sibling test file found, use original logic\r\n const sourceFileDir = path.dirname(sourceCodePath);\r\n const testFileRelativePath = sourceFileDir.replace(sourceFileProjectPath, \"\");\r\n const testProjecDir = path.dirname(testProjectPath);\r\n testFilePath = path.join(testProjecDir, testFileRelativePath, testFileName);\r\n }\r\n \r\n // Check if the test file already exists\r\n const testFileExist = fs.existsSync(testFilePath);\r\n if (testFileExist) {\r\n console.log(`Test file already exists at ${testFilePath}`);\r\n }\r\n \r\n return { testFilePath, testProjectPath, testFileExist };\r\n}\r\n\r\n/**\r\n * Search for matching test file in a specific test project.\r\n * Uses directory structure matching and file content analysis to find the best match.\r\n * \r\n * @param testCsprojPath - Path to the test project's csproj file\r\n * @param sourceCodePath - Path to the source file\r\n * @param sourceCsprojPath - Path to the source project's csproj file\r\n * @returns Object with testFilePath and testProjectPath if found, otherwise both null\r\n */\r\nfunction findMatchingTestFileInProject(\r\n testCsprojPath: string,\r\n sourceCodePath: string,\r\n sourceCsprojPath: string\r\n): { testFilePath: string | null, testProjectPath: string | null } {\r\n const testProjectDir = path.dirname(testCsprojPath);\r\n \r\n // Get source file's csproj directory\r\n const sourceCsprojDir = path.dirname(sourceCsprojPath);\r\n // Calculate relative path of source file from its csproj directory\r\n const sourceRelativePath = path.relative(sourceCsprojDir, sourceCodePath);\r\n \r\n // Get source file name for filtering\r\n const sourceFileName = path.basename(sourceCodePath, '.cs');\r\n \r\n // Find .cs files containing source file name (optimization to reduce file count)\r\n const allTestFiles = findPossibleTestFiles(testProjectDir, sourceFileName);\r\n \r\n // Filter by file name pattern first to reduce unnecessary file reads\r\n const testPattern = getTestFileNamePattern(sourceFileName);\r\n \r\n // Create list of candidates with their matching scores\r\n const candidatesWithScores = allTestFiles\r\n .filter(file => {\r\n const testFileName = path.basename(file, '.cs');\r\n return testPattern.test(testFileName);\r\n })\r\n .map(file => {\r\n const testRelativePath = path.relative(testProjectDir, file);\r\n const score = calculateMatchingScore(testRelativePath, sourceRelativePath);\r\n return {\r\n filePath: file,\r\n relativePath: testRelativePath,\r\n score: score\r\n };\r\n })\r\n // Sort by continuous match first, then by total match\r\n .sort((a, b) => {\r\n if (b.score.continuous !== a.score.continuous) {\r\n return b.score.continuous - a.score.continuous;\r\n }\r\n return b.score.total - a.score.total;\r\n });\r\n \r\n for (const candidate of candidatesWithScores) {\r\n if (isTestFileForSourceFile(candidate.filePath, sourceCodePath)) {\r\n return { testFilePath: candidate.filePath, testProjectPath: testCsprojPath };\r\n }\r\n }\r\n \r\n return { testFilePath: null, testProjectPath: null };\r\n}\r\n\r\nfunction getTestFileFromChangedFiles(sourceCodePath: string, changedTestFiles?: string[]): { testFilePath: string | null, testProjectPath: string | null } {\r\n if (!changedTestFiles || changedTestFiles.length === 0) {\r\n return { testFilePath: null, testProjectPath: null };\r\n }\r\n\r\n // Get the csproj of the source code\r\n const sourceCsprojPath = getSourceFileCsprojFilePath(sourceCodePath);\r\n if (!sourceCsprojPath) {\r\n return { testFilePath: null, testProjectPath: null };\r\n }\r\n\r\n // Cache to store project references from csproj to avoid re-parsing\r\n const csprojCache = new Map<string, string[]>();\r\n // Store test projects that reference the source project\r\n const referencingTestProjects = new Set<string>();\r\n\r\n for (const testFilePath of changedTestFiles) {\r\n if (!fs.existsSync(testFilePath)) {\r\n continue;\r\n }\r\n\r\n // Get the csproj of the test file\r\n const testCsprojPath = getSourceFileCsprojFilePath(testFilePath);\r\n if (!testCsprojPath) {\r\n continue;\r\n }\r\n\r\n // Check cache first\r\n let projectReferences = csprojCache.get(testCsprojPath);\r\n if (!projectReferences) {\r\n projectReferences = getProjectReferencesFromCsproj(testCsprojPath);\r\n csprojCache.set(testCsprojPath, projectReferences);\r\n }\r\n\r\n // Check if test project references the source project\r\n const normalizedSourceCsprojPath = path.normalize(sourceCsprojPath);\r\n if (checkTestProjectReferencesSourceProject(testCsprojPath, normalizedSourceCsprojPath, projectReferences)) {\r\n referencingTestProjects.add(testCsprojPath);\r\n // Check if this test file is for the source file\r\n if (isTestFileForSourceFile(testFilePath, sourceCodePath)) {\r\n return { testFilePath, testProjectPath: testCsprojPath };\r\n }\r\n }\r\n }\r\n\r\n // Fallback: search all test files in projects that reference the source project\r\n for (const testCsprojPath of referencingTestProjects) {\r\n const result = findMatchingTestFileInProject(testCsprojPath, sourceCodePath, sourceCsprojPath);\r\n if (result.testFilePath) {\r\n return result;\r\n }\r\n }\r\n\r\n return { testFilePath: null, testProjectPath: null };\r\n}\r\n\r\nexport function getOrCreatTestFile(sourceCodePath: string, testFilePath: string, changedTestFiles?: string[]) {\r\n let finalTestFilePath = testFilePath;\r\n let finalTestProjectPath;\r\n let testFileExist = !!testFilePath && fs.existsSync(testFilePath);\r\n \r\n // If test file exists, get its project path and return directly\r\n if (testFileExist) {\r\n finalTestProjectPath = getSourceFileCsprojFilePath(testFilePath);\r\n return { testFilePath: finalTestFilePath, testProjectPath: finalTestProjectPath, testFileExist };\r\n }\r\n \r\n // if outputPath param is not provided, try to find an existing test file\r\n // if no existing test file found, create a new one in test project\r\n if (!testFilePath || testFilePath.trim() === '') {\r\n console.log(' Output path is missing. outputPath:', testFilePath, ', start looking for existing test project');\r\n\r\n // Try to find test file from changed files first\r\n let resultFromChangedFiles = getTestFileFromChangedFiles(sourceCodePath, changedTestFiles);\r\n if (resultFromChangedFiles.testFilePath) {\r\n return { testFilePath: resultFromChangedFiles.testFilePath, testProjectPath: resultFromChangedFiles.testProjectPath, testFileExist: true };\r\n }\r\n\r\n // Fallback: try to find existing test file\r\n const possibleCSProjPaths = findPossibleTestProjectsFromCsFile(sourceCodePath);\r\n if (!possibleCSProjPaths || possibleCSProjPaths.length === 0) {\r\n console.error(` No test project found for source code: ${sourceCodePath}`);\r\n return { testFilePath: null, testProjectPath: null, testFileExist: false };\r\n }\r\n\r\n // Get the source csproj path for matching\r\n const sourceCsprojPath = getSourceFileCsprojFilePath(sourceCodePath);\r\n if (!sourceCsprojPath) {\r\n console.error(` Cannot find source csproj for: ${sourceCodePath}`);\r\n return { testFilePath: null, testProjectPath: null, testFileExist: false };\r\n }\r\n\r\n // Try to find existing test file in each possible test project\r\n for (const testCsprojPath of possibleCSProjPaths) {\r\n const result = findMatchingTestFileInProject(testCsprojPath, sourceCodePath, sourceCsprojPath);\r\n if (result.testFilePath) {\r\n testFileExist = true;\r\n finalTestFilePath = result.testFilePath;\r\n finalTestProjectPath = result.testProjectPath;\r\n return { testFilePath: finalTestFilePath, testProjectPath: finalTestProjectPath, testFileExist };\r\n }\r\n }\r\n\r\n // No existing test file found, determine test file path (may find existing)\r\n const result = determineTestFilePath(sourceCodePath, possibleCSProjPaths[0]);\r\n if (!result.testFilePath || !result.testProjectPath) {\r\n console.error(`Failed to determine test file path for ${sourceCodePath}`);\r\n return { testFilePath: null, testProjectPath: null, testFileExist: false };\r\n }\r\n finalTestFilePath = result.testFilePath;\r\n finalTestProjectPath = result.testProjectPath;\r\n testFileExist = result.testFileExist ?? false;\r\n \r\n // Create directory if test file doesn't exist\r\n if (!testFileExist) {\r\n getOrCreateDir(path.dirname(finalTestFilePath));\r\n }\r\n }\r\n\r\n return { testFilePath: finalTestFilePath, testProjectPath: finalTestProjectPath, testFileExist };\r\n\r\n}\r\n\r\n// // Verify test file paths for all projects\r\n// function verifyTestFilePaths(): void {\r\n// const baseFolder = 'Q:\\\\src\\\\Microsoft.News.OneService';\r\n// const outputFile = 'q:\\\\verifyTestFilePaths_results.txt';\r\n// const results: string[] = [];\r\n\r\n// try {\r\n// // Recursively find all non test csproj files \r\n// const getAllCsprojFiles = (dir: string): string[] => {\r\n// const csprojFiles: string[] = [];\r\n// try {\r\n// const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n// for (const entry of entries) {\r\n// const fullPath = path.join(dir, entry.name);\r\n// if (entry.isDirectory() && entry.name !== 'bin' && entry.name !== 'obj' && entry.name !== '.vs') {\r\n// csprojFiles.push(...getAllCsprojFiles(fullPath));\r\n// } else if (entry.isFile() && entry.name.endsWith('.csproj')) {\r\n// // Filter by test project naming patterns\r\n// const rawName = path.basename(fullPath, '.csproj');\r\n// const words = rawName\r\n// .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\r\n// .split(/[^a-zA-Z0-9]+/)\r\n// .filter(Boolean)\r\n// .map(w => w.toLowerCase());\r\n \r\n// if (words.every(w => !w.endsWith('test') && !w.endsWith('tests'))) {\r\n// csprojFiles.push(fullPath);\r\n// }\r\n// }\r\n// }\r\n// } catch (err) {\r\n// // Skip inaccessible directories\r\n// }\r\n// return csprojFiles;\r\n// };\r\n\r\n// // Get first .cs file in a directory (recursive search)\r\n// const getFirstCsFile = (dir: string): string | null => {\r\n// try {\r\n// const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n// // First pass: look for .cs files in current directory\r\n// for (const entry of entries) {\r\n// if (entry.isFile() && entry.name.endsWith('.cs')) {\r\n// return path.join(dir, entry.name);\r\n// }\r\n// }\r\n// // Second pass: recurse into subdirectories\r\n// for (const entry of entries) {\r\n// if (entry.isDirectory() && entry.name !== 'bin' && entry.name !== 'obj' && entry.name !== '.vs') {\r\n// const fullPath = path.join(dir, entry.name);\r\n// const result = getFirstCsFile(fullPath);\r\n// if (result) {\r\n// return result;\r\n// }\r\n// }\r\n// }\r\n// } catch (err) {\r\n// // Skip if directory can't be read\r\n// }\r\n// return null;\r\n// };\r\n\r\n// // Find all csproj files\r\n// const csprojFiles = getAllCsprojFiles(baseFolder);\r\n\r\n// // Process each csproj\r\n// for (const csprojPath of csprojFiles) {\r\n// const csprojDir = path.dirname(csprojPath);\r\n// const csFile = getFirstCsFile(csprojDir);\r\n\r\n// if (csFile) {\r\n// const result = getOrCreatTestFile(csFile, undefined);\r\n// const testFilePath = result.testFilePath || 'NOT_FOUND';\r\n// const testProjectPath = result.testProjectPath || 'NOT_FOUND';\r\n// results.push(`${csprojPath}\\t${csFile}\\t${testFilePath}\\t${testProjectPath}\\t${result.testFileExist}`);\r\n// console.log(`Processed: ${csprojPath}`);\r\n// }\r\n// }\r\n\r\n// // Write results to file\r\n// fs.writeFileSync(outputFile, results.join('\\n'), 'utf-8');\r\n// console.log(`Results written to ${outputFile}`);\r\n// } catch (err) {\r\n// console.error(`Error during verification: ${err}`);\r\n// }\r\n// }\r\n\r\n// // Uncomment to run verification\r\n// verifyTestFilePaths();\r\n\r\n// getOrCreatTestFile(\"Q:\\\\src\\\\Microsoft.News.OneService\\\\Libraries\\\\Tests\\\\MockStrategies\\\\MockDataException.cs\", undefined);\r\n\r\n\r\n// Example usage\r\n// const csFilePath = \"D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\LocationDetection\\\\AudienceResponseParser.cs\";\r\n// const csFilePath = \"D:\\\\code\\\\msnews-experiences\\\\experiences\\\\weather-right-rail\\\\src\\\\WeatherRightRail.connector.ts\";\r\n// const testProjectPath = \"D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Tests\\\\SharedSegments.Tests.csproj\";\r\n// if (!csFilePath) {\r\n// console.error('Usage: ts-node findTestFile.ts path/to/your/file.cs');\r\n// process.exit(1);\r\n// }\r\n\r\n// const start = Date.now();\r\n// const testFilePath = getExistingTestFile(csFilePath);\r\n// console.log(`Test file for ${csFilePath}:`, testFilePath ?? 'No test file found');\r\n// console.log(`Search completed in ${(Date.now() - start) / 1000} seconds`);\r\n// createXapTestFile(csFilePath);\r\n\r\n// const repoDir = getCodeRepoRoot(\"D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\LocationDetection\\\\AudienceResponseParser.cs\");\r\n// console.log(`Code repository root directory: ${repoDir}`);\r\n\r\n// const result = getExistingTestFile(\"D:\\\\code\\\\CS.Service.Fundamental\\\\SharedSegments\\\\SharedSegments\\\\SharedSegments.Plugins\\\\Workflows\\\\Eplant\\\\EplantConfigManager.cs\");\r\n// console.log(`Test file path: ${result.testFilePath}`);\r\n// console.log(`Test project path: ${result.testProjectPath}`);\r\n\r\n\r\n// falcon test project\r\n// const isFlaconUt = isFalconUnitTestProject(\"D:\\\\code\\\\WeatherNotification\\\\services\\\\WeatherUserInterest\\\\tests\\\\UnitTests\\\\Microsoft.Falcon.WeatherUserInterest.UnitTests.csproj\");\r\n// console.log(`Is Falcon Unit Test Project: ${isFlaconUt}`);\r\n// const testProject = findTestProjectFromCsFile(\"D:\\\\code\\\\WeatherNotification\\\\services\\\\WeatherUserInterest\\\\src\\\\Service\\\\Microsoft.Falcon.WeatherUserInterest.csproj\");\r\n// console.log(`Test project path: ${testProject}`);\r\n// const testFilePath = getOrCreatTestFile(\"D:\\\\code\\\\WeatherNotification\\\\services\\\\WeatherUserInterest\\\\src\\\\Service\\\\Components\\\\IUserInterestTable.cs\", undefined, false);\r\n// console.log(`Test file path: ${testFilePath.testFilePath}`);\r\n\r\n// ads projects\r\n// const testProjectPath = findTestProjectFromCsFile(\"D:\\\\code\\\\UCMDynamics\\\\private\\\\DynamicCode\\\\Plugin\\\\UCMDynamics.AppointmentPlugin\\\\AppointmentOperation.cs\");\r\n// console.log(`Test project path: ${testProjectPath}`);\r\n// const isValid = isGeneralTestProject(\"D:\\\\code\\\\UCMDynamics\\\\private\\\\DynamicCode\\\\Plugin\\\\UcmDynamics.AppointmentPlugin\\\\UcmDynamics.AppointmentPlugin.csproj\");\r\n// // const isValid = isGeneralTestProject(\"D:\\\\code\\\\UCMDynamics\\\\private\\\\DynamicCode\\\\Plugin\\\\UcmDynamics.AppointmentPlugin.Tests\\\\UcmDynamics.AppointmentPlugin.Tests.csproj\");\r\n// console.log(`Is General Test Project: ${isValid}`);\r\n\r\n// const testProjectPath = findTestProjectFromCsFile(\"D:\\\\code\\\\AIUnitTestExample\\\\src\\\\ToListinator.Analyzers\\\\ToListCountAnalyzer.cs\");\r\n// console.log(`Test project path: ${testProjectPath}`);"]}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { GenResult } from '../types/genResult';
|
|
2
|
+
import { BuildResult } from '../types/buildResult';
|
|
1
3
|
import { TestResult } from '../types/testResult';
|
|
2
4
|
export declare function removeFailedTests(filePath: string, failedTestNames: string[]): string;
|
|
3
5
|
export declare function removeFailedTestCasesInTestFile(vsTestRes: TestResult, testFilePath: string): void;
|
|
6
|
+
/**
|
|
7
|
+
* Try to remove failed test methods and verify if the remaining code is valid.
|
|
8
|
+
* If testResult has errors, remove the failed test methods and check syntax.
|
|
9
|
+
* If buildResult has errors, remove the test methods containing build errors and verify.
|
|
10
|
+
*/
|
|
11
|
+
export declare function removeFailedTestMethods(testCode: string, testFilePath: string, testProjectPath: string, sourceCodePath: string, buildResult: BuildResult[] | null, testResult: TestResult | null, isVsPlugin: boolean, originalTestLength?: number): Promise<GenResult | null>;
|
|
@@ -35,8 +35,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.removeFailedTests = removeFailedTests;
|
|
37
37
|
exports.removeFailedTestCasesInTestFile = removeFailedTestCasesInTestFile;
|
|
38
|
+
exports.removeFailedTestMethods = removeFailedTestMethods;
|
|
38
39
|
const fs = __importStar(require("fs"));
|
|
39
40
|
const constants_1 = require("../types/constants");
|
|
41
|
+
const checkCodeSyntax_1 = require("../analyze/checkCodeSyntax");
|
|
42
|
+
const verifyGeneratedCode_1 = require("./verifyGeneratedCode");
|
|
43
|
+
const fileUtils_1 = require("./fileUtils");
|
|
40
44
|
function removeFailedTests(filePath, failedTestNames) {
|
|
41
45
|
try {
|
|
42
46
|
if (!fs.existsSync(filePath)) {
|
|
@@ -205,4 +209,363 @@ function removeFailedTestCasesInTestFile(vsTestRes, testFilePath) {
|
|
|
205
209
|
}
|
|
206
210
|
// const testCode = removeFailedTests("D:\\code\\CS.Service.Fundamental\\SharedSegments\\SharedSegments\\SharedSegments.Tests\\Workflows\\Overview\\HttpRequestParser\\Utils\\SqmidHelperTests.cs", ["GetSqmidFromCookie_InvalidBlisIdFormat_ReturnsEmptyString", "GetSqmidFromCookie_MissingUsrLocCookie_ReturnsEmptyString"]);
|
|
207
211
|
// console.log(testCode);
|
|
212
|
+
/**
|
|
213
|
+
* Try to remove failed test methods and verify if the remaining code is valid.
|
|
214
|
+
* If testResult has errors, remove the failed test methods and check syntax.
|
|
215
|
+
* If buildResult has errors, remove the test methods containing build errors and verify.
|
|
216
|
+
*/
|
|
217
|
+
async function removeFailedTestMethods(testCode, testFilePath, testProjectPath, sourceCodePath, buildResult, testResult, isVsPlugin, originalTestLength = 0) {
|
|
218
|
+
if (!testCode) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
let updatedCode = testCode;
|
|
222
|
+
// Case 1: Test errors - intelligently remove failed assertions or test methods
|
|
223
|
+
if (testResult && testResult.failedTests && testResult.failedTests.length > 0) {
|
|
224
|
+
console.log("Attempting to remove failed test methods or assertions...");
|
|
225
|
+
// Extract line numbers from failed test stack traces
|
|
226
|
+
const errorLines = [];
|
|
227
|
+
for (const failedTest of testResult.failedTests) {
|
|
228
|
+
const lineMatches = failedTest.stackTrace.matchAll(/:line (\d+)/g);
|
|
229
|
+
for (const match of lineMatches) {
|
|
230
|
+
errorLines.push(parseInt(match[1], 10));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (errorLines.length > 0) {
|
|
234
|
+
updatedCode = removeFailedAssertionsOrMethods(updatedCode, errorLines);
|
|
235
|
+
if (updatedCode !== testCode) {
|
|
236
|
+
(0, fileUtils_1.writeTestCode)(updatedCode, testFilePath);
|
|
237
|
+
const syntaxResult = await (0, checkCodeSyntax_1.checkCodeSyntax)(testFilePath);
|
|
238
|
+
if (syntaxResult.success) {
|
|
239
|
+
// Check if code length is greater than original
|
|
240
|
+
if (originalTestLength > 0 && updatedCode.length <= originalTestLength) {
|
|
241
|
+
console.error("Remaining code length is not greater than original test code length after removing failed tests.");
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
console.log("Successfully removed failed test assertions/methods, syntax check passed.");
|
|
245
|
+
return { success: true, testFilePath, testProjectPath, codeFilePath: sourceCodePath, buildResult: null, testResult: null };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Case 2: Build errors - remove test methods containing build errors
|
|
251
|
+
if (buildResult && buildResult.length > 0) {
|
|
252
|
+
console.log("Attempting to remove test methods with build errors...");
|
|
253
|
+
const errorLines = buildResult.map(err => err.line);
|
|
254
|
+
updatedCode = removeTestMethodsByLines(updatedCode, errorLines);
|
|
255
|
+
if (updatedCode !== testCode) {
|
|
256
|
+
(0, fileUtils_1.writeTestCode)(updatedCode, testFilePath);
|
|
257
|
+
const verifyResult = await (0, verifyGeneratedCode_1.verifyGeneratedCode)(updatedCode, testProjectPath, testFilePath, isVsPlugin);
|
|
258
|
+
if (verifyResult.success) {
|
|
259
|
+
// Check if code length is greater than original
|
|
260
|
+
if (originalTestLength > 0 && updatedCode.length <= originalTestLength) {
|
|
261
|
+
console.error("Remaining code length is not greater than original test code length after removing build errors.");
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
console.log("Successfully removed test methods with build errors, verification passed.");
|
|
265
|
+
return { success: true, testFilePath, testProjectPath, codeFilePath: sourceCodePath, buildResult: null, testResult: verifyResult.testResult };
|
|
266
|
+
}
|
|
267
|
+
// If still has build errors, return failure
|
|
268
|
+
if (verifyResult.buildErrors && verifyResult.buildErrors.length > 0) {
|
|
269
|
+
console.error("Still has build errors after removing test methods.");
|
|
270
|
+
return { success: false, testFilePath, testProjectPath, codeFilePath: sourceCodePath, buildResult: verifyResult.buildErrors, testResult: null };
|
|
271
|
+
}
|
|
272
|
+
// If has test errors, try to remove failed test methods
|
|
273
|
+
if (verifyResult.testResult && verifyResult.testResult.failedTests && verifyResult.testResult.failedTests.length > 0) {
|
|
274
|
+
console.log("Build passed but has test errors, attempting to remove failed test methods...");
|
|
275
|
+
const testErrorLines = [];
|
|
276
|
+
for (const failedTest of verifyResult.testResult.failedTests) {
|
|
277
|
+
const lineMatches = failedTest.stackTrace.matchAll(/:line (\d+)/g);
|
|
278
|
+
for (const match of lineMatches) {
|
|
279
|
+
testErrorLines.push(parseInt(match[1], 10));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (testErrorLines.length > 0) {
|
|
283
|
+
const finalCode = removeFailedAssertionsOrMethods(updatedCode, testErrorLines);
|
|
284
|
+
if (finalCode !== updatedCode) {
|
|
285
|
+
(0, fileUtils_1.writeTestCode)(finalCode, testFilePath);
|
|
286
|
+
const syntaxResult = await (0, checkCodeSyntax_1.checkCodeSyntax)(testFilePath);
|
|
287
|
+
if (syntaxResult.success) {
|
|
288
|
+
// Check if code length is greater than original
|
|
289
|
+
if (originalTestLength > 0 && finalCode.length <= originalTestLength) {
|
|
290
|
+
console.error("Remaining code length is not greater than original test code length after removing failed tests.");
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
console.log("Successfully removed failed test methods after build fix, syntax check passed.");
|
|
294
|
+
return { success: true, testFilePath, testProjectPath, codeFilePath: sourceCodePath, buildResult: null, testResult: null };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Remove failed assertions or entire test methods based on the error lines.
|
|
305
|
+
* Strategy:
|
|
306
|
+
* - If only one assert in the method, remove the entire method
|
|
307
|
+
* - If multiple asserts and the failed assert has no other asserts above it, remove the entire method
|
|
308
|
+
* - If multiple asserts and there are asserts above the failed one, only remove from failed assert to method end
|
|
309
|
+
*/
|
|
310
|
+
function removeFailedAssertionsOrMethods(code, errorLines) {
|
|
311
|
+
const lines = code.split('\n');
|
|
312
|
+
const linesToRemove = new Set();
|
|
313
|
+
// Process each error line
|
|
314
|
+
for (const errorLine of errorLines) {
|
|
315
|
+
const targetLineIndex = errorLine - 1; // Convert to 0-based index
|
|
316
|
+
// Find the test method that contains this error line
|
|
317
|
+
const methodRange = findTestMethodRangeByBrace(lines, targetLineIndex);
|
|
318
|
+
if (!methodRange) {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
// Find all assert lines in this method
|
|
322
|
+
const assertLines = findAssertLinesInRange(lines, methodRange.start, methodRange.end);
|
|
323
|
+
if (assertLines.length <= 1) {
|
|
324
|
+
// Only one assert (or none), remove the entire method
|
|
325
|
+
for (let i = methodRange.start; i <= methodRange.end; i++) {
|
|
326
|
+
linesToRemove.add(i);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
// Multiple asserts - check if there are asserts above the failed line
|
|
331
|
+
const assertsAbove = assertLines.filter(line => line < targetLineIndex);
|
|
332
|
+
if (assertsAbove.length === 0) {
|
|
333
|
+
// No asserts above the failed line, remove the entire method
|
|
334
|
+
for (let i = methodRange.start; i <= methodRange.end; i++) {
|
|
335
|
+
linesToRemove.add(i);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
// There are asserts above, only remove from the failed assert line to method end
|
|
340
|
+
// Find the start of the failed assert statement (it might span multiple lines)
|
|
341
|
+
let assertStartLine = findAssertStatementStart(lines, targetLineIndex);
|
|
342
|
+
// Also include comments and empty lines above the assert
|
|
343
|
+
assertStartLine = findCodeBlockStart(lines, assertStartLine, methodRange.start);
|
|
344
|
+
// Remove from assert start to method end (but keep the closing brace on a new line)
|
|
345
|
+
for (let i = assertStartLine; i < methodRange.end; i++) {
|
|
346
|
+
linesToRemove.add(i);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (linesToRemove.size === 0) {
|
|
352
|
+
return code;
|
|
353
|
+
}
|
|
354
|
+
// Remove the lines (in reverse order to maintain indices)
|
|
355
|
+
const sortedLinesToRemove = Array.from(linesToRemove).sort((a, b) => b - a);
|
|
356
|
+
for (const lineIndex of sortedLinesToRemove) {
|
|
357
|
+
lines.splice(lineIndex, 1);
|
|
358
|
+
}
|
|
359
|
+
// Clean up consecutive empty lines (replace multiple empty lines with single empty line)
|
|
360
|
+
const result = lines.join('\n').replace(/\n{3,}/g, '\n\n');
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Find all assert statement lines within the given line range.
|
|
365
|
+
* Supports different test frameworks: MSTest, NUnit, xUnit
|
|
366
|
+
*/
|
|
367
|
+
function findAssertLinesInRange(lines, startLine, endLine) {
|
|
368
|
+
const assertLines = [];
|
|
369
|
+
// Patterns for different test frameworks
|
|
370
|
+
// MSTest: Assert.AreEqual, Assert.IsTrue, Assert.IsFalse, Assert.IsNull, Assert.IsNotNull, Assert.ThrowsException, etc.
|
|
371
|
+
// NUnit: Assert.AreEqual, Assert.That, Assert.IsTrue, Assert.Throws, etc.
|
|
372
|
+
// xUnit: Assert.Equal, Assert.True, Assert.False, Assert.Null, Assert.NotNull, Assert.Throws, etc.
|
|
373
|
+
// Also support FluentAssertions: .Should()
|
|
374
|
+
const assertPatterns = [
|
|
375
|
+
/\bAssert\s*\./i, // Assert.xxx (MSTest, NUnit, xUnit)
|
|
376
|
+
/\bStringAssert\s*\./i, // StringAssert.xxx (MSTest)
|
|
377
|
+
/\bCollectionAssert\s*\./i, // CollectionAssert.xxx (MSTest)
|
|
378
|
+
/\.Should\s*\(/i, // FluentAssertions: xxx.Should()
|
|
379
|
+
/\.ShouldBe\s*\(/i, // Shouldly: xxx.ShouldBe()
|
|
380
|
+
/\.ShouldEqual\s*\(/i, // Shouldly variants
|
|
381
|
+
/\bExpect\s*\(/i, // NUnit Expect
|
|
382
|
+
];
|
|
383
|
+
for (let i = startLine; i <= endLine; i++) {
|
|
384
|
+
const line = lines[i];
|
|
385
|
+
for (const pattern of assertPatterns) {
|
|
386
|
+
if (pattern.test(line)) {
|
|
387
|
+
assertLines.push(i);
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return assertLines;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Find the start line of an assert statement (it may span multiple lines).
|
|
396
|
+
* Searches backwards to find the actual start of the statement.
|
|
397
|
+
*/
|
|
398
|
+
function findAssertStatementStart(lines, assertLine) {
|
|
399
|
+
const assertPatterns = [
|
|
400
|
+
/\bAssert\s*\./i,
|
|
401
|
+
/\bStringAssert\s*\./i,
|
|
402
|
+
/\bCollectionAssert\s*\./i,
|
|
403
|
+
/\.Should\s*\(/i,
|
|
404
|
+
/\.ShouldBe\s*\(/i,
|
|
405
|
+
/\.ShouldEqual\s*\(/i,
|
|
406
|
+
/\bExpect\s*\(/i,
|
|
407
|
+
];
|
|
408
|
+
// Check if current line contains an assert pattern
|
|
409
|
+
const currentLine = lines[assertLine];
|
|
410
|
+
for (const pattern of assertPatterns) {
|
|
411
|
+
if (pattern.test(currentLine)) {
|
|
412
|
+
return assertLine; // Assert starts on this line
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// If not found on current line, search backwards for the statement start
|
|
416
|
+
// This handles cases where the assert call spans multiple lines
|
|
417
|
+
for (let i = assertLine - 1; i >= 0; i--) {
|
|
418
|
+
const line = lines[i];
|
|
419
|
+
for (const pattern of assertPatterns) {
|
|
420
|
+
if (pattern.test(line)) {
|
|
421
|
+
return i;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
// If we hit an empty line or a line ending with semicolon or opening brace, stop
|
|
425
|
+
const trimmedLine = line.trim();
|
|
426
|
+
if (trimmedLine === '' || trimmedLine.endsWith(';') || trimmedLine.endsWith('{')) {
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return assertLine; // Fallback to the original line
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Find the start of a code block including comments and empty lines above it.
|
|
434
|
+
* Searches backwards from the given line to include preceding comments and empty lines.
|
|
435
|
+
*/
|
|
436
|
+
function findCodeBlockStart(lines, startLine, minLine) {
|
|
437
|
+
let result = startLine;
|
|
438
|
+
// Comment patterns
|
|
439
|
+
const singleLineCommentPattern = /^\s*\/\//;
|
|
440
|
+
const blockCommentEndPattern = /\*\/\s*$/;
|
|
441
|
+
const blockCommentStartPattern = /^\s*\/\*/;
|
|
442
|
+
for (let i = startLine - 1; i >= minLine; i--) {
|
|
443
|
+
const line = lines[i];
|
|
444
|
+
const trimmedLine = line.trim();
|
|
445
|
+
if (trimmedLine === '') {
|
|
446
|
+
// Empty line, include it
|
|
447
|
+
result = i;
|
|
448
|
+
}
|
|
449
|
+
else if (singleLineCommentPattern.test(line)) {
|
|
450
|
+
// Single line comment (// comment), include it
|
|
451
|
+
result = i;
|
|
452
|
+
}
|
|
453
|
+
else if (blockCommentEndPattern.test(trimmedLine)) {
|
|
454
|
+
// End of block comment, search backwards for the start
|
|
455
|
+
result = i;
|
|
456
|
+
for (let j = i; j >= minLine; j--) {
|
|
457
|
+
result = j;
|
|
458
|
+
if (blockCommentStartPattern.test(lines[j])) {
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
i = result; // Continue from the block comment start
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
// Hit actual code, stop
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return result;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Remove test methods that contain the specified line numbers.
|
|
473
|
+
* Finds the test method boundaries and removes the entire method.
|
|
474
|
+
*/
|
|
475
|
+
function removeTestMethodsByLines(code, errorLines) {
|
|
476
|
+
const lines = code.split('\n');
|
|
477
|
+
const linesToRemove = new Set();
|
|
478
|
+
for (const errorLine of errorLines) {
|
|
479
|
+
// Find the test method that contains this error line
|
|
480
|
+
const methodRange = findTestMethodRangeByBrace(lines, errorLine - 1); // Convert to 0-based index
|
|
481
|
+
if (methodRange) {
|
|
482
|
+
for (let i = methodRange.start; i <= methodRange.end; i++) {
|
|
483
|
+
linesToRemove.add(i);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (linesToRemove.size === 0) {
|
|
488
|
+
return code;
|
|
489
|
+
}
|
|
490
|
+
// Remove the lines (in reverse order to maintain indices)
|
|
491
|
+
const sortedLinesToRemove = Array.from(linesToRemove).sort((a, b) => b - a);
|
|
492
|
+
for (const lineIndex of sortedLinesToRemove) {
|
|
493
|
+
lines.splice(lineIndex, 1);
|
|
494
|
+
}
|
|
495
|
+
// Clean up consecutive empty lines (replace multiple empty lines with single empty line)
|
|
496
|
+
const result = lines.join('\n').replace(/\n{3,}/g, '\n\n');
|
|
497
|
+
return result;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Find the start and end line indices of the test method containing the given line.
|
|
501
|
+
* Includes all attributes above the test method attribute (e.g., [DataRow], [Category], etc.)
|
|
502
|
+
*/
|
|
503
|
+
function findTestMethodRangeByBrace(lines, targetLineIndex) {
|
|
504
|
+
const testMethodPatterns = [
|
|
505
|
+
/\[TestMethod\]/i,
|
|
506
|
+
/\[Test\]/i,
|
|
507
|
+
/\[Fact\]/i,
|
|
508
|
+
/\[Theory\]/i
|
|
509
|
+
];
|
|
510
|
+
// Pattern to match any C# attribute
|
|
511
|
+
const attributePattern = /^\s*\[.+\]\s*$/;
|
|
512
|
+
// Search backwards to find the test method attribute
|
|
513
|
+
let testAttributeLine = -1;
|
|
514
|
+
for (let i = targetLineIndex; i >= 0; i--) {
|
|
515
|
+
const line = lines[i].trim();
|
|
516
|
+
for (const pattern of testMethodPatterns) {
|
|
517
|
+
if (pattern.test(line)) {
|
|
518
|
+
testAttributeLine = i;
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (testAttributeLine !== -1)
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
if (testAttributeLine === -1) {
|
|
526
|
+
return null; // Not inside a test method
|
|
527
|
+
}
|
|
528
|
+
// Search upward from test attribute to include all consecutive attributes above it
|
|
529
|
+
// (e.g., [DataRow], [Category], [Description], etc.)
|
|
530
|
+
let methodStart = testAttributeLine;
|
|
531
|
+
for (let i = testAttributeLine - 1; i >= 0; i--) {
|
|
532
|
+
const line = lines[i].trim();
|
|
533
|
+
if (line === '' || attributePattern.test(line)) {
|
|
534
|
+
// Skip empty lines and include attribute lines
|
|
535
|
+
if (attributePattern.test(line)) {
|
|
536
|
+
methodStart = i;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
// Stop when we hit a non-attribute, non-empty line
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
// Find the method's opening brace and closing brace
|
|
545
|
+
let braceCount = 0;
|
|
546
|
+
let foundOpeningBrace = false;
|
|
547
|
+
let methodEnd = -1;
|
|
548
|
+
for (let i = methodStart; i < lines.length; i++) {
|
|
549
|
+
const line = lines[i];
|
|
550
|
+
for (const char of line) {
|
|
551
|
+
if (char === '{') {
|
|
552
|
+
braceCount++;
|
|
553
|
+
foundOpeningBrace = true;
|
|
554
|
+
}
|
|
555
|
+
else if (char === '}') {
|
|
556
|
+
braceCount--;
|
|
557
|
+
if (foundOpeningBrace && braceCount === 0) {
|
|
558
|
+
methodEnd = i;
|
|
559
|
+
break;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (methodEnd !== -1)
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
if (methodEnd === -1) {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
return { start: methodStart, end: methodEnd };
|
|
570
|
+
}
|
|
208
571
|
//# sourceMappingURL=removeFailedTestMethods.js.map
|