View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.surefire.api.util;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Set;
27  
28  import org.apache.maven.surefire.api.testset.TestSetFailedException;
29  
30  import static java.lang.Math.max;
31  
32  /**
33   * Contains all the tests that have been found according to specified include/exclude
34   * specification for a given surefire run.
35   *
36   * @author Kristian Rosenvold (junit core adaption)
37   */
38  public class TestsToRun implements Iterable<Class<?>> {
39      private final List<Class<?>> locatedClasses;
40  
41      private volatile boolean finished;
42  
43      private int iteratedCount;
44  
45      /**
46       * Constructor
47       *
48       * @param locatedClasses A set of java.lang.Class objects representing tests to run
49       */
50      public TestsToRun(Set<Class<?>> locatedClasses) {
51          this.locatedClasses = new ArrayList<>(locatedClasses);
52      }
53  
54      public static TestsToRun fromClass(Class<?> clazz) throws TestSetFailedException {
55          return new TestsToRun(Collections.<Class<?>>singleton(clazz));
56      }
57  
58      /**
59       * @return test classes which have been retrieved by {@link TestsToRun#iterator()}.
60       */
61      public Iterator<Class<?>> iterated() {
62          return newWeakIterator();
63      }
64  
65      /**
66       * Returns an iterator over the located java.lang.Class objects
67       *
68       * @return an unmodifiable iterator
69       */
70      @Override
71      public Iterator<Class<?>> iterator() {
72          return new ClassesIterator();
73      }
74  
75      private final class ClassesIterator extends CloseableIterator<Class<?>> {
76          private final Iterator<Class<?>> it = TestsToRun.this.locatedClasses.iterator();
77  
78          private int iteratedCount;
79  
80          @Override
81          protected boolean isClosed() {
82              return TestsToRun.this.isFinished();
83          }
84  
85          @Override
86          protected boolean doHasNext() {
87              return it.hasNext();
88          }
89  
90          @Override
91          protected Class<?> doNext() {
92              Class<?> nextTest = it.next();
93              TestsToRun.this.iteratedCount = max(++iteratedCount, TestsToRun.this.iteratedCount);
94              return nextTest;
95          }
96  
97          @Override
98          protected void doRemove() {}
99  
100         @Override
101         public void remove() {
102             throw new UnsupportedOperationException("unsupported remove");
103         }
104     }
105 
106     public final void markTestSetFinished() {
107         finished = true;
108     }
109 
110     public final boolean isFinished() {
111         return finished;
112     }
113 
114     @Override
115     public String toString() {
116         StringBuilder sb = new StringBuilder("TestsToRun: [");
117         for (Class<?> clazz : this) {
118             sb.append(' ').append(clazz.getName());
119         }
120 
121         sb.append(']');
122         return sb.toString();
123     }
124 
125     public boolean containsAtLeast(int atLeast) {
126         return containsAtLeast(iterator(), atLeast);
127     }
128 
129     private boolean containsAtLeast(Iterator<Class<?>> it, int atLeast) {
130         for (int i = 0; i < atLeast; i++) {
131             if (!it.hasNext()) {
132                 return false;
133             }
134 
135             it.next();
136         }
137 
138         return true;
139     }
140 
141     public boolean containsExactly(int items) {
142         Iterator<Class<?>> it = iterator();
143         return containsAtLeast(it, items) && !it.hasNext();
144     }
145 
146     /**
147      * @return {@code true}, if the classes may be read eagerly. {@code false},
148      *         if the classes must only be read lazy.
149      */
150     public boolean allowEagerReading() {
151         return true;
152     }
153 
154     public Class<?>[] getLocatedClasses() {
155         if (!allowEagerReading()) {
156             throw new IllegalStateException("Cannot eagerly read");
157         }
158         Collection<Class<?>> result = new ArrayList<>();
159         for (Class<?> clazz : this) {
160             result.add(clazz);
161         }
162         return result.toArray(new Class<?>[result.size()]);
163     }
164 
165     /**
166      * Get test class which matches className
167      *
168      * @param className string used to find the test class
169      * @return Class object with the matching name, null if could not find a class with the matching name
170      */
171     public Class<?> getClassByName(String className) {
172         for (Class<?> clazz : this) {
173             if (clazz.getName().equals(className)) {
174                 return clazz;
175             }
176         }
177         return null;
178     }
179 
180     /**
181      * @return snapshot of tests upon constructs of internal iterator.
182      * Therefore weakly consistent while {@link TestsToRun#iterator()} is being iterated.
183      */
184     private Iterator<Class<?>> newWeakIterator() {
185         final Iterator<Class<?>> it = locatedClasses.subList(0, iteratedCount).iterator();
186         return new CloseableIterator<Class<?>>() {
187             @Override
188             protected boolean isClosed() {
189                 return TestsToRun.this.isFinished();
190             }
191 
192             @Override
193             protected boolean doHasNext() {
194                 return it.hasNext();
195             }
196 
197             @Override
198             protected Class<?> doNext() {
199                 return it.next();
200             }
201 
202             @Override
203             protected void doRemove() {}
204 
205             @Override
206             public void remove() {
207                 throw new UnsupportedOperationException("unsupported remove");
208             }
209         };
210     }
211 }