Conversation
override def describe(descriptor: SensorDescriptor): Unit = { | ||
descriptor | ||
.name(SensorName) | ||
.onlyOnLanguage(Scala.LanguageKey) |
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 believe this sensor only works for test files.
Thus, you may add
.onlyOnFileType(InputFile.Type.TEST)
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 definitely
|
||
val directories: List[File] = | ||
reports.flatMap(path => Try(pathResolver.relativeFile(fs.baseDir, path.toString)).toOption) | ||
// TODO: Is the injected fileSystem different from context.fileSystem? |
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.
To be honest, I had the same question before...
I don't remember why, but I ended up using the context one - I believe it was only to avoid dependency injection.
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'll find this out, but if it turns out it's the same then I think it's easier to just inject it instead of accessing it via the context or extracting it into a local value - but it's not a big difference though.
.asScala | ||
|
||
// Collect the first file here. | ||
files.headOption.flatMap(file => reports.headOption.map((file, _))) |
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.
Given you only care for the first file... - maybe, because it only makes sense that it matches just one file
Shouldn't you log a warning if more than one file is matched and also if any file is matched?
BTW, this reminds me #64 we may try to do this in a standardized way for all module.
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.
Shouldn't you log a warning if more than one file is matched
That's something I thought about improving, although we're using fully qualified names combined with the tests folders which should result in unique files, so I'm not worried too much about this.
The reason why I'm using inputFiles
instead of a single inputFile
is to avoid catching exceptions if the file is not found, but actually, now I think that it would probably be more useful to do it that way and log an error if an expected file is not found.
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.
BTW, this reminds me #64 we may try to do this in a standardised way for all module.
Agreed, I think we have been doing this in a wrong way using the cwd
which leads to a whole load of path issues like, e.g. those discussed in #122. Using the FileSystem and PathResolver is the right way to go about this.
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.
Cool, so if everything goes right we may replace our old attempts with the new resolve
helper method.
Probably work for another PR (on which we should be sure to test all reported issues).
def apply[T](clazz: Class[T], module: String): Log = Log(clazz, Some(module)) | ||
def apply[T](clazz: Class[T], module: Option[String] = None): Log = { | ||
val log: Logger = Loggers.get(clazz) | ||
val prefix: String = "sonar-scala" + module.map("-" + _).getOrElse("") |
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.
What about
val prefix: String = s"sonar-scala${module.fold("")("-" + _)}"
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, that's much nicer - thanks
/** | ||
* Save a new measure for the given metric. | ||
*/ | ||
def save[T <: java.io.Serializable]( |
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.
You may use this helper in the ScoverageSensor too.
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'll roll this out across the other modules, but I'd prefer to do this in a separate pr.
@@ -46,39 +48,52 @@ final class JUnitSensor( | |||
descriptor | |||
.name(SensorName) | |||
.onlyOnLanguage(Scala.LanguageKey) | |||
.onlyOnFileType(InputFile.Type.TEST) | |||
// TODO: Add a flag to disable the sensor. |
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.
// TODO: Add a flag to disable the sensor.
👍
@@ -27,7 +28,8 @@ import org.sonar.api.batch.measure.Metric | |||
import org.sonar.api.batch.sensor.SensorContext | |||
import org.sonar.api.config.Configuration | |||
|
|||
import scala.language.implicitConversions | |||
import scala.language.{higherKinds, implicitConversions} |
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.
Where/Why exactly do we need higherKinds
here ?
new SensorContextOps(context) | ||
} | ||
|
||
final class SensorContextOps(val context: SensorContext) extends AnyVal { |
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.
Why using an implicit conversion, instead of an implicit class?
The same goes to all syntax classes...
Also, what about having just one syntax.scala file with a object syntax { ... }
and put there all the syntax helper classes?
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've tried to achieve something similar to how the syntax package is structured in cats (see an example here), but what I haven't done yet was to put everything into the package object, but I 'll do this soon (example) - I think it's a nice way to organise implicit conversions.
Why using an implicit conversion, instead of an implicit class?
I think that implicit classes get compiled to an implicit function and a class, it's syntax sugar, so perhaps it was done that way in cats to optimise compile times? That's the only reason I can think of.
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've tried to achieve something similar to how the syntax package is structured in cats"
Oh, that would be nice - I assume we won't have an all
shortcut.
"I think that implicit classes get compiled to an implicit function and a class, it's syntax sugar".
I am almost sure that is correct, but it would allow us to omit the import scala.language.implicitConversions
Also, I have the impression that is the recommendation to use implicit classes over implicit conversions - but can't find nothing related to that, maybe I just heard about it on a conference.
"so perhaps it was done that way in cats to optimise compile times?"
No idea...
Maybe we should ask to the very sage wizards for clarification? 🤔
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.
Oh, that would be nice - I assume we won't have an
all
shortcut.
Yes, that's right - even though it's useful (and lazy!), my preference is to be more conscious about which implicit conversions I have in the scope, so I'll stay away from all
😆
I am almost sure that is correct, but it would allow us to omit the import scala.language.implicitConversions
Also, I have the impression that is the recommendation to use implicit classes over implicit conversions - but can't find nothing related to that, maybe I just heard about it on a conference.
This sounds sensible, and I'm happy with making that change unless we can find any evidence strongly supporting implicit conversions. I'll also try to dig a bit deeper and see if I can find anything more about it.
Thanks for your valuable feedback! 👍
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 turns out that a value class cannot be a member of another class (trait), so if we want to have this nice structure with individual syntax traits instantiated in the package object, we need to keep the implicit conversions in those traits and have the classes sitting outside. I'll push those changes for now but feel free to propose an alternative way of doing this.
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 turns out that a value class cannot be a member of another class (trait)".
Oh, that explains it all.
Well, to be honest I think the only reason in Cats they use traits
is to be able to combine them all in the all
shortcut.
Since we don't plan to do that, we may leave them as objects
and use implicit classes.
But at the end, there wouldn't be too much difference, so I will just leave it to your personal preference 👍
|
||
/** | ||
* Scala.Option <-> Java.Optional conversions. | ||
* @note Taken from https://gist.github.com/julienroubieu/fbb7e1467ab44203a09f. | ||
*/ | ||
object JavaOptionals { | ||
implicit def toRichOption[T >: Null](opt: Option[T]): RichOption[T] = new RichOption[T](opt) | ||
implicit def toRichOptional[T](optional: Optional[T]): RichOptional[T] = new RichOptional[T](optional) | ||
implicit final def toRichOption[T >: Null](opt: Option[T]): RichOption[T] = |
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 the method is part of an object
and an object
is already final...
marking a def
final is not redundant? - or does it help the JIT inlining or something like 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.
This should be a trait (as per the examples above), but I haven't finished refactoring everything yet 😄
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.
Oh that makes sense.
} | ||
|
||
/** Transform this Option to an equivalent Java Optional */ | ||
class RichOption[T >: Null](opt: Option[T]) { | ||
final class RichOption[T >: Null](val opt: Option[T]) extends AnyVal { |
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.
👍 for making this a value class - (I believe I was the one that first coded this, so thanks for improving it)
And again, why don't make this an implicit class instead of using implicit conversions?
Also, I think this counts as syntax too. Thus, we should move them to the syntax object/package
and rename them OptionOps and OptionalOps just to be consistent.
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.
👍 for making this a value class - (I believe I was the one that first coded this, so thanks for improving it)
👍
I think that I addressed the first part of your comment above, but regarding those option conversions I didn't want to refactor too much at once and tried to touch as little as possible that affects other modules, so, for now, I might only add a TODO to refactor this later or possibly in a separate PR.
I've also refactored the optional conversions. Almost finished, but still have some more tests to write. |
Ok, I'm finished - I also updated all of the examples projects with the junit settings required to get this to work. I'll open a separate PR with an updated readme and release notes in a separate PR once this is merged. I'd appreciate if you could have another look @BalmungSan. |
…thout ‘sonar.tests’)
@@ -31,12 +31,13 @@ class SonarConfigSpec extends FlatSpec with Matchers { | |||
val conf = new MapSettings() | |||
.setProperty("path", "this/is/a/path, another/path") | |||
.asConfig() | |||
val defaultPath = List(Paths.get("default/path"), Paths.get("default/path2")) |
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.
Just one minor thing, this should be called defaultPaths
(since now there are many).
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 of course, I'll fix that
I have been reviewing as you pushed, I just haven't commented anything because I saw everything O.K. 👍 Just one thing, why did you remove the disable property? Also, we must not forget to apply some of the changes we have discussed here to other parts of the project. I don't know if you want to to that before or after the new release. BTW, Happy holidays 🎉 |
Cool, thanks!
I came to the conclusion that there is no need to allow users to explicitly control when to disable this sensor because 1. the sensor won't run if you don't specify the
I don't want to delay the release too much, so I'll do this afterwards. I have some nice features planned in the pipeline, so there will be a little bit of refactoring this and early next year :)
Happy holidays to you too! |
Initial implementation of the JUnit XML report sensor.
Unit tests and some minor refactoring is still left to do on this PR.
Example result of scanning sonar-scala:
Implements #32.