Skip to content

Commit

Permalink
Add ReadOnlyTest with better error messages for comments while in r…
Browse files Browse the repository at this point in the history
…eadonly mode
  • Loading branch information
nedtwigg committed Jan 5, 2024
1 parent cacd21a commit 947e18b
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 15 deletions.
12 changes: 11 additions & 1 deletion selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/Mode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.diffplug.selfie

import com.diffplug.selfie.guts.CallStack
import com.diffplug.selfie.guts.CommentTracker
import com.diffplug.selfie.guts.SnapshotStorage

enum class Mode {
Expand All @@ -25,7 +26,16 @@ enum class Mode {
fun canWrite(isTodo: Boolean, call: CallStack, storage: SnapshotStorage): Boolean =
when (this) {
interactive -> isTodo || storage.sourceFileHasWritableComment(call)
readonly -> false
readonly -> {
if (storage.sourceFileHasWritableComment(call)) {
val layout = storage.layout
val path = layout.sourcePathForCall(call.location)!!
val (comment, line) = CommentTracker.commentString(path, storage.fs)
throw storage.fs.assertFailed(
"Selfie is in readonly mode, so `$comment` is illegal at ${call.location.withLine(line).ideLink(layout)}")
}
false
}
overwrite -> true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,34 @@ class CommentTracker {
return if (comment != null) {
comment.writable
} else {
val str = layout.fs.fileRead(path)
// TODO: there is a bug here due to string constants, and non-C file comments
val newComment =
if (str.contains("//selfieonce") || str.contains("// selfieonce")) {
WritableComment.ONCE
} else if (str.contains("//SELFIEWRITE") || str.contains("// SELFIEWRITE")) {
WritableComment.FOREVER
} else {
WritableComment.NO_COMMENT
}
val newComment = commentAndLine(path, layout.fs).first
cache.updateAndGet { it.plus(path, newComment) }
newComment.writable
}
}

companion object {
fun commentString(path: Path, fs: FS): Pair<String, Int> {
val (comment, line) = commentAndLine(path, fs)
return when (comment) {
WritableComment.NO_COMMENT -> throw UnsupportedOperationException()
WritableComment.ONCE -> Pair("//selfieonce", line)
WritableComment.FOREVER -> Pair("//SELFIEWRITE", line)
}
}
private fun commentAndLine(path: Path, fs: FS): Pair<WritableComment, Int> {
// TODO: there is a bug here due to string constants, and non-C file comments
val content = Slice(fs.fileRead(path))
for (comment in listOf("//selfieonce", "// selfieonce", "//SELFIEWRITE", "// SELFIEWRITE")) {
val index = content.indexOf(comment)
if (index != -1) {
val lineNumber = content.baseLineAtOffset(index)
val comment =
if (comment.contains("once")) WritableComment.ONCE else WritableComment.FOREVER
return Pair(comment, lineNumber)
}
}
return Pair(WritableComment.NO_COMMENT, -1)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,5 @@ internal class Slice(val base: String, val startIndex: Int = 0, val endIndex: In
builder.appendRange(base, endIndex, base.length)
return builder.toString()
}
fun baseLineAtOffset(index: Int) = 1 + Slice(base, 0, index).count { it == '\n' }
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ interface FS {
interface SnapshotStorage {
val fs: FS
val mode: Mode
val layout: SnapshotFileLayout
/** Returns true if the sourcecode for the given call has a writable annotation. */
fun sourceFileHasWritableComment(call: CallStack): Boolean
/** Indicates that the following value should be written into test sourcecode. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import com.diffplug.selfie.Snapshot
expect class CallLocation : Comparable<CallLocation> {
val fileName: String?
val line: Int
/** Returns a new CallLocation at the given line. */
fun withLine(line: Int): CallLocation
/** Returns a string which an IDE can render as a hyperlink. */
fun ideLink(layout: SnapshotFileLayout): String
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ actual data class CallLocation(actual val fileName: String?, actual val line: In
Comparable<CallLocation> {
override fun compareTo(other: CallLocation) =
compareValuesBy(this, other, { it.fileName }, { it.line })
actual fun withLine(line: Int): CallLocation = TODO()
actual fun ideLink(layout: SnapshotFileLayout): String = TODO()
actual fun samePathAs(other: CallLocation): Boolean = TODO()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ actual data class CallLocation(
actual val fileName: String?,
actual val line: Int
) : Comparable<CallLocation> {
actual fun withLine(line: Int): CallLocation = CallLocation(clazz, "<unknown>", fileName, line)
actual fun samePathAs(other: CallLocation): Boolean =
clazz == other.clazz && fileName == other.fileName
override fun compareTo(other: CallLocation): Int =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ internal object SnapshotStorageJUnit5 : SnapshotStorage {
@JvmStatic fun initStorage(): SnapshotStorage = this
override val fs = FSJava
override val mode = calcMode()
override val layout: SnapshotFileLayout
get() = classAndMethod().clazz.parent.layout

private class ClassMethod(val clazz: ClassProgress, val method: String)
private val threadCtx = ThreadLocal<ClassMethod?>()
Expand Down Expand Up @@ -292,10 +294,12 @@ internal class Progress {
fun finishedAllTests() {
val paths = commentTracker!!.pathsWithOnce()
commentTracker = null
for (path in paths) {
val source = SourceFile(layout.fs.name(path), layout.fs.fileRead(path))
source.removeSelfieOnceComments()
layout.fs.fileWrite(path, source.asString)
if (SnapshotStorageJUnit5.mode != Mode.readonly) {
for (path in paths) {
val source = SourceFile(layout.fs.name(path), layout.fs.fileRead(path))
source.removeSelfieOnceComments()
layout.fs.fileWrite(path, source.asString)
}
}
val written =
checkForInvalidStale.getAndSet(null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2024 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.selfie.junit5

import io.kotest.matchers.shouldBe
import kotlin.test.Test
import org.junit.jupiter.api.MethodOrderer
import org.junit.jupiter.api.Order
import org.junit.jupiter.api.TestMethodOrder
import org.junitpioneer.jupiter.DisableIfTestFails

@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
@DisableIfTestFails
class ReadOnlyTest : Harness("undertest-junit5") {
@Test @Order(1)
fun initialState() {
ut_mirror().lineWith("expectSelfie(").setContent(" expectSelfie(10).toBe(10)")
gradleReadSS()
}

@Test @Order(2)
fun inlineMismatchWithComment() {
ut_mirror().lineWith("expectSelfie(").setContent(" expectSelfie(10).toBe(5) //SELFIEWRITE")
gradleReadSSFail().message shouldBe
"Selfie is in readonly mode, so `//SELFIEWRITE` is illegal at undertest.junit5.UT_ReadOnlyTest.<unknown>(UT_ReadOnlyTest.kt:9)"
}

@Test @Order(3)
fun inlineMatchWithComment() {
ut_mirror().lineWith("expectSelfie(").setContent(" expectSelfie(5).toBe(5) // selfieonce")
gradleReadSS()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package undertest.junit5
// spotless:off
import com.diffplug.selfie.Selfie.expectSelfie
import kotlin.test.Test
// spotless:on

class UT_ReadOnlyTest {
@Test fun example() {
expectSelfie(5).toBe(5) // selfieonce
}
}

0 comments on commit 947e18b

Please sign in to comment.