kusumotolab / kGenProg

A High-performance, High-extensibility and High-portability APR System
MIT License
48 stars 13 forks source link

Mock+テストがうまく動かない #690

Closed tt-kuma closed 4 years ago

tt-kuma commented 4 years ago
YoshikiHigo commented 4 years ago

モック使用時のバグを再現するための簡単なプロジェクトを作りました. zipファイルを解凍後,README.mdファイルを読んでください.

SimpleMockitoProject.zip

YoshikiHigo commented 4 years ago

このissueのゴールって「再現する最小の題材を作る」でしたね. 作った題材でプルリクを送ります.

YoshikiHigo commented 4 years ago

あ,mergeするものがないからプルリク無理でした.あたりまえか...

shinsuke-mat commented 4 years ago

題材確認しました.

題材が単体で動作しません.

java.lang.IllegalStateException: Could not initialize plugin: interface org.mockito.plugins.MockMaker (alternate: null)
    at org.mockito.internal.configuration.plugins.PluginLoader$1.invoke(PluginLoader.java:74)
        ...

おそらくmockitoの依存libの不足が原因です.

net.bytebuddy » byte-buddy
net.bytebuddy » byte-buddy-agent
org.objenesis » objenesis

https://mvnrepository.com/artifact/org.mockito/mockito-core/3.2.4

依存libがないので,題材の実行ができず, 結果的にkgpが失敗している(ように見える),という状態だと思います.

YoshikiHigo commented 4 years ago

SimpleMockitoProject.zip

byte-buddy と objenesis を追加しました. もう一度試して頂けますか.

shinsuke-mat commented 4 years ago

後のための備忘録

mock題材をkgpが正しく処理できないことを確認.

ビルドに失敗している?

→ no ビルド自体は成功

実行に失敗している?

→ yes

実行時のエラーは?

TestThread のテスト実行失敗時にdebugログを入れた.

2020-03-16 12:53:55 [main] [WARN]  TestThread - java.lang.LinkageError: loader constraint violation: loader 'app' wants to load interface org.hamcrest.Matcher. A different interface with the same name was previously loaded by jp.kusumotolab.kgenprog.project.test.SkippingMemoryClassLoader @7cd1ac19. (org.hamcrest.Matcher is in unnamed module of loader jp.kusumotolab.kgenprog.project.test.SkippingMemoryClassLoader @7cd1ac19, parent loader 'app')
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
    at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
    at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:800)
    at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:698)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:621)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:579)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:12)
    at org.junit.Assert.assertThat(Assert.java:956)
    at org.junit.Assert.assertThat(Assert.java:923)
    at smt.SimpleMockitoTest.testMockitoMain(SimpleMockitoTest.java:17)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:52)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:303)
    at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:297)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.lang.Thread.run(Thread.java:834)

クラスロードの問題らしい. org.hamcrest.Matcherapp がロードしようとしたけど, SkippingMemoryCL がロード済みでまずい,とのこと.

対処

SMCL のロードスキップ条件を追加してみた.

- if (name.startsWith("org.junit.") || name.startsWith("junit.")) {
+ if (name.startsWith("org.junit.") || name.startsWith("junit.") || name.startsWith("org.hamcrest.")) {

このロードスキップ処理は #554 で追加された. JUnit関連のロードのみをAppClassLoaderに委譲するらしい.

SkippingMemoryClassLoader

MemoryClassLoaderの拡張クラス.クラスローダの委譲関係をあえて崩すことで,KGP本体のクラスロード(AppClassLoader)の副作用を回避する.委譲の流れは以下の通り.

 - SkippingMemoryClassLoader
 -> AppClassLoader (ここをスキップ)
 -> ExtensionClassLoader (ここにダイレクトに委譲)
 -> BootstrapClassLoader

ただし例外としてJUnit関係のクラスのみ,そのロードをAppClassLoaderに委譲する.KGPのテスト実行時のJUnitクラス,及び題材のテスト実行時のJUnitクラスを同一のクラスローダでロードしないと,JUnitが期待通りに動作しないため.

とりあえず上の対応でうまく動いていることを確認. もう少しデバッグしておくべき.