TestingPlugin.groovy
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2018-2019 Andres Almiray.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     https://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package org.kordamp.gradle.plugin.testing
019 
020 import groovy.transform.CompileDynamic
021 import groovy.transform.CompileStatic
022 import org.gradle.BuildAdapter
023 import org.gradle.api.Action
024 import org.gradle.api.DefaultTask
025 import org.gradle.api.Project
026 import org.gradle.api.invocation.Gradle
027 import org.gradle.api.tasks.TaskProvider
028 import org.gradle.api.tasks.testing.Test
029 import org.gradle.api.tasks.testing.TestDescriptor
030 import org.gradle.api.tasks.testing.TestReport
031 import org.gradle.api.tasks.testing.TestResult
032 import org.kordamp.gradle.AnsiConsole
033 import org.kordamp.gradle.plugin.AbstractKordampPlugin
034 import org.kordamp.gradle.plugin.base.BasePlugin
035 import org.kordamp.gradle.plugin.base.ProjectConfigurationExtension
036 import org.kordamp.gradle.plugin.base.plugins.Testing
037 import org.kordamp.gradle.plugin.test.tasks.FunctionalTest
038 import org.kordamp.gradle.plugin.test.tasks.IntegrationTest
039 
040 import static org.kordamp.gradle.PluginUtils.resolveEffectiveConfig
041 import static org.kordamp.gradle.plugin.base.BasePlugin.isRootProject
042 
043 /**
044  *
045  @author Andres Almiray
046  @since 0.14.0
047  */
048 @CompileStatic
049 class TestingPlugin extends AbstractKordampPlugin {
050     private static final boolean WINDOWS = System.getProperty('os.name').startsWith('Windows')
051 
052     Project project
053 
054     void apply(Project project) {
055         this.project = project
056 
057         if (isRootProject(project)) {
058             project.childProjects.values().each {
059                 configureProject(it)
060             }
061         }
062         configureProject(project)
063     }
064 
065     static void applyIfMissing(Project project) {
066         if (!project.plugins.findPlugin(TestingPlugin)) {
067             project.pluginManager.apply(TestingPlugin)
068         }
069     }
070 
071     private void configureProject(Project project) {
072         if (hasBeenVisited(project)) {
073             return
074         }
075         setVisited(project, true)
076 
077         BasePlugin.applyIfMissing(project)
078 
079         TaskProvider<DefaultTask> allTestsTask = null
080         if (project.childProjects.isEmpty()) {
081             allTestsTask = project.tasks.register('allTests', DefaultTask,
082                 new Action<DefaultTask>() {
083                     @Override
084                     void execute(DefaultTask t) {
085                         t.enabled = false
086                         t.group = 'Verification'
087                         t.description = 'Executes all tests.'
088                     }
089                 })
090         }
091 
092         project.afterEvaluate {
093             ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
094             setEnabled(effectiveConfig.testing.enabled)
095 
096             if (!enabled) {
097                 return
098             }
099 
100             project.tasks.withType(Test) { Test testTask ->
101                 if (!testTask.enabled) {
102                     return
103                 }
104 
105                 if (testTask instanceof IntegrationTest) {
106                     configureLogging(testTask, effectiveConfig.testing.integration.logging)
107                     effectiveConfig.testing.integrationTasks() << (IntegrationTesttestTask
108                 else if (testTask instanceof FunctionalTest) {
109                     configureLogging(testTask, effectiveConfig.testing.functional.logging)
110                     effectiveConfig.testing.functionalTestTasks() << (FunctionalTesttestTask
111                 else {
112                     configureLogging(testTask, effectiveConfig.testing.logging)
113                     effectiveConfig.testing.testTasks() << testTask
114                 }
115             }
116 
117             if (allTestsTask) {
118                 configureAllTestsTasks(project, allTestsTask)
119             }
120         }
121 
122         if (isRootProject(project&& !project.childProjects.isEmpty()) {
123             TaskProvider<TestReport> aggregateTestReportTask = project.tasks.register('aggregateTestReports', TestReport,
124                 new Action<TestReport>() {
125                     @Override
126                     void execute(TestReport t) {
127                         t.enabled = false
128                         t.group = 'Verification'
129                         t.description = 'Aggregate test reports.'
130                         t.destinationDir = project.file("${project.buildDir}/reports/aggregate-tests")
131                     }
132                 })
133 
134             TaskProvider<TestReport> aggregateIntegrationTestReportTask = project.tasks.register('aggregateIntegrationTestReports', TestReport,
135                 new Action<TestReport>() {
136                     @Override
137                     void execute(TestReport t) {
138                         t.enabled = false
139                         t.group = 'Verification'
140                         t.description = 'Aggregate integration test reports.'
141                         t.destinationDir = project.file("${project.buildDir}/reports/aggregate-integration-tests")
142                     }
143                 })
144 
145             TaskProvider<TestReport> aggregateFunctionalTestReportTask = project.tasks.register('aggregateFunctionalTestReports', TestReport,
146                 new Action<TestReport>() {
147                     @Override
148                     void execute(TestReport t) {
149                         t.enabled = false
150                         t.group = 'Verification'
151                         t.description = 'Aggregate functional test reports.'
152                         t.destinationDir = project.file("${project.buildDir}/reports/aggregate-functional-tests")
153                     }
154                 })
155 
156             TaskProvider<TestReport> aggregateAllTestReportTask = project.tasks.register('aggregateAllTestReports', TestReport,
157                 new Action<TestReport>() {
158                     @Override
159                     void execute(TestReport t) {
160                         t.enabled = false
161                         t.group = 'Verification'
162                         t.description = 'Aggregate all test reports.'
163                         t.destinationDir = project.file("${project.buildDir}/reports/aggregate-all-tests")
164                     }
165                 })
166 
167             project.gradle.addBuildListener(new BuildAdapter() {
168                 @Override
169                 void projectsEvaluated(Gradle gradle) {
170                     configureAggregateTestReportTasks(project,
171                         aggregateTestReportTask,
172                         aggregateIntegrationTestReportTask,
173                         aggregateFunctionalTestReportTask,
174                         aggregateAllTestReportTask)
175                 }
176             })
177         }
178     }
179 
180     private void configureLogging(Test testTask, boolean logging) {
181         if (!loggingreturn
182 
183         testTask.afterSuite TestDescriptor descriptor, TestResult result ->
184             if (descriptor.name.contains('Gradle Test Executor')) return
185 
186             AnsiConsole console = new AnsiConsole(project)
187             String indicator = console.green(WINDOWS ? '√' '✔')
188             if (result.failedTestCount > 0) {
189                 indicator = console.red(WINDOWS ? 'X' '✘')
190             }
191 
192             String str = console.erase("${indicator} Test ${descriptor.name} ")
193             str += "Executed: ${result.testCount}/${console.green(String.valueOf(result.successfulTestCount))}/"
194             str += "${console.red(String.valueOf(result.failedTestCount))}/"
195             str += "${console.yellow(String.valueOf(result.skippedTestCount))} "
196             project.logger.lifecycle(str.toString())
197         }
198     }
199 
200     private void configureAllTestsTasks(Project project,
201                                         TaskProvider<DefaultTask> allTestsTask) {
202         ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
203         if (!effectiveConfig.testing.enabled) {
204             return
205         }
206 
207         Set<Test> tt = new LinkedHashSet<>(effectiveConfig.testing.testTasks())
208         tt.addAll(effectiveConfig.testing.integrationTasks())
209         tt.addAll(effectiveConfig.testing.functionalTestTasks())
210 
211         allTestsTask.configure(new Action<DefaultTask>() {
212             @Override
213             void execute(DefaultTask t) {
214                 t.enabled = tt.size() 0
215                 t.dependsOn(tt)
216             }
217         })
218     }
219 
220     private void configureAggregateTestReportTasks(Project project,
221                                                    TaskProvider<TestReport> aggregateTestReportTask,
222                                                    TaskProvider<TestReport> aggregateIntegrationTestReportTask,
223                                                    TaskProvider<TestReport> aggregateFunctionalTestReportTask,
224                                                    TaskProvider<TestReport> aggregateAllTestReportTask) {
225         ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
226         if (!effectiveConfig.testing.enabled) {
227             return
228         }
229 
230         Set<Test> tt = new LinkedHashSet<>(effectiveConfig.testing.testTasks())
231         Set<IntegrationTest> itt = new LinkedHashSet<>(effectiveConfig.testing.integrationTasks())
232         Set<FunctionalTest> ftt = new LinkedHashSet<>(effectiveConfig.testing.functionalTestTasks())
233 
234         project.childProjects.values().each {
235             Testing e = resolveEffectiveConfig(it).testing
236             if (e.enabled) {
237                 tt.addAll(e.testTasks())
238                 itt.addAll(e.integrationTasks())
239                 ftt.addAll(e.functionalTestTasks())
240             }
241         }
242 
243         aggregateTestReportTask.configure(new Action<TestReport>() {
244             @Override
245             void execute(TestReport t) {
246                 t.enabled = tt.size() 0
247                 t.reportOn(tt)
248             }
249         })
250 
251         aggregateIntegrationTestReportTask.configure(new Action<TestReport>() {
252             @Override
253             void execute(TestReport t) {
254                 t.enabled = itt.size() 0
255                 t.reportOn(itt)
256             }
257         })
258 
259         aggregateFunctionalTestReportTask.configure(new Action<TestReport>() {
260             @Override
261             void execute(TestReport t) {
262                 t.enabled = ftt.size() 0
263                 t.reportOn(ftt)
264             }
265         })
266 
267         aggregateAllTestReportTask.configure(new Action<TestReport>() {
268             @Override
269             @CompileDynamic
270             void execute(TestReport t) {
271                 t.enabled = tt.size() || itt.size() || ftt.size() 0
272                 t.reportOn(tt + itt + ftt)
273             }
274         })
275     }
276 }