-
Couldn't load subscription status.
- Fork 1.4k
[test-sbt] More uniformity. #10120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[test-sbt] More uniformity. #10120
Conversation
61d736b to
246ce39
Compare
e0332e7 to
a908303
Compare
e4458d5 to
5464462
Compare
Currently, code implementing `sbt.testing.Framework` for JVM, Scala.js and Scala Native suffers from severe code triplication: code doing the same thing is present in three places. This pull request removes this gratuitous redundancy. An unfortunate - and inevitable - consequence of code duplication is code drift: different copies of the code do things differently for no good reason. Backends `sbt.testing.Framework` runs on _do_ have inherent differences: tests running on Scala.js and Scala Native are not running on JVM, and thus their outcomes need to be communicated to the process managing the test run; Scala.js is single-threaded by nature, which affects the way tests have to be run. Code differences not warranted by the inherent differences between the backends seem gratuitous, and this pull request removes some of them. One area where implementations of the `sbt.testing.Framework` diverged from one another is the machinery to test them: only JVM implementation is currently testable. With this pull request, `sbt.testing.Framework` tests run on all the backends. Another divergence is in the storage of test summaries: JVM uses `AtomicReference[Vector]`, Scala.js uses `Buffer`, Scala Native uses `ConcurrentLinkedQueue`; this pull request unifies summary storage for all backends: `AtomicReference[Vector]` works for all of them. Another divergence is the way test summary is rendered: counts of tests executed, ignored, or failed are included in the summary on JVM but not on Scala.js or Scala Native; setting test renderer to IntelliJ in the test arguments is honored only on JVM. This pull request makes test summary uniform across the backends. Another divergence is: currently, signal handlers are installed only on JVM. This pull request installs them on all backends. Another divergence affects a recent [enhancement](zio#9756), which turned out to be effective only on JVM; this pull request applies it to all backends (and tests that it works ;)). @kyri-petrou, your [remark](zio#9629 (comment)) > I gave it a go at this (basically trying to unify the JVM / JS / Native implementations so that we can reuse the existing event test suites that exist for the JVM, but it doesn't seem possible. > ... writing a full test suite from scratch (which frankly it's going to be extremely time consuming) is on target: this was not trivial ;)
5464462 to
af719de
Compare
|
@kyri-petrou what do I need to do for this to get merged? Thanks! |
@dubinsky I'm trying to get through the PR but it's taking some time. I'll leave comments when I'm finished |
Thank you!! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good to me, just a few minor-ish comments.
The one main comment I do have though is, can you please make sure that these changes don't break the IntelliJ ZIO plugin?
| lazy val red: String => String = colored(Console.RED) | ||
| lazy val green: String => String = colored(Console.GREEN) | ||
| lazy val cyan: String => String = colored(Console.CYAN) | ||
| lazy val blue: String => String = colored(Console.BLUE) | ||
| lazy val yellow: String => String = colored(Console.YELLOW) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need these to be lazy, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They always were for some (or no) reason; changing this now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 174ab24
|
|
||
| protected def sharedRuntimeSupported: Boolean | ||
|
|
||
| private val summaries: AtomicReference[Vector[Summary]] = new AtomicReference(Vector.empty) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very minor, but ConcurrentLinkedQueue is supported for all Scala flavours (JVM, JS and Native) and should be a lot more memory-friendly than using a Vector and appending items to it.
Alternatively, you could use AtomicReference[List[Summary]], prepend the summaries and the reverse the order in the end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I followed the approach currently used on JVM :)
Changing to ConcurrentLinkedQueue now.
Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 174ab24
| testEventHandler: ZTestEventHandler, | ||
| console: Console |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would keep the old method with the old signature, overload it, deprecate it and use the new method (where you need to use it). The reason for this is there is a discrepancy between the zio-test-sbt and zio-test versions for some reason in a codebase it'll start throwing errors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Doing that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 174ab24
| import scala.collection.mutable.ArrayBuffer | ||
| import scala.util.Try | ||
|
|
||
| object ZTestFrameworkSbtSpec { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, why do you want to throw away this code? I'm not against it but I thought perhaps it had a reason for being there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was there since the beginning, when there was no way to test using sbt.testing.Framework at all, even on JVM. When this became possible, some tests were moved (or copied) to real automated testing. As the behavior of test-sbt changed, the test left behind in this class eventually started failing, since they were checking the actual framework output, not the events emitted, and were disabled one-by-one. I moved whatever was salvageable and deleted the rest, which did not do anything anyway. The reason I think this should be removed is: it creates confusion about where new tests should be added; for instance, when working on #9756, I added tests in this class - and that was the wrong place to add them ;)
| final override def done(): String = { | ||
| // If tests are forked, this will only be relevant in the forked | ||
| // JVM, and will not be set in the original JVM. | ||
| sharedRuntimes.get.foreach(_.unsafe.shutdown()(zio.Unsafe)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use getAndSet(Vector.empty) instead here? Or it doesn't matter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also potentially we can use ConcurrentLinkedQueue as per my previous comment as well to ensure that we're closing each one exactly once
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling Runner.done() more than once is not a supported scenario. I am switching to ConcurrentLinkedQueue as you suggested :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 174ab24
| import sbt.testing._ | ||
|
|
||
| /** | ||
| * TODO update/remove outdated/misplaced doc comment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this still TODO?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment was outdated and/or misplaced before my pull request; it's ok for it to stay that way a bit longer I think ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
turned TODO into a note
| // TODO SBT loggers should be hooked in... | ||
| final private[sbt] def run(eventHandler: EventHandler)(implicit trace: zio.Trace): ZIO[Any, Throwable, Unit] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this TODO still needs to be resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Currently, test-sbt ignores the sbt loggers supplied via Task.execute() and just prints the progress of test execution to the Console. My pull request does not change that - but ideally, it should change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not need to be resolved for this pull request though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
turned TODO into a note
| .mapError((error: Throwable) => ::(error, Nil)) | ||
|
|
||
| for { | ||
| // TODO make this test console silent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this needs to be resolved?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, test-sbt is not testable on Scala.js nor Scala Native; with my pull request, it is. During testing I noticed that progress of execution of tests within framework instances used by tests is visible on non-JVM back-ends - but not on JVM. It would be nice to make this output more uniform across the back-ends - but it is not needed for this pull request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
turned TODO into a note
| specs: Seq[ZIOSpecAbstract], | ||
| args: Array[String], | ||
| selectors: Array[Selector] = Array(new SuiteSelector) | ||
| ): ZIO[Any, ::[Throwable], (Seq[String], Seq[Event])] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're initializing a mutable object inside, we need to be referentially transparent
| ): ZIO[Any, ::[Throwable], (Seq[String], Seq[Event])] = { | |
| ): ZIO[Any, ::[Throwable], (Seq[String], Seq[Event])] = ZIO.suspendSucceed { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Fixing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 174ab24
Thank you!
AFAIK, coupling between the IntelliJ ZIO plugin and Same seems to be true about the ZIO Test runner support for the ZIO IntelliJ plugin, which anyway is archived and was not touched for two years... |
Address review comments.
90bfe81 to
174ab24
Compare
| runtime | ||
| ) { | ||
| // Note: on Scala.js this method is never called - next one is. | ||
| override def execute(eventHandler: EventHandler, loggers: Array[Logger]): Array[Task] = ??? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you throw an error with a clear message stating the comment, something like:
new IllegalStateException("ZTestTask.execute unexpectedly invoked on Scala.js...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in eadcaa9
|
|
||
| override def execute(eventHandler: EventHandler, loggers: Array[Logger], continuation: Array[Task] => Unit): Unit = | ||
| unsafeAPI | ||
| .fork(run(eventHandler)(zio.Trace.empty))(zio.Trace.empty, zio.Unsafe) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you import these to avoid the prefix? same with exit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in eadcaa9 throughout the code; Runtime, System and Console remain zio.-prefixed to disambiguate them from the ones from Java/Scala.
| try { | ||
| resOutter = unsafeAPI.runToFuture(run(eventHandler)(zio.Trace.empty))(zio.Trace.empty, zio.Unsafe) | ||
| Await.result(resOutter, Duration.Inf) | ||
| } catch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try/finally?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so: resOutter.cancel() needs to be invoked only when the try block throws...
Address more review comments.
Address more review comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it looks good. I'm not too familiar with the sbt integration part but I'm really in favour of unifying the implementations for JVM/JS/Native as it's been the source of several bugs on the JS / Native side
@hearnadam are you okay with merging this one in as well?
|
Many thanks @dubinsky! I know this was not a trivial task - really appreciate this :) |
Glad I could be of help :) Thank you @kyri-petrou and @hearnadam! |
TL;DR: make
sbt.testing.Frameworkimplementation:Currently, code implementing
sbt.testing.Frameworkfor JVM, Scala.js and Scala Native suffers from severe code triplication: code doing the same thing is present in three places. This pull request removes this gratuitous redundancy.An unfortunate - and inevitable - consequence of code duplication is code drift: different copies of the code do things differently for no good reason.
Backends
sbt.testing.Frameworkruns on do have inherent differences: tests running on Scala.js and Scala Native are running outside of the JVM, and thus their outcomes need to be communicated to the process managing the test run; Scala.js is single-threaded by nature, which affects the way tests have to be run. Code differences not warranted by the inherent differences between the backends seem gratuitous, and this pull request removes some of them.One area where implementations of the
sbt.testing.Frameworkdiverged from one another is the machinery to test them: only JVM implementation is currently testable. With this pull request,sbt.testing.Frameworktests run on all the backends.Another divergence is in the storage of test summaries: JVM uses
AtomicReference[Vector], Scala.js usesBuffer, Scala Native usesConcurrentLinkedQueue; this pull request unifies summary storage for all backends:AtomicReference[Vector]works for all of them.Another divergence is the way test summary is rendered: counts of tests executed, ignored, or failed are included in the summary on JVM but not on Scala.js or Scala Native; setting test renderer to IntelliJ in the test arguments is honored only on JVM. This pull request makes test summary uniform across the backends.
Another divergence is: currently, signal handlers are installed only on JVM. This pull request installs them on all backends.
Another divergence affects a recent enhancement, which turned out to be effective only on JVM; this pull request applies it to all backends (and tests that it works ;)).
@kyri-petrou, your remark
is on target: this was not trivial ;)