-
-
Notifications
You must be signed in to change notification settings - Fork 757
/
ClassLoaderCacheSpec.kt
87 lines (71 loc) · 3.49 KB
/
ClassLoaderCacheSpec.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package io.gitlab.arturbosch.detekt.internal
import io.gitlab.arturbosch.detekt.gradle.TestFileCollection
import org.assertj.core.api.Assertions.assertThat
import org.gradle.api.internal.file.AbstractFileCollection
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
import java.io.File
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.function.Supplier
internal class ClassLoaderCacheSpec : Spek({
describe("classpath changes") {
it("same classloader is returned for the same files") {
val cache = DefaultClassLoaderCache()
val initialClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c")))
val secondClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c")))
assertThat(initialClassLoader === secondClassLoader).isTrue()
}
it("different classloaders are returned for different files") {
val cache = DefaultClassLoaderCache()
val firstClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c")))
val secondClassLoader = cache.getOrCreate(TestFileCollection(File("c/b/a")))
assertThat(firstClassLoader === secondClassLoader).isFalse()
}
it("same classloader for the same files in different order") {
val cache = DefaultClassLoaderCache()
val firstClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c"), File("d/e/f")))
val secondClassLoader = cache.getOrCreate(TestFileCollection(File("d/e/f"), File("a/b/c")))
assertThat(firstClassLoader === secondClassLoader).isTrue()
}
it("resolves files without synchronization") {
val file1 = File("/a/b/c")
val collection1 = CountdownFileCollection(file1)
val file2 = File("/c/b/a")
val collection2 = TestFileCollection(file2)
val cache = DefaultClassLoaderCache()
val executor = Executors.newSingleThreadExecutor()
val latch = CountDownLatch(1)
try {
val supplier = Supplier {
latch.countDown()
cache.getOrCreate(collection1)
}
val task = CompletableFuture.supplyAsync(supplier, executor)
@Suppress("UsePropertyAccessSyntax")
assertThat(latch.await(10L, TimeUnit.SECONDS)).isTrue()
// Will call `getOrCreate` next - wait a moment to be sure
Thread.sleep(2000L)
val classpath2 = cache.getOrCreate(collection2)
collection1.latch.countDown()
val classpath1 = task.join()
assertThat(classpath1.urLs).isEqualTo(arrayOf(file1.toURI().toURL()))
assertThat(classpath2.urLs).isEqualTo(arrayOf(file2.toURI().toURL()))
} finally {
val remaining = executor.shutdownNow()
assertThat(remaining).isEmpty()
}
}
}
})
private class CountdownFileCollection(private vararg val files: File) : AbstractFileCollection() {
val latch = CountDownLatch(1)
override fun getFiles(): MutableSet<File> {
@Suppress("UsePropertyAccessSyntax")
assertThat(latch.await(10L, TimeUnit.SECONDS)).isTrue()
return files.toMutableSet()
}
override fun getDisplayName(): String = "CountdownFileCollection"
}