1 package org.testng.xml; 2 3 4 import static org.testng.internal.Utils.isStringNotBlank; 5 6 import org.testng.collections.Lists; 7 import org.testng.internal.Utils; 8 import org.testng.log4testng.Logger; 9 import org.testng.remote.RemoteTestNG; 10 import org.testng.reporters.XMLStringBuffer; 11 12 import java.io.File; 13 import java.io.FileOutputStream; 14 import java.io.IOException; 15 import java.io.OutputStreamWriter; 16 import java.nio.charset.Charset; 17 import java.util.Collection; 18 import java.util.List; 19 import java.util.Map; 20 import java.util.Properties; 21 22 /** 23 * This class is used to encapsulate a launch. Various synthetic XML files are created 24 * depending on whether the user is trying to launch a suite, a class, a method, etc... 25 */ 26 public abstract class LaunchSuite { 27 /** This class's log4testng Logger. */ 28 private static final Logger LOGGER = Logger.getLogger(LaunchSuite.class); 29 30 protected boolean m_temporary; 31 32 /** 33 * Constructs a <code>LaunchSuite</code> 34 * 35 * @param isTemp the temporary status 36 */ LaunchSuite(boolean isTemp)37 protected LaunchSuite(boolean isTemp) { 38 m_temporary = isTemp; 39 } 40 41 /** 42 * Returns the temporary state. 43 * @return the temporary state. 44 */ isTemporary()45 public boolean isTemporary() { 46 return m_temporary; 47 } 48 49 /** 50 * Saves the suite file in the specified directory and returns the file 51 * pathname. 52 * 53 * @param directory the directory where the suite file is to be saved. 54 * @return the file pathname of the saved file. 55 */ save(File directory)56 public abstract File save(File directory); 57 getSuiteBuffer()58 public abstract XMLStringBuffer getSuiteBuffer(); 59 60 /** 61 * <code>ExistingSuite</code> is a non-temporary LaunchSuite based on an existing 62 * file. 63 */ 64 public static class ExistingSuite extends LaunchSuite { 65 66 /** 67 * The existing suite path (either relative to the project root or an absolute path) 68 */ 69 private File m_suitePath; 70 71 /** 72 * Constructs a <code>ExistingSuite</code> based on an existing file 73 * 74 * @param path the path to the existing Launch suite. 75 */ ExistingSuite(File path)76 public ExistingSuite(File path) { 77 super(false); 78 79 m_suitePath = path; 80 } 81 82 @Override getSuiteBuffer()83 public XMLStringBuffer getSuiteBuffer() { 84 throw new UnsupportedOperationException("Not implemented yet"); 85 } 86 87 /** 88 * Trying to run an existing XML file: copy its content to where the plug-in 89 * expects it. 90 */ 91 @Override save(File directory)92 public File save(File directory) { 93 if (RemoteTestNG.isDebug()) { 94 File result = new File(directory, RemoteTestNG.DEBUG_SUITE_FILE); 95 Utils.copyFile(m_suitePath, result); 96 return result; 97 } else { 98 return m_suitePath; 99 } 100 } 101 } 102 103 /** 104 * <code>CustomizedSuite</code> TODO cquezel JavaDoc. 105 */ 106 private abstract static class CustomizedSuite extends LaunchSuite { 107 protected String m_projectName; 108 protected String m_suiteName; 109 110 /** The annotation type. May be null. */ 111 protected Map<String, String> m_parameters; 112 113 /** The string buffer used to write the XML file. */ 114 private XMLStringBuffer m_suiteBuffer; 115 116 /** 117 * Constructs a <code>CustomizedSuite</code> TODO cquezel JavaDoc. 118 * 119 * @param projectName 120 * @param className 121 * @param parameters 122 * @param annotationType 123 */ CustomizedSuite(final String projectName, final String className, final Map<String, String> parameters, final String annotationType)124 private CustomizedSuite(final String projectName, 125 final String className, 126 final Map<String, String> parameters, 127 final String annotationType) 128 { 129 super(true); 130 131 m_projectName = projectName; 132 m_suiteName = className; 133 m_parameters = parameters; 134 } 135 136 /** 137 * TODO cquezel JavaDoc 138 * 139 * @return 140 */ createContentBuffer()141 protected XMLStringBuffer createContentBuffer() { 142 XMLStringBuffer suiteBuffer = new XMLStringBuffer(); 143 suiteBuffer.setDocType("suite SYSTEM \"" + Parser.TESTNG_DTD_URL + "\""); 144 145 Properties attrs = new Properties(); 146 attrs.setProperty("parallel", XmlSuite.ParallelMode.NONE.toString()); 147 attrs.setProperty("name", m_suiteName); 148 suiteBuffer.push("suite", attrs); 149 150 if (m_parameters != null) { 151 for (Map.Entry<String, String> entry : m_parameters.entrySet()) { 152 Properties paramAttrs = new Properties(); 153 paramAttrs.setProperty("name", entry.getKey()); 154 paramAttrs.setProperty("value", entry.getValue()); 155 suiteBuffer.push("parameter", paramAttrs); 156 suiteBuffer.pop("parameter"); 157 } 158 } 159 160 initContentBuffer(suiteBuffer); 161 162 suiteBuffer.pop("suite"); 163 164 return suiteBuffer; 165 } 166 167 /** 168 * TODO cquezel JavaDoc 169 * 170 * @return 171 */ 172 @Override getSuiteBuffer()173 public XMLStringBuffer getSuiteBuffer() { 174 if (null == m_suiteBuffer) { 175 m_suiteBuffer = createContentBuffer(); 176 } 177 178 return m_suiteBuffer; 179 } 180 181 /** 182 * Initializes the content of the xml string buffer. 183 * 184 * @param suiteBuffer the string buffer to initialize. 185 */ initContentBuffer(XMLStringBuffer suiteBuffer)186 protected abstract void initContentBuffer(XMLStringBuffer suiteBuffer); 187 188 /** 189 * {@inheritDoc} This implementation saves the suite to the "temp-testng-customsuite.xml" 190 * file in the specified directory. 191 */ 192 @Override save(File directory)193 public File save(File directory) { 194 final File suiteFile = new File(directory, "temp-testng-customsuite.xml"); 195 196 saveSuiteContent(suiteFile, getSuiteBuffer()); 197 198 return suiteFile; 199 } 200 201 /** 202 * Saves the content of the string buffer to the specified file. 203 * 204 * @param file the file to write to. 205 * @param content the content to write to the file. 206 */ saveSuiteContent(final File file, final XMLStringBuffer content)207 protected void saveSuiteContent(final File file, final XMLStringBuffer content) { 208 209 try { 210 OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8")); 211 try { 212 fw.write(content.getStringBuffer().toString()); 213 } 214 finally { 215 fw.close(); 216 } 217 } 218 catch (IOException ioe) { 219 // TODO CQ is this normal to swallow exception here 220 LOGGER.error("IO Exception", ioe); 221 } 222 } 223 } 224 225 /** 226 * A <code>MethodsSuite</code> is a suite made up of methods. 227 */ 228 static class MethodsSuite extends CustomizedSuite { 229 protected Collection<String> m_methodNames; 230 protected String m_className; 231 protected int m_logLevel; 232 233 /** 234 * Constructs a <code>MethodsSuite</code> TODO cquezel JavaDoc. 235 * 236 * @param projectName 237 * @param className 238 * @param methodNames 239 * @param parameters 240 * @param annotationType (may be null) 241 * @param logLevel 242 */ MethodsSuite(final String projectName, final String className, final Collection<String> methodNames, final Map<String, String> parameters, final String annotationType, final int logLevel)243 MethodsSuite(final String projectName, 244 final String className, 245 final Collection<String> methodNames, 246 final Map<String, String> parameters, 247 final String annotationType, 248 final int logLevel) { 249 super(projectName, className, parameters, annotationType); 250 251 m_className = className; 252 m_methodNames = methodNames; 253 m_logLevel = logLevel; 254 } 255 256 /** 257 * {@inheritDoc} 258 */ 259 @Override initContentBuffer(XMLStringBuffer suiteBuffer)260 protected void initContentBuffer(XMLStringBuffer suiteBuffer) { 261 Properties testAttrs = new Properties(); 262 testAttrs.setProperty("name", m_className); 263 testAttrs.setProperty("verbose", String.valueOf(m_logLevel)); 264 265 suiteBuffer.push("test", testAttrs); 266 267 suiteBuffer.push("classes"); 268 269 Properties classAttrs = new Properties(); 270 classAttrs.setProperty("name", m_className); 271 272 if ((null != m_methodNames) && (m_methodNames.size() > 0)) { 273 suiteBuffer.push("class", classAttrs); 274 275 suiteBuffer.push("methods"); 276 277 for (Object methodName : m_methodNames) { 278 Properties methodAttrs = new Properties(); 279 methodAttrs.setProperty("name", (String) methodName); 280 suiteBuffer.addEmptyElement("include", methodAttrs); 281 } 282 283 suiteBuffer.pop("methods"); 284 suiteBuffer.pop("class"); 285 } 286 else { 287 suiteBuffer.addEmptyElement("class", classAttrs); 288 } 289 suiteBuffer.pop("classes"); 290 suiteBuffer.pop("test"); 291 } 292 } 293 294 static class ClassesAndMethodsSuite extends CustomizedSuite { 295 protected Map<String, Collection<String>> m_classes; 296 protected int m_logLevel; 297 ClassesAndMethodsSuite(final String projectName, final Map<String, Collection<String>> classes, final Map<String, String> parameters, final String annotationType, final int logLevel)298 ClassesAndMethodsSuite(final String projectName, 299 final Map<String, Collection<String>> classes, 300 final Map<String, String> parameters, 301 final String annotationType, 302 final int logLevel) { 303 super(projectName, "Custom suite", parameters, annotationType); 304 m_classes = classes; 305 m_logLevel = logLevel; 306 } 307 308 /** 309 * {@inheritDoc} 310 */ 311 @Override initContentBuffer(XMLStringBuffer suiteBuffer)312 protected void initContentBuffer(XMLStringBuffer suiteBuffer) { 313 Properties testAttrs = new Properties(); 314 testAttrs.setProperty("name", m_projectName); 315 testAttrs.setProperty("verbose", String.valueOf(m_logLevel)); 316 317 suiteBuffer.push("test", testAttrs); 318 319 suiteBuffer.push("classes"); 320 321 for(Map.Entry<String, Collection<String>> entry : m_classes.entrySet()) { 322 Properties classAttrs = new Properties(); 323 classAttrs.setProperty("name", entry.getKey()); 324 325 Collection<String> methodNames= sanitize(entry.getValue()); 326 if ((null != methodNames) && (methodNames.size() > 0)) { 327 suiteBuffer.push("class", classAttrs); 328 329 suiteBuffer.push("methods"); 330 331 for (String methodName : methodNames) { 332 Properties methodAttrs = new Properties(); 333 methodAttrs.setProperty("name", methodName); 334 suiteBuffer.addEmptyElement("include", methodAttrs); 335 } 336 337 suiteBuffer.pop("methods"); 338 suiteBuffer.pop("class"); 339 } 340 else { 341 suiteBuffer.addEmptyElement("class", classAttrs); 342 } 343 } 344 suiteBuffer.pop("classes"); 345 suiteBuffer.pop("test"); 346 } 347 sanitize(Collection<String> source)348 private Collection<String> sanitize(Collection<String> source) { 349 if(null == source) { 350 return null; 351 } 352 353 List<String> result= Lists.newArrayList(); 354 for(String name: source) { 355 if(isStringNotBlank(name)) { 356 result.add(name); 357 } 358 } 359 360 return result; 361 } 362 } 363 364 /** 365 * <code>ClassListSuite</code> TODO cquezel JavaDoc. 366 */ 367 static class ClassListSuite extends CustomizedSuite { 368 protected Collection<String> m_packageNames; 369 protected Collection<String> m_classNames; 370 protected Collection<String> m_groupNames; 371 protected int m_logLevel; 372 ClassListSuite(final String projectName, final Collection<String> packageNames, final Collection<String> classNames, final Collection<String> groupNames, final Map<String, String> parameters, final String annotationType, final int logLevel)373 ClassListSuite(final String projectName, 374 final Collection<String> packageNames, 375 final Collection<String> classNames, 376 final Collection<String> groupNames, 377 final Map<String, String> parameters, 378 final String annotationType, 379 final int logLevel) { 380 super(projectName, "Custom suite", parameters, annotationType); 381 382 m_packageNames = packageNames; 383 m_classNames = classNames; 384 m_groupNames = groupNames; 385 m_logLevel = logLevel; 386 } 387 388 /** 389 * {@inheritDoc} 390 */ 391 @Override initContentBuffer(XMLStringBuffer suiteBuffer)392 protected void initContentBuffer(XMLStringBuffer suiteBuffer) { 393 Properties testAttrs = new Properties(); 394 testAttrs.setProperty("name", m_projectName); 395 testAttrs.setProperty("verbose", String.valueOf(m_logLevel)); 396 397 suiteBuffer.push("test", testAttrs); 398 399 if (null != m_groupNames) { 400 suiteBuffer.push("groups"); 401 suiteBuffer.push("run"); 402 403 for (String groupName : m_groupNames) { 404 Properties includeAttrs = new Properties(); 405 includeAttrs.setProperty("name", groupName); 406 suiteBuffer.addEmptyElement("include", includeAttrs); 407 } 408 409 suiteBuffer.pop("run"); 410 suiteBuffer.pop("groups"); 411 } 412 413 // packages belongs to suite according to the latest DTD 414 if ((m_packageNames != null) && (m_packageNames.size() > 0)) { 415 suiteBuffer.push("packages"); 416 417 for (String packageName : m_packageNames) { 418 Properties packageAttrs = new Properties(); 419 packageAttrs.setProperty("name", packageName); 420 suiteBuffer.addEmptyElement("package", packageAttrs); 421 } 422 suiteBuffer.pop("packages"); 423 } 424 425 if ((m_classNames != null) && (m_classNames.size() > 0)) { 426 suiteBuffer.push("classes"); 427 428 for (String className : m_classNames) { 429 Properties classAttrs = new Properties(); 430 classAttrs.setProperty("name", className); 431 suiteBuffer.addEmptyElement("class", classAttrs); 432 } 433 434 suiteBuffer.pop("classes"); 435 } 436 suiteBuffer.pop("test"); 437 } 438 } 439 } 440