From 002129dc37d1f7ffe1001caa4e219b189384f302 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Wed, 4 Oct 2023 00:18:10 -0700 Subject: [PATCH] Pipe the filesystem into InlineWriteTracker.persistWrites() --- .../diffplug/selfie/junit5/SelfieConfig.kt | 8 ++++++ .../junit5/SelfieTestExecutionListener.kt | 9 ++++--- .../diffplug/selfie/junit5/WriteTracker.kt | 26 ++++++++++++++++--- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieConfig.kt b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieConfig.kt index d6f8a0be..3f5e9da7 100644 --- a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieConfig.kt +++ b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieConfig.kt @@ -57,6 +57,14 @@ internal class SnapshotFileLayout( } return classnameWithSlashes.replace('/', '.') } + fun testSourceFile(location: CallLocation): Path { + // we should have a way to have multiple source roots to check against + val path = rootFolder.resolve(location.subpath) + if (!Files.exists(path)) { + throw AssertionError("Unable to find ${location.subpath} at ${path.toAbsolutePath()}") + } + return path + } companion object { private const val DEFAULT_SNAPSHOT_DIR = "__snapshots__" diff --git a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieTestExecutionListener.kt b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieTestExecutionListener.kt index cab185df..65175502 100644 --- a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieTestExecutionListener.kt +++ b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieTestExecutionListener.kt @@ -82,11 +82,12 @@ internal object Router { } threadCtx.set(null) } - fun fileLocationFor(className: String): Path { + fun fileLocationFor(className: String): Path = layout(className).snapshotPathForClass(className) + fun layout(className: String): SnapshotFileLayout { if (layout == null) { layout = SnapshotFileLayout.initialize(className) } - return layout!!.snapshotPathForClass(className) + return layout!! } var layout: SnapshotFileLayout? = null @@ -120,7 +121,9 @@ internal class ClassProgress(val className: String) { } @Synchronized fun finishedClassWithSuccess(success: Boolean) { assertNotTerminated() - inlineWriteTracker!!.persistWrites() + if (inlineWriteTracker!!.hasWrites()) { + inlineWriteTracker!!.persistWrites(Router.layout(className)) + } if (file != null) { val staleSnapshotIndices = MethodSnapshotGC.findStaleSnapshotsWithin(className, file!!.snapshots, methods) diff --git a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/WriteTracker.kt b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/WriteTracker.kt index cf2ad5a9..624e5b2e 100644 --- a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/WriteTracker.kt +++ b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/WriteTracker.kt @@ -18,6 +18,8 @@ package com.diffplug.selfie.junit5 import com.diffplug.selfie.LiteralValue import com.diffplug.selfie.RW import com.diffplug.selfie.Snapshot +import java.nio.file.Files +import java.nio.file.Path import java.util.stream.Collectors /** Represents the line at which user code called into Selfie. */ @@ -76,19 +78,37 @@ internal class InlineWriteTracker : WriteTracker>( fun record(call: CallStack, literalValue: LiteralValue<*>) { recordInternal(call.location, literalValue, call) } - fun persistWrites() { + fun hasWrites(): Boolean = writes.isNotEmpty() + fun persistWrites(layout: SnapshotFileLayout) { val locations = writes.toList().sortedBy { it.first } + var subpath = "" var deltaLineNumbers = 0 + var source = "" + var path: Path? = null + // If I was implementing this, I would use Slice https://github.com/diffplug/selfie/pull/22 + // as the type of source, but that is by no means a requirement for (location in locations) { - val currentlyInFile = "TODO" + if (location.first.subpath != subpath) { + path?.let { Files.writeString(it, source) } + subpath = location.first.subpath + deltaLineNumbers = 0 + path = layout.testSourceFile(location.first) + source = Files.readString(path) + } + // parse the location within the file + val currentlyInFile = "TODO parse using ${location.first.line + deltaLineNumbers}" val literalValue = location.second.snapshot val parsedInFile = literalValue.format.parse(currentlyInFile) if (parsedInFile != literalValue.expected) { - // warn that the parsing wasn't quite as expected + // warn that the parsing wasn't as expected + // TODO: we can't report failures to the user very well + // someday, we should verify that the parse works in the `record()` and + // throw an `AssertionFail` there so that the user sees it early } val toInjectIntoFile = literalValue.encodedActual() deltaLineNumbers += (toInjectIntoFile.countNewlines() - currentlyInFile.countNewlines()) // TODO: inject the encoded thing into the file } + path?.let { Files.writeString(it, source) } } }