10
10
11
11
package org .junit .jupiter .engine .discovery ;
12
12
13
+ import static java .util .Comparator .comparing ;
13
14
import static java .util .stream .Collectors .toCollection ;
15
+ import static java .util .stream .Collectors .toList ;
14
16
15
17
import java .util .ArrayList ;
16
- import java .util .LinkedHashSet ;
18
+ import java .util .HashMap ;
17
19
import java .util .List ;
18
- import java .util .Set ;
20
+ import java .util .Map ;
19
21
import java .util .function .Consumer ;
20
22
import java .util .function .Function ;
21
- import java .util .stream .Collectors ;
22
23
import java .util .stream .Stream ;
23
24
24
25
import org .junit .jupiter .engine .descriptor .ClassBasedTestDescriptor ;
@@ -60,9 +61,8 @@ protected void doWithMatchingDescriptor(Class<PARENT> parentTestDescriptorType,
60
61
protected void orderChildrenTestDescriptors (TestDescriptor parentTestDescriptor , Class <CHILD > matchingChildrenType ,
61
62
Function <CHILD , WRAPPER > descriptorWrapperFactory , DescriptorWrapperOrderer descriptorWrapperOrderer ) {
62
63
63
- Set <? extends TestDescriptor > children = parentTestDescriptor .getChildren ();
64
-
65
- List <WRAPPER > matchingDescriptorWrappers = children .stream ()//
64
+ List <WRAPPER > matchingDescriptorWrappers = parentTestDescriptor .getChildren ()//
65
+ .stream ()//
66
66
.filter (matchingChildrenType ::isInstance )//
67
67
.map (matchingChildrenType ::cast )//
68
68
.map (descriptorWrapperFactory )//
@@ -74,50 +74,33 @@ protected void orderChildrenTestDescriptors(TestDescriptor parentTestDescriptor,
74
74
}
75
75
76
76
if (descriptorWrapperOrderer .canOrderWrappers ()) {
77
- List <TestDescriptor > nonMatchingTestDescriptors = children .stream ()//
78
- .filter (childTestDescriptor -> !matchingChildrenType .isInstance (childTestDescriptor ))//
79
- .collect (Collectors .toList ());
80
-
81
- // Make a local copy for later validation
82
- Set <WRAPPER > originalWrappers = new LinkedHashSet <>(matchingDescriptorWrappers );
83
-
84
- descriptorWrapperOrderer .orderWrappers (matchingDescriptorWrappers );
85
-
86
- int difference = matchingDescriptorWrappers .size () - originalWrappers .size ();
87
- if (difference > 0 ) {
88
- descriptorWrapperOrderer .logDescriptorsAddedWarning (difference );
89
- }
90
- else if (difference < 0 ) {
91
- descriptorWrapperOrderer .logDescriptorsRemovedWarning (difference );
92
- }
93
-
94
- Set <TestDescriptor > orderedTestDescriptors = matchingDescriptorWrappers .stream ()//
95
- .filter (originalWrappers ::contains )//
96
- .map (AbstractAnnotatedDescriptorWrapper ::getTestDescriptor )//
97
- .collect (toCollection (LinkedHashSet ::new ));
98
-
99
- // There is currently no way to removeAll or addAll children at once, so we
100
- // first remove them all and then add them all back.
101
- Stream .concat (orderedTestDescriptors .stream (), nonMatchingTestDescriptors .stream ())//
102
- .forEach (parentTestDescriptor ::removeChild );
103
-
104
- // If we are ordering children of type ClassBasedTestDescriptor, that means we
105
- // are ordering top-level classes or @Nested test classes. Thus, the
106
- // nonMatchingTestDescriptors list is either empty (for top-level classes) or
107
- // contains only local test methods (for @Nested classes) which must be executed
108
- // before tests in @Nested test classes. So we add the test methods before adding
109
- // the @Nested test classes.
110
- if (matchingChildrenType == ClassBasedTestDescriptor .class ) {
111
- Stream .concat (nonMatchingTestDescriptors .stream (), orderedTestDescriptors .stream ())//
112
- .forEach (parentTestDescriptor ::addChild );
113
- }
114
- // Otherwise, we add the ordered descriptors before the non-matching descriptors,
115
- // which is the case when we are ordering test methods. In other words, local
116
- // test methods always get added before @Nested test classes.
117
- else {
118
- Stream .concat (orderedTestDescriptors .stream (), nonMatchingTestDescriptors .stream ())//
119
- .forEach (parentTestDescriptor ::addChild );
120
- }
77
+ parentTestDescriptor .orderChildren (children -> {
78
+ Stream <TestDescriptor > nonMatchingTestDescriptors = children .stream ()//
79
+ .filter (childTestDescriptor -> !matchingChildrenType .isInstance (childTestDescriptor ));
80
+
81
+ descriptorWrapperOrderer .orderWrappers (matchingDescriptorWrappers );
82
+
83
+ Stream <TestDescriptor > orderedTestDescriptors = matchingDescriptorWrappers .stream ()//
84
+ .map (AbstractAnnotatedDescriptorWrapper ::getTestDescriptor );
85
+
86
+ // If we are ordering children of type ClassBasedTestDescriptor, that means we
87
+ // are ordering top-level classes or @Nested test classes. Thus, the
88
+ // nonMatchingTestDescriptors list is either empty (for top-level classes) or
89
+ // contains only local test methods (for @Nested classes) which must be executed
90
+ // before tests in @Nested test classes. So we add the test methods before adding
91
+ // the @Nested test classes.
92
+ if (matchingChildrenType == ClassBasedTestDescriptor .class ) {
93
+ return Stream .concat (nonMatchingTestDescriptors , orderedTestDescriptors )//
94
+ .collect (toList ());
95
+ }
96
+ // Otherwise, we add the ordered descriptors before the non-matching descriptors,
97
+ // which is the case when we are ordering test methods. In other words, local
98
+ // test methods always get added before @Nested test classes.
99
+ else {
100
+ return Stream .concat (orderedTestDescriptors , nonMatchingTestDescriptors )//
101
+ .collect (toList ());
102
+ }
103
+ });
121
104
}
122
105
123
106
// Recurse through the children in order to support ordering for @Nested test classes.
@@ -167,7 +150,32 @@ private boolean canOrderWrappers() {
167
150
}
168
151
169
152
private void orderWrappers (List <WRAPPER > wrappers ) {
170
- this .orderingAction .accept (wrappers );
153
+ List <WRAPPER > orderedWrappers = new ArrayList <>(wrappers );
154
+ this .orderingAction .accept (orderedWrappers );
155
+ Map <Object , Integer > distinctWrappersToIndex = distinctWrappersToIndex (orderedWrappers );
156
+
157
+ int difference = orderedWrappers .size () - wrappers .size ();
158
+ int distinctDifference = distinctWrappersToIndex .size () - wrappers .size ();
159
+ if (difference > 0 ) { // difference >= distinctDifference
160
+ logDescriptorsAddedWarning (difference );
161
+ }
162
+ if (distinctDifference < 0 ) { // distinctDifference <= difference
163
+ logDescriptorsRemovedWarning (distinctDifference );
164
+ }
165
+
166
+ wrappers .sort (comparing (wrapper -> distinctWrappersToIndex .getOrDefault (wrapper , -1 )));
167
+ }
168
+
169
+ private Map <Object , Integer > distinctWrappersToIndex (List <?> wrappers ) {
170
+ Map <Object , Integer > toIndex = new HashMap <>();
171
+ for (int i = 0 ; i < wrappers .size (); i ++) {
172
+ // Avoid ClassCastException if a misbehaving ordering action added a non-WRAPPER
173
+ Object wrapper = wrappers .get (i );
174
+ if (!toIndex .containsKey (wrapper )) {
175
+ toIndex .put (wrapper , i );
176
+ }
177
+ }
178
+ return toIndex ;
171
179
}
172
180
173
181
private void logDescriptorsAddedWarning (int number ) {
0 commit comments