Fixed line endings

git-svn-id: svn://10.0.0.236/trunk@212454 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
szegedia%freemail.hu
2006-09-27 08:52:46 +00:00
parent fbcda0e82b
commit fcdaeac01f
4 changed files with 534 additions and 528 deletions

View File

@@ -1,9 +1,15 @@
Changes since Rhino 1.6R4
=========================
- Added org.mozilla.javascript.PolicySecurityController as a concrete
implementation of an org.mozilla.javascript.SecurityController and the
preferred way of integrating with Java security architecture.
- When no security controller is in use, generated classes and scripts run in
the ProtectionDomain of Rhino classes.
- Wrapped access to system properties and creation of class loaders into
AccessController.doPrivileged() to nicely play in secured environments.
This file version: $Id: CHANGELOG,v 1.2 2006-09-27 08:50:41 szegedia%freemail.hu Exp $
Changes since Rhino 1.6R4
=========================
- Added org.mozilla.javascript.PolicySecurityController as a concrete
implementation of an org.mozilla.javascript.SecurityController and the
preferred way of integrating with Java security architecture.
- When no security controller is in use, generated classes and scripts run in
the ProtectionDomain of Rhino classes.
- Wrapped access to system properties and creation of class loaders into
AccessController.doPrivileged() to nicely play in secured environments.
- Fixed #353300: Rhino now implements the ECMA-262 mandatory
String.localeCompare
- Fixed #352319: Rhino threw a ClassCastException when a continuation captured
from a catch() block was restarted.

View File

@@ -1,175 +1,175 @@
package org.mozilla.javascript;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.UndeclaredThrowableException;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.Policy;
import java.security.PrivilegedAction;
import java.security.SecureClassLoader;
import java.util.Map;
import java.util.Random;
import java.util.WeakHashMap;
import org.mozilla.classfile.ClassFileWriter;
/**
* A security controller relying on Java {@link Policy} in effect. When you use
* this security controller, your securityDomain objects must be instances of
* {@link CodeSource} representing the location from where you load your
* scripts. Any Java policy "grant" statements matching the URL and certificate
* in code sources will apply to the scripts. If you specify any certificates
* within your {@link CodeSource} objects, it is your responsibility to verify
* (or not) that the script source files are signed in whatever
* implementation-specific way you're using.
* @author Attila Szegedi
* @version $Id: PolicySecurityController.java,v 1.1 2006-09-10 12:16:46 szegedia%freemail.hu Exp $
*/
public class PolicySecurityController extends SecurityController
{
// Use weak map, so stuff related to abandoned code sources gets cleaned up
// automatically.
private static final Map secureCallers = new WeakHashMap();
private static class Loader extends SecureClassLoader
implements GeneratedClassLoader
{
private final CodeSource codeSource;
Loader(ClassLoader parent, CodeSource codeSource)
{
super(parent);
this.codeSource = codeSource;
}
public Class defineClass(String name, byte[] data)
{
return defineClass(name, data, 0, data.length, codeSource);
}
public void linkClass(Class cl)
{
resolveClass(cl);
}
}
public GeneratedClassLoader createClassLoader(ClassLoader parentLoader,
Object securityDomain)
{
return createLoader(parentLoader, (CodeSource)securityDomain);
}
private Loader createLoader(
final ClassLoader parent, final CodeSource securityDomain)
{
return (Loader)AccessController.doPrivileged(
new PrivilegedAction()
{
public Object run()
{
return new Loader(parent, securityDomain);
}
});
}
public Object getDynamicSecurityDomain(Object securityDomain)
{
// No separate notion of dynamic security domain - just return what was
// passed in.
return securityDomain;
}
public Object callWithDomain(Object securityDomain, Context cx,
Callable callable, Scriptable scope, Scriptable thisObj,
Object[] args)
{
SecureCaller secureCaller;
CodeSource codeSource = (CodeSource)securityDomain;
ClassLoader appClassLoader = cx.getApplicationClassLoader();
DomainKey key = new DomainKey(codeSource, appClassLoader);
synchronized(secureCallers)
{
Reference ref = (Reference)secureCallers.get(key);
if(ref != null)
{
secureCaller = (SecureCaller)ref.get();
}
else
{
secureCaller = null;
}
if(secureCaller == null)
{
Loader l = createLoader(appClassLoader, codeSource);
String name = SecureCaller.class.getName() + "$Sub" +
// Being a bit paranoid about name clashes - if anyone'd figure
// out how to get to the class loader this might become a
// concern
Integer.toHexString(key.hashCode()) + "_" + Long.toHexString(
new Random().nextLong());
// Just a trivial "class ... extends SecureCaller {}"
// class.
ClassFileWriter cfw = new ClassFileWriter(name,
SecureCaller.class.getName(), "<generated>");
Class clazz = l.defineClass(name, cfw.toByteArray());
try
{
secureCaller = (SecureCaller)clazz.newInstance();
// Using a soft reference, so we don't hold strongly on the
// code source and allow it to get cleaned up eventually.
secureCallers.put(key, new SoftReference(secureCaller));
}
catch (InstantiationException e)
{
throw new UndeclaredThrowableException(e);
}
catch (IllegalAccessException e)
{
throw new UndeclaredThrowableException(e);
}
}
}
return secureCaller.call(callable, cx, scope, thisObj, args);
}
public static class SecureCaller
{
public Object call(Callable callable, Context cx, Scriptable scope,
Scriptable thisObj, Object[] args)
{
return callable.call(cx, scope, thisObj, args);
}
}
private static final class DomainKey
{
private final CodeSource codeSource;
private final ClassLoader appClassLoader;
DomainKey(CodeSource codeSource, ClassLoader appClassLoader)
{
this.codeSource = codeSource;
if(codeSource == null)
{
throw new IllegalArgumentException("codeSource == null");
}
this.appClassLoader = appClassLoader;
}
public boolean equals(Object obj)
{
if(obj == null || obj.getClass() != DomainKey.class)
{
return false;
}
DomainKey other = (DomainKey)obj;
return codeSource.equals(other.codeSource) && appClassLoader == other.appClassLoader;
}
public int hashCode()
{
return codeSource.hashCode() ^ System.identityHashCode(appClassLoader);
}
}
}
package org.mozilla.javascript;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.UndeclaredThrowableException;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.Policy;
import java.security.PrivilegedAction;
import java.security.SecureClassLoader;
import java.util.Map;
import java.util.Random;
import java.util.WeakHashMap;
import org.mozilla.classfile.ClassFileWriter;
/**
* A security controller relying on Java {@link Policy} in effect. When you use
* this security controller, your securityDomain objects must be instances of
* {@link CodeSource} representing the location from where you load your
* scripts. Any Java policy "grant" statements matching the URL and certificate
* in code sources will apply to the scripts. If you specify any certificates
* within your {@link CodeSource} objects, it is your responsibility to verify
* (or not) that the script source files are signed in whatever
* implementation-specific way you're using.
* @author Attila Szegedi
* @version $Id: PolicySecurityController.java,v 1.2 2006-09-27 08:51:18 szegedia%freemail.hu Exp $
*/
public class PolicySecurityController extends SecurityController
{
// Use weak map, so stuff related to abandoned code sources gets cleaned up
// automatically.
private static final Map secureCallers = new WeakHashMap();
private static class Loader extends SecureClassLoader
implements GeneratedClassLoader
{
private final CodeSource codeSource;
Loader(ClassLoader parent, CodeSource codeSource)
{
super(parent);
this.codeSource = codeSource;
}
public Class defineClass(String name, byte[] data)
{
return defineClass(name, data, 0, data.length, codeSource);
}
public void linkClass(Class cl)
{
resolveClass(cl);
}
}
public GeneratedClassLoader createClassLoader(ClassLoader parentLoader,
Object securityDomain)
{
return createLoader(parentLoader, (CodeSource)securityDomain);
}
private Loader createLoader(
final ClassLoader parent, final CodeSource securityDomain)
{
return (Loader)AccessController.doPrivileged(
new PrivilegedAction()
{
public Object run()
{
return new Loader(parent, securityDomain);
}
});
}
public Object getDynamicSecurityDomain(Object securityDomain)
{
// No separate notion of dynamic security domain - just return what was
// passed in.
return securityDomain;
}
public Object callWithDomain(Object securityDomain, Context cx,
Callable callable, Scriptable scope, Scriptable thisObj,
Object[] args)
{
SecureCaller secureCaller;
CodeSource codeSource = (CodeSource)securityDomain;
ClassLoader appClassLoader = cx.getApplicationClassLoader();
DomainKey key = new DomainKey(codeSource, appClassLoader);
synchronized(secureCallers)
{
Reference ref = (Reference)secureCallers.get(key);
if(ref != null)
{
secureCaller = (SecureCaller)ref.get();
}
else
{
secureCaller = null;
}
if(secureCaller == null)
{
Loader l = createLoader(appClassLoader, codeSource);
String name = SecureCaller.class.getName() + "$Sub" +
// Being a bit paranoid about name clashes - if anyone'd figure
// out how to get to the class loader this might become a
// concern
Integer.toHexString(key.hashCode()) + "_" + Long.toHexString(
new Random().nextLong());
// Just a trivial "class ... extends SecureCaller {}"
// class.
ClassFileWriter cfw = new ClassFileWriter(name,
SecureCaller.class.getName(), "<generated>");
Class clazz = l.defineClass(name, cfw.toByteArray());
try
{
secureCaller = (SecureCaller)clazz.newInstance();
// Using a soft reference, so we don't hold strongly on the
// code source and allow it to get cleaned up eventually.
secureCallers.put(key, new SoftReference(secureCaller));
}
catch (InstantiationException e)
{
throw new UndeclaredThrowableException(e);
}
catch (IllegalAccessException e)
{
throw new UndeclaredThrowableException(e);
}
}
}
return secureCaller.call(callable, cx, scope, thisObj, args);
}
public static class SecureCaller
{
public Object call(Callable callable, Context cx, Scriptable scope,
Scriptable thisObj, Object[] args)
{
return callable.call(cx, scope, thisObj, args);
}
}
private static final class DomainKey
{
private final CodeSource codeSource;
private final ClassLoader appClassLoader;
DomainKey(CodeSource codeSource, ClassLoader appClassLoader)
{
this.codeSource = codeSource;
if(codeSource == null)
{
throw new IllegalArgumentException("codeSource == null");
}
this.appClassLoader = appClassLoader;
}
public boolean equals(Object obj)
{
if(obj == null || obj.getClass() != DomainKey.class)
{
return false;
}
DomainKey other = (DomainKey)obj;
return codeSource.equals(other.codeSource) && appClassLoader == other.appClassLoader;
}
public int hashCode()
{
return codeSource.hashCode() ^ System.identityHashCode(appClassLoader);
}
}
}

View File

@@ -1,78 +1,78 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Norris Boyd
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
package org.mozilla.javascript;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
/**
* @author Attila Szegedi
* @version $Id: SecurityUtilities.java,v 1.2 2006-09-06 13:35:16 szegedia%freemail.hu Exp $
*/
public class SecurityUtilities
{
/**
* Retrieves a system property within a privileged block. Use it only when
* the property is used from within Rhino code and is not passed out of it.
* @param name the name of the system property
* @return the value of the system property
*/
public static String getSystemProperty(final String name)
{
return (String)AccessController.doPrivileged(
new PrivilegedAction()
{
public Object run()
{
return System.getProperty(name);
}
});
}
public static ProtectionDomain getProtectionDomain(final Class clazz)
{
return (ProtectionDomain)AccessController.doPrivileged(
new PrivilegedAction()
{
public Object run()
{
return clazz.getProtectionDomain();
}
});
}
}
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (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.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Norris Boyd
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
package org.mozilla.javascript;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
/**
* @author Attila Szegedi
* @version $Id: SecurityUtilities.java,v 1.3 2006-09-27 08:51:18 szegedia%freemail.hu Exp $
*/
public class SecurityUtilities
{
/**
* Retrieves a system property within a privileged block. Use it only when
* the property is used from within Rhino code and is not passed out of it.
* @param name the name of the system property
* @return the value of the system property
*/
public static String getSystemProperty(final String name)
{
return (String)AccessController.doPrivileged(
new PrivilegedAction()
{
public Object run()
{
return System.getProperty(name);
}
});
}
public static ProtectionDomain getProtectionDomain(final Class clazz)
{
return (ProtectionDomain)AccessController.doPrivileged(
new PrivilegedAction()
{
public Object run()
{
return clazz.getProtectionDomain();
}
});
}
}

View File

@@ -1,266 +1,266 @@
package org.mozilla.javascript;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Properties;
import junit.framework.Assert;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.mozilla.javascript.tools.shell.Global;
import org.mozilla.javascript.tools.shell.Main;
import org.mozilla.javascript.tools.shell.ShellContextFactory;
/**
* Executes the tests in the js/tests directory, much like jsDriver.pl does.
* Excludes tests found in the js/tests/rhino-n.tests file.
* @author Attila Szegedi
* @version $Id: StandardTests.java,v 1.2 2006-08-31 10:17:48 szegedia%freemail.hu Exp $
*/
public class StandardTests extends TestSuite
{
public static TestSuite suite() throws Exception
{
TestSuite suite = new TestSuite("Standard JavaScript tests");
URL url = StandardTests.class.getResource(".");
String path = url.getFile();
int jsIndex = path.lastIndexOf("/js");
if(jsIndex == -1)
{
throw new IllegalStateException("You aren't running the tests from within the standard mozilla/js directory structure");
}
path = path.substring(0, jsIndex + 3).replace('/', File.separatorChar);
File testDir = new File(path, "tests");
if(!testDir.isDirectory())
{
throw new FileNotFoundException(testDir + " is not a directory");
}
Properties excludes = new Properties();
InputStream in = new FileInputStream(new File(testDir, "rhino-n.tests"));
try
{
excludes.load(in);
}
finally
{
in.close();
}
for(int i = -1; i < 2; ++i)
{
TestSuite optimizationLevelSuite = new TestSuite("Optimization level " + i);
addSuites(optimizationLevelSuite, testDir, excludes, i);
suite.addTest(optimizationLevelSuite);
}
return suite;
}
private static void addSuites(TestSuite topLevel, File testDir, Properties excludes, int optimizationLevel)
{
File[] subdirs = testDir.listFiles(new DirectoryFilter());
Arrays.sort(subdirs);
for (int i = 0; i < subdirs.length; i++)
{
File subdir = subdirs[i];
String name = subdir.getName();
if(name.equals("CVS"))
{
continue;
}
TestSuite testSuite = new TestSuite(name);
addCategories(testSuite, subdir, name + "/", excludes, optimizationLevel);
topLevel.addTest(testSuite);
}
}
private static void addCategories(TestSuite suite, File suiteDir, String prefix, Properties excludes, int optimizationLevel)
{
File[] subdirs = suiteDir.listFiles(new DirectoryFilter());
Arrays.sort(subdirs);
for (int i = 0; i < subdirs.length; i++)
{
File subdir = subdirs[i];
String name = subdir.getName();
if(name.equals("CVS"))
{
continue;
}
TestSuite testCategory = new TestSuite(name);
addTests(testCategory, subdir, prefix + name + "/", excludes, optimizationLevel);
suite.addTest(testCategory);
}
}
private static void addTests(TestSuite suite, File suiteDir, String prefix, Properties excludes, int optimizationLevel)
{
File[] jsFiles = suiteDir.listFiles(new JsFilter());
Arrays.sort(jsFiles);
for (int i = 0; i < jsFiles.length; i++)
{
File jsFile = jsFiles[i];
String name = jsFile.getName();
if(name.equals("shell.js") || name.equals("browser.js") || excludes.containsKey(prefix + name))
{
continue;
}
suite.addTest(new JsTestCase(jsFile, optimizationLevel));
}
}
private static final class JsTestCase extends TestCase
{
private final File jsFile;
private final int optimizationLevel;
JsTestCase(File jsFile, int optimizationLevel)
{
super(jsFile.getName() + (optimizationLevel == 1 ? "-compiled" : "-interpreted"));
this.jsFile = jsFile;
this.optimizationLevel = optimizationLevel;
}
public int countTestCases()
{
return 1;
}
private static class TestState
{
boolean finished;
Exception e;
}
public void runBare() throws Exception
{
final Global global = new Global();
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream p = new PrintStream(out);
global.setOut(p);
global.setErr(p);
final ShellContextFactory shellContextFactory = new ShellContextFactory();
shellContextFactory.setOptimizationLevel(optimizationLevel);
final TestState testState = new TestState();
Thread t = new Thread(new Runnable()
{
public void run()
{
try
{
shellContextFactory.call(new ContextAction()
{
public Object run(Context cx)
{
global.init(cx);
runFileIfExists(cx, global, new File(jsFile.getParentFile().getParentFile(), "shell.js"));
runFileIfExists(cx, global, new File(jsFile.getParentFile(), "shell.js"));
runFileIfExists(cx, global, jsFile);
return null;
}
});
}
catch(Exception e)
{
synchronized(testState)
{
testState.e = e;
}
}
synchronized(testState)
{
testState.finished = true;
}
}
});
t.start();
t.join(60000);
boolean isNegativeTest = jsFile.getName().endsWith("-n.js");
synchronized(testState)
{
if(!testState.finished)
{
t.stop();
Assert.fail("Timed out");
}
if(testState.e != null)
{
if(isNegativeTest)
{
if(testState.e instanceof EvaluatorException)
{
// Expected to bomb
return;
}
}
throw testState.e;
}
}
if(isNegativeTest)
{
Assert.fail("Test was expected to produce a runtime error");
}
int exitCode = 0;
int expectedExitCode = 0;
p.flush();
System.out.print(new String(out.toByteArray()));
BufferedReader r = new BufferedReader(new InputStreamReader(
new ByteArrayInputStream(out.toByteArray())));
String failures = "";
for(;;)
{
String s = r.readLine();
if(s == null)
{
break;
}
if(s.indexOf("FAILED!") != -1)
{
failures += s + '\n';
}
int expex = s.indexOf("EXPECT EXIT ");
if(expex != -1)
{
expectedExitCode = s.charAt(expex + "EXPECT EXIT ".length()) - '0';
}
}
Assert.assertEquals("Unexpected exit code", expectedExitCode, exitCode);
if(failures != "")
{
Assert.fail(failures);
}
}
}
private static void runFileIfExists(Context cx, Scriptable global, File f)
{
if(f.isFile())
{
Main.processFile(cx, global, f.getPath());
}
}
private static class DirectoryFilter implements FileFilter
{
public boolean accept(File pathname)
{
return pathname.isDirectory();
}
}
private static class JsFilter implements FileFilter
{
public boolean accept(File pathname)
{
return pathname.getName().endsWith(".js");
}
}
}
package org.mozilla.javascript;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Properties;
import junit.framework.Assert;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.mozilla.javascript.tools.shell.Global;
import org.mozilla.javascript.tools.shell.Main;
import org.mozilla.javascript.tools.shell.ShellContextFactory;
/**
* Executes the tests in the js/tests directory, much like jsDriver.pl does.
* Excludes tests found in the js/tests/rhino-n.tests file.
* @author Attila Szegedi
* @version $Id: StandardTests.java,v 1.3 2006-09-27 08:52:46 szegedia%freemail.hu Exp $
*/
public class StandardTests extends TestSuite
{
public static TestSuite suite() throws Exception
{
TestSuite suite = new TestSuite("Standard JavaScript tests");
URL url = StandardTests.class.getResource(".");
String path = url.getFile();
int jsIndex = path.lastIndexOf("/js");
if(jsIndex == -1)
{
throw new IllegalStateException("You aren't running the tests from within the standard mozilla/js directory structure");
}
path = path.substring(0, jsIndex + 3).replace('/', File.separatorChar);
File testDir = new File(path, "tests");
if(!testDir.isDirectory())
{
throw new FileNotFoundException(testDir + " is not a directory");
}
Properties excludes = new Properties();
InputStream in = new FileInputStream(new File(testDir, "rhino-n.tests"));
try
{
excludes.load(in);
}
finally
{
in.close();
}
for(int i = -1; i < 2; ++i)
{
TestSuite optimizationLevelSuite = new TestSuite("Optimization level " + i);
addSuites(optimizationLevelSuite, testDir, excludes, i);
suite.addTest(optimizationLevelSuite);
}
return suite;
}
private static void addSuites(TestSuite topLevel, File testDir, Properties excludes, int optimizationLevel)
{
File[] subdirs = testDir.listFiles(new DirectoryFilter());
Arrays.sort(subdirs);
for (int i = 0; i < subdirs.length; i++)
{
File subdir = subdirs[i];
String name = subdir.getName();
if(name.equals("CVS"))
{
continue;
}
TestSuite testSuite = new TestSuite(name);
addCategories(testSuite, subdir, name + "/", excludes, optimizationLevel);
topLevel.addTest(testSuite);
}
}
private static void addCategories(TestSuite suite, File suiteDir, String prefix, Properties excludes, int optimizationLevel)
{
File[] subdirs = suiteDir.listFiles(new DirectoryFilter());
Arrays.sort(subdirs);
for (int i = 0; i < subdirs.length; i++)
{
File subdir = subdirs[i];
String name = subdir.getName();
if(name.equals("CVS"))
{
continue;
}
TestSuite testCategory = new TestSuite(name);
addTests(testCategory, subdir, prefix + name + "/", excludes, optimizationLevel);
suite.addTest(testCategory);
}
}
private static void addTests(TestSuite suite, File suiteDir, String prefix, Properties excludes, int optimizationLevel)
{
File[] jsFiles = suiteDir.listFiles(new JsFilter());
Arrays.sort(jsFiles);
for (int i = 0; i < jsFiles.length; i++)
{
File jsFile = jsFiles[i];
String name = jsFile.getName();
if(name.equals("shell.js") || name.equals("browser.js") || excludes.containsKey(prefix + name))
{
continue;
}
suite.addTest(new JsTestCase(jsFile, optimizationLevel));
}
}
private static final class JsTestCase extends TestCase
{
private final File jsFile;
private final int optimizationLevel;
JsTestCase(File jsFile, int optimizationLevel)
{
super(jsFile.getName() + (optimizationLevel == 1 ? "-compiled" : "-interpreted"));
this.jsFile = jsFile;
this.optimizationLevel = optimizationLevel;
}
public int countTestCases()
{
return 1;
}
private static class TestState
{
boolean finished;
Exception e;
}
public void runBare() throws Exception
{
final Global global = new Global();
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream p = new PrintStream(out);
global.setOut(p);
global.setErr(p);
final ShellContextFactory shellContextFactory = new ShellContextFactory();
shellContextFactory.setOptimizationLevel(optimizationLevel);
final TestState testState = new TestState();
Thread t = new Thread(new Runnable()
{
public void run()
{
try
{
shellContextFactory.call(new ContextAction()
{
public Object run(Context cx)
{
global.init(cx);
runFileIfExists(cx, global, new File(jsFile.getParentFile().getParentFile(), "shell.js"));
runFileIfExists(cx, global, new File(jsFile.getParentFile(), "shell.js"));
runFileIfExists(cx, global, jsFile);
return null;
}
});
}
catch(Exception e)
{
synchronized(testState)
{
testState.e = e;
}
}
synchronized(testState)
{
testState.finished = true;
}
}
});
t.start();
t.join(60000);
boolean isNegativeTest = jsFile.getName().endsWith("-n.js");
synchronized(testState)
{
if(!testState.finished)
{
t.stop();
Assert.fail("Timed out");
}
if(testState.e != null)
{
if(isNegativeTest)
{
if(testState.e instanceof EvaluatorException)
{
// Expected to bomb
return;
}
}
throw testState.e;
}
}
if(isNegativeTest)
{
Assert.fail("Test was expected to produce a runtime error");
}
int exitCode = 0;
int expectedExitCode = 0;
p.flush();
System.out.print(new String(out.toByteArray()));
BufferedReader r = new BufferedReader(new InputStreamReader(
new ByteArrayInputStream(out.toByteArray())));
String failures = "";
for(;;)
{
String s = r.readLine();
if(s == null)
{
break;
}
if(s.indexOf("FAILED!") != -1)
{
failures += s + '\n';
}
int expex = s.indexOf("EXPECT EXIT ");
if(expex != -1)
{
expectedExitCode = s.charAt(expex + "EXPECT EXIT ".length()) - '0';
}
}
Assert.assertEquals("Unexpected exit code", expectedExitCode, exitCode);
if(failures != "")
{
Assert.fail(failures);
}
}
}
private static void runFileIfExists(Context cx, Scriptable global, File f)
{
if(f.isFile())
{
Main.processFile(cx, global, f.getPath());
}
}
private static class DirectoryFilter implements FileFilter
{
public boolean accept(File pathname)
{
return pathname.isDirectory();
}
}
private static class JsFilter implements FileFilter
{
public boolean accept(File pathname)
{
return pathname.getName().endsWith(".js");
}
}
}