View Javadoc
1   package org.apache.maven.surefire.testng.conf;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.lang.reflect.Method;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  import org.apache.maven.surefire.testset.TestSetFailedException;
28  import org.testng.TestNG;
29  import org.testng.xml.XmlSuite;
30  
31  import static java.lang.Integer.parseInt;
32  import static org.apache.maven.surefire.booter.ProviderParameterNames.PARALLEL_PROP;
33  import static org.apache.maven.surefire.booter.ProviderParameterNames.THREADCOUNT_PROP;
34  import static org.apache.maven.surefire.testng.conf.AbstractDirectConfigurator.loadListenerClasses;
35  
36  /**
37   * TestNG configurator for 5.3+ versions. TestNG exposes a {@link org.testng.TestNG#configure(java.util.Map)} method.
38   * All supported TestNG options are passed in String format, except
39   * {@link org.testng.TestNGCommandLineArgs#LISTENER_COMMAND_OPT} which is {@link java.util.List List>Class<},
40   * {@link org.testng.TestNGCommandLineArgs#JUNIT_DEF_OPT} which is a {@link Boolean},
41   * {@link org.testng.TestNGCommandLineArgs#SKIP_FAILED_INVOCATION_COUNT_OPT} which is a {@link Boolean},
42   * {@link org.testng.TestNGCommandLineArgs#OBJECT_FACTORY_COMMAND_OPT} which is a {@link Class},
43   * {@link org.testng.TestNGCommandLineArgs#REPORTERS_LIST} which is a {@link java.util.List List>ReporterConfig<}.
44   * <br>
45   * Test classes and/or suite files are not passed along as options parameters, but configured separately.
46   *
47   * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>
48   */
49  public class TestNGMapConfigurator
50      implements Configurator
51  {
52      @Override
53      public void configure( TestNG testng, Map<String, String> options )
54          throws TestSetFailedException
55      {
56          Map convertedOptions = getConvertedOptions( options );
57          testng.configure( convertedOptions );
58      }
59  
60      @Override
61      public void configure( XmlSuite suite, Map<String, String> options )
62          throws TestSetFailedException
63      {
64          String threadCountAsString = options.get( THREADCOUNT_PROP );
65          int threadCount = threadCountAsString == null ? 1 : parseInt( threadCountAsString );
66          suite.setThreadCount( threadCount );
67  
68          String parallel = options.get( PARALLEL_PROP );
69          if ( parallel != null )
70          {
71              suite.setParallel( parallel );
72          }
73      }
74  
75      Map<String, Object> getConvertedOptions( Map<String, String> options )
76          throws TestSetFailedException
77      {
78          Map<String, Object> convertedOptions = new HashMap<String, Object>();
79          convertedOptions.put( "-mixed", false );
80          for ( Map.Entry<String, String> entry : options.entrySet() )
81          {
82              String key = entry.getKey();
83              Object val = entry.getValue();
84              // todo: use switch-case of jdk7
85              if ( "listener".equals( key ) )
86              {
87                  val = convertListeners( entry.getValue() );
88              }
89              else if ( "objectfactory".equals( key ) )
90              {
91                  val = AbstractDirectConfigurator.loadClass( entry.getValue() );
92              }
93              else if ( "testrunfactory".equals( key ) )
94              {
95                  val = AbstractDirectConfigurator.loadClass( entry.getValue() );
96              }
97              else if ( "reporter".equals( key ) )
98              {
99                  // for TestNG 5.6 or higher
100                 // TODO support multiple reporters?
101                 val = convertReporterConfig( val );
102                 key = "reporterslist";
103             }
104             else if ( "junit".equals( key ) )
105             {
106                 val = convert( val, Boolean.class );
107             }
108             else if ( "skipfailedinvocationcounts".equals( key ) )
109             {
110                 val = convert( val, Boolean.class );
111             }
112             else if ( "mixed".equals( key ) )
113             {
114                 val = convert( val, Boolean.class );
115             }
116             else if ( "configfailurepolicy".equals( key ) )
117             {
118                 val = convert( val, String.class );
119             }
120             else if ( "group-by-instances".equals( key ) )
121             {
122                 val = convert( val, Boolean.class );
123             }
124             else if ( THREADCOUNT_PROP.equals( key ) )
125             {
126                 val = convert ( val, String.class );
127             }
128             else if ( "suitethreadpoolsize".equals( key ) )
129             {
130                 // for TestNG 6.9.7 or higher
131                 val = convert( val, Integer.class );
132             }
133             else if ( "dataproviderthreadcount".equals( key ) )
134             {
135                 // for TestNG 5.10 or higher
136                 val = convert( val, Integer.class );
137             }
138 
139             if ( key.startsWith( "-" ) )
140             {
141                 convertedOptions.put( key, val );
142             }
143             else
144             {
145                 convertedOptions.put( "-" + key, val );
146             }
147         }
148         return convertedOptions;
149     }
150 
151     // ReporterConfig only became available in later versions of TestNG
152     protected Object convertReporterConfig( Object val )
153     {
154         try
155         {
156             Class<?> reporterConfig = Class.forName( "org.testng.ReporterConfig" );
157             Method deserialize = reporterConfig.getMethod( "deserialize", String.class );
158             Object rc = deserialize.invoke( null, val );
159             ArrayList<Object> reportersList = new ArrayList<Object>();
160             reportersList.add( rc );
161             return reportersList;
162         }
163         catch ( Exception e )
164         {
165             return val;
166         }
167     }
168 
169     protected Object convertListeners( String listenerClasses ) throws TestSetFailedException
170     {
171         return loadListenerClasses( listenerClasses );
172     }
173 
174     protected Object convert( Object val, Class<?> type )
175     {
176         if ( val == null )
177         {
178             return null;
179         }
180         else if ( type.isAssignableFrom( val.getClass() ) )
181         {
182             return val;
183         }
184         else if ( ( type == Boolean.class || type == boolean.class ) && val.getClass() == String.class )
185         {
186             return Boolean.valueOf( (String) val );
187         }
188         else if ( ( type == Integer.class || type == int.class ) && val.getClass() == String.class )
189         {
190             return Integer.valueOf( (String) val );
191         }
192         else if ( type == String.class )
193         {
194             return val.toString();
195         }
196         else
197         {
198             return val;
199         }
200     }
201 }