Skip to content
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

Introduce reporting extensions #2755

Merged
merged 9 commits into from
Jun 4, 2020
Merged

Introduce reporting extensions #2755

merged 9 commits into from
Jun 4, 2020

Conversation

arturbosch
Copy link
Member

@arturbosch arturbosch commented May 31, 2020

This PR introduces the concept of a ReportingExtension.
With this extension the user can listen to results and manipulate them.

The first two implementations are BaselineFacade and OutputFacade.
With this concept the baseline and the reporting features do not need special treatment in the Runner class or the Intellij or Sonarqube plugin but are just loaded from the detekt core engine and executed.

No more code like:

result = arguments.baseline?.let { BaselineFacade().transformResult(it, result) } ?: result	                
val (outputResultsTime) = measure { OutputFacade(arguments.reportPaths, result, settings).run() }

Extracting the "special @Suppress" implemention to a ReportingExtension is conceivable in the future.

This PR also introduces a concept to share data between extensions and tooling frondends (cli, gradle, sonar, intellij) via the PropertiesAware interface.
It allows to retrieve data in a type safe way:

inline fun <reified T : Any> PropertiesAware.getOrNull(key: String): T?

This lightweight key-value storage reduces the need for extra parameters for the ProcessingSettings class.

I've marked both new api interfaces as @UnstableApi for now.

#2680

@codecov
Copy link

codecov bot commented May 31, 2020

Codecov Report

Merging #2755 into master will increase coverage by 0.07%.
The diff coverage is 88.77%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #2755      +/-   ##
============================================
+ Coverage     80.44%   80.51%   +0.07%     
- Complexity     2303     2314      +11     
============================================
  Files           378      384       +6     
  Lines          6949     6960      +11     
  Branches       1262     1260       -2     
============================================
+ Hits           5590     5604      +14     
+ Misses          729      728       -1     
+ Partials        630      628       -2     
Impacted Files Coverage Δ Complexity Δ
...itlab/arturbosch/detekt/api/FileProcessListener.kt 100.00% <ø> (ø) 0.00 <0.00> (ø)
...gitlab/arturbosch/detekt/api/ReportingExtension.kt 0.00% <0.00%> (ø) 0.00 <0.00> (?)
...in/kotlin/io/gitlab/arturbosch/detekt/core/Junk.kt 43.75% <0.00%> (-10.10%) 0.00 <0.00> (ø)
...gitlab/arturbosch/detekt/core/baseline/Baseline.kt 66.66% <ø> (ø) 4.00 <0.00> (ø)
...tlab/arturbosch/detekt/core/reporting/Reporting.kt 96.42% <ø> (ø) 0.00 <0.00> (ø)
...osch/detekt/core/baseline/BaselineResultMapping.kt 71.42% <71.42%> (ø) 6.00 <6.00> (?)
.../io/gitlab/arturbosch/detekt/cli/Configurations.kt 84.41% <90.32%> (+7.94%) 0.00 <0.00> (ø)
.../io/gitlab/arturbosch/detekt/cli/runners/Runner.kt 100.00% <100.00%> (+10.63%) 5.00 <2.00> (-4.00) ⬆️
.../arturbosch/detekt/cli/runners/SingleRuleRunner.kt 96.55% <100.00%> (-0.82%) 5.00 <0.00> (-3.00)
.../gitlab/arturbosch/detekt/core/DelegatingResult.kt 100.00% <100.00%> (ø) 2.00 <2.00> (?)
... and 17 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e1f213b...bc83a2c. Read the comment docs.

Comment on lines +26 to +32
inline fun <reified T : Any> PropertiesAware.getOrNull(key: String): T? {
val value = properties[key]
if (value != null) {
return value.safeAs() ?: error("No value of type ''${T::class} for key '$key'.")
}
return null
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we allow null values in the Map how are we going to know the difference between a value that it's there but it's null and a value that it's not there?

I vote for don't allo null as a possible value in the Map.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

/**
* Is called after all extensions's [transformFindings] were called.
*/
fun onFinalResult(result: Detektion) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should make Detektion a immutable class. Otherwise we are allowing to edit it here and in onRawResult. I know, this is out of the scope of this PR but we could add it to the architecture issue. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please feel free to write it down in the issue.

@OptIn(UnstableApi::class)
class BaselineResultMapping : ReportingExtension {

private var baselineFile: Path? = null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lateinit?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The baseline file can be null.

Comment on lines 43 to 44
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correctly fomatted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, still I extracted the long lambda to an function to make it more clear :).

val reportPaths: Collection<ReportPath> =
context.getOrNull(DETEKT_OUTPUT_REPORT_PATHS_KEY) ?: emptyList()
reports = reportPaths.associateBy { it.kind }
settings = context as? ProcessingSettings ?: error("ProcessingSettings expected.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 this cast is a bit strange, isn't it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I'm not sure how to handle this. It needs the plugin classloader. An extension which loads other extensions.
Feels kinda wrong.
I will convert it to a plain core class again.

@@ -13,6 +13,8 @@ import kotlin.system.measureTimeMillis
@OptIn(UnstableApi::class)
class OutputFacade : ReportingExtension {

override val priority: Int = Int.MIN_VALUE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary? OutputFacade only overrides onFinalResult and in that state no one should change the Detektion (for that reason I was talking about Detektion as an immutable class)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However Detektion is still mutable. So it might be that anyone may add additional ProjectMetric's and expect them to be printed on the console.

With running the output facade last we will ensure that everything stored will be printed.

…sion

An extensions which loads other extensions feels kind of wrong.
Reporting is a core logic and shouldn't be optional.
@arturbosch arturbosch merged commit 92acdbc into master Jun 4, 2020
@arturbosch arturbosch deleted the reporting-extensions branch June 4, 2020 05:07
@arturbosch arturbosch added this to the 1.10.0 milestone Jun 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants