-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathbuild.gradle
More file actions
169 lines (149 loc) · 5.78 KB
/
Copy pathbuild.gradle
File metadata and controls
169 lines (149 loc) · 5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
// The publish plugin is loaded here, once, and only when a publish task is requested. Loading it
// from each module's own buildscript gave every module its own copy of the plugin classes, and
// the plugin's shared Sonatype build service then failed with "cannot set property buildService
// ... loaded with a different classloader" as soon as two modules published in one build.
if (gradle.startParameter.taskNames.any { it.toLowerCase().contains("publish") }) {
// This requires minimum java 11 to work
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.28.0'
}
}
}
allprojects {
ext.CLY_VERSION = "26.8.0"
ext.POWERMOCK_VERSION = "1.7.4"
ext.JACOCO_VERSION = "0.8.13"
// Without this the compiler uses the platform default, which is windows-1252 on the Windows
// runner and fails on any non-ASCII character in a source file.
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
tasks.withType(Javadoc) {
options.encoding = 'UTF-8'
options.addStringOption('Xdoclint:none', '-quiet')
}
repositories {
google()
mavenCentral()
}
}
// ---------------------------------------------------------------------------
// Coverage
//
// The library modules apply the jacoco plugin themselves (see their build.gradle).
// "./gradlew coverage" runs their tests, writes HTML + XML reports and prints a
// per-module summary, so the numbers do not have to be dug out of the reports.
// ---------------------------------------------------------------------------
// Only the published libraries are measured; the demo apps are not shipped.
ext.COVERED_MODULES = [':sdk-java', ':sdk-java-ui']
// Measuring coverage must not be blocked by a flaky test: the jacoco agent adds a large
// wall-clock overhead, which is exactly what tips the SDK's few Thread.sleep based duration
// assertions over their tolerance. So under "coverage" the test tasks are allowed to fail, the
// report is still produced, and the summary reports how many tests failed. A plain
// "./gradlew test" is untouched and still fails the build.
gradle.taskGraph.whenReady { graph ->
if (!graph.allTasks.any { it.path == ':coverage' }) {
return
}
COVERED_MODULES.each { path ->
def module = findProject(path)
if (module != null) {
module.tasks.withType(Test).each { it.ignoreFailures = true }
}
}
}
tasks.register('coverage') {
group = 'verification'
description = 'Runs the tests of every published module and prints a line and branch coverage summary.'
// sdk-java-ui only exists on Java 11+, so resolve against the projects that are actually included.
def reportTasks = COVERED_MODULES
.findAll { findProject(it) != null }
.collect { "${it}:jacocoTestReport" }
dependsOn reportTasks
doLast {
println ''
println 'Coverage (jacoco)'
println '-'.multiply(64)
printf('%-16s %18s %18s%n', 'module', 'lines', 'branches')
COVERED_MODULES.each { path ->
def module = findProject(path)
if (module == null) {
printf('%-16s %18s %18s%n', path - ':', 'not included', '')
return
}
def report = new File(module.layout.buildDirectory.get().asFile, 'reports/jacoco/test/jacocoTestReport.xml')
if (!report.exists()) {
printf('%-16s %18s %18s%n', path - ':', 'no report', '')
return
}
def counters = readCounters(report)
printf('%-16s %18s %18s%n', path - ':', format(counters['LINE']), format(counters['BRANCH']))
}
println '-'.multiply(64)
COVERED_MODULES.findAll { findProject(it) != null }.each { path ->
def buildDir = findProject(path).layout.buildDirectory.get().asFile
println "HTML: ${buildDir}/reports/jacoco/test/html/index.html"
}
int failed = COVERED_MODULES
.findAll { findProject(it) != null }
.sum { countFailedTests(findProject(it)) } ?: 0
if (failed > 0) {
println ''
println "WARNING: ${failed} test(s) failed. The coverage numbers above still cover the whole run,"
println ' but read the test report before trusting them.'
}
println ''
}
}
/**
* Counts failures and errors across a module's JUnit XML results.
*/
ext.countFailedTests = { module ->
def resultsDir = new File(module.layout.buildDirectory.get().asFile, 'test-results/test')
if (!resultsDir.isDirectory()) {
return 0
}
int failed = 0
resultsDir.listFiles({ file -> file.name.endsWith('.xml') } as FileFilter)?.each { file ->
try {
def suite = new XmlParser(false, false).parse(file)
failed += (suite.@failures as int) + (suite.@errors as int)
} catch (Exception ignored) {
// An unreadable result file is not worth failing the summary over.
}
}
return failed
}
/**
* Reads the report level counters out of a jacoco XML report.
*
* The report declares a DOCTYPE pointing at a remote DTD; loading it would make the
* build depend on the network, so external entities are switched off.
*/
ext.readCounters = { File report ->
def parser = new XmlParser(false, false)
parser.setFeature('http://apache.org/xml/features/disallow-doctype-decl', false)
parser.setFeature('http://apache.org/xml/features/nonvalidating/load-external-dtd', false)
def root = parser.parse(report)
def counters = [:]
root.counter.each { counter ->
counters[counter.@type] = [missed: counter.@missed as int, covered: counter.@covered as int]
}
return counters
}
ext.format = { counter ->
if (counter == null) {
return 'n/a'
}
int total = counter.missed + counter.covered
if (total == 0) {
return 'n/a'
}
return String.format('%.1f%% (%d/%d)', counter.covered * 100.0d / total, counter.covered, total)
}