Skip to content

Commit 6c3d0ee

Browse files
committed
Implement comment tracking.
1 parent 2315fba commit 6c3d0ee

File tree

5 files changed

+136
-3
lines changed

5 files changed

+136
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (C) 2024 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.selfie.guts
17+
18+
import com.diffplug.selfie.ArrayMap
19+
20+
internal expect class CAS<T> {
21+
fun get(): T
22+
fun updateAndGet(update: (T) -> T): T
23+
}
24+
25+
internal expect fun <T> createCas(initial: T): CAS<T>
26+
27+
/**
28+
* Tracks whether a given file has a comment which allows it to be written to. Thread-safe on
29+
* multithreaded platforms.
30+
*/
31+
class CommentTracker {
32+
private enum class WritableComment {
33+
NO_COMMENT,
34+
ONCE,
35+
FOREVER;
36+
val writable: Boolean
37+
get() = this != NO_COMMENT
38+
val needsRemoval: Boolean
39+
get() = this == ONCE
40+
}
41+
private val cache = createCas(ArrayMap.empty<Path, WritableComment>())
42+
fun pathsWithOnce(): Iterable<Path> =
43+
cache.get().mapNotNull { if (it.value == WritableComment.ONCE) it.key else null }
44+
fun hasWritableComment(call: CallStack, layout: SnapshotFileLayout): Boolean {
45+
val path = layout.sourcePathForCall(call.location) ?: return false
46+
val comment = cache.get()[path]
47+
return if (comment != null) {
48+
comment.writable
49+
} else {
50+
val str = layout.fs.fileRead(path)
51+
// TODO: there is a bug here due to string constants, and non-C file comments
52+
val newComment =
53+
if (str.contains("//selfieonce") || str.contains("// selfieonce")) {
54+
WritableComment.ONCE
55+
} else if (str.contains("//SEFLIEWRITE") || str.contains("// SELFIEWRITE")) {
56+
WritableComment.FOREVER
57+
} else {
58+
WritableComment.NO_COMMENT
59+
}
60+
cache.updateAndGet { it.plus(path, newComment) }
61+
newComment.writable
62+
}
63+
}
64+
}

selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/SourceFile.kt

+5
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ class SourceFile(filename: String, content: String) {
8080
slice.subSequence(valueStart, slice.length - 1).toString(), language)
8181
}
8282
}
83+
fun removeSelfieOnceComments() {
84+
// TODO: there is a bug here due to string constants, and non-C file comments
85+
contentSlice =
86+
Slice(contentSlice.toString().replace("//selfieonce", "").replace("// selfieonce", ""))
87+
}
8388
private fun findOnLine(toFind: String, lineOneIndexed: Int): Slice {
8489
val lineContent = contentSlice.unixLine(lineOneIndexed)
8590
val idx = lineContent.indexOf(toFind)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (C) 2024 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.selfie.guts
17+
18+
internal actual fun <T> createCas(initial: T): CAS<T> = CAS(initial)
19+
20+
internal actual class CAS<T>(var value: T) {
21+
actual fun get() = value
22+
actual fun updateAndGet(update: (T) -> T): T {
23+
value = update(value)
24+
return value
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (C) 2024 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.selfie.guts
17+
18+
import java.util.concurrent.atomic.AtomicReference
19+
20+
internal actual fun <T> createCas(initial: T): CAS<T> = CAS(initial)
21+
22+
internal actual class CAS<T>(value: T) {
23+
val ref = AtomicReference(value)
24+
actual fun get() = ref.get()
25+
actual fun updateAndGet(update: (T) -> T): T = ref.updateAndGet(update)
26+
}

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

+15-3
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ package com.diffplug.selfie.junit5
1717

1818
import com.diffplug.selfie.*
1919
import com.diffplug.selfie.guts.CallStack
20+
import com.diffplug.selfie.guts.CommentTracker
2021
import com.diffplug.selfie.guts.DiskWriteTracker
2122
import com.diffplug.selfie.guts.FS
2223
import com.diffplug.selfie.guts.InlineWriteTracker
2324
import com.diffplug.selfie.guts.LiteralValue
2425
import com.diffplug.selfie.guts.Path
2526
import com.diffplug.selfie.guts.SnapshotFileLayout
2627
import com.diffplug.selfie.guts.SnapshotStorage
28+
import com.diffplug.selfie.guts.SourceFile
2729
import java.nio.charset.StandardCharsets
2830
import java.nio.file.Files
2931
import java.util.concurrent.ConcurrentSkipListSet
@@ -75,16 +77,17 @@ internal object SnapshotStorageJUnit5 : SnapshotStorage {
7577
@JvmStatic fun initStorage(): SnapshotStorage = this
7678
override val fs = FSJava
7779
override val mode = calcMode()
78-
override fun sourceFileHasWritableComment(call: CallStack): Boolean {
79-
TODO("Not yet implemented")
80-
}
8180

8281
private class ClassMethod(val clazz: ClassProgress, val method: String)
8382
private val threadCtx = ThreadLocal<ClassMethod?>()
8483
private fun classAndMethod() =
8584
threadCtx.get()
8685
?: throw AssertionError(
8786
"Selfie `toMatchDisk` must be called only on the original thread.")
87+
override fun sourceFileHasWritableComment(call: CallStack): Boolean {
88+
val cm = classAndMethod()
89+
return cm.clazz.parent.commentTracker!!.hasWritableComment(call, cm.clazz.parent.layout)
90+
}
8891
private fun suffix(sub: String) = if (sub == "") "" else "/$sub"
8992
override fun readDisk(sub: String, call: CallStack): Snapshot? {
9093
val cm = classAndMethod()
@@ -245,6 +248,7 @@ internal class ClassProgress(val parent: Progress, val className: String) {
245248
internal class Progress {
246249
val settings = SelfieSettingsAPI.initialize()
247250
val layout = SnapshotFileLayoutJUnit5(settings, SnapshotStorageJUnit5.fs)
251+
var commentTracker: CommentTracker? = CommentTracker()
248252

249253
private var progressPerClass = ArrayMap.empty<String, ClassProgress>()
250254
private fun forClass(className: String) = synchronized(this) { progressPerClass[className]!! }
@@ -286,6 +290,14 @@ internal class Progress {
286290
written.add(path)
287291
}
288292
fun finishedAllTests() {
293+
val paths = commentTracker!!.pathsWithOnce()
294+
commentTracker = null
295+
for (path in paths) {
296+
val source = SourceFile(layout.fs.name(path), layout.fs.fileRead(path))
297+
source.removeSelfieOnceComments()
298+
layout.fs.fileWrite(path, source.asString)
299+
}
300+
289301
val written =
290302
checkForInvalidStale.getAndSet(null)
291303
?: throw AssertionError("finishedAllTests() was called more than once.")

0 commit comments

Comments
 (0)