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.testng.conf;
20  
21  import java.lang.reflect.Method;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.maven.surefire.api.booter.ProviderParameterNames;
28  import org.apache.maven.surefire.api.testset.TestSetFailedException;
29  import org.testng.TestNG;
30  import org.testng.xml.XmlSuite;
31  
32  /**
33   * Configurator that relies on reflection to set parameters in TestNG.
34   */
35  public abstract class AbstractDirectConfigurator implements Configurator {
36      final Map<String, Setter> setters;
37  
38      AbstractDirectConfigurator() {
39          Map<String, Setter> options = new HashMap<>();
40          // options.put( ProviderParameterNames.TESTNG_GROUPS_PROP, new Setter( "setGroups", String.class ) );
41          // options.put( ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP, new Setter( "setExcludedGroups", String.class
42          // ) );
43          options.put("junit", new Setter("setJUnit", Boolean.class));
44          options.put(ProviderParameterNames.THREADCOUNT_PROP, new Setter("setThreadCount", int.class));
45          options.put("usedefaultlisteners", new Setter("setUseDefaultListeners", boolean.class));
46          this.setters = options;
47      }
48  
49      @Override
50      public void configure(TestNG testng, Map<String, String> options) throws TestSetFailedException {
51          System.out.println("\n\n\n\nCONFIGURING TESTNG\n\n\n\n");
52          // kind of ugly, but listeners are configured differently
53          final String listeners = options.remove("listener");
54          // DGF In 4.7, default listeners dump XML files in the surefire-reports directory,
55          // confusing the report plugin.  This was fixed in later versions.
56          testng.setUseDefaultListeners(false);
57          configureInstance(testng, options);
58          // TODO: we should have the Profile so that we can decide if this is needed or not
59          testng.setListenerClasses(loadListenerClasses(listeners));
60      }
61  
62      @Override
63      public void configure(XmlSuite suite, Map<String, String> options) throws TestSetFailedException {
64          Map<String, String> filtered = filterForSuite(options);
65          configureInstance(suite, filtered);
66      }
67  
68      protected Map<String, String> filterForSuite(Map<String, String> options) {
69          Map<String, String> result = new HashMap<>();
70          addPropIfNotNull(options, result, ProviderParameterNames.PARALLEL_PROP);
71          addPropIfNotNull(options, result, ProviderParameterNames.THREADCOUNT_PROP);
72          return result;
73      }
74  
75      private void addPropIfNotNull(Map<String, String> options, Map<String, String> result, String prop) {
76          if (options.containsKey(prop)) {
77              result.put(prop, options.get(prop));
78          }
79      }
80  
81      private void configureInstance(Object testngInstance, Map<String, String> options) {
82          for (Map.Entry<String, String> entry : options.entrySet()) {
83              String key = entry.getKey();
84              String val = entry.getValue();
85              Setter setter = setters.get(key);
86              if (setter != null) {
87                  try {
88                      setter.invoke(testngInstance, val);
89                  } catch (Exception e) {
90                      throw new RuntimeException("Cannot set option " + key + " with value " + val, e);
91                  }
92              }
93          }
94      }
95  
96      static List<Class> loadListenerClasses(String listenerClasses) throws TestSetFailedException {
97          if (listenerClasses == null || listenerClasses.trim().isEmpty()) {
98              return new ArrayList<>();
99          }
100 
101         List<Class> classes = new ArrayList<>();
102         String[] classNames = listenerClasses.split("\\s*,\\s*(\\r?\\n)?\\s*");
103         for (String className : classNames) {
104             Class<?> clazz = loadClass(className);
105             classes.add(clazz);
106         }
107 
108         return classes;
109     }
110 
111     static Class<?> loadClass(String className) throws TestSetFailedException {
112         try {
113             return Class.forName(className);
114         } catch (Exception ex) {
115             throw new TestSetFailedException("Cannot find listener class " + className, ex);
116         }
117     }
118 
119     /**
120      * Describes a property setter by method name and parameter class.
121      */
122     public static final class Setter {
123         private final String setterName;
124 
125         private final Class<?> paramClass;
126 
127         public Setter(String name, Class<?> clazz) {
128             setterName = name;
129             paramClass = clazz;
130         }
131 
132         public void invoke(Object target, String value) throws Exception {
133             Method setter = target.getClass().getMethod(setterName, paramClass);
134             if (setter != null) {
135                 setter.invoke(target, convertValue(value));
136             }
137         }
138 
139         private Object convertValue(String value) {
140             if (value == null) {
141                 return null;
142             }
143             if (paramClass.isAssignableFrom(value.getClass())) {
144                 return value;
145             }
146 
147             if (Boolean.class.equals(paramClass) || boolean.class.equals(paramClass)) {
148                 return Boolean.valueOf(value);
149             }
150             if (Integer.class.equals(paramClass) || int.class.equals(paramClass)) {
151                 return Integer.valueOf(value);
152             }
153 
154             return value;
155         }
156     }
157 }