Skip to content

Commit 3c0a1a4

Browse files
committed
Make SnapshotFileLayout responsible for locating exact file paths of test source.
1 parent 762f397 commit 3c0a1a4

File tree

4 files changed

+41
-20
lines changed

4 files changed

+41
-20
lines changed

selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieTestExecutionListener.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ internal class ClassProgress(val parent: Progress, val className: String) {
140140
@Synchronized fun write(method: String, suffix: String, snapshot: Snapshot, callStack: CallStack) {
141141
assertNotTerminated()
142142
val key = "$method$suffix"
143-
diskWriteTracker!!.record(key, snapshot, callStack)
143+
diskWriteTracker!!.record(key, snapshot, callStack, parent.layout)
144144
methods[method]!!.keepSuffix(suffix)
145145
read().setAtTestTime(key, snapshot)
146146
}

selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SnapshotFileLayout.kt

+15
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,29 @@
1515
*/
1616
package com.diffplug.selfie.junit5
1717

18+
import java.nio.file.Files
1819
import java.nio.file.Path
20+
import kotlin.io.path.name
1921

2022
class SnapshotFileLayout(
2123
val rootFolder: Path,
2224
val snapshotFolderName: String?,
2325
internal val unixNewlines: Boolean
2426
) {
2527
val extension: String = ".ss"
28+
fun sourcecodeForCall(call: CallLocation): Path? {
29+
if (call.file != null) {
30+
return Files.walk(rootFolder).use {
31+
it.filter { it.name == call.file }.findFirst().orElse(null)
32+
}
33+
}
34+
val fileWithoutExtension = call.clazz.substringAfterLast('.').substringBefore('$')
35+
val likelyExtensions = listOf("kt", "java", "scala", "groovy", "clj", "cljc")
36+
val filenames = likelyExtensions.map { "$fileWithoutExtension.$it" }.toSet()
37+
return Files.walk(rootFolder).use {
38+
it.filter { it.name in filenames }.findFirst().orElse(null)
39+
}
40+
}
2641
fun snapshotPathForClass(className: String): Path {
2742
val lastDot = className.lastIndexOf('.')
2843
val classFolder: Path

selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/WriteTracker.kt

+20-18
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ package com.diffplug.selfie.junit5
1717

1818
import com.diffplug.selfie.RW
1919
import com.diffplug.selfie.Snapshot
20-
import java.nio.file.Files
21-
import java.nio.file.Paths
2220
import java.util.stream.Collectors
2321
import kotlin.io.path.name
2422

@@ -32,24 +30,26 @@ data class CallLocation(val clazz: String, val method: String, val file: String?
3230
* If the runtime didn't give us the filename, guess it from the class, and try to find the source
3331
* file by walking the CWD. If we don't find it, report it as a `.class` file.
3432
*/
35-
private fun findFileIfAbsent(): String {
33+
private fun findFileIfAbsent(layout: SnapshotFileLayout): String {
3634
if (file != null) {
3735
return file
3836
}
39-
val fileWithoutExtension = clazz.substringAfterLast('.').substringBefore('$')
40-
val likelyExtensions = listOf("kt", "java", "scala", "groovy", "clj", "cljc")
41-
val filenames = likelyExtensions.map { "$fileWithoutExtension.$it" }.toSet()
42-
val firstPath = Files.walk(Paths.get("")).use { it.filter { it.name in filenames }.findFirst() }
43-
return if (firstPath.isEmpty) "${clazz.substringAfterLast('.')}.class" else firstPath.get().name
37+
return layout.sourcecodeForCall(this)?.name ?: "${clazz.substringAfterLast('.')}.class"
4438
}
4539

4640
/** A `toString` which an IDE will render as a clickable link. */
47-
override fun toString(): String = "$clazz.$method(${findFileIfAbsent()}:$line)"
41+
fun ideLink(layout: SnapshotFileLayout): String {
42+
return "$clazz.$method(${findFileIfAbsent(layout)}:$line)"
43+
}
4844
}
4945
/** Represents the callstack above a given CallLocation. */
5046
class CallStack(val location: CallLocation, val restOfStack: List<CallLocation>) {
51-
override fun toString(): String {
52-
return location.toString()
47+
fun ideLink(layout: SnapshotFileLayout): String {
48+
val list = buildList {
49+
add(location)
50+
addAll(restOfStack)
51+
}
52+
return list.joinToString("\n") { it.ideLink(layout) }
5353
}
5454
}
5555
/** Generates a CallLocation and the CallStack behind it. */
@@ -69,25 +69,27 @@ internal class FirstWrite<T>(val snapshot: T, val callStack: CallStack)
6969
/** For tracking the writes of disk snapshots literals. */
7070
internal open class WriteTracker<K : Comparable<K>, V> {
7171
val writes = mutableMapOf<K, FirstWrite<V>>()
72-
protected fun recordInternal(key: K, snapshot: V, call: CallStack) {
72+
protected fun recordInternal(key: K, snapshot: V, call: CallStack, layout: SnapshotFileLayout) {
7373
val existing = writes.putIfAbsent(key, FirstWrite(snapshot, call))
7474
if (existing != null) {
7575
if (existing.snapshot != snapshot) {
7676
throw org.opentest4j.AssertionFailedError(
77-
"Snapshot was set to multiple values!\n first time: ${existing.callStack}\n this time: ${call}",
77+
"Snapshot was set to multiple values!\n first time: ${existing.callStack.location.ideLink(layout)}\n this time: ${call.location.ideLink(layout)}",
7878
existing.snapshot,
7979
snapshot)
8080
} else if (RW.isWriteOnce) {
8181
throw org.opentest4j.AssertionFailedError(
82-
"Snapshot was set to the same value multiple times.", existing.callStack, call)
82+
"Snapshot was set to the same value multiple times.",
83+
existing.callStack.ideLink(layout),
84+
call.ideLink(layout))
8385
}
8486
}
8587
}
8688
}
8789

8890
internal class DiskWriteTracker : WriteTracker<String, Snapshot>() {
89-
fun record(key: String, snapshot: Snapshot, call: CallStack) {
90-
recordInternal(key, snapshot, call)
91+
fun record(key: String, snapshot: Snapshot, call: CallStack, layout: SnapshotFileLayout) {
92+
recordInternal(key, snapshot, call, layout)
9193
}
9294
}
9395

@@ -96,7 +98,7 @@ class LiteralValue {
9698
}
9799

98100
internal class InlineWriteTracker : WriteTracker<CallLocation, LiteralValue>() {
99-
fun record(call: CallStack, snapshot: LiteralValue) {
100-
recordInternal(call.location, snapshot, call)
101+
fun record(call: CallStack, snapshot: LiteralValue, layout: SnapshotFileLayout) {
102+
recordInternal(call.location, snapshot, call, layout)
101103
}
102104
}

selfie-runner-junit5/src/test/kotlin/testpkg/RecordCallTest.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,21 @@
1515
*/
1616
package testpkg
1717

18+
import com.diffplug.selfie.junit5.SnapshotFileLayout
1819
import com.diffplug.selfie.junit5.recordCall
1920
import io.kotest.matchers.ints.shouldBeGreaterThan
2021
import io.kotest.matchers.shouldBe
22+
import java.nio.file.Paths
2123
import org.junit.jupiter.api.Test
2224

2325
class RecordCallTest {
2426
@Test
2527
fun testRecordCall() {
2628
val stack = recordCall()
2729
// shows as clickable link in IDE
28-
stack.location.toString() shouldBe "testpkg.RecordCallTest.testRecordCall(RecordCallTest.kt:26)"
30+
val layout = SnapshotFileLayout(Paths.get(""), null, true)
31+
stack.location.ideLink(layout) shouldBe
32+
"testpkg.RecordCallTest.testRecordCall(RecordCallTest.kt:28)"
2933
stack.restOfStack.size shouldBeGreaterThan 0
3034
}
3135
}

0 commit comments

Comments
 (0)