From bdeda6f761dc10ba532713ab70a2c86e4b700154 Mon Sep 17 00:00:00 2001 From: Seetaramayya <3521036+Seetaramayya@users.noreply.github.com> Date: Mon, 20 Mar 2023 21:09:22 +0100 Subject: [PATCH 1/2] Fixed the issue that is throwing StringIndexOutOfBoundsException when duplicated replacements are used to edit the file. In other words, trying to replace single dependency more than once which is not valid. --- .../org/scalasteward/core/edit/EditAlg.scala | 2 +- .../core/edit/update/data/Substring.scala | 4 +-- .../scalasteward/core/edit/RewriteTest.scala | 34 ++++++++++++++++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/edit/EditAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/edit/EditAlg.scala index 01fd31468..bff7d86f9 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/edit/EditAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/edit/EditAlg.scala @@ -106,7 +106,7 @@ final class EditAlg[F[_]](implicit repoDir <- workspaceAlg.repoDir(data.repo) replacementsByPath = updateReplacements.groupBy(_.position.path).toList _ <- replacementsByPath.traverse { case (path, replacements) => - fileAlg.editFile(repoDir / path, Substring.Replacement.applyAll(replacements)) + fileAlg.editFile(repoDir / path, Substring.Replacement.applyAll(replacements.toSet)) } _ <- reformatChangedFiles(data) msgTemplate = data.config.commits.messageOrDefault diff --git a/modules/core/src/main/scala/org/scalasteward/core/edit/update/data/Substring.scala b/modules/core/src/main/scala/org/scalasteward/core/edit/update/data/Substring.scala index 526155b6b..50d181b8a 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/edit/update/data/Substring.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/edit/update/data/Substring.scala @@ -41,10 +41,10 @@ object Substring { final case class Replacement(position: Position, replacement: String) object Replacement { - def applyAll(replacements: List[Replacement])(source: String): String = { + def applyAll(replacements: Set[Replacement])(source: String): String = { var start = 0 val sb = new java.lang.StringBuilder(source.length) - replacements.sortBy(_.position.start).foreach { r => + replacements.toSeq.sortBy(_.position.start).foreach { r => val before = source.substring(start, r.position.start) start = r.position.start + r.position.value.length sb.append(before).append(r.replacement) diff --git a/modules/core/src/test/scala/org/scalasteward/core/edit/RewriteTest.scala b/modules/core/src/test/scala/org/scalasteward/core/edit/RewriteTest.scala index bbfde741e..1926e608e 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/edit/RewriteTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/edit/RewriteTest.scala @@ -1,10 +1,11 @@ package org.scalasteward.core.edit +import cats.data.NonEmptyList import cats.effect.unsafe.implicits.global import munit.FunSuite import org.scalasteward.core.TestInstances.dummyRepoCache import org.scalasteward.core.TestSyntax._ -import org.scalasteward.core.data.{Repo, RepoData, Update} +import org.scalasteward.core.data.{CrossDependency, GroupId, Repo, RepoData, Update, Version} import org.scalasteward.core.mock.MockContext.context._ import org.scalasteward.core.mock.MockState import org.scalasteward.core.repoconfig.RepoConfig @@ -931,6 +932,37 @@ class RewriteTest extends FunSuite { runApplyUpdate(update, original, expected) } + test("duplicate updates should be successful") { + val dependency = "com.pauldijou".g % "jwt-play-json".a % "5.0.0" + + val artifactId = Update.ForArtifactId( + CrossDependency(dependency), + newerVersions = NonEmptyList.of(Version("9.2.0")), + newerGroupId = Some(GroupId("com.github.jwt-scala")), + newerArtifactId = Some("jwt-play-json") + ) + val duplicatedUpdates = Update.ForGroupId(NonEmptyList.of(artifactId, artifactId)) + val buildSbtContent = + """ + | lazy val root = (project in file(".")) + | .settings( + | scalafmtOnCompile := true, + | scalaVersion := scala213, + | libraryDependencies ++= Seq( + | "com.pauldijou" %% "jwt-play-json" % "5.0.0", // JWT parsing + | "org.scalatestplus" %% "mockito-3-4" % "3.2.10.0" % Test + | ), + | crossScalaVersions := supportedScalaVersions + | ) + |""".stripMargin + val original = Map("build.sbt" -> buildSbtContent) + val expectedSbtContent = buildSbtContent + .replaceAll("com.pauldijou", "com.github.jwt-scala") + .replaceAll("5.0.0", "9.2.0") + val expected = Map("build.sbt" -> expectedSbtContent) + runApplyUpdate(duplicatedUpdates, original, expected) + } + private def runApplyUpdate( update: Update.Single, files: Map[String, String], From 47796a26752cfc659c3ee6213974628993c3f96a Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Fri, 24 Mar 2023 07:41:32 +0100 Subject: [PATCH 2/2] Minimize test case --- .../org/scalasteward/core/edit/EditAlg.scala | 2 +- .../core/edit/update/data/Substring.scala | 4 +- .../scalasteward/core/edit/RewriteTest.scala | 45 +++++-------------- 3 files changed, 15 insertions(+), 36 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/edit/EditAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/edit/EditAlg.scala index bff7d86f9..01fd31468 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/edit/EditAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/edit/EditAlg.scala @@ -106,7 +106,7 @@ final class EditAlg[F[_]](implicit repoDir <- workspaceAlg.repoDir(data.repo) replacementsByPath = updateReplacements.groupBy(_.position.path).toList _ <- replacementsByPath.traverse { case (path, replacements) => - fileAlg.editFile(repoDir / path, Substring.Replacement.applyAll(replacements.toSet)) + fileAlg.editFile(repoDir / path, Substring.Replacement.applyAll(replacements)) } _ <- reformatChangedFiles(data) msgTemplate = data.config.commits.messageOrDefault diff --git a/modules/core/src/main/scala/org/scalasteward/core/edit/update/data/Substring.scala b/modules/core/src/main/scala/org/scalasteward/core/edit/update/data/Substring.scala index 50d181b8a..4f4a18231 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/edit/update/data/Substring.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/edit/update/data/Substring.scala @@ -41,10 +41,10 @@ object Substring { final case class Replacement(position: Position, replacement: String) object Replacement { - def applyAll(replacements: Set[Replacement])(source: String): String = { + def applyAll(replacements: List[Replacement])(source: String): String = { var start = 0 val sb = new java.lang.StringBuilder(source.length) - replacements.toSeq.sortBy(_.position.start).foreach { r => + replacements.distinctBy(_.position.start).sortBy(_.position.start).foreach { r => val before = source.substring(start, r.position.start) start = r.position.start + r.position.value.length sb.append(before).append(r.replacement) diff --git a/modules/core/src/test/scala/org/scalasteward/core/edit/RewriteTest.scala b/modules/core/src/test/scala/org/scalasteward/core/edit/RewriteTest.scala index 1926e608e..32e3acedd 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/edit/RewriteTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/edit/RewriteTest.scala @@ -1,11 +1,10 @@ package org.scalasteward.core.edit -import cats.data.NonEmptyList import cats.effect.unsafe.implicits.global import munit.FunSuite import org.scalasteward.core.TestInstances.dummyRepoCache import org.scalasteward.core.TestSyntax._ -import org.scalasteward.core.data.{CrossDependency, GroupId, Repo, RepoData, Update, Version} +import org.scalasteward.core.data.{Repo, RepoData, Update} import org.scalasteward.core.mock.MockContext.context._ import org.scalasteward.core.mock.MockState import org.scalasteward.core.repoconfig.RepoConfig @@ -432,6 +431,17 @@ class RewriteTest extends FunSuite { runApplyUpdate(update, original, expected) } + // https://github.com/scala-steward-org/scala-steward/pull/3016 + test("artifact change with multiple artifactId cross names") { + val update = ("com.pauldijou".g % Nel.of( + ("jwt-core", "jwt-core_2.12").a, + ("jwt-core", "jwt-core_2.13").a + ) % "5.0.0" %> "9.2.0").single.copy(newerGroupId = Some("com.github.jwt-scala".g)) + val original = Map("build.sbt" -> """ "com.pauldijou" %% "jwt-core" % "5.0.0" """) + val expected = Map("build.sbt" -> """ "com.github.jwt-scala" %% "jwt-core" % "9.2.0" """) + runApplyUpdate(update, original, expected) + } + // https://github.com/scala-steward-org/scala-steward/pull/566 test("prevent exception: named capturing group is missing trailing '}'") { val update = @@ -932,37 +942,6 @@ class RewriteTest extends FunSuite { runApplyUpdate(update, original, expected) } - test("duplicate updates should be successful") { - val dependency = "com.pauldijou".g % "jwt-play-json".a % "5.0.0" - - val artifactId = Update.ForArtifactId( - CrossDependency(dependency), - newerVersions = NonEmptyList.of(Version("9.2.0")), - newerGroupId = Some(GroupId("com.github.jwt-scala")), - newerArtifactId = Some("jwt-play-json") - ) - val duplicatedUpdates = Update.ForGroupId(NonEmptyList.of(artifactId, artifactId)) - val buildSbtContent = - """ - | lazy val root = (project in file(".")) - | .settings( - | scalafmtOnCompile := true, - | scalaVersion := scala213, - | libraryDependencies ++= Seq( - | "com.pauldijou" %% "jwt-play-json" % "5.0.0", // JWT parsing - | "org.scalatestplus" %% "mockito-3-4" % "3.2.10.0" % Test - | ), - | crossScalaVersions := supportedScalaVersions - | ) - |""".stripMargin - val original = Map("build.sbt" -> buildSbtContent) - val expectedSbtContent = buildSbtContent - .replaceAll("com.pauldijou", "com.github.jwt-scala") - .replaceAll("5.0.0", "9.2.0") - val expected = Map("build.sbt" -> expectedSbtContent) - runApplyUpdate(duplicatedUpdates, original, expected) - } - private def runApplyUpdate( update: Update.Single, files: Map[String, String],