blob: 42e5bd15f59ce06ac5f470a4dc2476aa52c2a5b0 [file] [log] [blame]
/*
*
* Derby - Class org.apache.derbyTesting.junit.BaseTestCase
*
* 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 org.apache.derbyTesting.functionTests.harness.JavaVersionHolder;
import org.apache.derbyTesting.functionTests.util.PrivilegedFileOpsForTests;
import junit.framework.Assert;
import junit.framework.TestCase;
import junit.framework.AssertionFailedError;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.InterruptedIOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.net.URL;
import java.sql.SQLException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
/**
* Base class for JUnit tests.
*/
public abstract class BaseTestCase
extends TestCase {
private static final String JACOCO_AGENT_PROP = "derby.tests.jacoco.agent";
protected final static String ERRORSTACKTRACEFILE = "error-stacktrace.out";
protected final static String DEFAULT_DB_DIR = "system";
protected final static String DERBY_LOG = "derby.log";
private static int debugPort; // default 8800
/**
* No argument constructor made private to enforce naming of test cases.
* According to JUnit documentation, this constructor is provided for
* serialization, which we don't currently use.
*
* @see #BaseTestCase(String)
*/
private BaseTestCase() {}
/**
* Create a test case with the given name.
*
* @param name name of the test case.
*/
public BaseTestCase(String name) {
super(name);
}
/**
* Run the test and force installation of a security
* manager with the default test policy file.
* Individual tests can run without a security
* manager or with a different policy file using
* the decorators obtained from SecurityManagerSetup.
* <BR>
* Method is final to ensure security manager is
* enabled by default. Tests should not need to
* override runTest, instead use test methods
* setUp, tearDown methods and decorators.
*/
public void runBare() throws Throwable {
TestConfiguration config = getTestConfiguration();
boolean trace = config.doTrace();
boolean stopAfterFirstFail = config.stopAfterFirstFail();
long startTime = 0;
if ( trace )
{
startTime = System.currentTimeMillis();
out.println();
String junitClassName = this.getClass().getName();
junitClassName=Utilities.formatTestClassNames(junitClassName);
out.print(traceClientType());
out.print(junitClassName+"."+getName() + " ");
}
// install a default security manager if one has not already been
// installed
if ( System.getSecurityManager() == null )
{
if (config.defaultSecurityManagerSetup())
{
assertSecurityManager();
}
}
try {
super.runBare();
}
// To log the exception to file, copy the derby.log file and copy
// the database of the failed test.
catch (Throwable running) {
PrintWriter stackOut = null;
try{
String failPath = PrivilegedFileOpsForTests.getAbsolutePath(getFailureFolder());
// Write the stack trace of the error/failure to file.
stackOut = new PrintWriter(
PrivilegedFileOpsForTests.getFileOutputStream(
new File(failPath, ERRORSTACKTRACEFILE), true));
stackOut.println("[Error/failure logged at " +
new java.util.Date() + "]");
running.printStackTrace(stackOut);
stackOut.println(); // Add an extra blank line.
// Copy the derby.log file.
File origLog = new File(DEFAULT_DB_DIR, DERBY_LOG);
File newLog = new File(failPath, DERBY_LOG);
PrivilegedFileOpsForTests.copy(origLog, newLog);
// Copy some other likely files, the rolling log files
// These might occur if the tests are run with
// derby.stream.error.style=rollingFile
for (int i=0; i < 10; i++) {
String logName = "derby-" + i + ".log";
File origRolLog = new File(DEFAULT_DB_DIR, logName);
File newRolLog = new File(failPath, logName);
PrivilegedFileOpsForTests.copy(origRolLog, newRolLog);
}
// Copy the database.
String dbName = TestConfiguration.getCurrent().getDefaultDatabaseName();
File dbDir = new File(DEFAULT_DB_DIR, dbName );
File newDbDir = new File(failPath, dbName);
PrivilegedFileOpsForTests.copy(dbDir,newDbDir);
}
catch (IOException ioe) {
// We need to throw the original exception so if there
// is an exception saving the db or derby.log we will print it
// and additionally try to log it to file.
BaseTestCase.printStackTrace(ioe);
if (stackOut != null) {
stackOut.println("Copying derby.log or database failed:");
ioe.printStackTrace(stackOut);
stackOut.println();
}
}
finally {
if (stackOut != null) {
stackOut.close();
}
if (stopAfterFirstFail) {
// if run with -Dderby.tests.stopAfterFirstFail=true
// exit after reporting failure. Useful for debugging
// cascading failures or errors that lead to hang.
running.printStackTrace(out);
System.exit(1);
}
else
throw running;
}
}
finally{
if ( trace )
{
long timeUsed = System.currentTimeMillis() - startTime;
out.print("used " + timeUsed + " ms ");
}
}
}
/**
* Return the current configuration for the test.
*/
public final TestConfiguration getTestConfiguration()
{
return TestConfiguration.getCurrent();
}
/**
* Get the folder where a test leaves any information
* about its failure.
* @return Folder to use.
* @see TestConfiguration#getFailureFolder(TestCase)
*/
public final File getFailureFolder() {
return getTestConfiguration().getFailureFolder(this);
}
/**
* Print alarm string
* @param text String to print
*/
public static void alarm(final String text) {
out.println("ALARM: " + text);
}
/**
* Print debug string.
* @param text String to print
*/
public static void println(final String text) {
if (TestConfiguration.getCurrent().isVerbose()) {
out.println("DEBUG: " + text);
out.flush();
}
}
/**
* Print trace string.
* @param text String to print
*/
public static void traceit(final String text) {
if (TestConfiguration.getCurrent().doTrace()) {
out.println(text);
}
}
/**
* Print debug string.
* @param t Throwable object to print stack trace from
*/
public static void printStackTrace(Throwable t)
{
while ( t!= null) {
t.printStackTrace(out);
out.flush();
if (t instanceof SQLException) {
t = ((SQLException) t).getNextException();
} else {
break;
}
}
}
private final static PrintStream out = System.out;
/**
* Change the value of {@code System.out}.
*
* @param out the new stream
*/
protected static void setSystemOut(final PrintStream out) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
System.setOut(out);
return null;
}
});
}
/**
* Change the value of {@code System.err}.
*
* @param err the new stream
*/
protected static void setSystemErr(final PrintStream err) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
System.setErr(err);
return null;
}
});
}
/**
* Set system property
*
* @param name name of the property
* @param value value of the property
*/
protected static void setSystemProperty(final String name,
final String value)
{
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
System.setProperty(name, value);
return null;
}
});
}
/**
* Remove system property
*
* @param name name of the property
*/
public static void removeSystemProperty(final String name)
{
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
System.getProperties().remove(name);
return null;
}
});
}
/**
* Get system property.
*
* @param name name of the property
*/
protected static String getSystemProperty(final String name)
{
return AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty(name);
}
});
}
/**
* Get files in a directory which contain certain prefix
*
* @param dir
* The directory we are checking for files with certain prefix
* @param prefix
* The prefix pattern we are interested.
* @return The list indicates files with certain prefix.
*/
protected static String[] getFilesWith(final File dir, String prefix) {
return AccessController.doPrivileged(new PrivilegedAction<String[]>() {
public String[] run() {
//create a FilenameFilter and override its accept-method to file
//files start with "javacore"*
FilenameFilter filefilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
//if the file has prefix javacore return true, else false
return name.startsWith("javacore");
}
};
return dir.list(filefilter);
}
});
}
/**
* Obtain the URL for a test resource, e.g. a policy
* file or a SQL script.
* @param name Resource name, typically - org.apache.derbyTesing.something
* @return URL to the resource, null if it does not exist.
*/
protected static URL getTestResource(final String name)
{
return AccessController.doPrivileged(new PrivilegedAction<URL>() {
public URL run() {
return BaseTestCase.class.getClassLoader().getResource(name);
}
});
}
/**
* Open the URL for a a test resource, e.g. a policy
* file or a SQL script.
* @param url URL obtained from getTestResource
* @return An open stream
*/
protected static InputStream openTestResource(final URL url)
throws PrivilegedActionException
{
return AccessController.doPrivileged(
new PrivilegedExceptionAction<InputStream>() {
public InputStream run() throws IOException {
return url.openStream();
}
});
}
/**
* Assert a security manager is installed.
*
*/
public static void assertSecurityManager()
{
assertNotNull("No SecurityManager installed",
System.getSecurityManager());
}
/**
* Compare the contents of two streams.
* The streams are closed after they are exhausted.
*
* @param is1 the first stream
* @param is2 the second stream
* @throws IOException if reading from the streams fail
* @throws AssertionFailedError if the stream contents are not equal
*/
public static void assertEquals(InputStream is1, InputStream is2)
throws IOException {
if (is1 == null || is2 == null) {
assertNull("InputStream is2 is null, is1 is not", is1);
assertNull("InputStream is1 is null, is2 is not", is2);
return;
}
long index = 0;
int b1 = is1.read();
int b2 = is2.read();
do {
// Avoid string concatenation for every byte in the stream.
if (b1 != b2) {
assertEquals("Streams differ at index " + index, b1, b2);
}
index++;
b1 = is1.read();
b2 = is2.read();
} while (b1 != -1 || b2 != -1);
is1.close();
is2.close();
}
/**
* Compare the contents of two readers.
* The readers are closed after they are exhausted.
*
* @param r1 the first reader
* @param r2 the second reader
* @throws IOException if reading from the streams fail
* @throws AssertionFailedError if the reader contents are not equal
*/
public static void assertEquals(Reader r1, Reader r2)
throws IOException {
long index = 0;
if (r1 == null || r2 == null) {
assertNull("Reader r2 is null, r1 is not", r1);
assertNull("Reader r1 is null, r2 is not", r2);
return;
}
int c1 = r1.read();
int c2 = r2.read();
do {
// Avoid string concatenation for every char in the stream.
if (c1 != c2) {
assertEquals("Streams differ at index " + index, c1, c2);
}
index++;
c1 = r1.read();
c2 = r2.read();
} while (c1 != -1 || c2 != -1);
r1.close();
r2.close();
}
/**
* Assert that the detailed messages of the 2 passed-in Throwable's are
* equal (rather than '=='), as well as their class types.
*
* @param t1 first throwable to compare
* @param t2 second throwable to compare
*/
public static void assertThrowableEquals(Throwable t1,
Throwable t2) {
// Ensure non-null throwable's are being passed.
assertNotNull(
"Passed-in throwable t1 cannot be null to assert detailed message",
t1);
assertNotNull(
"Passed-in throwable t2 cannot be null to assert detailed message",
t2);
// Now verify that the passed-in throwable are of the same type
assertEquals("Throwable class types are different",
t1.getClass().getName(), t2.getClass().getName());
// Here we finally check that the detailed message of both
// throwable's is the same
assertEquals("Detailed messages of the throwable's are different",
t1.getMessage(), t2.getMessage());
}
/**
* <p>
* Assert the equivalence of two byte arrays.
* </p>
*/
public static void assertEquals( byte[] expected, byte[] actual )
{
if ( assertSameNullness( expected, actual ) ) { return; }
assertEquals( expected.length, actual.length );
for ( int i = 0; i < expected.length; i++ )
{
assertEquals( Integer.toString( i ), expected[ i ], actual[ i ] );
}
}
/**
* Assert that two objects are either both null or neither null.
* Returns true if they are null.
*/
public static boolean assertSameNullness( Object expected, Object actual )
{
if ( expected == null )
{
assertNull( actual );
return true;
}
else
{
assertNotNull( actual );
return false;
}
}
/**
* <p>
* Assert the equivalence of two int arrays.
* </p>
*/
public static void assertEquals( int[] expected, int[] actual )
{
if ( assertSameNullness( expected, actual ) ) { return; }
assertEquals( expected.length, actual.length );
for ( int i = 0; i < expected.length; i++ )
{
assertEquals( Integer.toString( i ), expected[ i ], actual[ i ] );
}
}
/**
* <p>
* Assert the equivalence of two long arrays.
* </p>
*/
public static void assertEquals( long[] expected, long[] actual )
{
if ( assertSameNullness( expected, actual ) ) { return; }
assertEquals( expected.length, actual.length );
for ( int i = 0; i < expected.length; i++ )
{
assertEquals( Integer.toString( i ), expected[ i ], actual[ i ] );
}
}
/**
* Assert that two files in the filesystem are identical.
*
* @param file1 the first file to compare
* @param file2 the second file to compare
*/
public static void assertEquals(final File file1, final File file2) {
AccessController.doPrivileged
(new PrivilegedAction<Void>() {
public Void run() {
try {
InputStream f1 = new BufferedInputStream(new FileInputStream(file1));
InputStream f2 = new BufferedInputStream(new FileInputStream(file2));
assertEquals(f1, f2);
} catch (FileNotFoundException e) {
fail("FileNotFoundException in assertEquals(File,File): " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
fail("IOException in assertEquals(File, File): " + e.getMessage());
e.printStackTrace();
}
return null;
}
});
}
/**
* Execute command using 'java' executable and verify that it completes
* with expected results
* @param expectedString String to compare the resulting output with. May be
* null if the output is not expected to be of interest.
* @param cmd array of java arguments for command
* @param expectedExitValue expected return value from the command
* @throws InterruptedException
* @throws IOException
*/
public static void assertExecJavaCmdAsExpected(String[] expectedString,
String[] cmd, int expectedExitValue) throws InterruptedException,
IOException {
Process pr = execJavaCmd(cmd);
String output = readProcessOutput(pr);
int exitValue = pr.exitValue();
String expectedStrings = "";
for (int i = 0; i < expectedString.length; i++)
expectedStrings += "\t[" +i + "]" + expectedString[i] + "\n";
Assert.assertEquals("expectedExitValue:" + expectedExitValue +
" does not match exitValue:" + exitValue +"\n" +
"expected output strings:\n" + expectedStrings +
" actual output:" + output,
expectedExitValue, exitValue);
if (expectedString != null) {
for (int i = 0; i < expectedString.length; i++) {
assertTrue("Could not find expectedString:" +
expectedString[i] + " in output:" + output,
output.indexOf(expectedString[i]) >= 0);
}
}
}
/**
* Same as {@link #execJavaCmd( String, String, String[], File, boolean )}
* but with {@code addClassPath == true}.
*/
public static Process execJavaCmd(
String jvm, String cp, String[] cmd, final File dir)
throws IOException {
return execJavaCmd(jvm, cp, cmd, dir, true);
}
/**
* Execute a java command and return the process.
* The caller should decide what to do with the process, if anything,
* typical activities would be to do a pr.waitFor, or to
* get a getInputStream or getErrorStream
* Note, that for verifying the output of a Java process, there is
* assertExecJavaCmdAsExpected
*
* @param jvm the path to the java executable, or {@code null} to use
* the default executable returned by
* {@link #getJavaExecutableName()}
* @param cp the classpath for the spawned process, or {@code null} to
* inherit the classpath from the parent process
* @param cmd array of java arguments for command
* @param dir working directory for the sub-process, or {@code null} to
* run in the same directory as the main test process
* @param addClassPath if {@code true},add classpath
* @return the process that was started
* @throws IOException
*/
public static Process execJavaCmd(
String jvm, String cp, String[] cmd, final File dir, boolean addClassPath)
throws IOException {
// Is this an invocation of a jar file with java -jar ...?
final boolean isJarInvocation = cmd.length > 0 && cmd[0].equals("-jar");
ArrayList<String> cmdlist = new ArrayList<String>();
cmdlist.add(jvm == null ? getJavaExecutableName() : jvm);
if (isJ9Platform())
{
cmdlist.add("-jcl:foun11");
// also add the setting for emma.active so any tests
// that fork will work correctly. See DERBY-5558.
String emmaactive=getSystemProperty("emma.active");
if (emmaactive != null) {
cmdlist.add("-Demma.active=" + emmaactive);
}
// Do the same for jacoco.active, see DERBY-6079.
String jacocoactive = getSystemProperty("jacoco.active");
if (jacocoactive != null) {
cmdlist.add("-Djacoco.active=" + jacocoactive);
}
}
if (isCVM()) {
// DERBY-5642: The default maximum heap size on CVM is very low.
// Increase it to prevent OOME in the forked process.
cmdlist.add("-Xmx32M");
}
if (runsWithEmma()) {
// DERBY-5801: If many processes write to the same file, it may
// end up corrupted. Let each process have its own file to which
// it writes coverage data.
cmdlist.add("-Demma.coverage.out.file=" + getEmmaOutFile());
// DERBY-5810: Make sure that emma.jar is included on the
// classpath of the sub-process. (Only needed if a specific
// classpath has been specified. Otherwise, the sub-process
// inherits the classpath from the parent process, which
// already includes emma.jar.)
if (cp != null) {
cp += File.pathSeparator + getEmmaJar().getPath();
}
// DERBY-5821: When starting a sub-process with java -jar, the
// classpath argument will be ignored, so we cannot add emma.jar
// that way. Add it to the boot classpath instead.
if (isJarInvocation) {
cmdlist.add("-Xbootclasspath/a:" + getEmmaJar().getPath());
}
}
if (runsWithJaCoCo()) {
// Property (http://www.eclemma.org/jacoco/trunk/doc/agent.html):
// -javaagent:[yourpath/]jacocoagent.jar=[opt1]=[val1],[opt2]=[val2]
String agent = getSystemProperty(JACOCO_AGENT_PROP);
cmdlist.add(agent + (agent.endsWith("=") ? "": ",") +
"destfile=" + getJaCoCoOutFile());
cmdlist.add("-Djacoco.active=");
}
if (isSunJVM() && Boolean.valueOf(
getSystemProperty("derby.test.debugSubprocesses")).
booleanValue()) {
setupForDebuggerAttach(cmdlist);
}
if (isJarInvocation) {
// If -jar is specified, the Java command will ignore the user's
// classpath, so don't set it. Fail if an explicit classpath has
// been set in addition to -jar, as that's probably a mistake in
// the calling code.
assertNull("Both -jar and classpath specified", cp);
} else if (addClassPath) {
cmdlist.add("-classpath");
cmdlist.add(cp == null ? getSystemProperty("java.class.path") : cp);
}
for (int i =0; i < cmd.length;i++) {
cmdlist.add(cmd[i]);
}
final String[] command = (String[]) cmdlist.toArray(cmd);
println("execute java command:");
for (int i = 0; i < command.length; i++) {
println("command[" + i + "]" + command[i]);
}
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<Process>() {
public Process run() throws IOException {
return Runtime.getRuntime().exec(
command, (String[]) null, dir);
}
});
} catch (PrivilegedActionException pe) {
throw (IOException) pe.getException();
}
}
/**
* Execute a java command and return the process. The process will run
* in the same directory as the main test process. This method is a
* shorthand for {@code execJavaCmd(null, null, cmd, null)}.
*/
public static Process execJavaCmd(String[] cmd) throws IOException {
return execJavaCmd(null, null, cmd, null);
}
/**
* Return the executable name for spawning java commands.
* This will be <path to j9>/j9 for j9 jvms.
* @return full path to java executable.
*/
public static final String getJavaExecutableName() {
String vmname = getSystemProperty("com.ibm.oti.vm.exe");
if (vmname == null) {
vmname = getSystemProperty("java.vm.name");
// Sun phoneME
if ("CVM".equals(vmname)) {
vmname = getSystemProperty("java.home") +
File.separator + "bin" +
File.separator + "cvm";
} else {
vmname = getSystemProperty("java.home") +
File.separator + "bin" +
File.separator + "java";
}
}
return vmname;
}
/**
* <p>
* Return the current directory.
* </p>
*/
public static File currentDirectory()
{
return new File( getSystemProperty( "user.dir" ) );
}
/**
* @return true if this is a j9 VM
*/
public static final boolean isJ9Platform() {
return getSystemProperty("com.ibm.oti.vm.exe") != null;
}
public static final boolean isSunJVM() {
String vendor = getSystemProperty("java.vendor");
return "Sun Microsystems Inc.".equals(vendor) ||
"Oracle Corporation".equals(vendor);
}
/**
* Check if this is a CVM-based VM (like phoneME or Oracle Java ME
* Embedded Client).
*/
public static boolean isCVM() {
return "CVM".equals(getSystemProperty("java.vm.name"));
}
/**
* Check if the VM is phoneME.
*
* @return true if it is phoneME
*/
public static boolean isPhoneME() {
return isCVM() &&
getSystemProperty("java.vm.version").startsWith("phoneme");
}
/**
* Determine if there is a platform match with os.name.
* This method uses an exact equals. Other methods might be useful
* later for starts with.
*
* @param osName value we want to check against the system property
* os.name
* @return return true if osName is an exact match for osName
*/
public static final boolean isPlatform(String osName) {
return getSystemProperty("os.name").equals(osName);
}
/**
* Determine if platform is a Windows variant.
* <p>
* Return true if platform is a windows platform. Just looks for
* os.name starting with "Windows". The os.name property
* can have at least the following values (there are probably more):
*
* AIX
* Digital Unix
* FreeBSD
* HP UX
* Irix
* Linux
* Mac OS
* Mac OS X
* MPE/iX
* Netware 4.11
* OS/2
* SunOS
* Windows 2000
* Windows 95
* Windows 98
* Windows NT
* Windows Vista
* Windows XP
* <p>
*
* @return true if running on a Windows platform.
**/
public static final boolean isWindowsPlatform() {
return getSystemProperty("os.name").startsWith("Windows");
}
/**
* Check if this is java 5
* @return true if java.version system property starts with 1.5
*/
public static final boolean isJava5() {
return getSystemProperty("java.version").startsWith("1.5");
}
public static final boolean isJava7() {
return getSystemProperty("java.version").startsWith("1.7");
}
public static final boolean runsWithEmma() {
return getSystemProperty("java.class.path").indexOf("emma.jar") != -1;
}
public static boolean runsWithJaCoCo() {
return SecurityManagerSetup.jacocoEnabled;
}
/**
* Counter used to produce unique file names based on process count.
*
* @see #getEmmaOutFile()
* @see #getJaCoCoOutFile()
*/
private static int spawnedCount = 0;
/**
* Get a unique file object that can be used by sub-processes to store
* JaCoCo code coverage data. Each separate sub-process should have its
* own file in order to prevent corruption of the coverage data.
*
* @return a file to which a sub-process can write code coverage data
*/
private static synchronized File getJaCoCoOutFile() {
return new File(currentDirectory(),
"jacoco.exec." + (++spawnedCount));
}
/**
* Get a unique file object that can be used by sub-processes to store
* EMMA code coverage data. Each separate sub-process should have its
* own file in order to prevent corruption of the coverage data.
*
* @return a file to which a sub-process can write code coverage data
*/
private static synchronized File getEmmaOutFile() {
return new File(currentDirectory(),
"coverage-" + (++spawnedCount) + ".ec");
}
/**
* Get a URL pointing to {@code emma.jar}, if the tests are running
* with EMMA code coverage. The method returns {@code null} if the
* tests are not running with EMMA.
*/
public static URL getEmmaJar() {
return SecurityManagerSetup.getURL("com.vladium.emma.EMMAException");
}
/**
* Returns the major version of the class specification version supported
* by the running JVM.
* <ul>
* <li>48 = Java 1.4</li>
* <li>49 = Java 1.5</li>
* <li>50 = Java 1.6</li>
* <li>51 = Java 1.7</li>
* </ul>
*
* @return Major version of class version specification, i.e. 49 for 49.0,
* or -1 if the version can't be obtained for some reason.
*/
public static int getClassVersionMajor() {
String tmp = getSystemProperty("java.class.version");
if (tmp == null) {
println("VM doesn't have property java.class.version");
return -1;
}
// Is String.split safe to use by now?
int dot = tmp.indexOf('.');
int major = -1;
try {
major = Integer.parseInt(tmp.substring(0, dot));
} catch (NumberFormatException nfe) {
// Ignore, return -1.
}
return major;
}
/**
* Check if we have old style (before Sun Java 1.7) Solaris interruptible
* IO. On Sun Java 1.5 &gt;= update 22 and Sun Java 1.6 this can be disabled
* with Java option {@code -XX:-UseVMInterruptibleIO}. On Sun Java 1.7 it
* is by default disabled.
*
* @return true if we have old style interruptible IO
*/
public static final boolean hasInterruptibleIO() {
boolean interruptibleIO = false;
try {
AccessController.doPrivileged(
new PrivilegedExceptionAction<Void>() {
public Void run() throws
IOException, InterruptedIOException {
TestConfiguration curr = TestConfiguration.getCurrent();
String sysHome = getSystemProperty("derby.system.home");
StringBuffer arbitraryRAFFileNameB = new StringBuffer();
arbitraryRAFFileNameB.append(sysHome);
arbitraryRAFFileNameB.append(File.separatorChar);
arbitraryRAFFileNameB.append("derby.log");
String arbitraryRAFFileName =
arbitraryRAFFileNameB.toString();
// Create if it does not exist:
new File(sysHome).mkdirs(); // e.g. "system"
new File(arbitraryRAFFileName).createNewFile();
RandomAccessFile f = new RandomAccessFile(
arbitraryRAFFileName, "r");
try {
Thread.currentThread().interrupt();
f.read();
} finally {
Thread.interrupted(); // clear flag
f.close();
}
return null;
}});
} catch (PrivilegedActionException e) {
if (e.getCause() instanceof InterruptedIOException) {
interruptibleIO = true;
} else {
// Better to assume nothing when the test fails. Then, tests
// will not be skipped and we would not miss that something is
// amiss.
println("Could not test for interruptible IO," +
" so assuming we don't have it: " + e);
e.getCause().printStackTrace();
return false;
}
}
return interruptibleIO;
}
public static final boolean isIBMJVM() {
return ("IBM Corporation".equals(
getSystemProperty("java.vendor")));
}
/**
* Reads output from a process and returns it as a string.
* <p>
* This will block until the process terminates.
*
* @param pr a running process
* @return Output of the process, both STDOUT and STDERR.
* @throws InterruptedException if interrupted while waiting for the
* subprocess or one of the output collector threads to terminate
*/
public static String readProcessOutput(Process pr)
throws InterruptedException {
SpawnedProcess wrapper = new SpawnedProcess(pr, "readProcessOutput");
wrapper.suppressOutputOnComplete();
try {
wrapper.complete();
} catch (IOException ioe) {
fail("process completion method failed", ioe);
}
String output = "<STDOUT>" + wrapper.getFullServerOutput() +
"<END STDOUT>\n";
output += "<STDERR>" + wrapper.getFullServerError() +
"<END STDERR>\n";
return output;
}
/**
* Deletes the specified directory and all its files and subdirectories.
* <p>
* This method will attempt to delete all the files inside the root
* directory, even if one of the delete operations fails.
* <p>
* After having tried to delete all files once, any remaining files will be
* attempted deleted again after a pause. This is repeated, resulting
* in multiple failed delete attempts for any single file before the method
* gives up and raises a failure.
* <p>
* The approach above will mask any slowness involved in releasing file
* handles, but should fail if a file handle actually isn't released on a
* system that doesn't allow deletes on files with open handles (i.e.
* Windows). It will also mask slowness caused by the JVM, the file system,
* or the operation system.
*
* @param dir the root to start deleting from (root will also be deleted)
*/
public static void assertDirectoryDeleted(File dir) {
File[] fl = null;
int attempts = 0;
while (attempts < 4) {
try {
Thread.sleep(attempts * 2000);
} catch (InterruptedException ie) {
// Ignore
}
try {
fl = PrivilegedFileOpsForTests.persistentRecursiveDelete(dir);
attempts++;
} catch (FileNotFoundException fnfe) {
if (attempts == 0) {
fail("directory doesn't exist: " +
PrivilegedFileOpsForTests.getAbsolutePath(dir));
} else {
// In the previous iteration we saw remaining files, but
// now the root directory is gone. Not what we expected...
System.out.println("<assertDirectoryDeleted> root " +
"directory unexpectedly gone - delayed, " +
"external or concurrent delete?");
return;
}
}
if (fl.length == 0) {
return;
} else {
// Print the list of remaining files to stdout for debugging.
StringBuffer sb = new StringBuffer();
sb.append("<assertDirectoryDeleted> attempt ").append(attempts).
append(" left ").append(fl.length).
append(" files/dirs behind:");
for (int i = 0; i < fl.length; i++) {
sb.append(' ').append(i).append('=').append(fl[i]);
}
System.out.println(sb);
}
}
// If we failed to delete some of the files, list them and obtain some
// information about each file.
StringBuffer sb = new StringBuffer();
for (int i = 0; i < fl.length; i++) {
File f = fl[i];
sb.append(PrivilegedFileOpsForTests.getAbsolutePath(f)).append(' ').
append(PrivilegedFileOpsForTests.getFileInfo(f)).append(", ");
}
sb.deleteCharAt(sb.length() - 1).deleteCharAt(sb.length() - 1);
fail("Failed to delete " + fl.length + " files (root=" +
PrivilegedFileOpsForTests.getAbsolutePath(dir) + "): " +
sb.toString());
}
/**
* Remove the directory and its contents.
* @param path Path of the directory
*/
public static void removeDirectory(String path)
{
DropDatabaseSetup.removeDirectory(path);
}
/**
* Remove the directory and its contents.
* @param dir File of the directory
*/
public static void removeDirectory(File dir)
{
DropDatabaseSetup.removeDirectory(dir);
}
/**
* Remove all the files in the list
* @param list the list contains all the files
*/
public static void removeFiles(String[] list)
{
DropDatabaseSetup.removeFiles(list);
}
/**
* Fail; attaching an exception for more detail on cause.
*
* @param msg message explaining the failure
* @param t the cause of the failure
*
* @exception AssertionFailedError
*/
public static void fail(String msg, Throwable t)
throws AssertionFailedError {
throw newAssertionFailedError(msg, t);
}
/**
* Create a new AssertionFailedError linked to another Throwable.
*
* @param message message explaining the failure
* @param cause the cause of the failure
* @return an AssertionFailedError
*/
public static AssertionFailedError newAssertionFailedError(
String message, Throwable cause) {
AssertionFailedError e = new AssertionFailedError(message);
e.initCause(cause);
return e;
}
/**
* assert a method from an executing test
*
* @param testLaunchMethod
* complete pathname of the method to be executed
* @throws Exception
*/
public static void assertLaunchedJUnitTestMethod(String testLaunchMethod)
throws Exception
{
String[] cmd = new String[] { "junit.textui.TestRunner", "-m",
testLaunchMethod };
assertExecJavaCmdAsExpected(new String[] { "OK (1 test)" }, cmd, 0);
}
/**
* assert a method from an executing test
*
* @param testLaunchMethod
* complete pathname of the method to be executed
* @param databaseName
* name of the database to be used
* @throws Exception
*/
public static void assertLaunchedJUnitTestMethod(String testLaunchMethod,
String databaseName)
throws Exception
{
String[] cmd = new String[] {
"-Dderby.tests.defaultDatabaseName=" + databaseName,
"junit.textui.TestRunner", "-m", testLaunchMethod };
assertExecJavaCmdAsExpected(new String[] { "OK (1 test)" }, cmd, 0);
}
/** Returns once the system timer has advanced at least one tick. */
public static void sleepAtLeastOneTick() {
long currentTime = System.currentTimeMillis();
while (System.currentTimeMillis() == currentTime) {
sleep(1);
}
}
/** Return true if the JVM is at least at the indicated rev level */
public static boolean vmAtLeast( int major, int minor )
{
String version = AccessController.doPrivileged
(new PrivilegedAction<String>(){
public String run(){
return System.getProperty( "java.version" );
}
}
);
JavaVersionHolder jvh = new JavaVersionHolder( version );
return jvh.atLeast( major, minor );
}
/** Makes the current thread sleep up to {@code ms} milliseconds. */
public static void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException ie) {
// For now we just print a warning if we are interrupted.
alarm("sleep interrupted");
}
}
private static String traceClientType() {
if (TestConfiguration.getCurrent().getJDBCClient().isEmbedded()) {
return "(emb)";
} else {
return "(net)";
}
}
private static void setupForDebuggerAttach(ArrayList<String> cmdlist) {
if (debugPort == 0) {
// lazy initialization
String dbp = getSystemProperty("derby.test.debugPortBase");
debugPort = 8800; // default
if (dbp != null) {
try {
debugPort = Integer.parseInt(dbp);
} catch (NumberFormatException e) {
// never mind
}
}
}
char suspend = 'y'; // default
String susp = getSystemProperty("derby.test.debugSuspend");
if (susp != null && "n".equals(susp.toLowerCase())) {
suspend = 'n';
}
cmdlist.add("-Xdebug");
cmdlist.add("-Xrunjdwp:transport=dt_socket,address=" + (debugPort++) +
",server=y,suspend=" + suspend);
}
} // End class BaseTestCase