Add proper testing environment

This commit is contained in:
Lassebq 2023-09-01 23:54:08 +03:00
parent e693b7254a
commit 629ca9d2d9
No known key found for this signature in database
GPG key ID: DE0866BB0C980B6E
12 changed files with 395 additions and 35 deletions

View file

@ -35,8 +35,6 @@ dependencies {
test {
useJUnitPlatform()
workingDir = "build"
testLogging.showStandardStreams = true
}
task sourcesJar(type: Jar) {

View file

@ -14,7 +14,6 @@ import org.mcphackers.launchwrapper.util.OS;
public class LaunchConfig {
private static final File defaultGameDir = getDefaultGameDir();
//TODO allow arbitary parameters instead of the ones documented below
private Map<String, LaunchParameter<?>> parameters = new HashMap<String, LaunchParameter<?>>();
public LaunchParameterSwitch demo = new LaunchParameterSwitch("demo", false);
@ -107,6 +106,7 @@ public class LaunchConfig {
if(args[i].startsWith("--")) {
String paramName = args[i].substring(2);
LaunchParameter<?> param = parameters.get(paramName.toLowerCase(Locale.ENGLISH));
//TODO allow arbitary parameter names instead of ignoring them
if(param == null) {
continue;
}
@ -116,21 +116,24 @@ public class LaunchConfig {
}
if(i + 1 < args.length) {
try {
// TODO better handling for alternative parameter names
if(param.equals(session)) {
sessionid.set(args[i + 1]);
}
if(param.equals(sessionid)) {
session.set(args[i + 1]);
}
if(param.equals(gameDir)) {
workDir.set(new File(args[i + 1]));
}
if(param.equals(workDir)) {
gameDir.set(new File(args[i + 1]));
}
param.setString(args[i + 1]);
i++;
// TODO better handling for alternative parameter names
if(param.equals(gameDir) && assetsDir.get() == null) {
assetsDir.set(new File(gameDir.get(), "assets"));
}
else if(param.equals(session)) {
sessionid.set(session.get());
}
else if(param.equals(sessionid)) {
session.set(sessionid.get());
}
else if(param.equals(gameDir)) {
workDir.set(gameDir.get());
}
else if(param.equals(workDir)) {
gameDir.set(workDir.get());
}
} catch (IllegalArgumentException e) {
}
}

View file

@ -303,12 +303,4 @@ public class LaunchClassLoader extends URLClassLoader implements ClassNodeSource
return INSTANCE = new LaunchClassLoader(getSystemClassLoader());
}
public URL getLocation(String className) {
try {
return loadClass(className).getProtectionDomain().getCodeSource().getLocation();
} catch (Exception e) {
return null;
}
}
}

View file

@ -1,6 +1,7 @@
package org.mcphackers.launchwrapper.tweak;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.mcphackers.launchwrapper.LaunchConfig;
@ -76,6 +77,10 @@ public abstract class Tweak {
return null; // Tweak not found
}
public List<FeatureInfo> getTweakInfo() {
return Collections.unmodifiableList(features);
}
protected void tweakInfo(String name, String... extra) {
features.add(new FeatureInfo(name));
}

View file

@ -1,7 +1,5 @@
package org.mcphackers.launchwrapper.util;
import java.net.URL;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
@ -15,7 +13,5 @@ public interface ClassNodeSource {
MethodNode getMethod(String owner, String name, String desc);
void overrideClass(ClassNode node);
URL getLocation(String className);
}

View file

@ -5,6 +5,7 @@ import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
@ -38,6 +39,16 @@ public final class Util {
return buffer.toByteArray();
}
public static void copyStream(InputStream stream1, OutputStream stream2) throws IOException {
int nRead;
byte[] data = new byte[16384];
while((nRead = stream1.read(data, 0, data.length)) != -1) {
stream2.write(data, 0, nRead);
}
stream1.close();
}
public static URL replaceHost(URL url, String hostName, int port) {
try {
return new URL(url.getProtocol(), hostName, port, url.getFile());

View file

@ -0,0 +1,34 @@
package org.mcphackers.launchwrapper.test;
import org.mcphackers.launchwrapper.LaunchConfig;
import org.mcphackers.launchwrapper.tweak.LegacyTweak;
import org.mcphackers.launchwrapper.tweak.Tweak;
import org.mcphackers.launchwrapper.util.ClassNodeSource;
public class BetaTest extends TweakTest {
@Override
protected String jarName() {
return "b1.1_02.jar";
}
@Override
protected String jarUrl() {
return "https://launcher.mojang.com/v1/objects/e1c682219df45ebda589a557aadadd6ed093c86c/client.jar";
}
@Override
public Tweak getTweak(ClassNodeSource source, LaunchConfig config) {
return new LegacyTweak(source, config);
}
@Override
public TestFeatureBuilder getTests() {
return new TestFeatureBuilder()
.tweakInfoList("Set default width and height", "Fullscreen init patch", "Fullscreen toggle patch",
"Replaced icon", "MouseHelper fix", "Replaced fullscreen", "Replaced width",
"Replaced height", "Splash fix", "Shutdown patch", "SoundManager shutdown",
"Options load fix", "Replaced canvas getWidth", "Replaced canvas getHeight",
"Replaced title", "Replaced gameDir", "Removed canvas null check", "Added main");
}
}

View file

@ -1,13 +1,32 @@
package org.mcphackers.launchwrapper.test;
import java.io.File;
import org.junit.Test;
import org.mcphackers.launchwrapper.LaunchConfig;
import org.mcphackers.launchwrapper.tweak.LegacyTweak;
import org.mcphackers.launchwrapper.tweak.Tweak;
import org.mcphackers.launchwrapper.util.ClassNodeSource;
public class ClassicTest extends TweakTest {
@Test
public void test() {
System.out.println(new File(".").getAbsolutePath());
@Override
protected String jarName() {
return "c0.30_01c.jar";
}
@Override
protected String jarUrl() {
return "https://launcher.mojang.com/v1/objects/54622801f5ef1bcc1549a842c5b04cb5d5583005/client.jar";
}
@Override
public Tweak getTweak(ClassNodeSource source, LaunchConfig config) {
return new LegacyTweak(source, config);
}
@Override
public TestFeatureBuilder getTests() {
return new TestFeatureBuilder()
.tweakInfoList("Shutdown patch", "Replaced gameDir", "Replaced title", "Replaced icon",
"Fullscreen init patch", "Replaced fullscreen", "Replaced width", "Replaced height",
"Set fullscreen", "Added main");
}
}

View file

@ -0,0 +1,112 @@
package org.mcphackers.launchwrapper.test;
import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import org.mcphackers.launchwrapper.util.ClassNodeSource;
import org.mcphackers.rdi.util.NodeHelper;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
public class FileClassNodeSource implements ClassNodeSource, Closeable {
private final ZipFile source;
private Map<String, ClassNode> classNodeCache = new HashMap<String, ClassNode>();
private File debugOutput;
public FileClassNodeSource(File jarFile) throws ZipException, IOException {
source = new ZipFile(jarFile);
}
public void setDebugOutput(File directory) {
debugOutput = directory;
}
private void saveDebugClass(ClassNode node) {
if(debugOutput == null) {
return;
}
ClassWriter writer = new ClassWriter(COMPUTE_MAXS);
node.accept(writer);
byte[] classData = writer.toByteArray();
File cls = new File(debugOutput, node.name + ".class");
cls.getParentFile().mkdirs();
try {
FileOutputStream fos = new FileOutputStream(cls);
fos.write(classData);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String classNodeName(String nameWithDots) {
return nameWithDots.replace('.', '/');
}
private static String classResourceName(String name) {
return name.replace('.', '/') + ".class";
}
private InputStream getClassAsStream(String name) throws IOException {
String className = classResourceName(name);
ZipEntry entry = source.getEntry(className);
if(entry == null) {
return null;
}
InputStream is = source.getInputStream(entry);
if(is != null) {
return is;
}
return null;
}
public ClassNode getClass(String name) {
ClassNode node = classNodeCache.get(classNodeName(name));
if(node != null) {
return node;
}
try {
InputStream is = getClassAsStream(name);
if(is == null) {
return null;
}
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(is);
classReader.accept(classNode, 0);
classNodeCache.put(classNode.name, classNode);
return classNode;
} catch (IOException e) {
return null;
}
}
public FieldNode getField(String owner, String name, String desc) {
return NodeHelper.getField(getClass(owner), name, desc);
}
public MethodNode getMethod(String owner, String name, String desc) {
return NodeHelper.getMethod(getClass(owner), name, desc);
}
public void overrideClass(ClassNode node) {
saveDebugClass(node);
}
public void close() throws IOException {
source.close();
}
}

View file

@ -0,0 +1,35 @@
package org.mcphackers.launchwrapper.test;
import org.mcphackers.launchwrapper.LaunchConfig;
import org.mcphackers.launchwrapper.tweak.LegacyTweak;
import org.mcphackers.launchwrapper.tweak.Tweak;
import org.mcphackers.launchwrapper.util.ClassNodeSource;
public class InfdevTest extends TweakTest {
@Override
protected String jarName() {
return "inf-20100630-2.jar";
}
@Override
protected String jarUrl() {
return "http://files.betacraft.uk/launcher/assets/versions/inf-20100630-2.jar";
}
@Override
public Tweak getTweak(ClassNodeSource source, LaunchConfig config) {
return new LegacyTweak(source, config);
}
@Override
public TestFeatureBuilder getTests() {
return new TestFeatureBuilder()
.tweakInfoList("Set default width and height", "Fullscreen init patch", "Fullscreen toggle patch",
"Replaced icon", "MouseHelper fix", "Replaced fullscreen", "Replaced width",
"Replaced height", "Shutdown patch", "SoundManager shutdown",
"Options load fix", "Replaced canvas getWidth", "Replaced canvas getHeight",
"Replaced title", "Replaced gameDir", "Removed canvas null check", "Set fullscreen",
"Set default width and height", "Added main");
}
}

View file

@ -0,0 +1,30 @@
package org.mcphackers.launchwrapper.test;
import org.mcphackers.launchwrapper.LaunchConfig;
import org.mcphackers.launchwrapper.tweak.Tweak;
import org.mcphackers.launchwrapper.tweak.VanillaTweak;
import org.mcphackers.launchwrapper.util.ClassNodeSource;
public class ReleaseTest extends TweakTest {
@Override
protected String jarName() {
return "1.12.2.jar";
}
@Override
protected String jarUrl() {
return "https://piston-data.mojang.com/v1/objects/0f275bc1547d01fa5f56ba34bdc87d981ee12daf/client.jar";
}
@Override
public Tweak getTweak(ClassNodeSource source, LaunchConfig config) {
return new VanillaTweak(source, config);
}
@Override
public TestFeatureBuilder getTests() {
return new TestFeatureBuilder()
.tweakInfoList("Minecraft.run()", "Replaced title", "Replaced icon");
}
}

View file

@ -1,7 +1,132 @@
package org.mcphackers.launchwrapper.test;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.mcphackers.launchwrapper.LaunchConfig;
import org.mcphackers.launchwrapper.tweak.FeatureInfo;
import org.mcphackers.launchwrapper.tweak.Tweak;
import org.mcphackers.launchwrapper.util.ClassNodeSource;
import org.mcphackers.launchwrapper.util.Util;
public abstract class TweakTest {
public void testFramePatch() {
@Test
public void test() {
try {
File testDir = new File("build/test/");
File gameJar = new File(testDir, jarName());
downloadMinecraftJar(
jarUrl(),
gameJar);
FileClassNodeSource classNodeSource = new FileClassNodeSource(gameJar);
LaunchConfig config = getDefaultConfig(testDir);
Tweak tweak = getTweak(classNodeSource, config);
assertTrue(tweak.transform());
for (FeatureInfo info : tweak.getTweakInfo()) {
System.out.println(info.feature);
}
featureTest(tweak, getTests());
} catch (Throwable e) {
fail(e);
}
}
protected abstract String jarName();
protected abstract String jarUrl();
public abstract Tweak getTweak(ClassNodeSource source, LaunchConfig config);
public abstract TestFeatureBuilder getTests();
protected final void downloadMinecraftJar(String url, File outFile) throws IOException {
if (outFile.exists()) {
return;
}
outFile.getParentFile().mkdirs();
InputStream is = new URL(url).openStream();
FileOutputStream fos = new FileOutputStream(outFile);
Util.copyStream(is, fos);
is.close();
fos.close();
}
protected static final LaunchConfig getDefaultConfig(File testDir) {
LaunchConfig config = new LaunchConfig();
File icon16 = new File("src/main/resources/icon_16x16.png");
File icon32 = new File("src/main/resources/icon_32x32.png");
config.icon.set(new File[] { icon16, icon32 });
config.username.set("Player");
config.session.set("-");
config.gameDir.set(testDir);
config.title.set("Minecraft Test");
return config;
}
protected void featureTest(Tweak tweak, TestFeatureBuilder test) {
try {
for (FeatureInfo featureInfo2 : test.bannedFeatures) {
for (FeatureInfo featureInfo : tweak.getTweakInfo()) {
if (featureInfo.feature.equals(featureInfo2.feature) &&
(featureInfo2.info == null || featureInfo2.info.equals(featureInfo.info))) {
fail("Banned feature found");
}
}
}
for (FeatureInfo featureInfo2 : test.features) {
boolean match = false;
for (FeatureInfo featureInfo : tweak.getTweakInfo()) {
if (featureInfo.feature.equals(featureInfo2.feature) &&
(featureInfo2.info == null || featureInfo2.info.equals(featureInfo.info))) {
match = true;
}
}
assertTrue(match, featureInfo2.feature);
}
} catch (Throwable e) {
fail(e);
}
}
public static class TestFeatureBuilder {
private List<FeatureInfo> features = new ArrayList<FeatureInfo>();
private List<FeatureInfo> bannedFeatures = new ArrayList<FeatureInfo>();
public TestFeatureBuilder() {
}
public TestFeatureBuilder tweakInfo(String name, String... extra) {
features.add(new FeatureInfo(name));
return this;
}
public TestFeatureBuilder tweakInfoBan(String name, String... extra) {
bannedFeatures.add(new FeatureInfo(name));
return this;
}
public TestFeatureBuilder tweakInfoList(String... names) {
for (String s : names) {
features.add(new FeatureInfo(s));
}
return this;
}
public TestFeatureBuilder tweakInfoBanList(String... names) {
for (String s : names) {
bannedFeatures.add(new FeatureInfo(s));
}
return this;
}
}
}