Return-Path: X-Original-To: apmail-db-derby-commits-archive@www.apache.org Delivered-To: apmail-db-derby-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 639DA9C2D for ; Thu, 26 Apr 2012 10:21:27 +0000 (UTC) Received: (qmail 49123 invoked by uid 500); 26 Apr 2012 10:21:27 -0000 Delivered-To: apmail-db-derby-commits-archive@db.apache.org Received: (qmail 49068 invoked by uid 500); 26 Apr 2012 10:21:26 -0000 Mailing-List: contact derby-commits-help@db.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: Reply-To: "Derby Development" List-Id: Delivered-To: mailing list derby-commits@db.apache.org Received: (qmail 49059 invoked by uid 99); 26 Apr 2012 10:21:26 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 26 Apr 2012 10:21:26 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 26 Apr 2012 10:21:21 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 371CD23889FD; Thu, 26 Apr 2012 10:20:59 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1330751 - in /db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit: DerbyDistribution.java ReleaseRepository.java Date: Thu, 26 Apr 2012 10:20:59 -0000 To: derby-commits@db.apache.org From: kristwaa@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120426102059.371CD23889FD@eris.apache.org> Author: kristwaa Date: Thu Apr 26 10:20:58 2012 New Revision: 1330751 URL: http://svn.apache.org/viewvc?rev=1330751&view=rev Log: DERBY-5475: Formalize use of old Derby distributions in tests Added a very simple repository for Derby releases. It is compatible with the existing property used to control where the tests look for old releases (i.e. the upgrade and the compatibility test): derbyTesting.oldReleasePath Added two new classes in the junit test framework directory: o ReleaseRepository The repository, from which you can obtain a list of available Derby distributions that exist on the local machine. o DerbyDistribution Represents an on-disk, JAR-based Derby distribution. The initial repository is very simple, I expect that its functionality may be somewhat extended as tests start using it. Patch file: derby-5475-3a-repository.diff Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DerbyDistribution.java (with props) db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/ReleaseRepository.java (with props) Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DerbyDistribution.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DerbyDistribution.java?rev=1330751&view=auto ============================================================================== --- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DerbyDistribution.java (added) +++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DerbyDistribution.java Thu Apr 26 10:20:58 2012 @@ -0,0 +1,321 @@ +/* + + Derby - Class org.apache.derbyTesting.junit.DerbyDistribution + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to you 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 org.apache.derbyTesting.junit; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.apache.derbyTesting.functionTests.util.PrivilegedFileOpsForTests; + +/** + * Holds information required to run a Derby distribution and make choices + * based on the version of the Derby distribution. + *

+ * Implementation note: For simplicity distributions off the classes + * directory has been forbidden. The main reason for this is that it is + * sometimes a hard requirement that you must include only a single JAR from a + * distribution on the classpath. One such example is the compatibility test, + * where you need the testing code from one distribution and the client driver + * from another. While it is possible to support such a configuration in many + * scenarios, it complicates things quite a bit. Generating the JARs when + * testing on trunk seems like an acceptable price to pay. + */ +public class DerbyDistribution + implements Comparable { + + private static File[] EMPTY_FILE_ARRAY = new File[] {}; + public static final String JAR_RUN = "derbyrun.jar"; + public static final String JAR_CLIENT = "derbyclient.jar"; + public static final String JAR_ENGINE = "derby.jar"; + public static final String JAR_NET = "derbynet.jar"; + public static final String JAR_TESTING = "derbyTesting.jar"; + private static final String[] REQUIRED_JARS = { + JAR_ENGINE, JAR_NET, JAR_CLIENT + }; + + /** The version of the Derby distribution, i.e. 10.8.1.2. */ + private final DerbyVersion version; + /** Path to derbyrun.jar (may be {@code null}). */ + private final String derbyRunJarPath; + /** Path to derbyclient.jar. */ + private final String derbyClientJarPath; + /** Path to derbyengine.jar. */ + private final String derbyEngineJarPath; + /** Path to derbynet.jar. */ + private final String derbyNetJarPath; + /** + * Production classpath, i.e. all JAR files found except for + * derbyTesting.jar. + */ + private final String productionClasspath; + /** Testing classpath, i.e. path to derbyTesting.jar. */ + private final String testingClasspath; + + /** + * @throws IOException if obtaining the canonical path of a file fails + * @throws NullPointerException if version is {@code null} + * @see #getInstance + */ + private DerbyDistribution(DerbyVersion version, + File[] productionJars, File[] testingJars) + throws IOException { + if (version == null) { + throw new NullPointerException("version is null"); + } + this.version = version; + this.productionClasspath = constructJarClasspath(productionJars); + this.testingClasspath = constructJarClasspath(testingJars); + File root = productionJars[0].getParentFile(); + this.derbyRunJarPath = getPath(root, JAR_RUN); + this.derbyClientJarPath = getPath(root, JAR_CLIENT); + this.derbyEngineJarPath = getPath(root, JAR_ENGINE); + this.derbyNetJarPath = getPath(root, JAR_NET); + } + + /** Returns the absolute path to the JAR if it exists, otherwise null. */ + private String getPath(File root, String jar) { + File f = new File(root, jar); + if (PrivilegedFileOpsForTests.exists(f)) { + return f.getAbsolutePath(); + } else { + return null; + } + } + + /** Tells if this distribution has a {@code derbyrun.jar}. */ + public boolean hasDerbyRunJar() { + return derbyRunJarPath != null; + } + + /** + * Returns the path to {@code derbyrun.jar}. + * + * @return A path, or {@code null} if this distribution doesn't come with + * {@code derbyrun.jar}. + * @see #hasDerbyRunJar() + */ + public String getDerbyRunJarPath() { + return derbyRunJarPath; + } + + /** Returns the path to {@code derbyclient.jar}. */ + public String getDerbyClientJarPath() { + return derbyClientJarPath; + } + + /** Returns the path to {@code derby.jar}. */ + public String getDerbyEngineJarPath() { + return derbyEngineJarPath; + } + + /** Returns the path to {@code derbynet.jar}. */ + public String getDerbyNetJarPath() { + return derbyEngineJarPath; + } + + /** Returns a classpath with the network server production JARs. */ + public String getServerClasspath() { + return + this.derbyNetJarPath + File.pathSeparator + this.derbyEngineJarPath; + } + + /** Returns a classpath with all production JARs. */ + public String getProductionClasspath() { + return productionClasspath; + } + + /** Returns a classpath with all testing JARs. */ + public String getTestingClasspath() { + return testingClasspath; + } + + /** Returns a classpath with all production and testing JARs. */ + public String getFullClassPath() { + return productionClasspath + File.pathSeparatorChar + testingClasspath; + } + + /** Returns the version of this distribution. */ + public DerbyVersion getVersion() { + return version; + } + + /** + * Orders this distribution and the other distribution based on the version. + *

+ * Implementation note: Remove this method when we can use + * Java SE 5.0 features. + * + * @param o the other distribution + * @return {@code 1} if this version is newer, {@code 0} if both + * distributions have the same version, and {@code -1} if the other + * version is newer. + * @see #compareTo(org.apache.derbyTesting.junit.DerbyDistribution) + */ + public int compareTo(Object o) { + return compareTo((DerbyDistribution)o); + } + + /** + * Orders this distribution and the other distribution based on the version. + * + * @param o the other distribution + * @return {@code 1} if this version is newer, {@code 0} if both + * distributions have the same version, and {@code -1} if the other + * version is newer. + */ + public int compareTo(DerbyDistribution o) { + return version.compareTo(o.version); + } + + private static boolean hasRequiredJars(List jars) { + for (int i=0; i < REQUIRED_JARS.length; i++) { + boolean hasJar = false; + for (Iterator jarIter = jars.iterator(); jarIter.hasNext(); ) { + File jar = (File)jarIter.next(); + if (jar.getName().equalsIgnoreCase(REQUIRED_JARS[i])) { + hasJar = true; + break; + } + } + if (!hasJar) { + return false; + } + } + return true; + } + + /** + * Helper method extracting Derby production JARs from a directory. + * + * @param libDir directory + * @return A list of JARs (possibly empty). + */ + private static File[] getProductionJars(File libDir) { + File[] pJars = libDir.listFiles(new FilenameFilter() { + + public boolean accept(File dir, String name) { + return name.toUpperCase().endsWith(".JAR") && + !isTestingJar(name); + } + }); + if (pJars == null) { + return EMPTY_FILE_ARRAY; + } else { + return pJars; + } + } + + /** + * Helper method extracting Derby testing JARs from a directory. + * + * @param libDir directory + * @return A list of JARs (possibly empty). + */ + private static File[] getTestingJars(File libDir) { + File[] tJars = libDir.listFiles(new FilenameFilter() { + + public boolean accept(File dir, String name) { + return isTestingJar(name); + } + }); + if (tJars == null) { + return EMPTY_FILE_ARRAY; + } else { + return tJars; + } + } + + public static File[] getJars(File libDir) { + File[] jars = libDir.listFiles(new FilenameFilter() { + + public boolean accept(File dir, String name) { + return name.toUpperCase().endsWith(".JAR"); + } + }); + return jars; + } + + /** + * Tells if the given file is a Derby testing JAR. + * + * @param name name of the file + * @return {@code true} if a testing JAR, {@code false} otherwise + */ + private static boolean isTestingJar(String name) { + return name.toUpperCase().endsWith(JAR_TESTING.toUpperCase()); + } + + /** + * Merges a list of JAR files into a classpath string. + * + * @param jars JAR files to merge + * @return A classpath string. + * @throws IOException if obtaining the canonical path of a file fails + */ + private static String constructJarClasspath(File[] jars) + throws IOException { + StringBuffer sb = new StringBuffer(512); + for (int i=0; i < jars.length; i++) { + sb.append(jars[i].getCanonicalPath()); + sb.append(File.pathSeparatorChar); + } + if (jars.length > 0) { + sb.deleteCharAt(sb.length() -1); + } + return sb.toString(); + } + + /** + * Returns an instance based on the given library directory and version. + *

+ * This method is capable of understanding the difference of JAR based + * distribution and a distribution running off the classes-directory. + * + * @param dir the base directory for the distribution (either the classes + * directory or a directory holding the Derby JAR files) + * @param version the version of the distribution + * @return A representation of the distribution, or {@code null} if + * the specified directory is determined to be invalid. + * @throws IOException if obtaining the required information fails + * @throws IllegalArgumentException if {@code version} is {@code null} + */ + public static DerbyDistribution getInstance(File dir, + DerbyVersion version) + throws IOException { + File[] productionJars = getProductionJars(dir); + File[] testingJars = getTestingJars(dir); + List tmpJars = new ArrayList(); + tmpJars.addAll(Arrays.asList(productionJars)); + tmpJars.addAll(Arrays.asList(testingJars)); + if (hasRequiredJars(tmpJars)) { + return new DerbyDistribution(version, productionJars, testingJars); + } + // Invalid distribution, ignore it. + BaseTestCase.println("Distribution deemed invalid (note that running " + + "off classes isn't supported): " + dir.getAbsolutePath()); + return null; + } +} Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DerbyDistribution.java ------------------------------------------------------------------------------ svn:eol-style = native Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/ReleaseRepository.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/ReleaseRepository.java?rev=1330751&view=auto ============================================================================== --- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/ReleaseRepository.java (added) +++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/ReleaseRepository.java Thu Apr 26 10:20:58 2012 @@ -0,0 +1,259 @@ +/* + + Derby - Class org.apache.derbyTesting.junit.ReleaseRepository + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to you 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 org.apache.derbyTesting.junit; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.derbyTesting.functionTests.util.PrivilegedFileOpsForTests; + +/** + * A repository for Derby releases. + *

+ * The releases are used by tests, for instance by the upgrade and compatibility + * tests, to verify characteristics and behavior across Derby releases. + *

+ * This particular repository is rather dumb - it is up to the user to keep the + * repository content updated. The repository layout is based on the layout of + * the SVN repository for releases at + * {@code https://svn.apache.org/repos/asf/db/derby/jars}. This means there will + * be a directory for each release, where the directory name is the release + * version. Inside this directory, all the distribution JARs can be found. + *

+ * The repository location defaults to {@code $HOME/.derbyTestingReleases} on + * UNIX-like systems, and to {@code %UserProfile%\.derbyTestingReleases} on + * Windows (in Java, both of these maps to the system property 'user.home'). + * The location can be overridden by specifying the system property + * {@code derbyTesting.oldReleasePath}. + *

+ * If the default location doesn't exist, and the system property + * {@code derbyTesting.oldReleasePath} is unspecified, it is up to the tests + * using the release repository to decide if this condition fails the test or + * not. If the system property is set to a non-existing directory an exception + * will be thrown when instantiating the repository. + *

+ * The repository is lazily initialized, as there's no reason to incur the + * initialization cost when running tests that don't require the repository. + * The disk is inspected only when the repository is instantiated, any updates + * to the on-disk repository after the repository has been instantiated will + * not take effect. + *

+ * Implementation note: This code should be runnable with J2ME, which + * means that it needs to be compatible with J2SE 1.4 for the time being. + */ +public class ReleaseRepository { + + /** + * The property used to override the location of the repository. The name + * is used for compatibility reasons. + */ + private static final String OVERRIDE_HOME_PROP = + "derbyTesting.oldReleasePath"; + private static final File DEFAULT_HOME; + static { + String home = BaseTestCase.getSystemProperty("user.home"); + DEFAULT_HOME = new File(home, ".derbyTestingReleases"); + } + + /** The repository instance. */ + private static ReleaseRepository repos; + + /** + * Returns the release repository object. + *

+ * The release repository will be built from a default directory, or + * from the directory specified by the system property + * {@code derbyTesting.oldReleasePath}. + * + * @return The release repository object. + */ + public static synchronized ReleaseRepository getInstance() + throws IOException { + if (repos == null) { + File location = DEFAULT_HOME; + String overrideLoc = BaseTestCase.getSystemProperty( + OVERRIDE_HOME_PROP); + if (overrideLoc != null) { + location = new File(overrideLoc); + if (!PrivilegedFileOpsForTests.exists(location)) { + throw new IOException("the specified Derby release " + + "repository doesn't exist: " + location.getPath()); + } + } + repos = new ReleaseRepository(location); + repos.buildDistributionList(); + } + return repos; + } + + /** The repository location (on disk). */ + private final File reposLocation; + /** + * List of distributions found in the repository. If {@code null}, the + * repository hasn't been initialized. + */ + private List dists; + + /** + * Creates a new, empty repository. + * + * @param reposLocation the location of the repository contents + * @see #buildDistributionList() + */ + private ReleaseRepository(File reposLocation) { + this.reposLocation = reposLocation; + } + + /** + * Returns the list of distributions in the repository. + * + * @return A sorted list of Derby distributions, with the newest + * distribution at index zero, or an empty list if there are no + * distributions in the repository. + */ + public DerbyDistribution[] getDistributions() + throws IOException { + DerbyDistribution[] clone = new DerbyDistribution[dists.size()]; + dists.toArray(clone); + return clone; + } + + private void buildDistributionList() + throws IOException { + if (dists != null) { + throw new IllegalStateException("repository already initialized"); + } + + File[] tmpCandDists = reposLocation.listFiles(new FileFilter() { + + public boolean accept(File pathname) { + if (!pathname.isDirectory()) { + return false; + } + String name = pathname.getName(); + // Stay away from regexp for now (JSR169). + // Allow only digits and three dots ("10.8.1.2") + int dots = 0; + for (int i=0; i < name.length(); i++) { + char ch = name.charAt(i); + if (ch == '.') { + dots++; + } else if (!Character.isDigit(ch)) { + return false; + } + } + return dots == 3; + } + }); + if (tmpCandDists == null) { + tmpCandDists = new File[0]; + } + traceit("{ReleaseRepository} " + tmpCandDists.length + + " candidate releases at " + reposLocation); + + dists = new ArrayList(tmpCandDists.length); + for (int i=0; i < tmpCandDists.length; i++) { + File dir = tmpCandDists[i]; + // We extract the version from the directory name. + // We can also extract it by running sysinfo if that turns out to + // be necessary. + // From the check in the FileFilter we know we'll get four + // components when splitting on dot. + String[] comp = Utilities.split(dir.getName(), '.'); + DerbyVersion version; + try { + version = new DerbyVersion( + Integer.parseInt(comp[0]), + Integer.parseInt(comp[1]), + Integer.parseInt(comp[2]), + Integer.parseInt(comp[3])); + } catch (NumberFormatException nfe) { + traceit("skipped distribution, invalid version: " + + dir.getAbsolutePath()); + continue; + } + DerbyDistribution dist = DerbyDistribution.getInstance( + dir, version); + // TODO: 10.0.1.2 is considered invalid because it doesn't have a + // a client JAR. Accept, ignore, or warn all the time? + if (dist == null) { + traceit("skipped invalid distribution: " + + dir.getAbsolutePath()); + } else { + dists.add(dist); + } + } + filterDistributions(dists); + Collections.sort(dists); + dists = Collections.unmodifiableList(dists); + } + + /** + * Filters out distributions that cannot be run in the current environment + * for some reason. + *

+ * The reason for getting filtered out is typically due to lacking + * functionality or a bug in a specific Derby distribution. + * + * @param dists the list of distributions to filter (modified in-place) + */ + private void filterDistributions(List dists) { + // Specific version we want to filter out in some situations. + DerbyVersion jsr169Support = DerbyVersion._10_1; + DerbyVersion noPhoneMEBoot = DerbyVersion._10_3_1_4; + + for (int i=dists.size() -1; i >= 0; i--) { + DerbyDistribution dist = (DerbyDistribution)dists.get(i); + DerbyVersion distVersion = dist.getVersion(); + // JSR169 support was only added with 10.1, so don't + // run 10.0 to later upgrade if that's what our jvm is supporting. + if (JDBC.vmSupportsJSR169() && + distVersion.lessThan(jsr169Support)) { + println("skipping " + distVersion.toString() + " on JSR169"); + dists.remove(i); + continue; + } + // Derby 10.3.1.4 does not boot on the phoneME advanced platform, + // (see DERBY-3176) so don't run upgrade tests in this combination. + if (BaseTestCase.isPhoneME() && + noPhoneMEBoot.equals(distVersion)) { + println("skipping " + noPhoneMEBoot.toString() + + " on CVM/phoneme"); + dists.remove(i); + continue; + } + } + } + + /** Prints a trace message if tracing is enabled. */ + private static void traceit(String msg) { + BaseTestCase.traceit(msg); + } + + /** Prints a debug message if debugging is enabled. */ + private static void println(String msg) { + BaseTestCase.println(msg); + } +} Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/ReleaseRepository.java ------------------------------------------------------------------------------ svn:eol-style = native