diff --git a/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/Mode.kt b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/Mode.kt index 0337ce73..cc962204 100644 --- a/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/Mode.kt +++ b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/Mode.kt @@ -29,7 +29,7 @@ enum class Mode { readonly -> { if (storage.sourceFileHasWritableComment(call)) { val layout = storage.layout - val path = layout.sourcePathForCall(call.location)!! + 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)}") diff --git a/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/Literals.kt b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/Literals.kt index 2d240999..84aedd45 100644 --- a/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/Literals.kt +++ b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/Literals.kt @@ -17,6 +17,8 @@ package com.diffplug.selfie.guts import kotlin.math.abs +internal expect fun jreVersion(): Int + enum class Language { JAVA, JAVA_PRE15, @@ -28,7 +30,7 @@ enum class Language { companion object { fun fromFilename(filename: String): Language { return when (filename.substringAfterLast('.')) { - "java" -> JAVA_PRE15 // TODO: detect JRE and use JAVA if JVM >= 15 + "java" -> if (jreVersion() < 15) JAVA_PRE15 else JAVA "kt" -> KOTLIN "groovy", "gvy", @@ -101,14 +103,24 @@ internal object LiteralLong : LiteralFormat() { } } +private const val TRIPLE_QUOTE = "\"\"\"" + internal object LiteralString : LiteralFormat() { - override fun encode(value: String, language: Language): String { - return singleLineJavaToSource(value) - } - override fun parse(str: String, language: Language): String { - return singleLineJavaFromSource(str) - } - private fun singleLineJavaToSource(value: String): String { + override fun encode(value: String, language: Language): String = + if (value.indexOf('\n') == -1) singleLineJavaToSource(value) + else + when (language) { + Language.GROOVY, + Language.SCALA, + Language.CLOJURE, + Language.JAVA_PRE15 -> singleLineJavaToSource(value) + Language.JAVA -> multiLineJavaToSource(value) + Language.KOTLIN -> multiLineJavaToSource(value) + } + override fun parse(str: String, language: Language): String = + if (str.startsWith(TRIPLE_QUOTE)) multiLineJavaFromSource(str) + else singleLineJavaFromSource(str) + fun singleLineJavaToSource(value: String): String { val source = StringBuilder() source.append("\"") for (char in value) { @@ -134,34 +146,92 @@ internal object LiteralString : LiteralFormat() { private fun isControlChar(c: Char): Boolean { return c in '\u0000'..'\u001F' || c == '\u007F' } - private fun singleLineJavaFromSource(source: String): String { + fun multiLineJavaToSource(arg: String): String { + val escapeBackslashes = arg.replace("\\", "\\\\") + val escapeTripleQuotes = escapeBackslashes.replace(TRIPLE_QUOTE, "\\\"\\\"\\\"") + val protectWhitespace = + escapeTripleQuotes.lines().joinToString("\n") { line -> + val protectTrailingWhitespace = + if (line.endsWith(" ")) { + line.dropLast(1) + "\\s" + } else if (line.endsWith("\t")) { + line.dropLast(1) + "\\t" + } else line + val protectLeadingWhitespace = + if (protectTrailingWhitespace.startsWith(" ")) { + "\\s" + protectTrailingWhitespace.drop(1) + } else if (protectTrailingWhitespace.startsWith("\t")) { + "\\t" + protectTrailingWhitespace.drop(1) + } else protectTrailingWhitespace + protectLeadingWhitespace + } + return "$TRIPLE_QUOTE\n$protectWhitespace$TRIPLE_QUOTE" + } + fun singleLineJavaFromSource(sourceWithQuotes: String): String { + check(sourceWithQuotes.startsWith('"')) + check(sourceWithQuotes.endsWith('"')) + return unescapeJava(sourceWithQuotes.substring(1, sourceWithQuotes.length - 1)) + } + private fun unescapeJava(source: String): String { + val firstEscape = source.indexOf('\\') + if (firstEscape == -1) { + return source + } val value = StringBuilder() - var i = 0 + value.append(source.substring(0, firstEscape)) + var i = firstEscape while (i < source.length) { var c = source[i] if (c == '\\') { i++ c = source[i] when (c) { + '\"' -> value.append('\"') + '\\' -> value.append('\\') 'b' -> value.append('\b') + 'f' -> value.append('\u000c') 'n' -> value.append('\n') 'r' -> value.append('\r') + 's' -> value.append(' ') 't' -> value.append('\t') - '\"' -> value.append('\"') - '\\' -> value.append('\\') 'u' -> { val code = source.substring(i + 1, i + 5).toInt(16) value.append(code.toChar()) i += 4 } + else -> throw IllegalArgumentException("Unknown escape sequence $c") } - } else if (c != '\"') { + } else { value.append(c) } i++ } return value.toString() } + fun multiLineJavaFromSource(sourceWithQuotes: String): String { + check(sourceWithQuotes.startsWith("$TRIPLE_QUOTE\n")) + check(sourceWithQuotes.endsWith(TRIPLE_QUOTE)) + val source = + sourceWithQuotes.substring( + TRIPLE_QUOTE.length + 1, sourceWithQuotes.length - TRIPLE_QUOTE.length) + val lines = source.lines() + val commonPrefix = + lines + .mapNotNull { line -> + if (line.isNotBlank()) line.takeWhile { it.isWhitespace() } else null + } + .minOrNull() ?: "" + return lines.joinToString("\n") { line -> + if (line.isBlank()) { + "" + } else { + val removedPrefix = if (commonPrefix.isEmpty()) line else line.removePrefix(commonPrefix) + val removeTrailingWhitespace = removedPrefix.trimEnd() + val handleEscapeSequences = unescapeJava(removeTrailingWhitespace) + handleEscapeSequences + } + } + } } internal object LiteralBoolean : LiteralFormat() { diff --git a/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/SnapshotStorage.kt b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/SnapshotStorage.kt index bd4d21ad..442cbd1c 100644 --- a/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/SnapshotStorage.kt +++ b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/SnapshotStorage.kt @@ -57,5 +57,6 @@ interface SnapshotFileLayout { val rootFolder: Path val fs: FS val allowMultipleEquivalentWritesToOneLocation: Boolean - fun sourcePathForCall(call: CallLocation): Path? + fun sourcePathForCall(call: CallLocation): Path + fun sourcePathForCallMaybe(call: CallLocation): Path? } diff --git a/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/WriteTracker.kt b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/WriteTracker.kt index bbfd072b..ba5e1ec4 100644 --- a/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/WriteTracker.kt +++ b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/guts/WriteTracker.kt @@ -83,9 +83,7 @@ class InlineWriteTracker : WriteTracker>() { recordInternal(call.location, literalValue, call, layout) // assert that the value passed at runtime matches the value we parse at compile time // because if that assert fails, we've got no business modifying test code - val file = - layout.sourcePathForCall(call.location) - ?: throw Error("Unable to find source file for ${call.location.ideLink(layout)}") + val file = layout.sourcePathForCall(call.location) if (literalValue.expected != null) { // if expected == null, it's a `toBe_TODO()`, so there's nothing to check val content = SourceFile(layout.fs.name(file), layout.fs.fileRead(file)) diff --git a/selfie-lib/src/commonTest/kotlin/com/diffplug/selfie/guts/LiteralStringTest.kt b/selfie-lib/src/commonTest/kotlin/com/diffplug/selfie/guts/LiteralStringTest.kt index ed5cc5b7..019860a3 100644 --- a/selfie-lib/src/commonTest/kotlin/com/diffplug/selfie/guts/LiteralStringTest.kt +++ b/selfie-lib/src/commonTest/kotlin/com/diffplug/selfie/guts/LiteralStringTest.kt @@ -20,42 +20,48 @@ import kotlin.test.Test class LiteralStringTest { @Test - fun encode() { - encode( - "1", - """ - "1" - """ - .trimIndent()) - encode( - "1\n\tABC", - """ - "1\n\tABC" - """ - .trimIndent()) - } - private fun encode(value: String, expected: String) { - val actual = LiteralString.encode(value, Language.JAVA) + fun singleLineJavaToSource() { + singleLineJavaToSource("1", "'1'") + singleLineJavaToSource("\\", "'\\\\'") + singleLineJavaToSource("1\n\tABC", "'1\\n\\tABC'") + } + private fun singleLineJavaToSource(value: String, expected: String) { + val actual = LiteralString.singleLineJavaToSource(value) + actual shouldBe expected.replace("'", "\"") + } + + @Test + fun multiLineJavaToSource() { + multiLineJavaToSource("1", "'''\n1'''") + multiLineJavaToSource("\\", "'''\n\\\\'''") + multiLineJavaToSource(" leading\ntrailing ", "'''\n" + "\\s leading\n" + "trailing \\s'''") + } + private fun multiLineJavaToSource(value: String, expected: String) { + val actual = LiteralString.multiLineJavaToSource(value) + actual shouldBe expected.replace("'", "\"") + } + + @Test + fun singleLineJavaFromSource() { + singleLineJavaFromSource("1", "1") + singleLineJavaFromSource("\\\\", "\\") + singleLineJavaFromSource("1\\n\\tABC", "1\n\tABC") + } + private fun singleLineJavaFromSource(value: String, expected: String) { + val actual = LiteralString.singleLineJavaFromSource("\"${value.replace("'", "\"")}\"") actual shouldBe expected } @Test - fun decode() { - decode( - """ - "1" - """ - .trimIndent(), - "1") - decode( - """ - "1\n\tABC" - """ - .trimIndent(), - "1\n\tABC") - } - private fun decode(value: String, expected: String) { - val actual = LiteralString.parse(value, Language.JAVA) + fun multiLineJavaFromSource() { + multiLineJavaFromSource("\n123\nabc", "123\nabc") + multiLineJavaFromSource("\n 123\n abc", "123\nabc") + multiLineJavaFromSource("\n 123 \n abc\t", "123\nabc") + multiLineJavaFromSource("\n 123 \n abc\t", "123\nabc") + multiLineJavaFromSource("\n 123 \\s\n abc\t\\s", "123 \nabc\t ") + } + private fun multiLineJavaFromSource(value: String, expected: String) { + val actual = LiteralString.multiLineJavaFromSource("\"\"\"${value.replace("'", "\"")}\"\"\"") actual shouldBe expected } } diff --git a/selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/guts/Literals.js.kt b/selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/guts/Literals.js.kt new file mode 100644 index 00000000..b054cd52 --- /dev/null +++ b/selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/guts/Literals.js.kt @@ -0,0 +1,22 @@ +/* + * 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.guts + +/** + * If the user is hip enough to run javascript, they're probably hip enough for multiline string + * literals. + */ +internal actual fun jreVersion(): Int = 15 diff --git a/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/guts/Literals.jvm.kt b/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/guts/Literals.jvm.kt new file mode 100644 index 00000000..ef6c10b4 --- /dev/null +++ b/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/guts/Literals.jvm.kt @@ -0,0 +1,25 @@ +/* + * 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.guts + +internal actual fun jreVersion(): Int { + val versionStr = System.getProperty("java.version") + return if (versionStr.startsWith("1.")) { + if (versionStr.startsWith("1.8")) 8 else throw Error("Unsupported java version: $versionStr") + } else { + versionStr.substringBefore('.').toInt() + } +} diff --git a/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/guts/WriteTracker.jvm.kt b/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/guts/WriteTracker.jvm.kt index d60c2237..60791878 100644 --- a/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/guts/WriteTracker.jvm.kt +++ b/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/guts/WriteTracker.jvm.kt @@ -40,7 +40,7 @@ actual data class CallLocation( if (fileName != null) { return fileName } - return layout.sourcePathForCall(this)?.let { layout.fs.name(it) } + return layout.sourcePathForCallMaybe(this)?.let { layout.fs.name(it) } ?: "${clazz.substringAfterLast('.')}.class" } diff --git a/selfie-lib/src/jvmTest/kotlin/testpkg/RecordCallTest.kt b/selfie-lib/src/jvmTest/kotlin/testpkg/RecordCallTest.kt index b6821644..9b75ba7c 100644 --- a/selfie-lib/src/jvmTest/kotlin/testpkg/RecordCallTest.kt +++ b/selfie-lib/src/jvmTest/kotlin/testpkg/RecordCallTest.kt @@ -37,6 +37,7 @@ class RecordCallTest { override val allowMultipleEquivalentWritesToOneLocation: Boolean get() = TODO() override fun sourcePathForCall(call: CallLocation) = Path("testpkg/RecordCallTest.kt") + override fun sourcePathForCallMaybe(call: CallLocation): Path? = sourcePathForCall(call) } stack.location.ideLink(layout) shouldBe "testpkg.RecordCallTest.testRecordCall(RecordCallTest.kt:30)" diff --git a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieSettingsAPI.kt b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieSettingsAPI.kt index 51e51372..94e9de11 100644 --- a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieSettingsAPI.kt +++ b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieSettingsAPI.kt @@ -50,6 +50,24 @@ open class SelfieSettingsAPI { "Could not find a standard test directory, 'user.dir' is equal to $userDir, looked in $STANDARD_DIRS") } + /** + * If Selfie should look for test sourcecode in places other than the rootFolder, you can specify + * them here. + */ + open val otherSourceRoots: List + get() { + return buildList { + val rootDir = rootFolder + val userDir = File(System.getProperty("user.dir")) + for (standardDir in STANDARD_DIRS) { + val candidate = userDir.resolve(standardDir) + if (candidate.isDirectory && candidate != rootDir) { + add(candidate) + } + } + } + } + internal companion object { private val STANDARD_DIRS = listOf( diff --git a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SnapshotFileLayoutJUnit5.kt b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SnapshotFileLayoutJUnit5.kt index aefd7a28..8185bd58 100644 --- a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SnapshotFileLayoutJUnit5.kt +++ b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SnapshotFileLayoutJUnit5.kt @@ -23,13 +23,21 @@ import com.diffplug.selfie.guts.SnapshotFileLayout class SnapshotFileLayoutJUnit5(settings: SelfieSettingsAPI, override val fs: FS) : SnapshotFileLayout { override val rootFolder = settings.rootFolder + private val otherSourceRoots = settings.otherSourceRoots override val allowMultipleEquivalentWritesToOneLocation = settings.allowMultipleEquivalentWritesToOneLocation val snapshotFolderName = settings.snapshotFolderName internal val unixNewlines = inferDefaultLineEndingIsUnix(settings.rootFolder, fs) val extension: String = ".ss" private val cache = ThreadLocal?>() - override fun sourcePathForCall(call: CallLocation): Path? { + override fun sourcePathForCall(call: CallLocation): Path { + val nonNull = + sourcePathForCallMaybe(call) + ?: throw fs.assertFailed( + "Couldn't find source file for $call, looked in $rootFolder and $otherSourceRoots, maybe there are other source roots?") + return nonNull + } + override fun sourcePathForCallMaybe(call: CallLocation): Path? { val cached = cache.get() if (cached?.first?.samePathAs(call) == true) { return cached.second @@ -41,16 +49,22 @@ class SnapshotFileLayoutJUnit5(settings: SelfieSettingsAPI, override val fs: FS) path } } - private fun computePathForCall(call: CallLocation): Path? { + private fun computePathForCall(call: CallLocation): Path? = + sequence { + yield(rootFolder) + yieldAll(otherSourceRoots) + } + .firstNotNullOfOrNull { computePathForCall(it, call) } + private fun computePathForCall(folder: Path, call: CallLocation): Path? { if (call.fileName != null) { - return fs.fileWalk(rootFolder) { walk -> + return fs.fileWalk(folder) { walk -> walk.filter { fs.name(it) == call.fileName }.firstOrNull() } } val fileWithoutExtension = call.clazz.substringAfterLast('.').substringBefore('$') val likelyExtensions = listOf("kt", "java", "scala", "groovy", "clj", "cljc") val possibleNames = likelyExtensions.map { "$fileWithoutExtension.$it" }.toSet() - return fs.fileWalk(rootFolder) { walk -> + return fs.fileWalk(folder) { walk -> walk.filter { fs.name(it) in possibleNames }.firstOrNull() } } diff --git a/selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/Harness.kt b/selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/Harness.kt index 31b460cd..fd6ec88e 100644 --- a/selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/Harness.kt +++ b/selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/Harness.kt @@ -51,6 +51,7 @@ open class Harness(subproject: String) { "The subproject folder $subproject must exist" } } + protected fun ut_mirrorJava() = file("UT_${javaClass.simpleName}.java") protected fun ut_mirror() = file("UT_${javaClass.simpleName}.kt") protected fun ut_snapshot() = file("UT_${javaClass.simpleName}.ss") fun file(nameOrSubpath: String): FileHarness { @@ -74,6 +75,11 @@ open class Harness(subproject: String) { } inner class FileHarness(val subpath: String) { + fun restoreFromGit() { + val path = subprojectFolder.resolve(subpath) + Runtime.getRuntime() + .exec("git checkout **/${path.toFile().name}", arrayOf(), subprojectFolder.toFile()) + } fun assertDoesNotExist() { if (FileSystem.SYSTEM.exists(subprojectFolder.resolve(subpath))) { throw AssertionError("Expected $subpath to not exist, but it does") diff --git a/selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/InlineStringTest.kt b/selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/StringLiteralsJavaTest.kt similarity index 54% rename from selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/InlineStringTest.kt rename to selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/StringLiteralsJavaTest.kt index 7f2ecd6e..85231e0d 100644 --- a/selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/InlineStringTest.kt +++ b/selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/StringLiteralsJavaTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 DiffPlug + * 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. @@ -15,34 +15,32 @@ */ 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 -/** Write-only test which asserts adding and removing snapshots results in same-class GC. */ @TestMethodOrder(MethodOrderer.OrderAnnotation::class) -// @DisableIfTestFails don't disable if test fails because we *have* to run cleanup -class InlineStringTest : Harness("undertest-junit5") { +@DisableIfTestFails +class StringLiteralsJavaTest : Harness("undertest-junit5") { @Test @Order(1) - fun toBe_TODO() { - ut_mirror().lineWith("@Ignore").setContent("//@Ignore") - ut_mirror().lineWith("expectSelfie").setContent(" expectSelfie(\"Hello world\").toBe_TODO()") + fun readFailsBecauseTodo() { gradleReadSSFail() } @Test @Order(2) - fun toBe_write() { + fun writeSucceeds() { gradleWriteSS() - ut_mirror().lineWith("expectSelfie").content() shouldBe - " expectSelfie(\"Hello world\").toBe(\"Hello world\")" - gradleReadSS() } @Test @Order(3) + fun nowReadSucceeds() { + gradleReadSS() + } + + @Test @Order(4) fun cleanup() { - ut_mirror().lineWith("expectSelfie").setContent(" expectSelfie(\"Hello world\").toBe_TODO()") - ut_mirror().lineWith("//@Ignore").setContent("@Ignore") + ut_mirrorJava().restoreFromGit() } } diff --git a/undertest-junit5/src/test/kotlin/undertest/junit5/UT_CarriageReturnTest.ss b/undertest-junit5/src/test/java/undertest/junit5/UT_CarriageReturnTest.ss similarity index 100% rename from undertest-junit5/src/test/kotlin/undertest/junit5/UT_CarriageReturnTest.ss rename to undertest-junit5/src/test/java/undertest/junit5/UT_CarriageReturnTest.ss diff --git a/undertest-junit5/src/test/kotlin/undertest/junit5/UT_DuplicateWriteTest.ss b/undertest-junit5/src/test/java/undertest/junit5/UT_DuplicateWriteTest.ss similarity index 100% rename from undertest-junit5/src/test/kotlin/undertest/junit5/UT_DuplicateWriteTest.ss rename to undertest-junit5/src/test/java/undertest/junit5/UT_DuplicateWriteTest.ss diff --git a/undertest-junit5/src/test/java/undertest/junit5/UT_StringLiteralsJavaTest.java b/undertest-junit5/src/test/java/undertest/junit5/UT_StringLiteralsJavaTest.java new file mode 100644 index 00000000..44d2059d --- /dev/null +++ b/undertest-junit5/src/test/java/undertest/junit5/UT_StringLiteralsJavaTest.java @@ -0,0 +1,35 @@ +package undertest.junit5; + +import org.junit.jupiter.api.Test; +import static com.diffplug.selfie.Selfie.expectSelfie; + +public class UT_StringLiteralsJavaTest { + @Test + public void empty() { + expectSelfie("").toBe_TODO(); + } + + @Test + public void tabs() { + expectSelfie("\t\t\t").toBe_TODO(); + } + + @Test + public void spaces() { + expectSelfie(" ").toBe_TODO(); + } + + @Test + public void newlines() { + expectSelfie("\n").toBe_TODO(); + expectSelfie("\n\n").toBe_TODO(); + expectSelfie("\n\n\n").toBe_TODO(); + } + + @Test + public void allOfIt() { + expectSelfie(" a\n" + + "a \n" + + "\t a \t\n").toBe_TODO(); + } +} diff --git a/undertest-junit5/src/test/kotlin/undertest/junit5/UT_WithinMethodGCTest.ss b/undertest-junit5/src/test/java/undertest/junit5/UT_WithinMethodGCTest.ss similarity index 100% rename from undertest-junit5/src/test/kotlin/undertest/junit5/UT_WithinMethodGCTest.ss rename to undertest-junit5/src/test/java/undertest/junit5/UT_WithinMethodGCTest.ss diff --git a/undertest-junit5/src/test/kotlin/undertest/junit5/UT_InlineStringTest.kt b/undertest-junit5/src/test/kotlin/undertest/junit5/UT_InlineStringTest.kt deleted file mode 100644 index f1e6fe03..00000000 --- a/undertest-junit5/src/test/kotlin/undertest/junit5/UT_InlineStringTest.kt +++ /dev/null @@ -1,13 +0,0 @@ -package undertest.junit5 -// spotless:off -import com.diffplug.selfie.Selfie.expectSelfie -import kotlin.test.Ignore -import kotlin.test.Test -// spotless:on - -@Ignore -class UT_InlineStringTest { - @Test fun singleLine() { - expectSelfie("Hello world").toBe_TODO() - } -}