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  *     http://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.Project
025 import org.gradle.api.invocation.Gradle
026 import org.gradle.api.tasks.TaskProvider
027 import org.gradle.api.tasks.testing.Test
028 import org.gradle.api.tasks.testing.TestDescriptor
029 import org.gradle.api.tasks.testing.TestReport
030 import org.gradle.api.tasks.testing.TestResult
031 import org.kordamp.gradle.AnsiConsole
032 import org.kordamp.gradle.plugin.AbstractKordampPlugin
033 import org.kordamp.gradle.plugin.base.BasePlugin
034 import org.kordamp.gradle.plugin.base.ProjectConfigurationExtension
035 import org.kordamp.gradle.plugin.base.plugins.Testing
036 import org.kordamp.gradle.plugin.test.tasks.FunctionalTest
037 import org.kordamp.gradle.plugin.test.tasks.IntegrationTest
038 
039 import static org.kordamp.gradle.PluginUtils.resolveEffectiveConfig
040 import static org.kordamp.gradle.plugin.base.BasePlugin.isRootProject
041 
042 /**
043  *
044  @author Andres Almiray
045  @since 0.14.0
046  */
047 @CompileStatic
048 class TestingPlugin extends AbstractKordampPlugin {
049     private static final boolean WINDOWS = System.getProperty('os.name').startsWith('Windows')
050 
051     Project project
052 
053     void apply(Project project) {
054         this.project = project
055 
056         if (isRootProject(project)) {
057             project.childProjects.values().each {
058                 configureProject(it)
059             }
060         }
061         configureProject(project)
062     }
063 
064     static void applyIfMissing(Project project) {
065         if (!project.plugins.findPlugin(TestingPlugin)) {
066             project.plugins.apply(TestingPlugin)
067         }
068     }
069 
070     private void configureProject(Project project) {
071         if (hasBeenVisited(project)) {
072             return
073         }
074         setVisited(project, true)
075 
076         BasePlugin.applyIfMissing(project)
077 
078         project.afterEvaluate {
079             ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
080             setEnabled(effectiveConfig.testing.enabled)
081 
082             if (!enabled) {
083                 return
084             }
085 
086             project.tasks.withType(Test) { Test testTask ->
087                 if (!testTask.enabled) {
088                     return
089                 }
090 
091                 if (testTask instanceof IntegrationTest) {
092                     configureLogging(testTask, effectiveConfig.testing.integration.logging)
093                     effectiveConfig.testing.integrationTasks() << (IntegrationTesttestTask
094                 else if (testTask instanceof FunctionalTest) {
095                     configureLogging(testTask, effectiveConfig.testing.functional.logging)
096                     effectiveConfig.testing.functionalTestTasks() << (FunctionalTesttestTask
097                 else {
098                     configureLogging(testTask, effectiveConfig.testing.logging)
099                     effectiveConfig.testing.testTasks() << testTask
100                 }
101             }
102         }
103 
104         if (isRootProject(project&& !project.childProjects.isEmpty()) {
105             TaskProvider<TestReport> aggregateTestReportTask = project.tasks.register('aggregateTestReports', TestReport,
106                 new Action<TestReport>() {
107                     @Override
108                     void execute(TestReport t) {
109                         t.enabled = false
110                         t.group = 'Verification'
111                         t.description = 'Aggregate test reports.'
112                         t.destinationDir = project.file("${project.buildDir}/reports/aggregate-tests")
113                     }
114                 })
115 
116             TaskProvider<TestReport> aggregateIntegrationTestReportTask = project.tasks.register('aggregateIntegrationTestReports', TestReport,
117                 new Action<TestReport>() {
118                     @Override
119                     void execute(TestReport t) {
120                         t.enabled = false
121                         t.group = 'Verification'
122                         t.description = 'Aggregate integration test reports.'
123                         t.destinationDir = project.file("${project.buildDir}/reports/aggregate-integration-tests")
124                     }
125                 })
126 
127             TaskProvider<TestReport> aggregateFunctionalTestReportTask = project.tasks.register('aggregateFunctionalTestReports', TestReport,
128                 new Action<TestReport>() {
129                     @Override
130                     void execute(TestReport t) {
131                         t.enabled = false
132                         t.group = 'Verification'
133                         t.description = 'Aggregate functional test reports.'
134                         t.destinationDir = project.file("${project.buildDir}/reports/aggregate-functional-tests")
135                     }
136                 })
137 
138             TaskProvider<TestReport> aggregateAllTestReportTask = project.tasks.register('aggregateAllTestReports', TestReport,
139                 new Action<TestReport>() {
140                     @Override
141                     void execute(TestReport t) {
142                         t.enabled = false
143                         t.group = 'Verification'
144                         t.description = 'Aggregate all test reports.'
145                         t.destinationDir = project.file("${project.buildDir}/reports/aggregate-all-tests")
146                     }
147                 })
148 
149             project.gradle.addBuildListener(new BuildAdapter() {
150                 @Override
151                 void projectsEvaluated(Gradle gradle) {
152                     configureAggregateTestReportTasks(project,
153                         aggregateTestReportTask,
154                         aggregateIntegrationTestReportTask,
155                         aggregateFunctionalTestReportTask,
156                         aggregateAllTestReportTask)
157                 }
158             })
159         }
160     }
161 
162     private void configureLogging(Test testTask, boolean logging) {
163         if (!loggingreturn
164 
165         testTask.afterSuite TestDescriptor descriptor, TestResult result ->
166             AnsiConsole console = new AnsiConsole(project)
167             String indicator = console.green(WINDOWS ? '√' '✔')
168             if (result.failedTestCount > 0) {
169                 indicator = console.red(WINDOWS ? 'X' '✘')
170             }
171 
172             String str = console.erase("${indicator} Test ${descriptor.name}; ")
173             str += "Executed: ${result.testCount}/${console.green(String.valueOf(result.successfulTestCount))}/"
174             str += "${console.red(String.valueOf(result.failedTestCount))}/"
175             str += "${console.yellow(String.valueOf(result.skippedTestCount))} "
176             project.logger.lifecycle(str.toString())
177         }
178     }
179 
180     private void configureAggregateTestReportTasks(Project project,
181                                                    TaskProvider<TestReport> aggregateTestReportTask,
182                                                    TaskProvider<TestReport> aggregateIntegrationTestReportTask,
183                                                    TaskProvider<TestReport> aggregateFunctionalTestReportTask,
184                                                    TaskProvider<TestReport> aggregateAllTestReportTask) {
185         ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
186         if (!effectiveConfig.testing.enabled) {
187             return
188         }
189 
190         Set<Test> tt = new LinkedHashSet<>(effectiveConfig.testing.testTasks())
191         Set<IntegrationTest> itt = new LinkedHashSet<>(effectiveConfig.testing.integrationTasks())
192         Set<FunctionalTest> ftt = new LinkedHashSet<>(effectiveConfig.testing.functionalTestTasks())
193 
194         project.childProjects.values().each {
195             Testing e = resolveEffectiveConfig(it).testing
196             if (e.enabled) {
197                 tt.addAll(e.testTasks())
198                 itt.addAll(e.integrationTasks())
199                 ftt.addAll(e.functionalTestTasks())
200             }
201         }
202 
203         aggregateTestReportTask.configure(new Action<TestReport>() {
204             @Override
205             void execute(TestReport t) {
206                 t.enabled = tt.size() 0
207                 t.reportOn(tt)
208             }
209         })
210 
211         aggregateIntegrationTestReportTask.configure(new Action<TestReport>() {
212             @Override
213             void execute(TestReport t) {
214                 t.enabled = itt.size() 0
215                 t.reportOn(itt)
216             }
217         })
218 
219         aggregateFunctionalTestReportTask.configure(new Action<TestReport>() {
220             @Override
221             void execute(TestReport t) {
222                 t.enabled = ftt.size() 0
223                 t.reportOn(ftt)
224             }
225         })
226 
227         aggregateAllTestReportTask.configure(new Action<TestReport>() {
228             @Override
229             @CompileDynamic
230             void execute(TestReport t) {
231                 t.enabled = tt.size() || itt.size() || ftt.size() 0
232                 t.reportOn(tt + itt + ftt)
233             }
234         })
235     }
236 }