Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Files.copy() such that parent dirs are created #2764

Merged
merged 1 commit into from May 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.txt
@@ -1,4 +1,7 @@
Current

7.6.1
Fixed: GITHUB-2761: Exception: ERROR java.nio.file.NoSuchFileException: /tmp/testngXmlPathInJar-15086412835569336174 (Krishnan Mahadevan)
7.6.0
Fixed: GITHUB-2741: Show fully qualified name of the test instead of just the function name for better readability of test output.(Krishnan Mahadevan)
Fixed: GITHUB-2725: Honour custom attribute values in TestNG default reports (Krishnan Mahadevan)
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Expand Up @@ -5,7 +5,7 @@ kotlin.code.style=official
# Note: testng.kotlin-library.gradle.kts adds kotlin-stdlib for testImplementation
kotlin.stdlib.default.dependency=false

testng.version=7.7.0
testng.version=7.6.1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not the latest version or the next one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last released version was 7.6.0. Am guessing that this qualifies to be a bugfix release. Hence the version as 7.6.1

7.7.0 - Will be the version for the next release (I have already moved the milestones etc., Dont want to go through that ritual once again)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if we release 7.6.x during this year and maybe once per year lift the major version to allow smaller releases from time to time.


group=org.testng

Expand Down
1 change: 1 addition & 0 deletions testng-core/src/main/java/org/testng/JarFileUtils.java
Expand Up @@ -76,6 +76,7 @@ private boolean testngXmlExistsInJar(File jarFile, List<String> classes) throws
if (Parser.canParse(jeName.toLowerCase())) {
InputStream inputStream = jf.getInputStream(je);
File copyFile = new File(file, jeName);
krmahadevan marked this conversation as resolved.
Show resolved Hide resolved
copyFile.getParentFile().mkdirs();
Files.copy(inputStream, copyFile.toPath());
if (matchesXmlPathInJar(je)) {
suitePath = copyFile.toString();
Expand Down
Expand Up @@ -90,7 +90,9 @@ public void generateReport(
if (is == null) {
throw new AssertionError("Couldn't find resource: " + fileName);
}
java.nio.file.Files.copy(is, new File(outputDirectory, fileName).toPath());
File fileToCopy = new File(outputDirectory, fileName);
fileToCopy.getParentFile().mkdirs();
java.nio.file.Files.copy(is, fileToCopy.toPath());
}
}
all = Files.readFile(header);
Expand Down
Expand Up @@ -14,7 +14,7 @@
import java.util.List;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.testng.jarfileutils.JarCreator;
import org.testng.testhelper.JarCreator;
import org.testng.xml.IPostProcessor;
import org.testng.xml.XmlClass;
import org.testng.xml.XmlSuite;
Expand Down
@@ -1,4 +1,4 @@
package testhelper;
package org.testng.testhelper;

import java.io.File;
import java.io.IOException;
Expand All @@ -11,9 +11,22 @@ public class CompiledCode {
private final String name;

public CompiledCode(String name, File directory, boolean skipLoading) throws IOException {
this("", name, directory, skipLoading);
}

public CompiledCode(String packageName, String name, File directory, boolean skipLoading)
throws IOException {
this.skipLoading = skipLoading;
this.name = name;
File classFile = new File(directory, name + Kind.CLASS.extension);
if (packageName != null && !packageName.trim().isEmpty()) {
this.name = packageName + "." + name;
} else {
this.name = name;
}
String location = name;
if (packageName != null && !packageName.trim().isEmpty()) {
location = packageName.replaceAll("\\Q.\\E", "/") + "/" + name;
}
File classFile = new File(directory, location + Kind.CLASS.extension);
this.byteCode = Files.readAllBytes(classFile.toPath());
}

Expand Down
@@ -1,4 +1,4 @@
package org.testng.jarfileutils;
package org.testng.testhelper;

import java.io.File;
import java.io.IOException;
Expand Down
@@ -1,4 +1,4 @@
package testhelper;
package org.testng.testhelper;

/**
* <code>OutputDirectoryPatch</code> is a helper class to provide an output directory for TestNG
Expand Down
@@ -1,4 +1,4 @@
package testhelper;
package org.testng.testhelper;

import java.io.File;
import java.io.IOException;
Expand All @@ -25,8 +25,18 @@ public static List<CompiledCode> compileSourceCode(SourceCode... sources) throws
List<CompiledCode> compiledCodes = Lists.newArrayList();
for (SourceCode source : sources) {
source.getLocation().delete();
CompiledCode compiledCode =
new CompiledCode(source.getName(), source.getDirectory(), source.isSkipLoading());
CompiledCode compiledCode;
if (source.hasPackageName()) {
compiledCode =
new CompiledCode(
source.getPackageName(),
source.getName(),
source.getDirectory(),
source.isSkipLoading());
} else {
compiledCode =
new CompiledCode(source.getName(), source.getDirectory(), source.isSkipLoading());
}
compiledCodes.add(compiledCode);
}
return compiledCodes;
Expand Down
69 changes: 69 additions & 0 deletions testng-core/src/test/java/org/testng/testhelper/SourceCode.java
@@ -0,0 +1,69 @@
package org.testng.testhelper;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.tools.JavaFileObject.Kind;

public class SourceCode {

private final String packageName;
private final String name;
private final File directory;
private final boolean skipLoading;
private final File location;

public SourceCode(String name, String src, File directory, boolean skipLoading)
throws IOException {
this("", name, src, directory, skipLoading);
}

public SourceCode(
String packageName, String name, String src, File directory, boolean skipLoading)
throws IOException {
this.packageName = packageName;
this.name = name;
this.directory = directory;
this.skipLoading = skipLoading;
String path = name;
boolean includesPackageName = false;
if (packageName != null && !packageName.trim().isEmpty()) {
path = packageName.replaceAll("\\Q.\\E", "/") + "/" + name;
includesPackageName = true;
}
this.location = new File(directory, path + Kind.SOURCE.extension);
if (!includesPackageName && this.location.exists()) {
this.location.delete();
}
Path parentDir = this.location.getParentFile().toPath();
if (!Files.exists(parentDir)) {
Files.createDirectories(parentDir);
}
Files.write(location.toPath(), src.getBytes());
}

public File getDirectory() {
return directory;
}

public String getName() {
return name;
}

public boolean hasPackageName() {
return packageName != null && !packageName.trim().isEmpty();
}

public String getPackageName() {
return packageName;
}

public boolean isSkipLoading() {
return skipLoading;
}

public File getLocation() {
return location;
}
}
@@ -0,0 +1,38 @@
package org.testng.testhelper;

import java.io.File;
import java.io.IOException;
import java.util.List;

public final class TestClassGenerator {
private static final File projectDir = SimpleCompiler.createTempDir();

private TestClassGenerator() {
// Utility class. Defeat instantiation.
}

public static File getProjectDir() {
return projectDir;
}

public static SourceCode[] generate(String packageName, List<String> classNames) {
return classNames.stream()
.map(className -> generateCode(packageName, className))
.toArray(SourceCode[]::new);
}

private static SourceCode generateCode(String packageName, String className) {
String source = "package " + packageName + ";\n\n";
source += "import org.testng.annotations.Test;\n";
source += "public class " + className + " {\n";
source += " @Test\n";
source += " public void testMethod() {\n";
source += " }\n";
source += "}\n";
try {
return new SourceCode(packageName, className, source, projectDir, false);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,36 @@
package org.testng.testhelper;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

public final class TestNGSimpleClassLoader extends ClassLoader {

private final File baseDir;

public TestNGSimpleClassLoader() {
this(null);
}

public TestNGSimpleClassLoader(File baseDir) {
this.baseDir = baseDir;
}

public Class<?> injectByteCode(CompiledCode byteCode) throws ClassNotFoundException {
Class<?> clazz =
defineClass(byteCode.getName(), byteCode.getByteCode(), 0, byteCode.getByteCode().length);
return loadClass(clazz.getName());
}

@Override
protected URL findResource(String name) {
if (this.baseDir != null) {
try {
return new File(this.baseDir.getAbsolutePath() + "/" + name).toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
return super.findResource(name);
}
}
2 changes: 1 addition & 1 deletion testng-core/src/test/java/test/CommandLineTest.java
Expand Up @@ -19,8 +19,8 @@
import org.testng.TestNG;
import org.testng.annotations.Test;
import org.testng.internal.IConfiguration;
import org.testng.testhelper.OutputDirectoryPatch;
import test.sample.JUnitSample1;
import testhelper.OutputDirectoryPatch;

public class CommandLineTest {

Expand Down
6 changes: 3 additions & 3 deletions testng-core/src/test/java/test/JUnitTestClassLoader.java
Expand Up @@ -6,9 +6,9 @@
import java.util.List;
import org.testng.*;
import org.testng.annotations.Test;
import testhelper.CompiledCode;
import testhelper.SimpleCompiler;
import testhelper.SourceCode;
import org.testng.testhelper.CompiledCode;
import org.testng.testhelper.SimpleCompiler;
import org.testng.testhelper.SourceCode;

public class JUnitTestClassLoader extends ClassLoader {

Expand Down
Expand Up @@ -5,8 +5,8 @@
import org.testng.TestListenerAdapter;
import org.testng.TestNG;
import org.testng.annotations.Test;
import org.testng.testhelper.OutputDirectoryPatch;
import test.SimpleBaseTest;
import testhelper.OutputDirectoryPatch;

public class AlwaysRunTest extends SimpleBaseTest {

Expand Down
60 changes: 60 additions & 0 deletions testng-core/src/test/java/test/cli/CliTest.java
Expand Up @@ -2,14 +2,28 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import org.testng.Assert;
import org.testng.CommandLineArgs;
import org.testng.IInvokedMethod;
import org.testng.IInvokedMethodListener;
import org.testng.ITestResult;
import org.testng.TestNG;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.testhelper.CompiledCode;
import org.testng.testhelper.JarCreator;
import org.testng.testhelper.SimpleCompiler;
import org.testng.testhelper.SourceCode;
import org.testng.testhelper.TestClassGenerator;
import org.testng.testhelper.TestNGSimpleClassLoader;
import test.SimpleBaseTest;
import test.cli.github1517.TestClassWithConfigFailureSample;
import test.cli.github1517.TestClassWithConfigSkipAndFailureSample;
Expand Down Expand Up @@ -70,6 +84,52 @@ public Object[][] getData() {
};
}

@Test(description = "GITHUB-2761")
public void testToEnsureSuitesInJarAreExecutedViaCli() throws IOException {
SourceCode[] sources =
TestClassGenerator.generate("com.kungfu.panda", Arrays.asList("DragonWarrior", "Tigress"));
List<CompiledCode> compiledSources = SimpleCompiler.compileSourceCode(sources);
TestNGSimpleClassLoader classLoader =
new TestNGSimpleClassLoader(TestClassGenerator.getProjectDir());
Class<?>[] classes =
compiledSources.stream()
.map(compiledCode -> compile(classLoader, compiledCode))
.toArray(size -> new Class<?>[size]);
File jar = JarCreator.generateJar(classes);
LogInvocations logInvocations = new LogInvocations();
TestNG testng = new TestNG();
testng.addClassLoader(classLoader);
testng.setTestJar(jar.getAbsolutePath());
testng.addListener(logInvocations);
testng.setVerbose(2);
testng.run();
assertThat(testng.getStatus()).isEqualTo(0);
assertThat(logInvocations.logs)
.containsExactlyInAnyOrder(
"com.kungfu.panda.DragonWarrior.testMethod", "com.kungfu.panda.Tigress.testMethod");
}

private static Class<?> compile(TestNGSimpleClassLoader loader, CompiledCode code) {
try {
return loader.injectByteCode(code);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}

public static class LogInvocations implements IInvokedMethodListener {
private final List<String> logs = new ArrayList<>();

@Override
public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
logs.add(method.getTestMethod().getQualifiedName());
}

public List<String> getLogs() {
return logs;
}
}

public static class CustomTestNG extends TestNG {

@Override
Expand Down
Expand Up @@ -14,7 +14,7 @@
import org.testng.TestListenerAdapter;
import org.testng.TestNG;
import org.testng.annotations.Test;
import org.testng.jarfileutils.JarCreator;
import org.testng.testhelper.JarCreator;
import org.testng.xml.XmlSuite;
import test.SimpleBaseTest;
import test.TestHelper;
Expand Down
Expand Up @@ -6,11 +6,11 @@
import org.testng.TestListenerAdapter;
import org.testng.TestNG;
import org.testng.annotations.Test;
import org.testng.testhelper.OutputDirectoryPatch;
import org.testng.xml.XmlSuite;
import test.SimpleBaseTest;
import test.conffailure.github990.AbstractBaseSample;
import test.conffailure.github990.ChildClassSample;
import testhelper.OutputDirectoryPatch;

/**
* Test various cases where the @Configuration methods fail
Expand Down