diff --git a/jvm/CHANGELOG.md b/jvm/CHANGELOG.md index 3fe27bdd..8a102a7e 100644 --- a/jvm/CHANGELOG.md +++ b/jvm/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Fixed - Off-by-one in the error message for a VCR key mismatch. ([#526](https://github.com/diffplug/selfie/pull/526)) +- Fix `StringIndexOutOfBoundsException` when an empty snapshot had a facet added. (fixes [#529](https://github.com/diffplug/selfie/issues/529)) ## [2.5.1] - 2025-03-04 ### Fixed diff --git a/jvm/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/SelfieImplementations.kt b/jvm/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/SelfieImplementations.kt index 0b641dfd..088b7aa2 100644 --- a/jvm/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/SelfieImplementations.kt +++ b/jvm/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/SelfieImplementations.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 DiffPlug + * Copyright (C) 2024-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -212,7 +212,7 @@ class StringSelfie( * Returns a serialized form of only the given facets if they are available, silently omits missing * facets. */ -private fun serializeOnlyFacets(snapshot: Snapshot, keys: Collection): String { +internal fun serializeOnlyFacets(snapshot: Snapshot, keys: Collection): String { val writer = StringBuilder() for (key in keys) { if (key.isEmpty()) { @@ -226,7 +226,10 @@ private fun serializeOnlyFacets(snapshot: Snapshot, keys: Collection): S // this codepath is triggered by the `key.isEmpty()` line above writer.subSequence(EMPTY_KEY_AND_FACET.length, writer.length - 1).toString() } else { - writer.setLength(writer.length - 1) + // Check if the writer is empty to avoid StringIndexOutOfBoundsException + if (writer.length > 0) { + writer.setLength(writer.length - 1) + } writer.toString() } } diff --git a/jvm/selfie-lib/src/commonTest/kotlin/com/diffplug/selfie/SelfieImplementationsTest.kt b/jvm/selfie-lib/src/commonTest/kotlin/com/diffplug/selfie/SelfieImplementationsTest.kt new file mode 100644 index 00000000..7e6635e9 --- /dev/null +++ b/jvm/selfie-lib/src/commonTest/kotlin/com/diffplug/selfie/SelfieImplementationsTest.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2025 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 + +import io.kotest.matchers.shouldBe +import kotlin.test.Test + +class SelfieImplementationsTest { + @Test + fun issue_529() { + val empty = Snapshot.of("") + val emptyPlusFacet = Snapshot.of("").plusFacet("new-facet", "new-facet-value") + val mismathedKeys = listOf("new-facet") + serializeOnlyFacets(empty, mismathedKeys) shouldBe "" + serializeOnlyFacets(emptyPlusFacet, mismathedKeys) shouldBe "╔═ [new-facet] ═╗\nnew-facet-value" + } +}