Commit eddd21f4e8287cd232b0fc07b2f1be8d6f83d840
1 parent
83ce168a
fix(infra): 修复 Windows 下 Codegen 单测
Showing
4 changed files
with
21 additions
and
213 deletions
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
| ... | ... | @@ -457,8 +457,10 @@ public class CodegenEngine { |
| 457 | 457 | * @return 格式化后的代码 |
| 458 | 458 | */ |
| 459 | 459 | private String prettyCode(String content, String vmPath) { |
| 460 | - // Vue 界面:去除字段后面多余的 , 逗号,解决前端的 Pretty 代码格式检查的报错(内含换行归一化) | |
| 461 | - content = CodegenFormatUtils.applyVueTrailingCommaFix(content, vmPath); | |
| 460 | + // Vue 界面:去除字段后面多余的 , 逗号,解决前端的 Pretty 代码格式检查的报错(需要排除 vben5、vue3_admin_uniapp) | |
| 461 | + if (!StrUtil.containsAny(vmPath, "vben5", "vue3_admin_uniapp")) { | |
| 462 | + content = content.replaceAll(",\\r?\\n}", "\n}").replaceAll(",\\r?\\n }", "\n }"); | |
| 463 | + } | |
| 462 | 464 | // Vue 界面:去除多的 dateFormatter,只有一个的情况下,说明没使用到 |
| 463 | 465 | if (StrUtil.count(content, "dateFormatter") == 1) { |
| 464 | 466 | content = StrUtils.removeLineContains(content, "dateFormatter"); |
| ... | ... | @@ -480,7 +482,7 @@ public class CodegenEngine { |
| 480 | 482 | if (StrUtil.count(content, "DICT_TYPE.") == 0) { |
| 481 | 483 | content = StrUtils.removeLineContains(content, "DICT_TYPE"); |
| 482 | 484 | } |
| 483 | - return CodegenFormatUtils.formatGeneratedContent(content); | |
| 485 | + return content; | |
| 484 | 486 | } |
| 485 | 487 | |
| 486 | 488 | private Map<String, Object> initBindingMap(DbType dbType, CodegenTableDO table, List<CodegenColumnDO> columns, | ... | ... |
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenFormatUtils.java deleted
| 1 | -package cn.iocoder.yudao.module.infra.service.codegen.inner; | |
| 2 | - | |
| 3 | -import cn.hutool.core.util.StrUtil; | |
| 4 | - | |
| 5 | -/** | |
| 6 | - * 代码生成结果格式化工具 | |
| 7 | - */ | |
| 8 | -final class CodegenFormatUtils { | |
| 9 | - | |
| 10 | - /** | |
| 11 | - * 经典 Vue2 模板目录,与 {@link CodegenEngine#vueTemplatePath(String)} 一致 | |
| 12 | - */ | |
| 13 | - private static final String CLASSIC_VUE2_TEMPLATE_PREFIX = "codegen/vue/"; | |
| 14 | - | |
| 15 | - /** | |
| 16 | - * 经典 Vue3 模板目录,与 {@link CodegenEngine#vue3TemplatePath(String)} 一致。 | |
| 17 | - * 使用前缀 {@code codegen/vue3/} 而非子串匹配,避免误伤 {@code vue3_vben5}、{@code vue3_admin_uniapp} 等目录。 | |
| 18 | - */ | |
| 19 | - private static final String CLASSIC_VUE3_TEMPLATE_PREFIX = "codegen/vue3/"; | |
| 20 | - | |
| 21 | - private CodegenFormatUtils() { | |
| 22 | - } | |
| 23 | - | |
| 24 | - /** | |
| 25 | - * 经典 Vue2 / Vue3 模板(Element UI / Element Plus 标准模版) | |
| 26 | - */ | |
| 27 | - static boolean isClassicVueTemplate(String vmPath) { | |
| 28 | - if (StrUtil.isEmpty(vmPath)) { | |
| 29 | - return false; | |
| 30 | - } | |
| 31 | - return StrUtil.startWith(vmPath, CLASSIC_VUE2_TEMPLATE_PREFIX) | |
| 32 | - || StrUtil.startWith(vmPath, CLASSIC_VUE3_TEMPLATE_PREFIX); | |
| 33 | - } | |
| 34 | - | |
| 35 | - /** | |
| 36 | - * 是否处理 {@code ,\n}} / {@code ,\n }} 形式的末尾逗号(经典 Vue + vue3_vben2;排除 vben5、uniapp) | |
| 37 | - */ | |
| 38 | - static boolean needsVueTrailingCommaBeforeBraceFix(String vmPath) { | |
| 39 | - if (StrUtil.containsAny(vmPath, "vben5", "vue3_admin_uniapp")) { | |
| 40 | - return false; | |
| 41 | - } | |
| 42 | - return isClassicVueTemplate(vmPath) || StrUtil.startWith(vmPath, "codegen/vue3_vben/"); | |
| 43 | - } | |
| 44 | - | |
| 45 | - /** | |
| 46 | - * 是否处理 {@code ,\n)} 形式的末尾逗号(仅经典 Vue 的 .vue 视图模板) | |
| 47 | - */ | |
| 48 | - static boolean needsReactiveTrailingCommaFix(String vmPath) { | |
| 49 | - return isClassicVueTemplate(vmPath) && StrUtil.endWith(vmPath, ".vue.vm"); | |
| 50 | - } | |
| 51 | - | |
| 52 | - /** | |
| 53 | - * Vue 模板:统一去除对象/参数列表末尾多余逗号(与 {@link CodegenEngine#prettyCode} 历史行为一致) | |
| 54 | - */ | |
| 55 | - static String applyVueTrailingCommaFix(String content, String vmPath) { | |
| 56 | - if (content == null) { | |
| 57 | - return null; | |
| 58 | - } | |
| 59 | - content = normalizeLineSeparators(content); | |
| 60 | - if (needsVueTrailingCommaBeforeBraceFix(vmPath)) { | |
| 61 | - content = content.replaceAll(",\\r?\\n}", "\n}") | |
| 62 | - .replaceAll(",\\r?\\n }", "\n }"); | |
| 63 | - } | |
| 64 | - if (needsReactiveTrailingCommaFix(vmPath)) { | |
| 65 | - content = removeTrailingCommaBeforeCloseParen(content); | |
| 66 | - } | |
| 67 | - return content; | |
| 68 | - } | |
| 69 | - | |
| 70 | - /** | |
| 71 | - * 去除多行实参中 {@code ,\n)} 形式的末尾逗号(如 {@code foo(bar,\n)};{@code reactive} 的 {@code ,\n})} 由 {@code ,\n}} 规则处理) | |
| 72 | - */ | |
| 73 | - static String removeTrailingCommaBeforeCloseParen(String content) { | |
| 74 | - if (content == null) { | |
| 75 | - return null; | |
| 76 | - } | |
| 77 | - return content.replaceAll(",\\r?\\n\\)", "\n)"); | |
| 78 | - } | |
| 79 | - | |
| 80 | - /** | |
| 81 | - * 统一换行符为 LF | |
| 82 | - */ | |
| 83 | - static String normalizeLineSeparators(String content) { | |
| 84 | - if (content == null) { | |
| 85 | - return null; | |
| 86 | - } | |
| 87 | - return content.replace("\r\n", "\n").replace('\r', '\n'); | |
| 88 | - } | |
| 89 | - | |
| 90 | - /** | |
| 91 | - * 生产生成结果:LF + 保证文件以单个换行结尾(符合 POSIX / IDE 习惯) | |
| 92 | - */ | |
| 93 | - static String formatGeneratedContent(String content) { | |
| 94 | - content = normalizeLineSeparators(content); | |
| 95 | - if (StrUtil.isEmpty(content)) { | |
| 96 | - return content; | |
| 97 | - } | |
| 98 | - while (content.endsWith("\n\n")) { | |
| 99 | - content = content.substring(0, content.length() - 1); | |
| 100 | - } | |
| 101 | - if (!content.endsWith("\n")) { | |
| 102 | - content = content + "\n"; | |
| 103 | - } | |
| 104 | - return content; | |
| 105 | - } | |
| 106 | - | |
| 107 | - /** | |
| 108 | - * 单测断言:LF + 去掉末尾空白(与历史 fixture 无尾换行对齐) | |
| 109 | - */ | |
| 110 | - static String normalizeForAssert(String content) { | |
| 111 | - if (content == null) { | |
| 112 | - return null; | |
| 113 | - } | |
| 114 | - return normalizeLineSeparators(content).stripTrailing(); | |
| 115 | - } | |
| 116 | -} |
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java
| ... | ... | @@ -18,7 +18,12 @@ import org.mockito.Spy; |
| 18 | 18 | |
| 19 | 19 | import java.io.ByteArrayInputStream; |
| 20 | 20 | import java.io.ByteArrayOutputStream; |
| 21 | -import java.util.*; | |
| 21 | +import java.util.ArrayList; | |
| 22 | +import java.util.HashMap; | |
| 23 | +import java.util.LinkedHashSet; | |
| 24 | +import java.util.List; | |
| 25 | +import java.util.Map; | |
| 26 | +import java.util.Set; | |
| 22 | 27 | |
| 23 | 28 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| 24 | 29 | |
| ... | ... | @@ -99,17 +104,20 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest { |
| 99 | 104 | .map(m -> (String) m.get("filePath")) |
| 100 | 105 | .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); |
| 101 | 106 | assertEquals(expectedFiles, result.keySet(), "生成文件集合不匹配"); |
| 102 | - // 校验每个文件;归一化换行符,让断言不依赖文件落盘的换行风格 | |
| 107 | + // 校验每个文件;归一化 \r\n 为 \n,让断言不依赖文件落盘的换行风格 | |
| 103 | 108 | asserts.forEach(assertMap -> { |
| 104 | 109 | String contentPath = (String) assertMap.get("contentPath"); |
| 105 | 110 | String filePath = (String) assertMap.get("filePath"); |
| 106 | - String expected = CodegenFormatUtils.normalizeForAssert( | |
| 107 | - ResourceUtil.readUtf8Str("codegen/" + path + "/" + contentPath)); | |
| 108 | - String actual = CodegenFormatUtils.normalizeForAssert(result.get(filePath)); | |
| 109 | - assertEquals(expected, actual, filePath + ":不匹配"); | |
| 111 | + String expected = normalizeLineEndings(ResourceUtil.readUtf8Str("codegen/" + path + "/" + contentPath)); | |
| 112 | + String actual = result.get(filePath); | |
| 113 | + assertEquals(expected, normalizeLineEndings(actual), filePath + ":不匹配"); | |
| 110 | 114 | }); |
| 111 | 115 | } |
| 112 | 116 | |
| 117 | + private static String normalizeLineEndings(String content) { | |
| 118 | + return content == null ? null : content.replace("\r\n", "\n").replace("\r", "\n").stripTrailing(); | |
| 119 | + } | |
| 120 | + | |
| 113 | 121 | // ==================== 调试专用 ==================== |
| 114 | 122 | |
| 115 | 123 | /** |
| ... | ... | @@ -143,10 +151,10 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest { |
| 143 | 151 | + '/' + StrUtil.subBefore(lastFilePath, '.', true); |
| 144 | 152 | asserts.add(MapUtil.<String, String>builder().put("filePath", filePath) |
| 145 | 153 | .put("contentPath", contentPath).build()); |
| 146 | - FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath); | |
| 154 | + FileUtil.writeUtf8String(normalizeLineEndings(fileContent), basePath + "/" + contentPath); | |
| 147 | 155 | }); |
| 148 | 156 | // 写入 assert.json 文件 |
| 149 | - FileUtil.writeUtf8String(JsonUtils.toJsonPrettyString(asserts), basePath + "/assert.json"); | |
| 157 | + FileUtil.writeUtf8String(normalizeLineEndings(JsonUtils.toJsonPrettyString(asserts)), basePath + "/assert.json"); | |
| 150 | 158 | } |
| 151 | 159 | |
| 152 | 160 | } | ... | ... |
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenFormatUtilsTest.java deleted
| 1 | -package cn.iocoder.yudao.module.infra.service.codegen.inner; | |
| 2 | - | |
| 3 | -import org.junit.jupiter.api.Test; | |
| 4 | - | |
| 5 | -import static org.junit.jupiter.api.Assertions.*; | |
| 6 | - | |
| 7 | -class CodegenFormatUtilsTest { | |
| 8 | - | |
| 9 | - @Test | |
| 10 | - void testNormalizeLineSeparators() { | |
| 11 | - assertEquals("a\nb", CodegenFormatUtils.normalizeLineSeparators("a\r\nb")); | |
| 12 | - assertEquals("a\nb", CodegenFormatUtils.normalizeLineSeparators("a\rb")); | |
| 13 | - } | |
| 14 | - | |
| 15 | - @Test | |
| 16 | - void testFormatGeneratedContent() { | |
| 17 | - assertEquals("class A {\n}\n", CodegenFormatUtils.formatGeneratedContent("class A {\r\n}\r\n")); | |
| 18 | - assertEquals("x\n", CodegenFormatUtils.formatGeneratedContent("x")); | |
| 19 | - assertEquals("x\n", CodegenFormatUtils.formatGeneratedContent("x\n\n\n")); | |
| 20 | - assertEquals("", CodegenFormatUtils.formatGeneratedContent("")); | |
| 21 | - assertEquals(" \n", CodegenFormatUtils.formatGeneratedContent(" ")); | |
| 22 | - } | |
| 23 | - | |
| 24 | - @Test | |
| 25 | - void testNormalizeForAssert() { | |
| 26 | - assertEquals("}", CodegenFormatUtils.normalizeForAssert("}\n")); | |
| 27 | - assertEquals("a\nb", CodegenFormatUtils.normalizeForAssert("a\r\nb\r\n")); | |
| 28 | - } | |
| 29 | - | |
| 30 | - @Test | |
| 31 | - void testNormalizeForAssert_matchesFormatGeneratedContentForAssert() { | |
| 32 | - String raw = "package demo;\r\n\r\npublic class A {\r\n private Long id;\r\n}\r\n"; | |
| 33 | - String generated = CodegenFormatUtils.formatGeneratedContent(raw); | |
| 34 | - assertEquals(CodegenFormatUtils.normalizeForAssert(raw), | |
| 35 | - CodegenFormatUtils.normalizeForAssert(generated)); | |
| 36 | - } | |
| 37 | - | |
| 38 | - @Test | |
| 39 | - void testRemoveTrailingCommaBeforeCloseParen() { | |
| 40 | - assertEquals("foo(bar\n)", CodegenFormatUtils.removeTrailingCommaBeforeCloseParen("foo(bar,\n)")); | |
| 41 | - } | |
| 42 | - | |
| 43 | - @Test | |
| 44 | - void testApplyVueTrailingCommaFix_classicVueReactive() { | |
| 45 | - String input = "const queryParams = reactive({\n createTime: [],\n})\n"; | |
| 46 | - String expected = "const queryParams = reactive({\n createTime: []\n})\n"; | |
| 47 | - assertEquals(expected, CodegenFormatUtils.applyVueTrailingCommaFix( | |
| 48 | - input, "codegen/vue3/views/index.vue.vm")); | |
| 49 | - } | |
| 50 | - | |
| 51 | - @Test | |
| 52 | - void testApplyVueTrailingCommaFix_skipVben5() { | |
| 53 | - String input = "const x = {\n a: 1,\n}\n"; | |
| 54 | - assertEquals(input, CodegenFormatUtils.applyVueTrailingCommaFix( | |
| 55 | - input, "codegen/vue3_vben5_antd/general/views/index.vue.vm")); | |
| 56 | - } | |
| 57 | - | |
| 58 | - @Test | |
| 59 | - void testIsClassicVueTemplate() { | |
| 60 | - assertTrue(CodegenFormatUtils.isClassicVueTemplate("codegen/vue/views/index.vue.vm")); | |
| 61 | - assertTrue(CodegenFormatUtils.isClassicVueTemplate("codegen/vue3/views/index.vue.vm")); | |
| 62 | - assertTrue(CodegenFormatUtils.isClassicVueTemplate("codegen/vue3/api/api.ts.vm")); | |
| 63 | - assertFalse(CodegenFormatUtils.isClassicVueTemplate("codegen/vue3_vben5_antd/schema/views/index.vue.vm")); | |
| 64 | - assertFalse(CodegenFormatUtils.isClassicVueTemplate("codegen/vue3_vben/views/index.vue.vm")); | |
| 65 | - assertFalse(CodegenFormatUtils.isClassicVueTemplate("codegen/vue3_admin_uniapp/views/index.vue.vm")); | |
| 66 | - assertFalse(CodegenFormatUtils.isClassicVueTemplate("codegen/vue30/views/index.vue.vm")); // 非 codegen/vue3/ 前缀 | |
| 67 | - assertFalse(CodegenFormatUtils.isClassicVueTemplate("codegen/java/controller/controller.vm")); | |
| 68 | - assertFalse(CodegenFormatUtils.isClassicVueTemplate(null)); | |
| 69 | - } | |
| 70 | - | |
| 71 | - @Test | |
| 72 | - void testNeedsReactiveTrailingCommaFix() { | |
| 73 | - assertTrue(CodegenFormatUtils.needsReactiveTrailingCommaFix("codegen/vue3/views/index.vue.vm")); | |
| 74 | - assertFalse(CodegenFormatUtils.needsReactiveTrailingCommaFix("codegen/vue3/api/api.ts.vm")); | |
| 75 | - assertFalse(CodegenFormatUtils.needsReactiveTrailingCommaFix("codegen/vue3_vben5_antd/general/views/index.vue.vm")); | |
| 76 | - } | |
| 77 | - | |
| 78 | - @Test | |
| 79 | - void testNeedsVueTrailingCommaBeforeBraceFix() { | |
| 80 | - assertTrue(CodegenFormatUtils.needsVueTrailingCommaBeforeBraceFix("codegen/vue3/views/index.vue.vm")); | |
| 81 | - assertTrue(CodegenFormatUtils.needsVueTrailingCommaBeforeBraceFix("codegen/vue3_vben/views/index.vue.vm")); | |
| 82 | - assertFalse(CodegenFormatUtils.needsVueTrailingCommaBeforeBraceFix("codegen/vue3_vben5_antd/general/views/index.vue.vm")); | |
| 83 | - assertFalse(CodegenFormatUtils.needsVueTrailingCommaBeforeBraceFix("codegen/vue3_admin_uniapp/views/index.vue.vm")); | |
| 84 | - } | |
| 85 | -} | |
| 86 | - |