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,8 +457,10 @@ public class CodegenEngine { | ||
| 457 | * @return 格式化后的代码 | 457 | * @return 格式化后的代码 |
| 458 | */ | 458 | */ |
| 459 | private String prettyCode(String content, String vmPath) { | 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 | // Vue 界面:去除多的 dateFormatter,只有一个的情况下,说明没使用到 | 464 | // Vue 界面:去除多的 dateFormatter,只有一个的情况下,说明没使用到 |
| 463 | if (StrUtil.count(content, "dateFormatter") == 1) { | 465 | if (StrUtil.count(content, "dateFormatter") == 1) { |
| 464 | content = StrUtils.removeLineContains(content, "dateFormatter"); | 466 | content = StrUtils.removeLineContains(content, "dateFormatter"); |
| @@ -480,7 +482,7 @@ public class CodegenEngine { | @@ -480,7 +482,7 @@ public class CodegenEngine { | ||
| 480 | if (StrUtil.count(content, "DICT_TYPE.") == 0) { | 482 | if (StrUtil.count(content, "DICT_TYPE.") == 0) { |
| 481 | content = StrUtils.removeLineContains(content, "DICT_TYPE"); | 483 | content = StrUtils.removeLineContains(content, "DICT_TYPE"); |
| 482 | } | 484 | } |
| 483 | - return CodegenFormatUtils.formatGeneratedContent(content); | 485 | + return content; |
| 484 | } | 486 | } |
| 485 | 487 | ||
| 486 | private Map<String, Object> initBindingMap(DbType dbType, CodegenTableDO table, List<CodegenColumnDO> columns, | 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,7 +18,12 @@ import org.mockito.Spy; | ||
| 18 | 18 | ||
| 19 | import java.io.ByteArrayInputStream; | 19 | import java.io.ByteArrayInputStream; |
| 20 | import java.io.ByteArrayOutputStream; | 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 | import static org.junit.jupiter.api.Assertions.assertEquals; | 28 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| 24 | 29 | ||
| @@ -99,17 +104,20 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest { | @@ -99,17 +104,20 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest { | ||
| 99 | .map(m -> (String) m.get("filePath")) | 104 | .map(m -> (String) m.get("filePath")) |
| 100 | .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); | 105 | .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); |
| 101 | assertEquals(expectedFiles, result.keySet(), "生成文件集合不匹配"); | 106 | assertEquals(expectedFiles, result.keySet(), "生成文件集合不匹配"); |
| 102 | - // 校验每个文件;归一化换行符,让断言不依赖文件落盘的换行风格 | 107 | + // 校验每个文件;归一化 \r\n 为 \n,让断言不依赖文件落盘的换行风格 |
| 103 | asserts.forEach(assertMap -> { | 108 | asserts.forEach(assertMap -> { |
| 104 | String contentPath = (String) assertMap.get("contentPath"); | 109 | String contentPath = (String) assertMap.get("contentPath"); |
| 105 | String filePath = (String) assertMap.get("filePath"); | 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,10 +151,10 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest { | ||
| 143 | + '/' + StrUtil.subBefore(lastFilePath, '.', true); | 151 | + '/' + StrUtil.subBefore(lastFilePath, '.', true); |
| 144 | asserts.add(MapUtil.<String, String>builder().put("filePath", filePath) | 152 | asserts.add(MapUtil.<String, String>builder().put("filePath", filePath) |
| 145 | .put("contentPath", contentPath).build()); | 153 | .put("contentPath", contentPath).build()); |
| 146 | - FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath); | 154 | + FileUtil.writeUtf8String(normalizeLineEndings(fileContent), basePath + "/" + contentPath); |
| 147 | }); | 155 | }); |
| 148 | // 写入 assert.json 文件 | 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 | - |