IllegalStateException: Failed to transform class with name backend.model.posting.PostingTest. Reason: before ()V in backend.model.posting.PostingTest: inconsistent stack height -1
#779 建立於 2017年4月14日
描述
Hey Guys, I see an IllegalStateException when running one particular test.
What steps will reproduce the problem? The following code fails each time it is run
Testcase:
package backend.model.posting
import backend.controller.exceptions.ConflictException
import backend.model.location.Location
import backend.model.user.UserAccount
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.powermock.api.mockito.PowerMockito
import org.powermock.api.mockito.PowerMockito.mock
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import java.time.LocalDateTime
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
@RunWith(PowerMockRunner::class)
@PrepareForTest(Location::class, UserAccount::class)
class PostingTest {
private lateinit var now: LocalDateTime
private lateinit var later: LocalDateTime
private lateinit var someLocation: Location
private lateinit var creator: UserAccount
private lateinit var posting: Posting
@Before
fun before() {
now = LocalDateTime.now()
later = now.plusMinutes(10)
someLocation = mock(Location::class.java)
creator = mock(UserAccount::class.java)
posting = Posting("Lalala", LocalDateTime.MAX, someLocation, creator, mutableListOf())
}
@Test
fun like() {
posting.like(later, creator)
assertEquals(posting.likes.count(), 1)
}
@Test
fun multipleLikes() {
val other = mock(UserAccount::class.java)
PowerMockito.`when`(creator.id).thenReturn(1)
PowerMockito.`when`(other.id).thenReturn(2)
posting.like(later, creator)
posting.like(later, other)
assertEquals(posting.likes.count(), 2)
}
fun cantLikeTwice() {
PowerMockito.`when`(creator.id).thenReturn(1)
posting.like(later, creator)
assertFailsWith<ConflictException> {
posting.like(later, creator)
}
}
@Test
fun unlike() {
posting.like(later, creator)
posting.unlike(creator)
}
@Test
fun cantUnlikeNotLiked() {
assertFailsWith<ConflictException> {
posting.unlike(creator)
}
}
@Test
@Ignore("Posting::hasLikesBy needs to be refactored first")
fun hasLikesBy() {
}
}
Class under Test:
package backend.model.posting
import backend.controller.exceptions.BadRequestException
import backend.controller.exceptions.ConflictException
import backend.controller.exceptions.NotFoundException
import backend.model.BasicEntity
import backend.model.challenges.Challenge
import backend.model.location.Location
import backend.model.media.Media
import backend.model.user.UserAccount
import java.time.LocalDateTime
import java.util.*
import java.util.regex.Matcher
import java.util.regex.Pattern
import javax.persistence.*
import javax.persistence.CascadeType.PERSIST
@Entity
class Posting : BasicEntity {
private constructor() : super()
@Column(columnDefinition = "TEXT")
var text: String? = null
@ElementCollection
var hashtags: List<Hashtag> = ArrayList()
lateinit var date: LocalDateTime
@OneToOne(cascade = arrayOf(PERSIST))
var location: Location? = null
@OneToOne(cascade = arrayOf(PERSIST))
var challenge: Challenge? = null
@ManyToOne
var user: UserAccount? = null
@OneToMany(cascade = arrayOf(CascadeType.ALL))
var media: MutableList<Media> = arrayListOf()
@OneToMany(cascade = arrayOf(CascadeType.ALL), orphanRemoval = true)
var comments: MutableList<Comment> = arrayListOf()
@OneToMany(cascade = arrayOf(CascadeType.ALL), orphanRemoval = true)
@JoinTable(
joinColumns = arrayOf(JoinColumn(name = "posting_id", referencedColumnName = "id")),
inverseJoinColumns = arrayOf(JoinColumn(name = "like_id", referencedColumnName = "id"))
)
var likes: MutableSet<Like> = hashSetOf()
@Transient
var hasLiked = false
constructor(text: String?, date: LocalDateTime, location: Location?, user: UserAccount, media: MutableList<Media>) : this() {
this.text = text
this.date = date
this.location = location
this.user = user
this.media = media
if (text != null) this.hashtags = extractHashtags(text)
}
private fun extractHashtags(text: String): List<Hashtag> {
val pattern: Pattern = Pattern.compile("#([^\\s]*)")
val matcher: Matcher = pattern.matcher(text)
val hashtags = ArrayList<Hashtag>()
while (matcher.find()) {
val hashtag = matcher.group(1)
hashtags.add(Hashtag(hashtag))
}
return hashtags
}
fun like(createdAt: LocalDateTime, user: UserAccount): Like {
val like = Like(createdAt, user)
if (this.isLikedBy(user)) {
throw ConflictException("User ${user.id} has already liked this posting!")
} else {
this.likes.add(like)
}
return like
}
fun unlike(user: UserAccount) {
if (this.isLikedBy(user)) {
val like = findLikeByUser(user)
this.likes.remove(like)
} else {
throw ConflictException("Can't unlike because user ${user.id} has not liked posting ${this.id}")
}
}
private fun findLikeByUser(user: UserAccount): Like? {
return this.likes
.filter { it.user?.id == user.id } // TODO: use equals here somehow?
.firstOrNull()
}
private fun isLikedBy(user: UserAccount): Boolean {
return findLikeByUser(user) != null
}
@PreRemove
fun preRemove() {
this.likes.clear()
this.comments.clear()
this.media.clear()
this.challenge = null
this.user = null
}
// TODO: Refactor this!
fun hasLikesBy(userId: Long?): Posting {
if (userId != null) {
this.hasLiked = this.likes.any { it.user?.id == userId }
}
return this
}
private fun findCommentById(commentId: Long): Comment? {
return this.comments.filter { it.id == commentId }.firstOrNull()
}
fun removeComment(commentId: Long) {
this.findCommentById(commentId)?.let {
this.comments.remove(it)
return
}
throw NotFoundException("Comment with id $commentId not found at posting $id")
}
fun addComment(from: UserAccount, at: LocalDateTime, withText: String): Comment {
if (withText.trim().isEmpty()) {
throw BadRequestException("Empty comments are not allowed")
}
val comment = Comment(withText, at, from)
this.comments.add(comment)
return comment
}
}
What is the expected output? All tests run without exceptions
What I see instead Stacktrace:
java.lang.IllegalStateException: Failed to transform class with name backend.model.posting.PostingTest. Reason: before ()V in backend.model.posting.PostingTest: inconsistent stack height -1
at org.powermock.core.classloader.MockClassLoader.loadMockClass(MockClassLoader.java:283)
at org.powermock.core.classloader.MockClassLoader.loadModifiedClass(MockClassLoader.java:192)
at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass(DeferSupportingClassLoader.java:71)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.createDelegatorFromClassloader(JUnit4TestSuiteChunkerImpl.java:161)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.createDelegatorFromClassloader(JUnit4TestSuiteChunkerImpl.java:48)
at org.powermock.tests.utils.impl.AbstractTestSuiteChunkerImpl.createTestDelegators(AbstractTestSuiteChunkerImpl.java:113)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.<init>(JUnit4TestSuiteChunkerImpl.java:71)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.<init>(AbstractCommonPowerMockRunner.java:32)
at org.powermock.modules.junit4.PowerMockRunner.<init>(PowerMockRunner.java:34)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:49)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: javassist.CannotCompileException: before ()V in backend.model.posting.PostingTest: inconsistent stack height -1
at javassist.expr.ExprEditor.doit(ExprEditor.java:117)
at javassist.CtClassType.instrument(CtClassType.java:1465)
at org.powermock.core.transformers.impl.ClassMockTransformer.transformMockClass(ClassMockTransformer.java:65)
at org.powermock.core.transformers.impl.AbstractMainMockTransformer.transform(AbstractMainMockTransformer.java:247)
at org.powermock.core.classloader.MockClassLoader.loadMockClass(MockClassLoader.java:264)
... 25 more
Caused by: javassist.bytecode.BadBytecode: before ()V in backend.model.posting.PostingTest: inconsistent stack height -1
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:111)
at javassist.bytecode.MethodInfo.rebuildStackMap(MethodInfo.java:456)
at javassist.bytecode.MethodInfo.rebuildStackMapIf6(MethodInfo.java:438)
at javassist.expr.ExprEditor.doit(ExprEditor.java:113)
... 29 more
Caused by: javassist.bytecode.BadBytecode: inconsistent stack height -1
at javassist.bytecode.stackmap.Tracer.doOpcode(Tracer.java:84)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:187)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:199)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:164)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:108)
... 32 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: -1
at javassist.bytecode.stackmap.Tracer.doASTORE(Tracer.java:425)
at javassist.bytecode.stackmap.Tracer.doOpcode54_95(Tracer.java:342)
at javassist.bytecode.stackmap.Tracer.doOpcode(Tracer.java:76)
... 57 more
What version of the product are you using? 1.6.5'
On what operating system? MacOS Sierra (10.12.3)
Please provide any additional information below. We are using PowerMock with Kotlin in a Spring Boot Project. This is a unit test without any spring context running.
We additionally use the kotlin-spring plugin which makes kotlin classes open at compile time (https://kotlinlang.org/docs/reference/compiler-plugins.html) (I tried disabling it, it did not have any effect on the bug)
The error seems to be triggered by the line posting = Posting("Lalala", LocalDateTime.MAX, someLocation, creator, mutableListOf()) in the before function. Without this line, the error does not occur but my tests fail as expected instead (because posting is not initialised)
I would be more than happy to assist with debugging this, but would possibly need a lead on where to search / what to look for, as I don't have much experience on the low level / bytecode level stuff yet