Skip to content

Commit a1f2e3f

Browse files
Yaml parser: implement loadClasses flag
Parser loadClasses flag controls, whether test classes will be loaded during suite parsing. Setting it to false allow to load yaml suite with non-existent classes, whereas true will fail parsing on test class loading.
1 parent e8f17fe commit a1f2e3f

File tree

5 files changed

+75
-2
lines changed

5 files changed

+75
-2
lines changed

CHANGES.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Current
2+
New: Yaml parser: implement loadClasses flag (Dzmitry Sankouski)
23
Fixed: GITHUB-2676: NPE is triggered when working with ITestObjectFactory (Krishnan Mahadevan)
34
Fixed: GITHUB-2674: Run onTestSkipped for each value from data provider (Krishnan Mahadevan)
45
Fixed: GITHUB-2672: Log real stacktrace when test times out. (cdalexndr)

testng-core/src/main/java/org/testng/internal/Yaml.java

+44-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.List;
88
import java.util.Map;
99
import java.util.function.Consumer;
10+
import org.testng.TestNGException;
1011
import org.testng.util.Strings;
1112
import org.testng.xml.XmlClass;
1213
import org.testng.xml.XmlInclude;
@@ -27,7 +28,8 @@ public final class Yaml {
2728

2829
private Yaml() {}
2930

30-
public static XmlSuite parse(String filePath, InputStream is) throws FileNotFoundException {
31+
public static XmlSuite parse(String filePath, InputStream is, boolean loadClasses)
32+
throws FileNotFoundException {
3133
Constructor constructor = new TestNGConstructor(XmlSuite.class);
3234
{
3335
TypeDescription suiteDescription = new TypeDescription(XmlSuite.class);
@@ -46,6 +48,9 @@ public static XmlSuite parse(String filePath, InputStream is) throws FileNotFoun
4648
constructor.addTypeDescription(testDescription);
4749
}
4850

51+
TypeDescription xmlClassDescription = new XmlClassTypeDescriptor(loadClasses);
52+
constructor.addTypeDescription(xmlClassDescription);
53+
4954
org.yaml.snakeyaml.Yaml y = new org.yaml.snakeyaml.Yaml(constructor);
5055
if (is == null) {
5156
is = new FileInputStream(new File(filePath));
@@ -347,4 +352,42 @@ public Object construct(Node node) {
347352
}
348353
}
349354
}
355+
356+
private static class XmlClassTypeDescriptor extends TypeDescription {
357+
358+
private final boolean loadClasses;
359+
360+
public XmlClassTypeDescriptor(boolean loadClasses) {
361+
super(XmlClass.class);
362+
this.loadClasses = loadClasses;
363+
}
364+
365+
@Override
366+
public Object newInstance(Node node) {
367+
String className;
368+
369+
try {
370+
java.lang.reflect.Constructor<?> c =
371+
XmlClass.class.getDeclaredConstructor(String.class, boolean.class);
372+
c.setAccessible(true);
373+
if (node instanceof MappingNode) {
374+
Node valueNode =
375+
((MappingNode) node)
376+
.getValue().stream()
377+
.filter(
378+
nodeTuple ->
379+
((ScalarNode) nodeTuple.getKeyNode()).getValue().equals("name"))
380+
.findFirst()
381+
.orElseThrow(RuntimeException::new)
382+
.getValueNode();
383+
className = ((ScalarNode) valueNode).getValue();
384+
} else {
385+
className = ((ScalarNode) node).getValue();
386+
}
387+
return c.newInstance(className, loadClasses);
388+
} catch (Exception e) {
389+
throw new TestNGException("Failed to instantiate class", e);
390+
}
391+
}
392+
}
350393
}

testng-core/src/main/java/org/testng/internal/YamlParser.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class YamlParser implements ISuiteParser {
1313
public XmlSuite parse(String filePath, InputStream is, boolean loadClasses)
1414
throws TestNGException {
1515
try {
16-
return Yaml.parse(filePath, is);
16+
return Yaml.parse(filePath, is, loadClasses);
1717
} catch (FileNotFoundException e) {
1818
throw new TestNGException(e);
1919
}

testng-core/src/test/java/test/yaml/YamlTest.java

+24
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414
import org.testng.annotations.DataProvider;
1515
import org.testng.annotations.Test;
1616
import org.testng.internal.Yaml;
17+
import org.testng.internal.YamlParser;
1718
import org.testng.reporters.Files;
1819
import org.testng.xml.SuiteXmlParser;
1920
import org.testng.xml.XmlSuite;
2021
import org.testng.xml.internal.Parser;
2122
import test.SimpleBaseTest;
2223

2324
public class YamlTest extends SimpleBaseTest {
25+
public static final String CLASS_NOT_FOUND_MESSAGE = "Cannot find class in classpath";
2426

2527
@DataProvider
2628
public Object[][] dp() {
@@ -69,4 +71,26 @@ public void testXmlDependencyGroups() throws IOException {
6971
java.nio.file.Files.readAllBytes(Paths.get(expectedYamlFile)), StandardCharsets.UTF_8);
7072
assertThat(Yaml.toYaml(actualXmlSuite).toString()).isEqualToNormalizingNewlines(expectedYaml);
7173
}
74+
75+
@Test
76+
public void testLoadClassesFlag() throws IOException {
77+
YamlParser yamlParser = new YamlParser();
78+
String yamlSuiteFile = "src/test/resources/yaml/suiteWithNonExistentTest.yaml";
79+
80+
try {
81+
yamlParser.parse(yamlSuiteFile, new FileInputStream(yamlSuiteFile), false);
82+
} catch (Throwable throwable) {
83+
Throwable rootCause = getRootCause(throwable);
84+
String rootCauseMessage = rootCause.getMessage();
85+
if (rootCauseMessage.contains(CLASS_NOT_FOUND_MESSAGE)) {
86+
throw new AssertionError("TestNG shouldn't attempt to load test class", throwable);
87+
}
88+
89+
throw new AssertionError("Yaml parser failed to parse suite", throwable);
90+
}
91+
}
92+
93+
private Throwable getRootCause(Throwable throwable) {
94+
return throwable.getCause() != null ? getRootCause(throwable.getCause()) : throwable;
95+
}
7296
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
name: My_Suite
2+
tests:
3+
- name: My_test
4+
classes:
5+
- this.class.does.not.Exists

0 commit comments

Comments
 (0)