-
Couldn't load subscription status.
- Fork 1.4k
Include source location (filename, line number) of the executing specs #4242
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
Conversation
|
@hmemcpy Thanks for working on this! Conceptually I feel like our tracing functionality should be supporting this and if it is not then we need either need additional functionality in the tracing or we need to be using the existing functionality better in ZIO Test. I am cognizant that this is a feature we have wanted to support for a while and so we could potentially add with the understanding that we are going to work to implement ourselves and remove the dependency. But since being zero dependency is one of our selling points I am a little hesitant about adding a dependency we don't think we ultimately are going to need. What issues did you run into trying to use the traces for this? I can look at it too. |
|
Thanks, @adamgfraser, for your comments! Admittedly, I haven't looked in a while at the current state of tracing, but as I recall from my last attempt, I wasn't quite able to extract the correct line number of the calling test. I will have to look again! Mainly, I really want this functionality added because I keep finding myself double-clicking on the test results in IntelliJ and NOTHING HAPPENS! And unfortunately, that part is not very extensible so I can't work around it on the plugin side... I will try again with using traces. However, what do you think about storing this information in an annotation? Particularly, it seems this would be an "internal" annotation which doesn't get rendered. |
|
I think using an annotation for this is fantastic. I think if we implemented it with tracing we would potentially implement it with an annotation anyway so I think it is more a question of how we get the information in the first place than how we store it. |
|
Given test("name")(<by-name IO of type `=> ZIO[...]`, which is `Function0` at runtime>)pass the But this won't work on Scala.js though (the entirety of tracing doesn't). package izumi.fundamentals.platform.language
final case class SourceFilePosition(file: String, line: Int) {
override def toString: String = s"($file:$line)" // It will be a clickable link in intellij console
}
object SourceFilePosition {
def unknown = SourceFilePosition("?", 0)
}
package izumi.fundamentals.platform.language
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
final case class SourceFilePositionMaterializer(get: SourceFilePosition) extends AnyVal
object SourceFilePositionMaterializer {
@inline def sourcePosition(implicit ev: SourceFilePositionMaterializer): SourceFilePosition = ev.get
implicit def materialize: SourceFilePositionMaterializer = macro SourcePositionMaterializerMacro.getSourceFilePositionMaterializer
object SourcePositionMaterializerMacro {
// scalactic.source.Position does all that manually and uses `setType`s to avoid retypechecking, oh well might as well...
// PS: To ensure that retypechecking does not happen, put a known bad type into `setType` and watch compiler crash.
// If it _doesn't_ crash, that means one of the tree nodes was missing a `setType`
def literal(c: blackbox.Context)(tpe: c.Type)(a: Any): c.universe.Literal = {
c.internal.setType(c.universe.Literal(c.universe.Constant(a)), tpe)
}
def getSourceFilePositionMaterializer(c: blackbox.Context): c.Tree = {
import c.universe._
import c.universe.internal._
import c.universe.internal.gen.{mkAttributedIdent, mkAttributedSelect}
val sourceFilePosition = getSourceFilePosition(c)
val matTpe = typeOf[SourceFilePositionMaterializer]
val matModule = matTpe.companion.typeSymbol.asClass.module
val sourceFilePositionMaterliazer = Apply(
mkAttributedSelect(
mkAttributedIdent(matModule),
matModule.typeSignature.decl(TermName("apply")),
),
sourceFilePosition :: Nil,
)
setType(sourceFilePositionMaterliazer, matTpe)
}
def getSourceFilePosition(c: blackbox.Context): c.Tree = {
import c.universe._
import c.universe.internal._
import c.universe.internal.gen.{mkAttributedIdent, mkAttributedSelect}
val posTpe = typeOf[SourceFilePosition]
val posModule = posTpe.companion.typeSymbol.asClass.module
val sourceFilePosition = Apply(
mkAttributedSelect(
mkAttributedIdent(posModule),
posModule.typeSignature.decl(TermName("apply")),
),
literal(c)(definitions.StringClass.toTypeConstructor)(c.enclosingPosition.source.file.name) ::
literal(c)(definitions.IntTpe)(c.enclosingPosition.line) :: Nil,
)
setType(sourceFilePosition, posTpe)
}
}
} |
|
Sorry for the lack of progress on this. I'd like to give this a serious go during the hackathon. |
45a2e06 to
6b5c892
Compare
|
Alright. After taking a good look the problem turned out to be very simple. One tiny macro to capture the info. I implemented two variants - one for dotty and one for Scala 2.x. Finally, I had an issue when trying to test this. I wanted to do something like: testM("captures source info") {
for {
a <- Annotations.get(TestAnnotation.location)
loc = a.head
yield assert(loc)(equalTo("...."))
}however Other than that - it solves the problem and opens the possibility to create richer test and error reporting (see munit, for example), with tooling support for free. |
bcf4ac3 to
f693570
Compare
45c2804 to
275ecd3
Compare
|
Well... seems that I am blocked by 2 bugs I found in dotty... both have to do with a combination of using by-name parameters and defining traits in them. I reported them to dotty, will see if I have a suitable workaround or fix... |
|
So it looks like I'm blocked by two issues in dotty, and according to this comment by Odersky: it's not going to be resolved soon. The problem is with any test code that defines sealed trait hierarchies inside the test body - dotty currently can't inline it properly. In ZIO tests there are two such tests, causing different crashes in the compiler, but the underlying cause is the same - both define sealed traits. Instead of "working around" the crash by fixing the tests, I propose returning to the previous implementation of defining an implicit @adamgfraser, WDYT? |
87b9a50 to
bdd061d
Compare
bdd061d to
05de879
Compare
|
@hmemcpy Too bad we take a step back on this with Scala 3 but I think it is time to get this in. I agree with your approach. |
66639a0 to
e2081c7
Compare
|
I rebased on top of the latest changes in dotty, everything is passing again! @adamgfraser, could we get this merged? |
e2081c7 to
4144d46
Compare
|
I finally rebased it again on top of all the other test-related changes. Made a few tweaks/renames to the layout of the dotty variants to make it consistent with Scala 2 (mainly, extracted Macros into its own file, renamed the proxy methods to be consistent). Also added capturing source location in the new MutableSpecs. Let's hope this works this time :) |
d31422f to
cc81055
Compare
cc81055 to
ce70212
Compare
|
Thank you @jdegoes! |
Fixes #3152
This PR adds source information of the currently executing
test/testM, and storing it inside a test annotation.This is done with a small macro, without any external libraries!
Result (with
locationrendering enabled):This functionality is required in order to provide external tooling support with better navigation to the location of the test, for instance, from IntelliJ's test runner window - double clicking on the test entry can take you to the file.
To achieve this, IntelliJ expects test runners to provide "location hints" in form of a
filename:lineNumberURL. This URL can be constructed externally, but only if this information is available from ZIO Test.I was, unfortunately, not able to extract the exact information I required from ZIO's stack traces.
I welcome a discussion/alternative implementation suggestions!
Related: #3911