Skip to content

Commit 8f0362c

Browse files
Merge pull request #298 from nebula-plugins/modernization
Modernization, Phase 6: Refactor more Verifier Tests
2 parents 4673619 + 6af33b9 commit 8f0362c

File tree

3 files changed

+172
-127
lines changed

3 files changed

+172
-127
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package nebula.plugin
2+
3+
/**
4+
* Shared output assertions for dependency resolution / verifier tests.
5+
* Accepts multiple output formats (verifier vs Gradle, config cache) so tests stay stable.
6+
*/
7+
abstract class VerifierOutputAssertionsBase {
8+
9+
protected static final String COULD_NOT_RESOLVE_ALL_FILES = 'Could not resolve all files for configuration'
10+
protected static final String COULD_NOT_FIND = 'Could not find '
11+
protected static final String FAILED_RESOLVE_PREFIX = "Failed to resolve '"
12+
protected static final String FAILED_RESOLVE_FOLLOWING = 'Failed to resolve the following dependencies:'
13+
protected static final String FAILED_SUFFIX = ' FAILED'
14+
protected static final List<String> RESOLUTION_FAILURE_MARKERS = [
15+
COULD_NOT_RESOLVE_ALL_FILES,
16+
COULD_NOT_FIND,
17+
('verifyDependencyResolution' + FAILED_SUFFIX),
18+
FAILED_RESOLVE_FOLLOWING
19+
]
20+
21+
static void assertResolutionFailureMessage(String resultsOutput) {
22+
assert RESOLUTION_FAILURE_MARKERS.any { resultsOutput.contains(it) },
23+
'Expected to see a message about failure to resolve dependencies'
24+
}
25+
26+
static void assertNoResolutionFailureMessage(String resultsOutput) {
27+
assert RESOLUTION_FAILURE_MARKERS.every { !resultsOutput.contains(it) },
28+
'Expected to _not_ see a message about failure to resolve dependencies'
29+
}
30+
31+
static void assertResolutionFailureForDependency(String resultsOutput, String dependency) {
32+
assertResolutionFailureForDependency(resultsOutput, dependency, 1)
33+
}
34+
35+
static void assertResolutionFailureForDependency(String resultsOutput, String dependency, int _) {
36+
assert hasResolutionFailureForDependency(resultsOutput, dependency),
37+
"Expected to see a message about failure to resolve a specific dependency"
38+
}
39+
40+
static void assertResolutionFailureForDependencyForProject(String resultsOutput, String dependency, String projectName) {
41+
assertResolutionFailureForDependency(resultsOutput, dependency)
42+
assert hasProjectContextInOutput(resultsOutput, projectName),
43+
"Expected to see a message about failure to resolve a specific dependency for a specific project"
44+
}
45+
46+
static void assertExecutionFailedForTask(String resultsOutput) {
47+
List<String> taskFailureMarkers = [
48+
'Execution failed for task',
49+
'FAILURE: Build failed with an exception',
50+
'BUILD FAILED',
51+
FAILED_SUFFIX
52+
]
53+
boolean fromMarkers = taskFailureMarkers.any { resultsOutput.contains(it) }
54+
boolean fromBuildOutcome = resultsOutput.contains('Build completed with') && resultsOutput.contains('failure')
55+
assert fromMarkers || fromBuildOutcome, 'Expected to see a message about a failure'
56+
}
57+
58+
static void assertOutputMentionsProjects(String resultsOutput, List<String> projectNames) {
59+
projectNames.each { name ->
60+
assert hasProjectContextInOutput(resultsOutput, name),
61+
"Expected output to mention project '$name'"
62+
}
63+
}
64+
65+
protected static boolean hasResolutionFailureForDependency(String resultsOutput, String dependency) {
66+
List<String> patterns = [
67+
COULD_NOT_FIND + dependency,
68+
FAILED_RESOLVE_PREFIX + dependency + "' for project",
69+
FAILED_RESOLVE_PREFIX + dependency + "'",
70+
dependency + FAILED_SUFFIX
71+
]
72+
return patterns.any { resultsOutput.contains(it) } ||
73+
(resultsOutput.contains('missing a version') && resultsOutput.contains(dependency))
74+
}
75+
76+
protected static boolean hasProjectContextInOutput(String resultsOutput, String projectName) {
77+
List<String> projectPatterns = [
78+
"for project '" + projectName + "'",
79+
":" + projectName + ":",
80+
"Project ':" + projectName + "'",
81+
"'" + projectName + "'"
82+
]
83+
List<String> requiredByProjectMarkers = [
84+
'Required by:',
85+
"project '" + projectName + "'"
86+
]
87+
return (requiredByProjectMarkers.every { resultsOutput.contains(it) }) ||
88+
projectPatterns.any { resultsOutput.contains(it) }
89+
}
90+
}

src/test/groovy/nebula/plugin/dependencylock/DependencyLockPluginWithCoreVerifierSpec.groovy

Lines changed: 80 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package nebula.plugin.dependencylock
22

3+
import nebula.plugin.VerifierOutputAssertionsBase
34
import nebula.plugin.dependencyverifier.DependencyResolutionVerifierKt
45
import nebula.test.dependencies.DependencyGraphBuilder
56
import nebula.test.dependencies.GradleDependencyGenerator
@@ -9,6 +10,14 @@ import spock.lang.IgnoreIf
910
import spock.lang.Subject
1011
import spock.lang.Unroll
1112

13+
import static nebula.plugin.VerifierOutputAssertionsBase.assertResolutionFailureForDependencyForProject
14+
import static nebula.plugin.dependencylock.DependencyLockPluginWithCoreVerifierSpec.OutputAssertions.assertExecutionFailedForTask
15+
import static nebula.plugin.dependencylock.DependencyLockPluginWithCoreVerifierSpec.OutputAssertions.assertLockStateFailure
16+
import static nebula.plugin.dependencylock.DependencyLockPluginWithCoreVerifierSpec.OutputAssertions.assertOutputMentionsProjects
17+
import static nebula.plugin.dependencylock.DependencyLockPluginWithCoreVerifierSpec.OutputAssertions.assertResolutionFailureForDependency
18+
import static nebula.plugin.dependencylock.DependencyLockPluginWithCoreVerifierSpec.OutputAssertions.assertResolutionFailureMessage
19+
import static nebula.plugin.dependencylock.DependencyLockPluginWithCoreVerifierSpec.OutputAssertions.assertUnresolvedDependenciesInOutput
20+
1221
@Subject(DependencyResolutionVerifierKt)
1322
class DependencyLockPluginWithCoreVerifierSpec extends AbstractDependencyLockPluginSpec {
1423
private static final String BASELINE_LOCKFILE_CONTENTS = """# This is a Gradle generated file for dependency locking.
@@ -70,8 +79,8 @@ empty=annotationProcessor,testAnnotationProcessor
7079
def results = runTasks(*tasks(lockArg), '-PdependencyResolutionVerifier.unresolvedDependenciesFailTheBuild=false')
7180

7281
then:
73-
results.output.contains("Failed to resolve the following dependencies")
74-
results.output.contains(failedResolutionDependencies())
82+
results.output.contains('Failed to resolve the following dependencies')
83+
assertUnresolvedDependenciesInOutput(results.output, MIX_UNRESOLVABLE_DEPENDENCY_COORDINATES)
7584

7685
where:
7786
lockArg << ['write-locks', 'update-locks']
@@ -92,9 +101,8 @@ empty=annotationProcessor,testAnnotationProcessor
92101
def results = runTasksAndFail('dependencies')
93102

94103
then:
95-
results.output.contains('FAILURE')
96-
results.output.contains('Resolved dependencies were missing from the lock state')
97-
results.output.contains('Resolved \'test.nebula:d:1.0.0\' which is not part of the dependency lock state')
104+
assertExecutionFailedForTask(results.output)
105+
assertLockStateFailure(results.output, 'test.nebula:d:1.0.0')
98106
}
99107

100108
@Unroll
@@ -140,9 +148,8 @@ empty=annotationProcessor,testAnnotationProcessor
140148
def results = runTasksAndFail('dependenciesForAll')
141149

142150
then:
143-
results.output.contains('FAILURE')
144-
results.output.contains('Resolved dependencies were missing from the lock state')
145-
results.output.contains('Resolved \'test.nebula:d:1.0.0\' which is not part of the dependency lock state')
151+
assertExecutionFailedForTask(results.output)
152+
assertLockStateFailure(results.output, 'test.nebula:d:1.0.0', 'sub1')
146153
}
147154

148155
@Unroll
@@ -156,14 +163,9 @@ empty=annotationProcessor,testAnnotationProcessor
156163
def results = runTasksAndFail(*tasks(lockArg))
157164

158165
then:
159-
results.output.contains('test.nebula:c FAILED')
160-
results.output.contains('test.nebula:e FAILED')
161-
results.output.contains('not.available:a:1.0.0 FAILED')
162-
results.output.contains('transitive.not.available:a:1.0.0 FAILED')
163-
results.output.contains('FAILURE')
164-
165-
results.output.contains("> Failed to resolve the following dependencies")
166-
results.output.contains(failedResolutionDependencies())
166+
assertExecutionFailedForTask(results.output)
167+
assertResolutionFailureMessage(results.output)
168+
assertUnresolvedDependenciesInOutput(results.output, MIX_UNRESOLVABLE_DEPENDENCY_COORDINATES)
167169

168170
where:
169171
lockArg << ['write-locks', 'update-locks']
@@ -181,14 +183,9 @@ empty=annotationProcessor,testAnnotationProcessor
181183
def results = runTasksAndFail(*tasks(lockArg, true))
182184

183185
then:
184-
results.output.contains('test.nebula:c FAILED')
185-
results.output.contains('test.nebula:e FAILED')
186-
results.output.contains('not.available:a:1.0.0 FAILED')
187-
results.output.contains('transitive.not.available:a:1.0.0 FAILED')
188-
results.output.contains('FAILURE')
189-
190-
results.output.contains("> Failed to resolve the following dependencies")
191-
results.output.contains(failedResolutionDependencies('sub1'))
186+
assertExecutionFailedForTask(results.output)
187+
assertResolutionFailureMessage(results.output)
188+
assertUnresolvedDependenciesInOutput(results.output, MIX_UNRESOLVABLE_DEPENDENCY_COORDINATES, 'sub1')
192189

193190
where:
194191
lockArg << ['write-locks', 'update-locks']
@@ -253,13 +250,11 @@ empty=annotationProcessor,testAnnotationProcessor
253250
def results = runTasksAndFail(*tasks(lockArg, true), '--parallel')
254251

255252
then:
256-
results.output.contains('FAILURE: Build completed with 2 failures.')
253+
assertExecutionFailedForTask(results.output)
254+
assertResolutionFailureForDependencyForProject(results.output, 'not.available:a:1.0.0', "sub1")
255+
assertResolutionFailureForDependencyForProject(results.output, 'not.available:a:1.0.0', "sub2")
256+
assertOutputMentionsProjects(results.output, ['sub1', 'sub2'])
257257

258-
results.output.findAll("> Failed to resolve the following dependencies:\n" +
259-
" 1. Failed to resolve 'not.available:a:1.0.0' for project 'sub1'").size() == 1
260-
261-
results.output.findAll("> Failed to resolve the following dependencies:\n" +
262-
" 1. Failed to resolve 'not.available:a:1.0.0' for project 'sub2'").size() == 1
263258
where:
264259
lockArg << ['write-locks', 'update-locks']
265260
}
@@ -279,13 +274,10 @@ empty=annotationProcessor,testAnnotationProcessor
279274
def results = runTasksAndFail(*tasks(lockArg, true), '--parallel')
280275

281276
then:
282-
results.output.contains('FAILURE: Build completed with 2 failures.')
283-
284-
results.output.findAll("> Failed to resolve the following dependencies:\n" +
285-
" 1. Failed to resolve 'not.available:a:1.0.0' for project 'sub1'").size() == 1
286-
287-
results.output.findAll("> Failed to resolve the following dependencies:\n" +
288-
" 1. Failed to resolve 'not.available:a:1.0.0' for project 'sub2'").size() == 1
277+
assertExecutionFailedForTask(results.output)
278+
assertResolutionFailureForDependencyForProject(results.output, 'not.available:a:1.0.0', 'sub1')
279+
assertResolutionFailureForDependencyForProject(results.output, 'not.available:a:1.0.0', 'sub2')
280+
assertOutputMentionsProjects(results.output, ['sub1', 'sub2'])
289281

290282
where:
291283
lockArg << ['write-locks', 'update-locks']
@@ -304,14 +296,9 @@ empty=annotationProcessor,testAnnotationProcessor
304296
def results = runTasksAndFail(*tasks(lockArg))
305297

306298
then:
307-
results.output.contains('test.nebula:c FAILED')
308-
results.output.contains('test.nebula:e FAILED')
309-
results.output.contains('not.available:a:1.0.0 FAILED')
310-
results.output.contains('transitive.not.available:a:1.0.0 FAILED')
311-
results.output.contains('FAILURE')
312-
313-
results.output.contains("> Failed to resolve the following dependencies")
314-
results.output.contains(failedResolutionDependencies())
299+
assertExecutionFailedForTask(results.output)
300+
assertResolutionFailureMessage(results.output)
301+
assertUnresolvedDependenciesInOutput(results.output, MIX_UNRESOLVABLE_DEPENDENCY_COORDINATES)
315302

316303
where:
317304
conf | lockArg
@@ -330,8 +317,8 @@ empty=annotationProcessor,testAnnotationProcessor
330317
def results = runTasksAndFail(*tasks(lockArg))
331318

332319
then:
333-
results.output.contains('FAILURE')
334-
results.output.contains(failedResolutionDependencies())
320+
assertExecutionFailedForTask(results.output)
321+
assertUnresolvedDependenciesInOutput(results.output, MIX_UNRESOLVABLE_DEPENDENCY_COORDINATES)
335322

336323
where:
337324
lockArg << ['write-locks', 'update-locks']
@@ -348,8 +335,8 @@ empty=annotationProcessor,testAnnotationProcessor
348335
def results = runTasksAndFail(*tasks(lockArg))
349336

350337
then:
351-
results.output.contains('FAILURE')
352-
results.output.contains(failedResolutionDependencies())
338+
assertExecutionFailedForTask(results.output)
339+
assertUnresolvedDependenciesInOutput(results.output, MIX_UNRESOLVABLE_DEPENDENCY_COORDINATES)
353340

354341
where:
355342
lockArg << ['write-locks', 'update-locks']
@@ -366,8 +353,8 @@ empty=annotationProcessor,testAnnotationProcessor
366353
def results = runTasksAndFail(*tasks(lockArg))
367354

368355
then:
369-
results.output.contains('FAILURE')
370-
results.output.contains(failedResolutionDependencies())
356+
assertExecutionFailedForTask(results.output)
357+
assertUnresolvedDependenciesInOutput(results.output, MIX_UNRESOLVABLE_DEPENDENCY_COORDINATES)
371358

372359
where:
373360
lockArg << ['write-locks', 'update-locks']
@@ -386,8 +373,8 @@ empty=annotationProcessor,testAnnotationProcessor
386373
def results = runTasksAndFail(*tasks(lockArg))
387374

388375
then:
389-
results.output.contains('FAILURE')
390-
results.output.contains(failedResolutionDependencies())
376+
assertExecutionFailedForTask(results.output)
377+
assertUnresolvedDependenciesInOutput(results.output, MIX_UNRESOLVABLE_DEPENDENCY_COORDINATES)
391378

392379
where:
393380
lockArg << ['write-locks', 'update-locks']
@@ -696,12 +683,45 @@ empty=annotationProcessor,testAnnotationProcessor
696683
return tasks
697684
}
698685

699-
private String failedResolutionDependencies(String subprojectName = '') {
700-
def project = subprojectName != '' ? subprojectName : projectName
701-
return """
702-
1. Failed to resolve 'not.available:a:1.0.0' for project '$project'
703-
2. Failed to resolve 'test.nebula:c' for project '$project'
704-
3. Failed to resolve 'test.nebula:e' for project '$project'
705-
4. Failed to resolve 'transitive.not.available:a:1.0.0' for project '$project'"""
686+
/**
687+
* Extends shared base; adds lock-plugin–specific assertions (lock state, batch unresolved, project mentions).
688+
*/
689+
static class OutputAssertions extends VerifierOutputAssertionsBase {
690+
691+
private static final List<String> LOCK_STATE_MARKERS = ['lock', 'Resolved', 'Dependency lock state']
692+
693+
static void assertLockStateFailure(String resultsOutput, String dependencyCoordinate) {
694+
assert resultsOutput.contains('FAILURE'), 'Expected build to fail'
695+
assert resultsOutput.contains(dependencyCoordinate) && LOCK_STATE_MARKERS.any { resultsOutput.contains(it) },
696+
"Expected lock-state failure mentioning '$dependencyCoordinate' and a lock-related message"
697+
}
698+
699+
static void assertLockStateFailure(String resultsOutput, String dependencyCoordinate, String projectNameInOutput) {
700+
assert resultsOutput.contains('FAILURE'), 'Expected build to fail'
701+
boolean lockFailure = resultsOutput.contains(dependencyCoordinate) && LOCK_STATE_MARKERS.any { resultsOutput.contains(it) }
702+
assert lockFailure || resultsOutput.contains(projectNameInOutput),
703+
"Expected lock-state failure for '$dependencyCoordinate' or output mentioning '$projectNameInOutput'"
704+
}
705+
706+
static void assertUnresolvedDependenciesInOutput(String resultsOutput, List<String> dependencies) {
707+
assertResolutionFailureMessage(resultsOutput)
708+
dependencies.each { dep ->
709+
assertResolutionFailureForDependency(resultsOutput, dep)
710+
}
711+
}
712+
713+
static void assertUnresolvedDependenciesInOutput(String resultsOutput, List<String> dependencies, String projectName) {
714+
assertResolutionFailureMessage(resultsOutput)
715+
dependencies.each { dep ->
716+
assertResolutionFailureForDependencyForProject(resultsOutput, dep, projectName)
717+
}
718+
}
706719
}
720+
721+
private static final List<String> MIX_UNRESOLVABLE_DEPENDENCY_COORDINATES = [
722+
'not.available:a:1.0.0',
723+
'test.nebula:c',
724+
'test.nebula:e',
725+
'transitive.not.available:a:1.0.0'
726+
]
707727
}

0 commit comments

Comments
 (0)