Skip to content

Commit

Permalink
HTML Report via a bit updated model, and tasks automatically added (#14)
Browse files Browse the repository at this point in the history
* Describe mavenLocal()
* Add new model and mapping
* Move tasks to verification group
* Add Violations HTML report supporting Lint for now
* Allow violations task to be used from all modules, not just root
  • Loading branch information
TWiStErRob committed Oct 8, 2018
1 parent 213538f commit 48df075
Show file tree
Hide file tree
Showing 15 changed files with 1,062 additions and 59 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ out/

# IDEA Project files
.idea/
!/.idea/misc.xml
*.iml
*.ipr
*.iws
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
> As part of making the publishing plugins stable, the 'deferred configurable' behavior of
the 'publishing {}' block is now deprecated
* 4.10.2 is GA, but has many breaking changes (e.g. kotlin-dsl and lazy task configuration)
* New model for Violations, a grouped property approach
* New task for HTML report with limited Lint support
* Tasks are now automatically added when applying `quality` plugin

## 0.6 *(2018-08-17 --- 2018-10-01)*
* Gradle: 4.5.1
Expand Down
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ Plugins that configure the built-in plugins with saner defaults.

```gradle
buildscript {
repositories {
maven { name = 'TWiStErRob'; url = 'https://dl.bintray.com/twisterrob/maven'}
}
dependencies {
classpath 'net.twisterrob.gradle:twister-quality'
classpath "net.twisterrob.gradle:twister-quality:${VERSION_TWISTER_QUALITY}"
}
}
apply plugin: 'net.twisterrob.quality'
Expand All @@ -20,7 +23,9 @@ allprojects {
apply plugin: 'net.twisterrob.quality'
}
task('printViolationCounts', type: net.twisterrob.gradle.quality.ValidateViolationsTask) {
// this is added by default as `violationReportConsole`, but can be used to customize
task('printViolationCounts', type: net.twisterrob.gradle.quality.tasks.ValidateViolationsTask) {
action = {net.twisterrob.gradle.common.grouper.Grouper.Start<se.bjurr.violations.lib.model.Violation> results ->
results.by.parser.module.variant.group().each { checker, byModule ->
println "\t${checker}"
Expand Down Expand Up @@ -65,6 +70,23 @@ Most of the code is written in Kotlin, some in Groovy to test the integration wi
2. After it builds successfully it's ok to import the root `build.gradle` into IntelliJ IDEA/Android Studio.
3. For running and debugging info see [test/README.md](test/README.md)

### Using the `-SNAPSHOT` from a local repo
#### Publish
Change `gradle.properties`: `VERSION=x.y-SNAPSHOT`
```console
gradlew publishToMavenLocal
```

#### Consume
```
buildscript {
repositories {
mavenLocal() // make sure it's first
}
}
```
and then business as usual (`buildscript { dependencies { classpath "..."` etc.).

### Using the `-SNAPSHOT` from a local build

Add in root `build.gradle`:
Expand Down
4 changes: 3 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ VERSION_JUNIT=4.12
VERSION_HAMCREST=1.3
# https://bintray.com/mockito/maven
# https://github.com/mockito/mockito/blob/release/2.x/doc/release-notes/official.md
VERSION_MOCKITO=2.22.1
VERSION_MOCKITO=2.23.0
# https://github.com/google/guava/releases
VERSION_GUAVA=26.0-jre
# https://github.com/tomasbjerre/violations-lib/blob/master/CHANGELOG.md
# https://github.com/tomasbjerre/violations-lib/releases
VERSION_VIOLATIONS=1.68
# https://github.com/redundent/kotlin-xml-builder#release-notes
VERSION_XML_BUILDER=1.4.3
4 changes: 4 additions & 0 deletions quality/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ val VERSION_ANDROID_PLUGIN: String by project
val VERSION_VIOLATIONS: String by project
val VERSION_JUNIT: String by project
val VERSION_HAMCREST: String by project
val VERSION_MOCKITO: String by project
val VERSION_JETBRAINS_ANNOTATIONS: String by project
val VERSION_XML_BUILDER: String by project

dependencies {
implementation(project(":common"))
Expand All @@ -19,12 +21,14 @@ dependencies {
compileOnly("com.android.tools.build:gradle:${VERSION_ANDROID_PLUGIN}")
// compileOnly ("de.aaschmid:gradle-cpd-plugin:1.0")
implementation("se.bjurr.violations:violations-lib:${VERSION_VIOLATIONS}")
implementation("org.redundent:kotlin-xml-builder:${VERSION_XML_BUILDER}")

testImplementation(gradleTestKit())
testImplementation(project(":test"))

testImplementation("junit:junit:${VERSION_JUNIT}")
testImplementation("org.hamcrest:hamcrest-all:${VERSION_HAMCREST}")
testImplementation("org.mockito:mockito-core:${VERSION_MOCKITO}")
testImplementation("org.jetbrains:annotations:${VERSION_JETBRAINS_ANNOTATIONS}")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package net.twisterrob.gradle.quality
import net.twisterrob.gradle.checkstyle.CheckStylePlugin
import net.twisterrob.gradle.common.BaseExposedPlugin
import net.twisterrob.gradle.pmd.PmdPlugin
import net.twisterrob.gradle.quality.tasks.GlobalLintGlobalFinalizerTask
import net.twisterrob.gradle.quality.tasks.HtmlReportTask
import net.twisterrob.gradle.quality.tasks.ValidateViolationsTask
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply

Expand All @@ -12,7 +15,17 @@ class QualityPlugin : BaseExposedPlugin() {
super.apply(target)

project.extensions.create("quality", QualityExtension::class.java, project)
// needed for accessing ReportingExtension to get `build/reporting` folder
project.plugins.apply("org.gradle.reporting-base")
project.apply<CheckStylePlugin>()
project.apply<PmdPlugin>()

project.tasks.register("violationReportConsole", ValidateViolationsTask::class.java)
project.tasks.register("violationReportHtml", HtmlReportTask::class.java)
project.afterEvaluate {
if (project.tasks.findByName("lint") == null) {
project.tasks.register("lint", GlobalLintGlobalFinalizerTask::class.java)
}
}
}
}
76 changes: 60 additions & 16 deletions quality/src/main/kotlin/net/twisterrob/gradle/quality/Violations.kt
Original file line number Diff line number Diff line change
@@ -1,25 +1,69 @@
package net.twisterrob.gradle.quality

import se.bjurr.violations.lib.model.Violation
import org.gradle.api.Project
import java.io.File

class Violations(
@JvmField val parser: String,
@JvmField val module: String,
@JvmField val variant: String,
/**
* Parseable result.
*/
@JvmField val result: File,
/**
* Human-consumable report.
*/
@JvmField val report: File,
/**
* Report file missing, or error during read.
*/
@JvmField val violations: List<Violation>?
@JvmField val parser: String,
@JvmField val module: String,
@JvmField val variant: String,
/**
* Parseable result.
*/
@JvmField val result: File,
/**
* Human-consumable report.
*/
@JvmField val report: File,
/**
* Report file missing, or error during read.
*/
@JvmField val violations: List<Violation>?
) {

override fun toString() = "${module}:${parser}@${variant} (${result}): ${violations}"
}

class Violation(
val rule: String,
val category: String?,
val severity: Severity,
val message: String,
val specifics: Map<String, String> = emptyMap(),
val location: Location,
val source: Source
) {

override fun toString() =
"Violation(rule='$rule', category=$category, severity=$severity, message='$message', specifics=$specifics, location=$location, source=$source)"

enum class Severity {
INFO,
WARNING,
ERROR
}

class Location(
val module: Project,
val variant: String,
val file: File,
val startLine: Int,
val endLine: Int,
val column: Int
) {
override fun toString() =
"Location(module=$module, variant='$variant', file=$file, startLine=$startLine, endLine=$endLine, column=$column)"
}

class Source(
val parser: String,
val gatherer: String,
val reporter: String,
val source: String,
val report: File,
val humanReport: File?
) {
override fun toString() =
"Source(parser='$parser', gatherer='$gatherer', reporter='$reporter', source='$source', report=$report, humanReport=$humanReport)"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package net.twisterrob.gradle.quality.report.html

import net.twisterrob.gradle.quality.Violation
import net.twisterrob.gradle.quality.Violation.Location

/**
* Assumes order by category then by rule then by file then by line then by column.
*/
internal fun collapse(violations: List<Violation>): List<Violation> =
violations
.groupBy { it.rule }
.mapValues { (_, list) -> collapseUniform(list) }
.flatMap { it.value }

internal fun collapseUniform(violations: List<Violation>): List<Violation> =
violations
.groupBy { it.location.file }
.mapValues { (_, list) -> collapseFile(list) }
.flatMap { it.value }

internal fun collapseFile(violations: List<Violation>): List<Violation> {
@Suppress("SimplifyBooleanWithConstants")
fun verySimilarProblem(v1: Violation, v2: Violation): Boolean =
true
&& v1.rule == v2.rule
&& v1.category == v2.category
&& v1.severity == v2.severity
&& v1.specifics == v2.specifics
//&& v1.message == v2.message
&& v1.location.module == v2.location.module
&& v1.location.variant == v2.location.variant
&& v1.location.file == v2.location.file
//&& v1.location.startLine == v2.location.startLine
//&& v1.location.endLine == v2.location.endLine
//&& v1.location.column == v2.location.column
&& v1.source.parser == v2.source.parser
&& v1.source.gatherer == v2.source.gatherer
&& v1.source.reporter == v2.source.reporter
&& v1.source.source == v2.source.source
&& v1.source.report == v2.source.report
&& v1.source.humanReport == v2.source.humanReport

fun merge(list: List<Violation>): Violation {
val first = list.first()
return Violation(
first.rule,
first.category,
first.severity,
list.joinToString { it.message },
first.specifics,
Location(
first.location.module,
first.location.variant,
first.location.file,
first.location.startLine,
list.last().location.endLine,
first.location.column
),
first.source
)
}

var continuation: MutableList<Violation> = mutableListOf(violations.first())
val mergeds = mutableListOf<Violation>()
for (next in violations.asSequence().drop(1)) {
if (verySimilarProblem(next, continuation.last())) {
if (continuation.last().location.endLine + 1 == next.location.startLine) {
// found a continuation, save and continue searching
continuation.add(next)
continue
}
}
// if reached here, something is different and next is not part of the [first, last] group
// continuation was already found, merge them (possible that continuation.size == 1, it still works
mergeds.add(merge(continuation))
// continue searching from next one
continuation = mutableListOf(next)
}
// merge remainder (i.e. last group); this could be a single input item as well
mergeds.add(merge(continuation))
return mergeds
}
Loading

0 comments on commit 48df075

Please sign in to comment.