Skip to content

Commit 0180681

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
fix: skip methods with unmockable interface args in smoke tests
Methods taking non-IBinder interface arguments (or structs containing them) would panic on nil dereference — a test limitation, not a codegen bug. Now detected and skipped. Methods with only mockable arguments still get panic-checked, catching genuine codegen bugs.
1 parent 74717ef commit 0180681

2 files changed

Lines changed: 61 additions & 4 deletions

File tree

tools/pkg/testutil/smoke.go

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,27 @@ func SmokeTestAllMethods(
6363
continue
6464
}
6565

66-
result.Total++
6766
methodName := method.Name
6867
methodValue := v.Method(i)
6968
methodType := methodValue.Type()
7069

70+
if hasUnmockableArgs(methodType) {
71+
t.Run(methodName, func(t *testing.T) {
72+
t.Skipf("method %s skipped (has unmockable interface args)", methodName)
73+
})
74+
continue
75+
}
76+
77+
result.Total++
78+
7179
t.Run(methodName, func(t *testing.T) {
7280
args := buildZeroArgs(methodType)
7381

7482
outcome := callWithRecover(methodValue, args)
7583
switch outcome {
7684
case outcomePanicked:
7785
result.Panicked++
78-
t.Logf("method %s panicked (nil interface arg)", methodName)
86+
t.Errorf("method %s panicked unexpectedly", methodName)
7987
case outcomeFailed:
8088
result.Failed++
8189
t.Logf("method %s returned an error", methodName)
@@ -141,6 +149,55 @@ var (
141149
iBinderType = reflect.TypeOf((*binder.IBinder)(nil)).Elem()
142150
)
143151

152+
// hasUnmockableArgs returns true if the method has arguments containing
153+
// interface types that buildZeroArgs cannot provide a non-nil value for.
154+
// This includes direct interface arguments and struct fields that are
155+
// interfaces (e.g., union types with binder interface fields).
156+
func hasUnmockableArgs(methodType reflect.Type) bool {
157+
for i := 0; i < methodType.NumIn(); i++ {
158+
if typeContainsUnmockableInterface(methodType.In(i)) {
159+
return true
160+
}
161+
}
162+
return false
163+
}
164+
165+
// typeContainsUnmockableInterface checks whether a type is or contains
166+
// an interface that buildZeroArgs cannot mock. The visited map prevents
167+
// infinite recursion on self-referencing struct types.
168+
func typeContainsUnmockableInterface(t reflect.Type) bool {
169+
return typeContainsUnmockableInterfaceVisited(t, map[reflect.Type]bool{})
170+
}
171+
172+
func typeContainsUnmockableInterfaceVisited(t reflect.Type, visited map[reflect.Type]bool) bool {
173+
if visited[t] {
174+
return false
175+
}
176+
visited[t] = true
177+
178+
switch t.Kind() {
179+
case reflect.Interface:
180+
if t.Implements(contextType) {
181+
return false
182+
}
183+
if t.Implements(iBinderType) || t == iBinderType {
184+
return false
185+
}
186+
return true
187+
case reflect.Struct:
188+
for i := 0; i < t.NumField(); i++ {
189+
if typeContainsUnmockableInterfaceVisited(t.Field(i).Type, visited) {
190+
return true
191+
}
192+
}
193+
case reflect.Ptr:
194+
return typeContainsUnmockableInterfaceVisited(t.Elem(), visited)
195+
case reflect.Slice, reflect.Array:
196+
return typeContainsUnmockableInterfaceVisited(t.Elem(), visited)
197+
}
198+
return false
199+
}
200+
144201
// buildZeroArgs builds zero-value arguments for a method.
145202
// context.Context parameters get context.Background().
146203
// binder.IBinder parameters get a MockBinder.

tools/pkg/testutil/smoke_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ func TestSmokeTestAllMethods(t *testing.T) {
104104

105105
result := SmokeTestAllMethods(t, proxy)
106106

107-
assert.Equal(t, 4, result.Total, "should test 4 methods (AsBinder is skipped)")
107+
assert.Equal(t, 3, result.Total, "should test 3 methods (AsBinder skipped, PanicMethod skipped for unmockable interface)")
108108
assert.Equal(t, 0, result.Passed, "no methods should pass (mock returns SecurityException)")
109-
assert.Equal(t, 1, result.Panicked, "PanicMethod should panic on nil interface")
109+
assert.Equal(t, 0, result.Panicked, "no panics expected (unmockable methods are skipped)")
110110
assert.Equal(t, 3, result.Failed, "SimpleMethod, MethodWithArgs, and MethodWithIBinder should fail (SecurityException)")
111111
}
112112

0 commit comments

Comments
 (0)