From 9739b479c5e3acd959cd586a2c0615bfa5011637 Mon Sep 17 00:00:00 2001 From: Florian Weikert Date: Tue, 21 Feb 2017 12:39:21 +0000 Subject: Move Classpath.java from src/test/... to src/main/... since it will be used by the documentation generation in the future. -- PiperOrigin-RevId: 148081562 MOS_MIGRATED_REVID=148081562 --- .../google/devtools/build/lib/util/Classpath.java | 179 +++++++++++++++++++++ .../devtools/build/lib/testutil/Classpath.java | 174 -------------------- .../build/lib/testutil/TestSuiteBuilder.java | 19 ++- 3 files changed, 190 insertions(+), 182 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/lib/util/Classpath.java delete mode 100644 src/test/java/com/google/devtools/build/lib/testutil/Classpath.java diff --git a/src/main/java/com/google/devtools/build/lib/util/Classpath.java b/src/main/java/com/google/devtools/build/lib/util/Classpath.java new file mode 100644 index 0000000000..5c26e6505e --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/util/Classpath.java @@ -0,0 +1,179 @@ +// Copyright 2014 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.util; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Paths; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * A helper class to find all classes on the current classpath. This is used to automatically create + * JUnit 3 and 4 test suites. + */ +public final class Classpath { + private static final String CLASS_EXTENSION = ".class"; + + /** + * Base exception for any classpath related errors. + */ + public static final class ClassPathException extends Exception { + public ClassPathException(String format, Object... args) { + super(String.format(format, args)); + } + } + + /** Finds all classes that live in or below the given package. */ + public static Set> findClasses(String packageName) throws ClassPathException { + Set> result = new LinkedHashSet<>(); + String pathPrefix = (packageName + '.').replace('.', '/'); + for (String entryName : getClassPath()) { + File classPathEntry = new File(entryName); + if (classPathEntry.exists()) { + try { + Set classNames; + if (classPathEntry.isDirectory()) { + classNames = findClassesInDirectory(classPathEntry, pathPrefix); + } else { + classNames = findClassesInJar(classPathEntry, pathPrefix); + } + for (String className : classNames) { + Class clazz = Class.forName(className); + result.add(clazz); + } + } catch (IOException e) { + throw new ClassPathException( + "Can't read classpath entry %s: %s", entryName, e.getMessage()); + } catch (ClassNotFoundException e) { + throw new ClassPathException( + "Class not found even though it is on the classpath %s: %s", + entryName, e.getMessage()); + } + } + } + return result; + } + + private static Set findClassesInDirectory(File classPathEntry, String pathPrefix) { + Set result = new TreeSet<>(); + File directory = new File(classPathEntry, pathPrefix); + innerFindClassesInDirectory(result, directory, pathPrefix); + return result; + } + + /** + * Finds all classes and sub packages in the given directory that are below the given package and + * add them to the respective sets. + * + * @param directory Directory to inspect + * @param pathPrefix Prefix for the path to the classes that are requested + * (ex: {@code com/google/foo/bar}) + */ + private static void innerFindClassesInDirectory(Set classNames, File directory, + String pathPrefix) { + Preconditions.checkArgument(pathPrefix.endsWith("/")); + if (directory.exists()) { + for (File f : directory.listFiles()) { + String name = f.getName(); + if (name.endsWith(CLASS_EXTENSION)) { + String clzName = getClassName(pathPrefix + name); + classNames.add(clzName); + } else if (f.isDirectory()) { + findClassesInDirectory(f, pathPrefix + name + "/"); + } + } + } + } + + /** + * Returns a set of all classes in the jar that start with the given prefix. + */ + private static Set findClassesInJar(File jarFile, String pathPrefix) throws IOException { + Set classNames = new TreeSet<>(); + try (ZipFile zipFile = new ZipFile(jarFile)) { + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + String entryName = entries.nextElement().getName(); + if (entryName.startsWith(pathPrefix) && entryName.endsWith(CLASS_EXTENSION)) { + classNames.add(getClassName(entryName)); + } + } + } + return classNames; + } + + /** + * Given the absolute path of a class file, return the class name. + */ + private static String getClassName(String className) { + int classNameEnd = className.length() - CLASS_EXTENSION.length(); + return className.substring(0, classNameEnd).replace('/', '.'); + } + + private static void getClassPathsFromClasspathJar(File classpathJar, Set classPaths) + throws IOException, ClassPathException { + Manifest manifest = new JarFile(classpathJar).getManifest(); + Attributes attributes = manifest.getMainAttributes(); + for (String classPath : attributes.getValue("Class-Path").split(" ")) { + try { + classPaths.add(Paths.get(new URI(classPath)).toAbsolutePath().toString()); + } catch (URISyntaxException e) { + throw new ClassPathException( + "Error parsing classpath uri %s: %s", classPath, e.getMessage()); + } + } + } + + /** Gets the classpath from current classloader. */ + private static Set getClassPath() throws ClassPathException { + ClassLoader classloader = Classpath.class.getClassLoader(); + if (!(classloader instanceof URLClassLoader)) { + throw new IllegalStateException("Unable to find classes to test, since Test Suite class is " + + "loaded by an unsupported Classloader."); + } + + Set completeClassPaths = new TreeSet<>(); + URL[] urls = ((URLClassLoader) classloader).getURLs(); + for (URL url : urls) { + String entryName = url.getPath(); + completeClassPaths.add(entryName); + if (entryName.endsWith("-classpath.jar")) { + // Bazel creates a classpath jar when the class path length exceeds command line length + // limit, read the class path value from its manifest file if it's a classpath jar. + File classPathEntry = new File(entryName); + if (classPathEntry.exists() && classPathEntry.isFile()) { + try { + getClassPathsFromClasspathJar(classPathEntry, completeClassPaths); + } catch (IOException e) { + throw new ClassPathException( + "Can't read classpath entry %s: %s", entryName, e.getMessage()); + } + } + } + } + return completeClassPaths; + } +} diff --git a/src/test/java/com/google/devtools/build/lib/testutil/Classpath.java b/src/test/java/com/google/devtools/build/lib/testutil/Classpath.java deleted file mode 100644 index 059ef6f1c3..0000000000 --- a/src/test/java/com/google/devtools/build/lib/testutil/Classpath.java +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2014 The Bazel Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package com.google.devtools.build.lib.testutil; - -import com.google.devtools.build.lib.util.Preconditions; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Paths; -import java.util.Enumeration; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.TreeSet; -import java.util.jar.Attributes; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - * A helper class to find all classes on the current classpath. This is used to automatically create - * JUnit 3 and 4 test suites. - */ -final class Classpath { - private static final String CLASS_EXTENSION = ".class"; - - /** - * Finds all classes that live in or below the given package. - */ - static Set> findClasses(String packageName) { - Set> result = new LinkedHashSet<>(); - String pathPrefix = (packageName + '.').replace('.', '/'); - for (String entryName : getClassPath()) { - File classPathEntry = new File(entryName); - if (classPathEntry.exists()) { - try { - Set classNames; - if (classPathEntry.isDirectory()) { - classNames = findClassesInDirectory(classPathEntry, pathPrefix); - } else { - classNames = findClassesInJar(classPathEntry, pathPrefix); - } - for (String className : classNames) { - Class clazz = Class.forName(className); - result.add(clazz); - } - } catch (IOException e) { - throw new AssertionError("Can't read classpath entry " - + entryName + ": " + e.getMessage()); - } catch (ClassNotFoundException e) { - throw new AssertionError("Class not found even though it is on the classpath " - + entryName + ": " + e.getMessage()); - } - } - } - return result; - } - - private static Set findClassesInDirectory(File classPathEntry, String pathPrefix) { - Set result = new TreeSet<>(); - File directory = new File(classPathEntry, pathPrefix); - innerFindClassesInDirectory(result, directory, pathPrefix); - return result; - } - - /** - * Finds all classes and sub packages in the given directory that are below the given package and - * add them to the respective sets. - * - * @param directory Directory to inspect - * @param pathPrefix Prefix for the path to the classes that are requested - * (ex: {@code com/google/foo/bar}) - */ - private static void innerFindClassesInDirectory(Set classNames, File directory, - String pathPrefix) { - Preconditions.checkArgument(pathPrefix.endsWith("/")); - if (directory.exists()) { - for (File f : directory.listFiles()) { - String name = f.getName(); - if (name.endsWith(CLASS_EXTENSION)) { - String clzName = getClassName(pathPrefix + name); - classNames.add(clzName); - } else if (f.isDirectory()) { - findClassesInDirectory(f, pathPrefix + name + "/"); - } - } - } - } - - /** - * Returns a set of all classes in the jar that start with the given prefix. - */ - private static Set findClassesInJar(File jarFile, String pathPrefix) throws IOException { - Set classNames = new TreeSet<>(); - try (ZipFile zipFile = new ZipFile(jarFile)) { - Enumeration entries = zipFile.entries(); - while (entries.hasMoreElements()) { - String entryName = entries.nextElement().getName(); - if (entryName.startsWith(pathPrefix) && entryName.endsWith(CLASS_EXTENSION)) { - classNames.add(getClassName(entryName)); - } - } - } - return classNames; - } - - /** - * Given the absolute path of a class file, return the class name. - */ - private static String getClassName(String className) { - int classNameEnd = className.length() - CLASS_EXTENSION.length(); - return className.substring(0, classNameEnd).replace('/', '.'); - } - - private static void getClassPathsFromClasspathJar(File classpathJar, Set classPaths) - throws IOException { - Manifest manifest = new JarFile(classpathJar).getManifest(); - Attributes attributes = manifest.getMainAttributes(); - for (String classPath : attributes.getValue("Class-Path").split(" ")) { - try { - classPaths.add(Paths.get(new URI(classPath)).toAbsolutePath().toString()); - } catch (URISyntaxException e) { - throw new AssertionError( - "Error parsing classpath uri " + classPath + ": " + e.getMessage()); - } - } - } - - /** - * Gets the classpath from current classloader. - */ - private static Set getClassPath() { - ClassLoader classloader = Classpath.class.getClassLoader(); - if (!(classloader instanceof URLClassLoader)) { - throw new IllegalStateException("Unable to find classes to test, since Test Suite class is " - + "loaded by an unsupported Classloader."); - } - - Set completeClassPaths = new TreeSet<>(); - URL[] urls = ((URLClassLoader) classloader).getURLs(); - for (URL url : urls) { - String entryName = url.getPath(); - completeClassPaths.add(entryName); - if (entryName.endsWith("-classpath.jar")) { - // Bazel creates a classpath jar when the class path length exceeds command line length - // limit, read the class path value from its manifest file if it's a classpath jar. - File classPathEntry = new File(entryName); - if (classPathEntry.exists() && classPathEntry.isFile()) { - try { - getClassPathsFromClasspathJar(classPathEntry, completeClassPaths); - } catch (IOException e) { - throw new AssertionError( - "Can't read classpath entry " + entryName + ": " + e.getMessage()); - } - } - } - } - return completeClassPaths; - } -} diff --git a/src/test/java/com/google/devtools/build/lib/testutil/TestSuiteBuilder.java b/src/test/java/com/google/devtools/build/lib/testutil/TestSuiteBuilder.java index 59a5d8583c..81a20dd616 100644 --- a/src/test/java/com/google/devtools/build/lib/testutil/TestSuiteBuilder.java +++ b/src/test/java/com/google/devtools/build/lib/testutil/TestSuiteBuilder.java @@ -17,15 +17,14 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; - -import junit.framework.TestCase; - -import org.junit.runner.RunWith; - +import com.google.devtools.build.lib.util.Classpath; +import com.google.devtools.build.lib.util.Classpath.ClassPathException; import java.lang.reflect.Modifier; import java.util.Comparator; import java.util.LinkedHashSet; import java.util.Set; +import junit.framework.TestCase; +import org.junit.runner.RunWith; /** * A collector for test classes, for both JUnit 3 and 4. To be used in combination with {@link @@ -73,10 +72,14 @@ public final class TestSuiteBuilder { private Set> getClassesRecursive(String pkgName) { Set> result = new LinkedHashSet<>(); - for (Class clazz : Classpath.findClasses(pkgName)) { - if (isTestClass(clazz)) { - result.add(clazz); + try { + for (Class clazz : Classpath.findClasses(pkgName)) { + if (isTestClass(clazz)) { + result.add(clazz); + } } + } catch (ClassPathException e) { + throw new AssertionError("Cannot retrive classes: " + e.getMessage()); } return result; } -- cgit v1.2.3