ClasspathManagerTestCase.java

/* *******************************************************************
 * Copyright (c) 2019 Contributors
 * All rights reserved.
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Public License v 2.0
 * which accompanies this distribution and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
 * ******************************************************************/
package org.aspectj.weaver.bcel;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;

import org.aspectj.apache.bcel.classfile.ClassFormatException;
import org.aspectj.apache.bcel.classfile.ClassParser;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.bridge.AbortException;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessage.Kind;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.util.LangUtil;
import org.aspectj.weaver.TypeFactory;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.bcel.ClassPathManager.ClassFile;

import junit.framework.TestCase;

/**
 * Should run these tests 3 times on each JDK level (8, 9, 11). On each one 3 of the tests should pass indicating that
 * JDK can successfully access system types in each JDK level.
 *
 * @author Andy Clement
 */
public class ClasspathManagerTestCase extends TestCase {

	// Works on my machine where all jvms under ~/jvms
	private static final String java18_rtjar = findJvm("j.*18.*", "rt.jar");
	private static final String java9_jrtfsjar = findJvm("j.*9.*", "jrt-fs.jar");
	private static final String java11_jrtfsjar = findJvm("j.*11.*", "jrt-fs.jar");

	private static String findJvm(String pattern, String jar) {
		String start = System.getProperty("user.home") + "/jvms";
		if (new File(start).isDirectory()) {
			for (File f : Objects.requireNonNull(new File(start).listFiles())) {
				if (f.isDirectory() && Pattern.matches(pattern, f.getName())) {
					File result = walk(f, jar);
					if (result != null) {
						System.out.println("For " + pattern + " found " + result.getAbsolutePath());
						return result.getAbsolutePath();
					}
				}
			}
		}
		return null;
	}

	private static File walk(File dir, String jar) {
		File[] fs = dir.listFiles();
		if (fs != null) {
			for (File f : fs) {
				if (f.getName().equals(jar))
					return f;
				else if (f.isDirectory()) {
					File s = walk(f, jar);
					if (s != null)
						return s;
				}
			}
		}
		return null;
	}

	public void testInstructions() {
		System.out.println(
			"This test is mostly for stand-alone usage (rename 'xtest*' to 'test*'), " +
				"as it needs execution on multiple JDK levels"
		);
	}

	/**
	 * See <a href="https://github.com/eclipse-aspectj/aspectj/issues/306">GitHub issue 306</a>
	 */
	public void testUnfoundClassPerformance() {
		final int ROUNDS = 10_000;
		final int MAX_TIME = 500;

		List<String> classPath = Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator));
		//System.out.println(classPath);
		ClassPathManager classPathManager = new ClassPathManager(classPath, null);
		UnresolvedType unresolvedType = TypeFactory.createTypeFromSignature("Ljava/lang/String;");

		long startTime = System.currentTimeMillis();
		for (int i = 0; i < ROUNDS; i++)
			classPathManager.find(unresolvedType);
		long duration = System.currentTimeMillis() - startTime;

		assertTrue(
			String.format("Duration for %d rounds should be < %d ms, but was %d ms", ROUNDS, MAX_TIME, duration),
			duration < MAX_TIME
		);
	}

	public void xtestSanity18accessing18RTJAR() {
		if (LangUtil.getVmVersion() > 8)
			fail("Must be Java 8");
		List<String> classpath = new ArrayList<>();
		classpath.add(java18_rtjar);
		ClassPathManager cpm = new ClassPathManager(classpath, new MH());
		ClassFile t = cpm.find(UnresolvedType.forSignature("Ljava/lang/Object;"));
		assertNotNull(t);
	}

	public void xtestJava18accessing11JRT() throws ClassFormatException, IOException {
		if (LangUtil.getVmVersion() > 8)
			fail("Must be Java 8");
		List<String> classpath = new ArrayList<>();
		classpath.add(java11_jrtfsjar);
		ClassPathManager cpm = new ClassPathManager(classpath, new MH());
		ClassFile t = cpm.find(UnresolvedType.forSignature("Ljava/lang/String;"));
		assertNotNull(t);
		ClassParser classParser = new ClassParser(t.getInputStream(), t.getPath());
		JavaClass clazz = classParser.parse();
		// isBlank() exists on Java 11
		long c = Arrays.stream(clazz.getMethods()).filter(m -> m.getName().equals("isBlank")).count();
		assertEquals(1, c);
	}

	public void xtestJava18accessing19JRT() throws ClassFormatException, IOException {
		if (LangUtil.getVmVersion() > 8)
			fail("Must be Java 8");
		List<String> classpath = new ArrayList<>();
		classpath.add(java9_jrtfsjar);
		ClassPathManager cpm = new ClassPathManager(classpath, new MH());
		ClassFile t = cpm.find(UnresolvedType.forSignature("Ljava/lang/String;"));
		assertNotNull(t);
		ClassParser classParser = new ClassParser(t.getInputStream(), t.getPath());
		JavaClass clazz = classParser.parse();
		// isBlank() exists on Java 11, but not on Java9
		long c = Arrays.stream(clazz.getMethods()).filter(m -> m.getName().equals("isBlank")).count();
		assertEquals(0, c);
	}


	public void xtestSanity19accessing18RTJAR() {
		assertEquals(9.0, LangUtil.getVmVersion());
		List<String> classpath = new ArrayList<>();
		classpath.add(java18_rtjar);
		ClassPathManager cpm = new ClassPathManager(classpath, new MH());
		ClassFile t = cpm.find(UnresolvedType.forSignature("Ljava/lang/Object;"));
		assertNotNull(t);
	}

	public void xtestJava19accessing11JRT() throws ClassFormatException, IOException {
		assertEquals(9.0, LangUtil.getVmVersion());
		List<String> classpath = new ArrayList<>();
		classpath.add(java11_jrtfsjar);
		ClassPathManager cpm = new ClassPathManager(classpath, new MH());
		ClassFile t = cpm.find(UnresolvedType.forSignature("Ljava/lang/String;"));
		assertNotNull(t);
		ClassParser classParser = new ClassParser(t.getInputStream(), t.getPath());
		JavaClass clazz = classParser.parse();
		// isBlank() exists on Java 11
		long c = Arrays.stream(clazz.getMethods()).filter(m -> m.getName().equals("isBlank")).count();
		assertEquals(1, c);
	}

	public void xtestJava19accessing19JRT() throws ClassFormatException, IOException {
		assertEquals(9.0, LangUtil.getVmVersion());
		List<String> classpath = new ArrayList<>();
		classpath.add(java9_jrtfsjar);
		ClassPathManager cpm = new ClassPathManager(classpath, new MH());
		ClassFile t = cpm.find(UnresolvedType.forSignature("Ljava/lang/String;"));
		assertNotNull(t);
		ClassParser classParser = new ClassParser(t.getInputStream(), t.getPath());
		JavaClass clazz = classParser.parse();
		// isBlank() exists on Java 11, but not on Java9
		long c = Arrays.stream(clazz.getMethods()).filter(m -> m.getName().equals("isBlank")).count();
		assertEquals(0, c);
	}

	public void xtestSanity11accessing18RTJAR() {
		assertEquals(11.0, LangUtil.getVmVersion());
		List<String> classpath = new ArrayList<>();
		classpath.add(java18_rtjar);
		ClassPathManager cpm = new ClassPathManager(classpath, new MH());
		ClassFile t = cpm.find(UnresolvedType.forSignature("Ljava/lang/Object;"));
		assertNotNull(t);
	}

	public void xtestJava11accessing11JRT() throws ClassFormatException, IOException {
		assertEquals(11.0, LangUtil.getVmVersion());
		List<String> classpath = new ArrayList<>();
		classpath.add(java11_jrtfsjar);
		ClassPathManager cpm = new ClassPathManager(classpath, new MH());
		ClassFile t = cpm.find(UnresolvedType.forSignature("Ljava/lang/String;"));
		assertNotNull(t);
		ClassParser classParser = new ClassParser(t.getInputStream(), t.getPath());
		JavaClass clazz = classParser.parse();
		// isBlank() exists on Java 11
		long c = Arrays.stream(clazz.getMethods()).filter(m -> m.getName().equals("isBlank")).count();
		assertEquals(1, c);
	}

	public void xtestJava11accessing19JRT() throws ClassFormatException, IOException {
		assertEquals(11.0, LangUtil.getVmVersion());
		List<String> classpath = new ArrayList<>();
		classpath.add(java9_jrtfsjar);
		ClassPathManager cpm = new ClassPathManager(classpath, new MH());
		ClassFile t = cpm.find(UnresolvedType.forSignature("Ljava/lang/String;"));
		assertNotNull(t);
		ClassParser classParser = new ClassParser(t.getInputStream(), t.getPath());
		JavaClass clazz = classParser.parse();
		// isBlank() exists on Java 11, but not on Java9
		long c = Arrays.stream(clazz.getMethods()).filter(m -> m.getName().equals("isBlank")).count();
		assertEquals(0, c);
	}

	static class MH implements IMessageHandler {

		@Override
		public boolean handleMessage(IMessage message) throws AbortException {
			System.out.println(message);
			return false;
		}

		@Override
		public boolean isIgnoring(Kind kind) {
			return false;
		}

		@Override
		public void dontIgnore(Kind kind) {}

		@Override
		public void ignore(Kind kind) {}

	}
}