-
Notifications
You must be signed in to change notification settings - Fork 177
/
Copy pathbuild.gradle
131 lines (112 loc) · 4.95 KB
/
build.gradle
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
// !!!!!!!!!!!!!!!!!!!!!!
// DO NOT TOUCH THIS FILE
// !!!!!!!!!!!!!!!!!!!!!!
// This file is an extensible Gradle build file for EPFL's Software Engineering course,
// designed to allow students to easily build and test their code from the command-line,
// and to allow the staff to automatically test code including coverage.
// The main tasks to use are 'build' (includes tests + coverage) and 'run'.
import org.gradle.api.JavaVersion
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import org.gradle.api.tasks.testing.TestResult
import org.gradle.api.tasks.testing.TestResult.ResultType
import java.nio.file.Files
plugins {
id("java")
id("application")
id("jacoco")
}
if (file("build-extra.gradle").exists()) {
apply from: "build-extra.gradle"
}
repositories {
mavenCentral()
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.3")
testImplementation("org.hamcrest:hamcrest:2.2")
}
def javaVersionTarget = "17"
if (JavaVersion.current().toString() != javaVersionTarget) {
println "!!!\n!!!"
println "WARNING: This project was designed for Java ${javaVersionTarget} but you are instead using Java ${JavaVersion.current()}."
println " If you get this message while configuring an IDE such as IntelliJ or Android Studio, feel free to ignore it as long as it does not appear when running the app or tests."
println " Otherwise, please use Java ${javaVersionTarget}, or continue using your version at your own risk."
println "!!!\n!!!"
}
application {
mainClass = "App"
}
run {
systemProperty("file.encoding", "UTF-8")
// Allow users to input stuff during the 'run' task, necessary for System.in to work
standardInput = System.in
}
// We maintain our own list of test results, mapping full name to (display name, result)
// Otherwise, we cannot get the full name of tests with e.g. a @DisplayName, or using @RepeatedTest
// We then print them all at once into a CSV file, for use by scripts
// (We cannot print one by one because of the possible presence of repeated tests; for those, any failure => overall failure)
var currentSuite = new HashMap<String, Tuple2<String, TestResult>>()
test {
useJUnitPlatform()
systemProperty("file.encoding", "UTF-8")
testLogging {
// Always show all tests, even passed ones; also, show stdout/stderr, helps for debugging
events(TestLogEvent.FAILED, TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.STANDARD_OUT, TestLogEvent.STANDARD_ERROR)
// Always run all tests
outputs.upToDateWhen { false }
// Show detailed information about failures
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
afterTest { test, res ->
if (res.resultType == ResultType.SKIPPED) {
return
}
var fullName = "${test.className}.${test.name.split('\\(')[0]}"
if (test.name.contains('[') && currentSuite.containsKey(fullName) && res.resultType == ResultType.SUCCESS) {
// [ in a test name is an indicator of a repeated test
return
}
currentSuite.put(fullName, new Tuple2(test.displayName, res))
}
afterSuite { desc, result ->
if (desc.parent != null) {
// We only care about the root suite
return
}
var file = new File(buildDir, "tests.csv")
file.write("FullName,DisplayName,Result,ErrorMessage" + System.lineSeparator())
currentSuite.each { name, props ->
file.append("${name},\"${props.first.replace('"', '""')}\",${props.second.resultType},\"${props.second.exception?.message?.replace('"', '""') ?: ""}\"" + System.lineSeparator())
}
}
}
jacocoTestReport {
// XML report, for scripts
reports.xml.required = true
// HTML report, for humans
reports.html.destination = file("${buildDir}/reports/coverage")
}
build {
// Always generate coverage
dependsOn(jacocoTestReport)
doLast {
def msg = """Notes:
<ul>
<li>JaCoCo calls statements 'instructions'</li>
<li>JaCoCo does not compute path coverage, because it is usually not helpful in real code</li>
<li>The column 'Cxty' is the <a target="_blank" href="https://en.wikipedia.org/wiki/Cyclomatic_complexity">cyclomatic complexity</a></li>
</ul>"""
fileTree("${buildDir}/reports/coverage").include("**/*.html").each { f ->
Files.writeString(f.toPath(), Files.readString(f.toPath()).replace("</h1>", "</h1><p>${msg}</p>"))
}
}
doLast {
println("If you added tests, you can view the code coverage report in ${buildDir}/reports/coverage/index.html")
}
}