Skip to content

Commit

Permalink
Merge pull request #3354 from InversionSpaces/fix/fix-path-root-handling
Browse files Browse the repository at this point in the history
FIX: Handle root path correctly
  • Loading branch information
armanbilge authored Dec 6, 2023
2 parents 128e888 + 0e2495d commit 8badc91
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 13 deletions.
14 changes: 9 additions & 5 deletions io/js/src/main/scala/fs2/io/file/Path.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,13 @@ final case class Path private[file] (override val toString: String) extends Path
object Path extends PathCompanionApi {
private[file] val sep = facade.path.sep

def apply(path: String): Path =
if (path.endsWith(sep))
new Path(path.dropRight(sep.length))
else
new Path(path)
def apply(path: String): Path = {
// Parse and then reconstruct the path
// to drop all trailing separators
// to match `java.nio.file.Paths.get` behaviour.
val parsed = facade.path.parse(path)
val formatted = facade.path.format(parsed)

new Path(formatted)
}
}
4 changes: 4 additions & 0 deletions io/js/src/main/scala/fs2/io/internal/facade/path.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,8 @@ private[io] object path {
def name: String = js.native
def ext: String = js.native
}

@js.native
@JSImport("path", "format")
def format(pathObject: ParsedPath): String = js.native
}
49 changes: 41 additions & 8 deletions io/shared/src/test/scala/fs2/io/file/PathSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,54 @@ import org.scalacheck.Prop.forAll

class PathSuite extends Fs2IoSuite {

override def scalaCheckTestParameters =
super.scalaCheckTestParameters
/* Increase number of succesful test because
inherited value is too small and tests
here are not so heavy */
.withMinSuccessfulTests(100)

implicit val arbitraryPath: Arbitrary[Path] = Arbitrary(for {
names <- Gen.listOf(Gen.alphaNumStr)
root <- Gen.oneOf("/", "")
} yield names.foldLeft(Path(root))((p, n) => p / Path(n)))

val nonEmptyPath = arbitraryPath.arbitrary.filter(
_.toString.nonEmpty
)

implicit val cogenPath: Cogen[Path] =
Cogen.cogenList[String].contramap(_.names.map(_.toString).toList)

/* ScalaCheck goes from smaller to bigger sizes.
So this generator is sized to make sure small values
like 0 and 1 are probably tested. */
val sepLengthGen = Gen.sized(size => Gen.choose(0, size))

test("construction") {
assertEquals(Path("foo/bar"), Path("foo") / "bar")
assertEquals(Path("/foo/bar"), Path("/foo") / "bar")
}

property("construction handles path of just separators") {
val nonEmptySepLength = sepLengthGen.filter(_ > 0)
forAll(nonEmptySepLength) { (sepLength: Int) =>
assertEquals(
Path("/".repeat(sepLength)).toString,
"/"
)
}
}

property("construction handles separators at the end")(
forAll(sepLengthGen, nonEmptyPath) { (sepLength: Int, path: Path) =>
assertEquals(
Path(path.toString + "/".repeat(sepLength)),
path
)
}
)

test("normalize") {
assertEquals(Path("foo/bar/baz").normalize, Path("foo/bar/baz"))
assertEquals(Path("./foo/bar/baz").normalize, Path("foo/bar/baz"))
Expand Down Expand Up @@ -107,14 +142,12 @@ class PathSuite extends Fs2IoSuite {
assert(!Path("foo").endsWith(".xml"))
}

test("startsWith/endsWith") {
forAll { (start: Path, end: Path) =>
if (start.toString.nonEmpty && end.toString.nonEmpty) {
val path = start.resolve(end)
// TODO
// assert(path.startsWith(start), s"$path doesn't start with $start")
assert(path.endsWith(end), s"$path doesn't end with $end")
}
property("startsWith/endsWith") {
forAll(nonEmptyPath, nonEmptyPath) { (start: Path, end: Path) =>
val path = start.resolve(end)
// TODO
// assert(path.startsWith(start), s"$path doesn't start with $start")
assert(path.endsWith(end), s"$path doesn't end with $end")
}
}

Expand Down

0 comments on commit 8badc91

Please sign in to comment.