powermock: IllegalStateException: Failed to transform class with name backend.model.posting.PostingTest. Reason: before ()V in backend.model.posting.PostingTest: inconsistent stack height -1
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
About this issue
- Original URL
- State: open
- Created 7 years ago
- Comments: 18 (2 by maintainers)
Minimal sample to reproduce problem:
‘test1’ method bytecode :
Could such bytecode be supported with Powermock or it’s Javassist problem?
Thanks for the tip, the following workaround did work:
BTW: This issue happened only after upgrading from Kotlin 1.0.2 to 1.1.1, the same code did work flawlessly with Kotlin 1.0.2
Not sure if this will help, but here are some more scenarios that succeed/fail:
Bytecode of Foo.kt: https://pastebin.com/s4T3qTkF
Error log: https://pastebin.com/fxLZzyg1
Foo.kt
FooTest.kt