1package org.testng.internal;
2
3import org.testng.xml.XmlClass;
4import org.testng.xml.XmlInclude;
5import org.testng.xml.XmlPackage;
6import org.testng.xml.XmlSuite;
7import org.testng.xml.XmlTest;
8import org.yaml.snakeyaml.TypeDescription;
9import org.yaml.snakeyaml.constructor.Constructor;
10import org.yaml.snakeyaml.nodes.Node;
11import org.yaml.snakeyaml.nodes.NodeId;
12import org.yaml.snakeyaml.nodes.ScalarNode;
13
14import java.io.File;
15import java.io.FileInputStream;
16import java.io.FileNotFoundException;
17import java.io.InputStream;
18import java.util.List;
19import java.util.Map;
20
21/**
22 * YAML support for TestNG.
23 *
24 * @author Cedric Beust <cedric@beust.com>
25 */
26public class Yaml {
27
28  public static XmlSuite parse(String filePath, InputStream is)
29      throws FileNotFoundException {
30    Constructor constructor = new TestNGConstructor(XmlSuite.class);
31    {
32      TypeDescription suiteDescription = new TypeDescription(XmlSuite.class);
33      suiteDescription.putListPropertyType("packages", XmlPackage.class);
34      suiteDescription.putListPropertyType("listeners", String.class);
35      suiteDescription.putListPropertyType("tests", XmlTest.class);
36      suiteDescription.putListPropertyType("method-selectors", XmlMethodSelector.class);
37      constructor.addTypeDescription(suiteDescription);
38    }
39
40    {
41      TypeDescription testDescription = new TypeDescription(XmlTest.class);
42      testDescription.putListPropertyType("classes", XmlClass.class);
43      testDescription.putMapPropertyType("metaGroups", String.class, List.class);
44      testDescription.putListPropertyType("method-selectors", XmlMethodSelector.class);
45      constructor.addTypeDescription(testDescription);
46    }
47
48    org.yaml.snakeyaml.Yaml y = new org.yaml.snakeyaml.Yaml(constructor);
49    if (is == null) is = new FileInputStream(new File(filePath));
50    XmlSuite result = (XmlSuite) y.load(is);
51
52    result.setFileName(filePath);
53    // DEBUG
54//    System.out.println("[Yaml] " + result.toXml());
55
56    // Adjust XmlTest parents and indices
57    for (XmlTest t : result.getTests()) {
58      t.setSuite(result);
59      int index = 0;
60      for (XmlClass c : t.getClasses()) {
61        c.setIndex(index++);
62      }
63    }
64
65    return result;
66  }
67
68  private static void maybeAdd(StringBuilder sb, String key, Object value, Object def) {
69    maybeAdd(sb, "", key, value, def);
70  }
71
72  private static void maybeAdd(StringBuilder sb, String sp, String key, Object value, Object def) {
73    if (value != null && ! value.equals(def)) {
74      sb.append(sp).append(key).append(": ").append(value.toString()).append("\n");
75    }
76  }
77
78  /**
79   * The main entry point to convert an XmlSuite into YAML. This method is allowed to be used
80   * by external tools (e.g. Eclipse).
81   */
82  public static StringBuilder toYaml(XmlSuite suite) {
83    StringBuilder result = new StringBuilder();
84
85    maybeAdd(result, "name", suite.getName(), null);
86    maybeAdd(result, "junit", suite.isJUnit(), XmlSuite.DEFAULT_JUNIT);
87    maybeAdd(result, "verbose", suite.getVerbose(), XmlSuite.DEFAULT_VERBOSE);
88    maybeAdd(result, "threadCount", suite.getThreadCount(), XmlSuite.DEFAULT_THREAD_COUNT);
89    maybeAdd(result, "dataProviderThreadCount", suite.getDataProviderThreadCount(),
90        XmlSuite.DEFAULT_DATA_PROVIDER_THREAD_COUNT);
91    maybeAdd(result, "timeOut", suite.getTimeOut(), null);
92    maybeAdd(result, "parallel", suite.getParallel(), XmlSuite.DEFAULT_PARALLEL);
93    maybeAdd(result, "skipFailedInvocationCounts", suite.skipFailedInvocationCounts(),
94        XmlSuite.DEFAULT_SKIP_FAILED_INVOCATION_COUNTS);
95
96    toYaml(result, "parameters", "", suite.getParameters());
97    toYaml(result, suite.getPackages());
98
99    if (suite.getListeners().size() > 0) {
100      result.append("listeners:\n");
101      toYaml(result, "  ", suite.getListeners());
102    }
103
104    if (suite.getPackages().size() > 0) {
105      result.append("packages:\n");
106      toYaml(result, suite.getPackages());
107    }
108    if (suite.getTests().size() > 0) {
109      result.append("tests:\n");
110      for (XmlTest t : suite.getTests()) {
111        toYaml(result, "  ", t);
112      }
113    }
114
115    if (suite.getChildSuites().size() > 0) {
116      result.append("suite-files:\n");
117      toYaml(result, "  ", suite.getSuiteFiles());
118    }
119
120    return result;
121  }
122
123  private static void toYaml(StringBuilder result, String sp, XmlTest t) {
124    String sp2 = sp + "  ";
125    result.append(sp).append("- name: ").append(t.getName()).append("\n");
126
127    maybeAdd(result, sp2, "junit", t.isJUnit(), XmlSuite.DEFAULT_JUNIT);
128    maybeAdd(result, sp2, "verbose", t.getVerbose(), XmlSuite.DEFAULT_VERBOSE);
129    maybeAdd(result, sp2, "timeOut", t.getTimeOut(), null);
130    maybeAdd(result, sp2, "parallel", t.getParallel(), XmlSuite.DEFAULT_PARALLEL);
131    maybeAdd(result, sp2, "skipFailedInvocationCounts", t.skipFailedInvocationCounts(),
132        XmlSuite.DEFAULT_SKIP_FAILED_INVOCATION_COUNTS);
133
134    maybeAdd(result, "preserveOrder", sp2, t.getPreserveOrder(), XmlSuite.DEFAULT_PRESERVE_ORDER);
135
136    toYaml(result, "parameters", sp2, t.getTestParameters());
137
138    if (t.getIncludedGroups().size() > 0) {
139      result.append(sp2).append("includedGroups: [ ")
140          .append(Utils.join(t.getIncludedGroups(), ","))
141          .append(" ]\n");
142    }
143
144    if (t.getExcludedGroups().size() > 0) {
145      result.append(sp2).append("excludedGroups: [ ")
146          .append(Utils.join(t.getExcludedGroups(), ","))
147          .append(" ]\n");
148    }
149
150    Map<String, List<String>> mg = t.getMetaGroups();
151    if (mg.size() > 0) {
152      result.append(sp2).append("metaGroups: { ");
153      boolean first = true;
154      for (Map.Entry<String, List<String>> entry : mg.entrySet()) {
155        if (! first) result.append(", ");
156        result.append(entry.getKey()).append(": [ ")
157        .append(Utils.join(entry.getValue(), ",")).append(" ] ");
158        first = false;
159      }
160      result.append(" }\n");
161    }
162
163    if (t.getXmlPackages().size() > 0) {
164      result.append(sp2).append("xmlPackages:\n");
165      for (XmlPackage xp : t.getXmlPackages())  {
166        toYaml(result, sp2 + "  - ", xp);
167      }
168    }
169
170    if (t.getXmlClasses().size() > 0) {
171      result.append(sp2).append("classes:\n");
172      for (XmlClass xc : t.getXmlClasses())  {
173        toYaml(result, sp2 + "  ", xc);
174      }
175    }
176
177
178    result.append("\n");
179  }
180
181  private static void toYaml(StringBuilder result, String sp2, XmlClass xc) {
182    List<XmlInclude> im = xc.getIncludedMethods();
183    List<String> em = xc.getExcludedMethods();
184    String name = im.size() > 0 || em.size() > 0 ? "name: " : "";
185
186    result.append(sp2).append("- " + name).append(xc.getName()).append("\n");
187    if (im.size() > 0) {
188      result.append(sp2 + "  includedMethods:\n");
189      for (XmlInclude xi : im) {
190        toYaml(result, sp2 + "    ", xi);
191      }
192    }
193
194    if (em.size() > 0) {
195      result.append(sp2 + "  excludedMethods:\n");
196      toYaml(result, sp2 + "    ", em);
197    }
198  }
199
200  private static void toYaml(StringBuilder result, String sp2, XmlInclude xi) {
201    result.append(sp2 + "- " + xi.getName()).append("\n");
202  }
203
204  private static void toYaml(StringBuilder result, String sp, List<String> strings) {
205    for (String l : strings) {
206      result.append(sp).append("- ").append(l).append("\n");
207    }
208  }
209
210  private static final String SP = "  ";
211
212  private static void toYaml(StringBuilder sb, List<XmlPackage> packages) {
213    if (packages.size() > 0) {
214      sb.append("packages:\n");
215      for (XmlPackage p : packages) {
216        toYaml(sb, "  ", p);
217      }
218    }
219    for (XmlPackage p : packages) {
220      toYaml(sb, "  ", p);
221    }
222  }
223
224  private static void toYaml(StringBuilder sb, String sp, XmlPackage p) {
225    sb.append(sp).append("name: ").append(p.getName()).append("\n");
226
227    generateIncludeExclude(sb, sp, "includes", p.getInclude());
228    generateIncludeExclude(sb, sp, "excludes", p.getExclude());
229  }
230
231  private static void generateIncludeExclude(StringBuilder sb, String sp,
232      String key, List<String> includes) {
233    if (includes.size() > 0) {
234      sb.append(sp).append("  ").append(key).append("\n");
235      for (String inc : includes) {
236        sb.append(sp).append("    ").append(inc);
237      }
238    }
239  }
240
241  private static void mapToYaml(Map<String, String> map, StringBuilder out) {
242    if (map.size() > 0) {
243      out.append("{ ");
244      boolean first = true;
245      for (Map.Entry<String, String> e : map.entrySet()) {
246        if (! first) out.append(", ");
247        first = false;
248        out.append(e.getKey() + ": " + e.getValue());
249      }
250      out.append(" }\n");
251    }
252  }
253
254  private static void toYaml(StringBuilder sb, String key, String sp,
255      Map<String, String> parameters) {
256    if (parameters.size() > 0) {
257      sb.append(sp).append(key).append(": ");
258      mapToYaml(parameters, sb);
259    }
260  }
261
262  private static class TestNGConstructor extends Constructor {
263    public TestNGConstructor(Class<? extends Object> theRoot) {
264      super(theRoot);
265      yamlClassConstructors.put(NodeId.scalar, new ConstructParallelMode());
266    }
267
268    private class ConstructParallelMode extends ConstructScalar {
269      public Object construct(Node node) {
270        if (node.getType().equals(XmlSuite.ParallelMode.class)) {
271          String parallel = (String) constructScalar((ScalarNode) node);
272          return XmlSuite.ParallelMode.getValidParallel(parallel);
273        }
274        return super.construct(node);
275      }
276    }
277  }
278}
279