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