From 887431967e7d74bafb77d65583ac25fa3bd0e143 Mon Sep 17 00:00:00 2001 From: ChenCMD Date: Wed, 9 Oct 2024 03:02:23 +0900 Subject: [PATCH 1/2] Add writeUtf8Lines with side-effect test --- .../test/scala/fs2/io/file/FilesSuite.scala | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/io/shared/src/test/scala/fs2/io/file/FilesSuite.scala b/io/shared/src/test/scala/fs2/io/file/FilesSuite.scala index bc1359373b..57014f76d6 100644 --- a/io/shared/src/test/scala/fs2/io/file/FilesSuite.scala +++ b/io/shared/src/test/scala/fs2/io/file/FilesSuite.scala @@ -23,7 +23,7 @@ package fs2 package io package file -import cats.effect.{IO, Resource} +import cats.effect.{IO, Resource, Ref} import cats.kernel.Order import cats.syntax.all._ @@ -170,6 +170,23 @@ class FilesSuite extends Fs2IoSuite with BaseFileSuite { |""".stripMargin) } + test("writeUtf8Lines - side effect") { + Stream + .resource(tempFile) + .flatMap { path => + Stream.eval(Ref[IO].of(0)).flatMap { counter => + Stream + .eval(counter.update(_ + 1).as("")) + .append(Stream.eval(counter.update(_ + 1).as(""))) + .through(Files[IO].writeUtf8Lines(path)) ++ + Stream.eval(counter.get) + } + } + .compile + .foldMonoid + .assertEquals(2) + } + test("writeUtf8Lines - empty stream") { Stream .resource(tempFile) From 14bdb4d77503e9ef196dd70443b3e25fde0bf947 Mon Sep 17 00:00:00 2001 From: ChenCMD Date: Wed, 9 Oct 2024 03:18:16 +0900 Subject: [PATCH 2/2] Fix writeUtf8Lines evaluates leading effects in the input stream twice Fix #3488 --- io/shared/src/main/scala/fs2/io/file/Files.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/io/shared/src/main/scala/fs2/io/file/Files.scala b/io/shared/src/main/scala/fs2/io/file/Files.scala index ae618aba1d..bc4e307846 100644 --- a/io/shared/src/main/scala/fs2/io/file/Files.scala +++ b/io/shared/src/main/scala/fs2/io/file/Files.scala @@ -464,8 +464,13 @@ sealed trait Files[F[_]] extends FilesPlatform[F] { def writeUtf8Lines(path: Path, flags: Flags): Pipe[F, String, Nothing] = in => in.pull.uncons .flatMap { - case Some(_) => - in.intersperse(lineSeparator).append(Stream[F, String](lineSeparator)).underlying + case Some((next, rest)) => + Stream + .chunk(next) + .append(rest) + .intersperse(lineSeparator) + .append(Stream[F, String](lineSeparator)) + .underlying case None => Pull.done } .stream