Skip to content

<test>[testlib]: cjlib improve#4162

Open
MatheMatrix wants to merge 1 commit into
4.8.38from
sync/yaohua.wu/bugfix/ZSTAC-85588@@2
Open

<test>[testlib]: cjlib improve#4162
MatheMatrix wants to merge 1 commit into
4.8.38from
sync/yaohua.wu/bugfix/ZSTAC-85588@@2

Conversation

@MatheMatrix
Copy link
Copy Markdown
Owner

Backport ZSTAC-85588 to 4.8.38 (cross-repo @@2)

Cherry-pick of original fix (含配套测试) to 4.8.38. 本 issue 同时涉及 zstack 与 premium 两仓,分支统一命名 bugfix/ZSTAC-85588@@2 以便 DevOps 关联跑 case。

Sibling repo MR: premium MR (see below)

Commits

commit 4aab481762 <test>[testlib]: cjlib improve
 .../java/org/zstack/testlib/util/TProxy.groovy     | 27 ++++++++++++++++++++++
 1 file changed, 27 insertions(+)

Related: ZSTAC-85588

sync from gitlab !10067

cjlib improve

Resolves: ZSTAC-63145

Change-Id: I7077737376616b66696c6b617269786b6e72617a
(cherry picked from commit 6d8e56c)
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

概览

为 TProxy 代理类新增参数化构造函数,支持在创建代理对象时传递构造参数给被代理类,并保留适配器字段值的复制逻辑。

变更

TProxy 参数化构造函数

图层 / 文件 说明
参数化构造函数实现
testlib/src/main/java/org/zstack/testlib/util/TProxy.groovy
新增构造函数接收适配对象、构造参数类型数组和参数值数组,通过 Enhancer 创建代理对象,复制可变实例字段,并对 IllegalArgumentException 进行条件包装以提供清晰的错误消息。

代码审查工作量估计

🎯 2 (简单) | ⏱️ ~10 分钟

庆祝诗

🐰 代理又添新能力,参数构造不再难,
Enhancer 轻轻一挥手,字段复制自动搬,
异常捕获细心包装,错误信息更清晰,
TProxy 功能更完整,测试库更开心!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed 标题与变更集相关,提到了 testlib 和 cjlib 改进,与代码变更的具体内容不够直接。
Description check ✅ Passed 描述与变更集相关,说明了这是对 ZSTAC-85588 问题的反向移植,包含提交信息、相关问题和来源。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/yaohua.wu/bugfix/ZSTAC-85588@@2

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@testlib/src/main/java/org/zstack/testlib/util/TProxy.groovy`:
- Around line 90-96: The catch block handling IllegalArgumentException in TProxy
(where adaptee.getClass() is referenced) uses a misleading message "has no
non-argument constructor"; update that exception message to reflect the actual
situation when a parameterized constructor cannot be found (e.g., "has no
matching parameterized constructor for the provided arguments") and rethrow the
new Exception with the original cause so the message correctly describes the
lookup failure for a parameterized constructor.
- Around line 72-73: 构造函数 TProxy(Object adaptee, Class[] argumentTypes, Object[]
arguments) 缺少对 argumentTypes 和 arguments 的校验,会导致 NPE 或参数数量/类型不匹配的运行时错误;在方法开始处验证
argumentTypes 和 arguments 非 null,并且检查 argumentTypes.length ==
arguments.length(不满足时抛出 IllegalArgumentException 或 AssertionError),同时保留对 adaptee
的现有断言并在错误消息中说明期望的参数数量和类型以便调试。
- Around line 72-97: The two constructors TProxy(Object adaptee) and
TProxy(Object adaptee, Class[] argumentTypes, Object[] arguments) duplicate
field-copy and exception-handling logic; refactor by extracting the shared logic
into a private helper (e.g., initProxyFields(Object proxyedObject, Object
adaptee)) that performs the FieldUtils.getAllFields loop and the
IllegalArgumentException handling, and have each constructor only create the
Enhancer, call enhancer.create(...) with the appropriate signature, then pass
the created proxy object and adaptee into that helper; ensure the helper
preserves the current behavior (skip static/final fields, setAccessible, copy
values, and translate the specific IllegalArgumentException message).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: eb06b31a-a585-4773-a1aa-21b43899111d

📥 Commits

Reviewing files that changed from the base of the PR and between 00abf62 and 4aab481.

📒 Files selected for processing (1)
  • testlib/src/main/java/org/zstack/testlib/util/TProxy.groovy

Comment on lines +72 to +73
TProxy(Object adaptee, Class[] argumentTypes, Object[] arguments) {
assert adaptee != null : "call TProxy(Class adapteeClass) instead"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

添加参数验证以防止运行时错误。

构造函数缺少对 argumentTypesarguments 的验证:

  • 未检查 null 值,可能导致 NPE
  • 未验证 argumentTypes.length == arguments.length,类型与参数数量不匹配会在运行时失败
🛡️ 建议添加参数验证
 TProxy(Object adaptee, Class[] argumentTypes, Object[] arguments) {
     assert adaptee != null : "call TProxy(Class adapteeClass) instead"
+    assert argumentTypes != null : "argumentTypes cannot be null"
+    assert arguments != null : "arguments cannot be null"
+    assert argumentTypes.length == arguments.length : "argumentTypes and arguments must have the same length"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@testlib/src/main/java/org/zstack/testlib/util/TProxy.groovy` around lines 72
- 73, 构造函数 TProxy(Object adaptee, Class[] argumentTypes, Object[] arguments) 缺少对
argumentTypes 和 arguments 的校验,会导致 NPE 或参数数量/类型不匹配的运行时错误;在方法开始处验证 argumentTypes 和
arguments 非 null,并且检查 argumentTypes.length == arguments.length(不满足时抛出
IllegalArgumentException 或 AssertionError),同时保留对 adaptee
的现有断言并在错误消息中说明期望的参数数量和类型以便调试。

Comment on lines +72 to +97
TProxy(Object adaptee, Class[] argumentTypes, Object[] arguments) {
assert adaptee != null : "call TProxy(Class adapteeClass) instead"

Enhancer enhancer = new Enhancer()
enhancer.setSuperclass(adaptee.getClass())
enhancer.setCallback(new ProxyHandler())
try {
proxyedObject = enhancer.create(argumentTypes, arguments)
if (adaptee != null) {
FieldUtils.getAllFields(adaptee.getClass()).each { f ->
if (Modifier.isStatic(f.modifiers) || Modifier.isFinal(f.modifiers)) {
return
}

f.setAccessible(true)
f.set(proxyedObject, f.get(adaptee))
}
}
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Superclass has no null constructors" )) {
throw new Exception("the class ${adaptee.getClass()} has no non-argument constructor", e)
}

throw e
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

消除与现有构造函数的代码重复。

新构造函数与 TProxy(Object adaptee)(第 45-70 行)几乎完全重复,唯一区别是第 79 行调用 enhancer.create(argumentTypes, arguments) 而非无参的 enhancer.create()。字段复制和异常处理逻辑完全相同,违反 DRY 原则。

建议提取公共逻辑到私有辅助方法中,以提高可维护性并减少未来出现不一致的风险。

♻️ 建议重构以消除重复
+    private void copyFields(Object source, Object target) {
+        FieldUtils.getAllFields(source.getClass()).each { f ->
+            if (Modifier.isStatic(f.modifiers) || Modifier.isFinal(f.modifiers)) {
+                return
+            }
+            f.setAccessible(true)
+            f.set(target, f.get(source))
+        }
+    }
+
+    private Object createProxy(Object adaptee, Closure createEnhancer) {
+        Enhancer enhancer = new Enhancer()
+        enhancer.setSuperclass(adaptee.getClass())
+        enhancer.setCallback(new ProxyHandler())
+        try {
+            def proxy = createEnhancer(enhancer)
+            if (adaptee != null) {
+                copyFields(adaptee, proxy)
+            }
+            return proxy
+        } catch (IllegalArgumentException e) {
+            if (e.getMessage().contains("Superclass has no null constructors")) {
+                throw new Exception("the class ${adaptee.getClass()} has no matching constructor", e)
+            }
+            throw e
+        }
+    }
+
     TProxy(Object adaptee) {
         assert adaptee != null : "call TProxy(Class adapteeClass) instead"
-        Enhancer enhancer = new Enhancer()
-        enhancer.setSuperclass(adaptee.getClass())
-        enhancer.setCallback(new ProxyHandler())
-        try {
-            proxyedObject = enhancer.create()
-            if (adaptee != null) {
-                FieldUtils.getAllFields(adaptee.getClass()).each { f ->
-                    if (Modifier.isStatic(f.modifiers) || Modifier.isFinal(f.modifiers)) {
-                        return
-                    }
-
-                    f.setAccessible(true)
-                    f.set(proxyedObject, f.get(adaptee))
-                }
-            }
-        } catch (IllegalArgumentException e) {
-            if (e.getMessage().contains("Superclass has no null constructors" )) {
-                throw new Exception("the class ${adaptee.getClass()} has no non-argument constructor", e)
-            }
-
-            throw e
-        }
+        proxyedObject = createProxy(adaptee) { enhancer -> enhancer.create() }
     }
 
     TProxy(Object adaptee, Class[] argumentTypes, Object[] arguments) {
         assert adaptee != null : "call TProxy(Class adapteeClass) instead"
-        Enhancer enhancer = new Enhancer()
-        enhancer.setSuperclass(adaptee.getClass())
-        enhancer.setCallback(new ProxyHandler())
-        try {
-            proxyedObject = enhancer.create(argumentTypes, arguments)
-            if (adaptee != null) {
-                FieldUtils.getAllFields(adaptee.getClass()).each { f ->
-                    if (Modifier.isStatic(f.modifiers) || Modifier.isFinal(f.modifiers)) {
-                        return
-                    }
-
-                    f.setAccessible(true)
-                    f.set(proxyedObject, f.get(adaptee))
-                }
-            }
-        } catch (IllegalArgumentException e) {
-            if (e.getMessage().contains("Superclass has no null constructors" )) {
-                throw new Exception("the class ${adaptee.getClass()} has no non-argument constructor", e)
-            }
-
-            throw e
-        }
+        proxyedObject = createProxy(adaptee) { enhancer -> enhancer.create(argumentTypes, arguments) }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@testlib/src/main/java/org/zstack/testlib/util/TProxy.groovy` around lines 72
- 97, The two constructors TProxy(Object adaptee) and TProxy(Object adaptee,
Class[] argumentTypes, Object[] arguments) duplicate field-copy and
exception-handling logic; refactor by extracting the shared logic into a private
helper (e.g., initProxyFields(Object proxyedObject, Object adaptee)) that
performs the FieldUtils.getAllFields loop and the IllegalArgumentException
handling, and have each constructor only create the Enhancer, call
enhancer.create(...) with the appropriate signature, then pass the created proxy
object and adaptee into that helper; ensure the helper preserves the current
behavior (skip static/final fields, setAccessible, copy values, and translate
the specific IllegalArgumentException message).

Comment on lines +90 to +96
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Superclass has no null constructors" )) {
throw new Exception("the class ${adaptee.getClass()} has no non-argument constructor", e)
}

throw e
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

修正异常消息以匹配参数化构造的上下文。

第 92 行的错误消息"has no non-argument constructor"在此上下文中不正确。当调用带参数的构造函数时,该消息具有误导性——实际问题应该是找不到匹配的参数化构造函数,而非无参构造函数缺失。

🐛 建议修正错误消息
         } catch (IllegalArgumentException e) {
             if (e.getMessage().contains("Superclass has no null constructors" )) {
-                throw new Exception("the class ${adaptee.getClass()} has no non-argument constructor", e)
+                throw new Exception("the class ${adaptee.getClass()} has no matching constructor for the given argument types", e)
             }
 
             throw e
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Superclass has no null constructors" )) {
throw new Exception("the class ${adaptee.getClass()} has no non-argument constructor", e)
}
throw e
}
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Superclass has no null constructors" )) {
throw new Exception("the class ${adaptee.getClass()} has no matching constructor for the given argument types", e)
}
throw e
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@testlib/src/main/java/org/zstack/testlib/util/TProxy.groovy` around lines 90
- 96, The catch block handling IllegalArgumentException in TProxy (where
adaptee.getClass() is referenced) uses a misleading message "has no non-argument
constructor"; update that exception message to reflect the actual situation when
a parameterized constructor cannot be found (e.g., "has no matching
parameterized constructor for the provided arguments") and rethrow the new
Exception with the original cause so the message correctly describes the lookup
failure for a parameterized constructor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant