ICU-3842 fix ModuleTest

X-SVN-Rev: 19891
This commit is contained in:
Ram Viswanadha 2006-07-25 21:26:17 +00:00
parent b1b20ec190
commit 14f3f13cd7
5 changed files with 605 additions and 973 deletions

View file

@ -8,108 +8,84 @@ package com.ibm.icu.dev.test;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import com.ibm.icu.dev.test.TestDataModule.TestData;
import com.ibm.icu.dev.test.TestDataModule.DataMap;
import com.ibm.icu.dev.test.TestDataModule.DataModuleFormatError;
import com.ibm.icu.dev.test.TestDataModule.Factory;
import com.ibm.icu.dev.test.TestDataModule.TestData;
/**
* A convenience extension of TestFmwk for use by data module-driven
* tests. Tests can implement this if they make extensive use of
* information in a TestDataModule. The module should be openable
* using TestDataModule.open() passing in the class name of this test
* + "Data". For example, a test named MyTest would have a module
* named MyTestData. Each test method should also have a
* corresponding test data in the module whose name matches the test
* method name.
*
* Subclasses can allow for test methods that don't use data from the
* modeul by overriding validateMethod to return true for these
* methods. Tests are also free to instantiate their own modules and
* run from them, though care should be taken not to interfere with
* the methods in this class.
*
* See ModuleTestSample for an example. */
public class ModuleTest extends TestFmwk {
* Ray: An adapter class for TestDataMoule to make it like TestFmwk
*
* A convenience extension of TestFmwk for use by data module-driven tests.
*
* Tests can implement this if they make extensive use of information in a
* TestDataModule.
*
* The module should be openable using TestDataModule.open() passing in the
* class name of this test + "Data". For example, a test named MyTest would have
* a module named MyTestData. Each test method should also have a corresponding
* test data in the module whose name matches the test method name.
*
* Subclasses can allow for test methods that don't use data from the modeul by
* overriding validateMethod to return true for these methods. Tests are also
* free to instantiate their own modules and run from them, though care should
* be taken not to interfere with the methods in this class.
*
* See ModuleTestSample for an example.
*/
public abstract class ModuleTest extends TestFmwk {
private TestDataModule m;
private TestData t;
private String methodName;
protected ModuleTest() {
this(null);
}
/**
* Pass the name of a public no-arg method in this class if
* you want use that method to run all the tests off the
* data, otherwise pass null for the typical test behavior.
*/
protected ModuleTest(String methodName) {
this.methodName = methodName;
}
/**
* Subclasses access this after calling nextSettings and getting
* a true result.
*/
protected DataMap settings;
/**
* Subclasses access this after calling nextCase and getting a
* true result.
*/
protected DataMap testcase;
protected TestData t;
abstract protected void processModules();
protected Target getTargets(String targetName) {
if (methodName != null && params.doMethods()) {
if (params.doMethods()) {
Target target = null;
List list = getModuleNames();
if (list != null) {
if (!validate()) {
return null;
}
Iterator testData = m.getTestDataIterator();
if (testData != null) {
try {
Method method = getClass().getMethod(methodName, null);
Iterator i = list.iterator();
while (i.hasNext()) {
target = new MethodTarget((String)i.next(), method).setNext(target);
Method method = getClass().getMethod("processModules", null);
while (testData.hasNext()) {
target = new MethodTarget(((TestData)testData.next()).getName(), method).setNext(target);
}
}
catch (Exception e) {
e.printStackTrace();
throw new IllegalStateException(e.getMessage());
}
}
return target;
}
return super.getTargets(targetName);
}
/*
* If we were initialized with the name of a method to run the
* data driven tests, drive tests off the data using that method.
protected Map getAvailableTests() {
if (methodName != null) {
List list = getModuleNames();
if (list != null) {
Map map = new HashMap(list.size());
addNamedMethodToMap(list, methodName, map);
return map;
}
return Collections.EMPTY_MAP;
} else {
return super.getAvailableTests();
return null;
}
}
*/
/**
*
* TestFmwk calls this before trying to run a suite of tests.
* The test suite if valid if a module whose name is the name of
* this class + "Data" can be opened. Subclasses can override
* this if there are different or additional data required.
*/
protected boolean validate() {
return openModule(getClass().getName()+"Data");
t = null;
String n = getClass().getName();
String localename = "DataDriven" + n.substring(n.lastIndexOf('.') + 1);
String baseName = "com/ibm/icu/dev/data/testdata/";
try {
//m = Factory.get(getClass().getName()+"Data", this);
m = Factory.get(baseName, localename);
} catch (DataModuleFormatError e) {
e.printStackTrace();
m = null;
}
return m != null;
}
/**
@ -130,7 +106,7 @@ public class ModuleTest extends TestFmwk {
protected String getDescription() {
DataMap info = moduleInfo();
if (info != null) {
return info.getString(TestDataModule.DESCRIPTION);
// return info.getString(TestDataModule.DESCRIPTION);
}
return null;
}
@ -143,72 +119,31 @@ public class ModuleTest extends TestFmwk {
if (openTestData(methodName)) {
DataMap info = testInfo();
if (info != null) {
return info.getString(TestDataModule.DESCRIPTION);
// return info.getString(TestDataModule.DESCRIPTION);
}
}
return null;
}
/**
* Opens the module with the given name, and return true if success.
* All contents are reset.
*/
protected boolean openModule(String name) {
t = null;
m = TestDataModule.open(name, this);
return m != null;
}
/**
* Open the test data in the module with the given name, and return
* true if success. The current test is reset.
* @throws DataModuleFormatError
*/
protected boolean openTestData(String name) {
t = m == null ? null : m.createTestData(name);
protected boolean openTestData(String name){
try {
t = m == null ? null : m.getTestData(name);
} catch (DataModuleFormatError e) {
return false;
}
return t != null;
}
/**
* Return an unmodifiable List of the names of all the tests in the module.
*/
protected List getModuleNames() {
if (validate()) {
return m.getTestDataNames();
}
return null;
}
/**
* Utility that associates a public named function in this class
* with all the names in the provided Set. The function should be
* public and take no parameters (like a TestXXX method). You can
* use this to override getAvailableTests from TestFmwk, which by
* default associates the names TestXXX or testXXX with
* the corresponding methods in the class. ModuleTests may wish
* to use a single method to drive all the tests in the module.
*
* @param names the names to associated with the named method.
* @param methodName the name of the method in this class to associate with names
* @param dest the map to hold the name->method mapping
protected void addNamedMethodToMap(List names, String methodName, Map dest) {
try {
Method method = getClass().getMethod(methodName, null);
Iterator i = names.iterator();
while (i.hasNext()) {
dest.put(i.next(), method);
}
}
catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
}
*/
/**
* Get information on this module. Returns null if no module
* open or no info for the module.
*/
protected DataMap moduleInfo() {
private DataMap moduleInfo() {
return m == null ? null : m.getInfo();
}
@ -216,53 +151,15 @@ public class ModuleTest extends TestFmwk {
* Get information on this test. Returns null if no module
* open or no test open or not info for this test.
*/
protected DataMap testInfo() {
private DataMap testInfo() {
return t == null ? null : t.getInfo();
}
/**
* Advance test to the next settings, and return true if
* there are more settings. The protected member variable
* 'settings' holds the new settings data.
*/
protected boolean nextSettings() {
settings = t == null ? null : t.nextSettings();
return settings != null;
}
/**
* Advance test to the next case, and return true if there
* is another case. The protected member variable
* 'testcase' holds the new case data.
*/
protected boolean nextCase() {
testcase = t == null ? null : t.nextCase();
return testcase != null;
}
public void msg(String message, int level, boolean incCount, boolean newln) {
if (level == ERR && t != null) {
t.stopIteration();
// t.stopIteration();
}
super.msg(message, level, incCount, newln);
}
/**
* Report an error, and stop iteration of the current test.
public void err(String message) {
if (t != null) {
t.stopIteration();
}
super.err(message);
}
/**
* Report an error, and stop iteration of the current test.
public void errln(String message) {
if (t != null) {
t.stopIteration();
}
super.errln(message);
}
*/
}

View file

@ -1,605 +1,388 @@
/**
*******************************************************************************
* Copyright (C) 2001-2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
/*
**********************************************************************
* Copyright (c) 2006, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Created on 2006-4-21
*/
package com.ibm.icu.dev.test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ResourceBundle;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.NoSuchElementException;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.ICUResourceBundleIterator;
import com.ibm.icu.util.UResourceBundle;
import com.ibm.icu.util.UResourceTypeMismatchException;
/**
* Resource-based module.
* Represents a collection of test data described in a UResourceBoundle file.
*
* The root of the UResourceBoundle file is a table resource, and it has one
* Info and one TestData sub-resources. The Info describes the data module
* itself. The TestData, which is a table resource, has a collection of test
* data.
*
* The test data is a named table resource which has Info, Settings, Headers,
* and Cases sub-resources.
*
* <pre>
* DataModule:table(nofallback){
* Info:table {}
* TestData:table {
* entry_name:table{
* Info:table{}
* Settings:array{}
* Headers:array{}
* Cases:array{}
* }
* }
* }
* </pre>
*
* The test data is expected to be fed to test code by following sequence
*
* for each setting in Setting{
* prepare the setting
* for each test data in Cases{
* perform the test
* }
* }
*
* For detail of the specification, please refer to the code. The code is
* initially ported from "icu4c/source/tools/ctestfw/unicode/tstdtmod.h"
* and should be maintained parallelly.
*
* @author Raymond Yang
*/
public class ResourceModule extends TestDataModule {
private NamedArray tests;
private RBDataMap info;
private String[] defaultHeaders;
class ResourceModule implements TestDataModule {
private static final String INFO = "Info";
// private static final String DESCRIPTION = "Description";
// private static final String LONG_DESCRIPTION = "LongDescription";
private static final String TEST_DATA = "TestData";
private static final String SETTINGS = "Settings";
private static final String HEADER = "Headers";
private static final String DATA = "Cases";
/**
* For internal use.
*/
static final String TESTS = "TestData";
/**
* For internal use.
*/
static final String INFO = "Info";
/**
* For internal use.
*/
static final String SETTINGS = "Settings";
/**
* For internal use.
*/
static final String CASES = "Cases";
public ResourceModule(String name, TestLog log, ResourceBundle b) {
super(name, log);
if (b == null) {
log.errln("ResourceModule must have non-null bundle");
return;
}
Object[][] data = (Object[][])b.getObject(TESTS);
if (data == null) {
log.errln("ResourceModule does not contain tests!");
return;
}
this.tests = NamedArray.create(data);
Object[][] temp = (Object[][])b.getObject(INFO);
if (temp != null) {
NamedArray na = NamedArray.create(temp);
if (na.count() < 0) {
log.errln("Bad data for " + INFO);
} else {
this.info = new RBDataMap(log, na, false);
this.defaultHeaders = info.getStringArray(HEADERS);
ICUResourceBundle res;
ICUResourceBundle info;
ICUResourceBundle defaultHeader;
ICUResourceBundle testData;
ResourceModule(String baseName, String localeName) throws DataModuleFormatError{
try{
res = (ICUResourceBundle) UResourceBundle.getBundleInstance(baseName, localeName);
info = getFromTable(res, INFO, ICUResourceBundle.TABLE);
testData = getFromTable(res, TEST_DATA, ICUResourceBundle.TABLE);
} catch (MissingResourceException e){
throw new DataModuleFormatError("Unable to find resource", e);
}
try {
// unfortunately, actually, data can be either ARRAY or STRING
defaultHeader = getFromTable(info, HEADER, new int[]{ICUResourceBundle.ARRAY, ICUResourceBundle.STRING});
} catch (MissingResourceException e){
defaultHeader = null;
}
}
public String getName() {
return res.getKey();
}
/**
* Get additional data related to the module.
* Returns true if successfully got info.
*/
public DataMap getInfo() {
return info;
return new UTableResource(info);
}
public TestData getTestData(String testName) throws DataModuleFormatError {
return new UResourceTestData(defaultHeader, testData.get(testName));
}
public Iterator getTestDataIterator() {
return new IteratorAdapter(testData){
protected Object prepareNext(ICUResourceBundle nextRes) throws DataModuleFormatError {
return new UResourceTestData(defaultHeader, nextRes);
}
};
}
/**
* Returns the Test object corresponding to name,
* or null if name not found in this module.
* To make ICUResourceBundleIterator works like Iterator
* and return various data-driven test object for next() call
*
* @author Raymond Yang
*/
public TestData createTestData(String name) {
if (tests != null) {
Object[] td = tests.getEntry(name);
if (td != null) {
return createTestData(td);
private abstract static class IteratorAdapter implements Iterator{
private ICUResourceBundle res;
private ICUResourceBundleIterator itr;
private Object preparedNextElement = null;
// fix a strange behavior for ICUResourceBundleIterator for
// ICUResourceBundle.STRING. It support hasNext(), but does
// not support next() now.
//
// Use the iterated resource itself as the result from next() call
private boolean isStrRes = false;
private boolean isStrResPrepared = false; // for STRING resouce, we only prepare once
IteratorAdapter(ICUResourceBundle theRes) {
assert_not (theRes == null);
res = theRes;
itr = res.getIterator();
isStrRes = res.getType() == ICUResourceBundle.STRING;
}
}
return null;
}
/**
* Returns the Test object corresponding to index,
* or null if index is out of range for this module.
* No error logged if index is out of bounds, the assumption is that
* iteration is being used and proceeds until a null return.
*/
public TestData createTestData(int index) {
if (tests != null) {
Object[] td = tests.getEntry(index);
if (td != null) {
return createTestData(td);
public void remove() {
// do nothing
}
}
return null;
}
/**
* Return an unmodifiable list of the test data names, in index order.
*/
public List getTestDataNames() {
if (tests != null) {
return Collections.unmodifiableList(Arrays.asList(tests.names()));
private boolean hasNextForStrRes(){
assert_is (isStrRes);
assert_not (!isStrResPrepared && preparedNextElement != null);
if (isStrResPrepared && preparedNextElement != null) return true;
if (isStrResPrepared && preparedNextElement == null) return false; // only prepare once
assert_is (!isStrResPrepared && preparedNextElement == null);
try {
preparedNextElement = prepareNext(res);
assert_not (preparedNextElement == null, "prepareNext() should not return null");
isStrResPrepared = true; // toggle the tag
return true;
} catch (DataModuleFormatError e) {
throw new RuntimeException(e.getMessage(),e);
}
}
return null;
}
public boolean hasNext() {
if (isStrRes) return hasNextForStrRes();
if (preparedNextElement != null) return true;
ICUResourceBundle t = null;
if (itr.hasNext()) {
// Notice, other RuntimeException may be throwed
t = itr.next();
} else {
return false;
}
private TestData createTestData(Object[] d) {
return new RBTestData(this, (String)d[0], (Object[][])d[1], defaultHeaders);
}
/**
* Provides access to values either by index or by case-insensitive search on
* the name. Poor man's Map with index.
*/
static abstract class NamedArray {
int txcache;
int count;
static NamedArray create(Object[][] pairs) {
return new NamedArrayOfPairs(pairs);
}
static NamedArray create(String[] names, Object[] values) {
return new NamedArrayTwoLists(names, values);
}
protected NamedArray(int count) {
this.count = count;
}
public final int count() {
return count;
}
public final boolean isDefined(String name) {
int ix = nameToIndex(name);
if (ix != -1) {
txcache = (ix == 0 ? count : ix) - 1;
return true;
try {
preparedNextElement = prepareNext(t);
assert_not (preparedNextElement == null, "prepareNext() should not return null");
return true;
} catch (DataModuleFormatError e) {
// Sadly, we throw RuntimeException also
throw new RuntimeException(e.getMessage(),e);
}
}
return false;
}
public final Object[] getEntry(String name) {
return getEntry(nameToIndex(name));
}
public final Object getValue(String name) {
return getValue(nameToIndex(name));
public Object next(){
if (hasNext()) {
Object t = preparedNextElement;
preparedNextElement = null;
return t;
} else {
throw new NoSuchElementException();
}
}
/**
* To prepare data-driven test object for next() call, should not return null
*/
abstract protected Object prepareNext(ICUResourceBundle nextRes) throws DataModuleFormatError;
}
public final Object[] getEntry(int index) {
if (index >= 0 && index < count) {
return entryAtIndex(index);
}
return null;
}
public final Object getValue(int index) {
if (index >= 0 && index < count) {
return valueAtIndex(index);
}
return null;
}
public String[] names() {
if (count > 0) {
String[] result = new String[count];
for (int i = 0; i < count; ++i) {
result[i] = nameAtIndex(i);
}
return result;
}
return null;
}
public Object[] values() {
if (count > 0) {
Object[] result = new Object[count];
for (int i = 0; i < count; ++i) {
result[i] = valueAtIndex(i);
}
return result;
}
return null;
}
public String toString() {
StringBuffer buf = new StringBuffer(super.toString());
buf.append("count: " + count + "{");
for (int i = 0; i < count; ++i) {
buf.append("\n { " + nameAtIndex(i) + ", " + valueAtIndex(i) + " }");
}
buf.append("}");
return buf.toString();
}
protected int nameToIndex(String name) {
if (name != null && count > 0) {
int i = txcache;
do {
if (++i == count) {
i = 0;
}
if (name.equalsIgnoreCase(nameAtIndex(i))) {
txcache = i;
return i;
}
} while (i != txcache);
}
return -1;
}
protected Object[] entryAtIndex(int index) {
return new Object[] { nameAtIndex(index), valueAtIndex(index) };
}
protected abstract String nameAtIndex(int index);
protected abstract Object valueAtIndex(int index);
}
/**
* NamedArray implemented using an array of name/value
* pairs, represented by an Object[][].
* Avoid use Java 1.4 language new assert keyword
*/
static final class NamedArrayOfPairs extends NamedArray {
private Object[][] data;
public NamedArrayOfPairs(Object[][] data) {
super(data == null ? -1 : data.length);
this.data = data;
static void assert_is(boolean eq, String msg){
if (!eq) throw new Error("test code itself has error: " + msg);
}
protected Object[] entryAtIndex(int index) {
return data[index];
static void assert_is(boolean eq){
if (!eq) throw new Error("test code itself has error.");
}
protected String nameAtIndex(int index) {
return (String)data[index][0];
static void assert_not(boolean eq, String msg){
assert_is(!eq, msg);
}
protected Object valueAtIndex(int index) {
return data[index][1];
static void assert_not(boolean eq){
assert_is(!eq);
}
}
/**
* NamedArray implemented using two arrays, one of String and one of Object.
* Internal helper function to get resource with following add-on
*
* 1. Assert the returned resource is never null.
* 2. Check the type of resource.
*
* The UResourceTypeMismatchException for various get() method is a
* RuntimeException which can be silently bypassed. This behavior is a
* trouble. One purpose of the class is to enforce format checking for
* resource file. We don't want to the exceptions are silently bypassed
* and spreaded to our customer's code.
*
* Notice, the MissingResourceException for get() method is also a
* RuntimeException. The caller functions should avoid sepread the execption
* silently also. The behavior is modified because some resource are
* optional and can be missed.
*/
static final class NamedArrayTwoLists extends NamedArray {
String[] names;
Object[] values;
static ICUResourceBundle getFromTable(ICUResourceBundle res, String key, int expResType) throws DataModuleFormatError{
return getFromTable(res, key, new int[]{expResType});
}
static ICUResourceBundle getFromTable(ICUResourceBundle res, String key, int[] expResTypes) throws DataModuleFormatError{
assert_is (res != null && key != null && res.getType() == ICUResourceBundle.TABLE);
ICUResourceBundle t = res.get(key);
assert_not (t ==null);
int type = t.getType();
Arrays.sort(expResTypes);
if (Arrays.binarySearch(expResTypes, type) >= 0) {
return t;
} else {
throw new DataModuleFormatError(new UResourceTypeMismatchException("Actual type " + t.getType() + " != expected types " + expResTypes + "."));
}
}
/**
* Unfortunately, ICUResourceBundle is unable to treat one string as string array.
* This function return a String[] from ICUResourceBundle, regardless it is an array or a string
*/
static String[] getStringArrayHelper(ICUResourceBundle res, String key) throws DataModuleFormatError{
ICUResourceBundle t = getFromTable(res, key, new int[]{ICUResourceBundle.ARRAY, ICUResourceBundle.STRING});
return getStringArrayHelper(t);
}
public NamedArrayTwoLists(String[] names, Object[] values) {
super (values != null && names != null && names.length == values.length ?
names.length :
-1);
this.names = names;
this.values = values;
static String[] getStringArrayHelper(ICUResourceBundle res) throws DataModuleFormatError{
try{
int type = res.getType();
switch (type) {
case ICUResourceBundle.ARRAY:
return res.getStringArray();
case ICUResourceBundle.STRING:
return new String[]{res.getString()};
default:
throw new UResourceTypeMismatchException("Only accept ARRAY and STRING types.");
}
} catch (UResourceTypeMismatchException e){
throw new DataModuleFormatError(e);
}
}
public static void main(String[] args){
try {
TestDataModule m = new ResourceModule("com/ibm/icu/dev/data/testdata/","DataDrivenCollationTest");
System.out.println("hello: " + m.getName());
m.getInfo();
m.getTestDataIterator();
} catch (DataModuleFormatError e) {
// TODO Auto-generated catch block
System.out.println("???");
e.printStackTrace();
}
}
private static class UResourceTestData implements TestData{
private ICUResourceBundle res;
private ICUResourceBundle info;
private ICUResourceBundle settings;
private ICUResourceBundle header;
private ICUResourceBundle data;
UResourceTestData(ICUResourceBundle defaultHeader, ICUResourceBundle theRes) throws DataModuleFormatError{
try{
assert_is (theRes != null && theRes.getType() == ICUResourceBundle.TABLE);
res = theRes;
// unfortunately, actually, data can be either ARRAY or STRING
data = getFromTable(res, DATA, new int[]{ICUResourceBundle.ARRAY, ICUResourceBundle.STRING});
} catch (MissingResourceException e){
throw new DataModuleFormatError("Unable to find resource", e);
}
try{
settings = getFromTable(res, SETTINGS, ICUResourceBundle.ARRAY);
info = getFromTable(res, INFO, ICUResourceBundle.TABLE);
} catch (MissingResourceException e){
// do nothing, left them null;
}
try {
// unfortunately, actually, data can be either ARRAY or STRING
header = getFromTable(res, HEADER, new int[]{ICUResourceBundle.ARRAY, ICUResourceBundle.STRING});
} catch (MissingResourceException e){
if (defaultHeader == null) {
throw new DataModuleFormatError("Unable to find a header for test data '" + res.getKey() + "' and no default header exist.");
} else {
header = defaultHeader;
}
}
}
public String getName() {
return res.getKey();
}
public DataMap getInfo() {
return info == null ? null : new UTableResource(info);
}
public Iterator getSettingsIterator() {
assert_is (settings.getType() == ICUResourceBundle.ARRAY);
return new IteratorAdapter(settings){
protected Object prepareNext(ICUResourceBundle nextRes) throws DataModuleFormatError {
return new UTableResource(nextRes);
}
};
}
public Iterator getDataIterator() {
// unfortunately,
assert_is (data.getType() == ICUResourceBundle.ARRAY
|| data.getType() == ICUResourceBundle.STRING);
return new IteratorAdapter(data){
protected Object prepareNext(ICUResourceBundle nextRes) throws DataModuleFormatError {
return new UArrayResource(header, nextRes);
}
};
}
}
public String[] names() {
return names;
}
private static class UTableResource implements DataMap{
private ICUResourceBundle res;
public Object[] values() {
return values;
}
protected String nameAtIndex(int index) {
return names[index];
}
protected Object valueAtIndex(int index) {
return values[index];
}
}
static class RBTestData extends TestData {
ResourceModule m;
RBDataMap info;
Object[] settings;
/**
* changed to fit the c genrb format. this is actually Object[] {Object[]}
*/
Object[] cases;
String[] headers;
int sx;
int cx;
RBTestData(ResourceModule m, String name, Object[][] data, String[] defaultHeaders) {
super(name);
this.m = m;
NamedArray namedData = NamedArray.create(data);
try {
Object[][] temp = (Object[][])namedData.getValue(INFO);
if (temp != null) {
NamedArray na = NamedArray.create(temp);
if (na.count() < 1) {
m.log.errln("Bad data for " + INFO);
} else {
this.info = new RBDataMap(m.log, na, false);
UTableResource(ICUResourceBundle theRes){
res = theRes;
}
public String getString(String key) {
String t;
try{
t = res.getString(key);
} catch (MissingResourceException e){
t = null;
}
return t;
}
}
catch (ClassCastException e) {
m.log.errln("Test " + name + " in module " + m.getName() + " has bad type for " + INFO);
}
try {
this.settings = (Object[])namedData.getValue(SETTINGS);
}
catch (ClassCastException e) {
m.log.errln("Test " + name + " in module " + m.getName() + " has bad type for " + SETTINGS);
}
try {
this.cases = (Object[])namedData.getValue(CASES);
}
catch (ClassCastException e) {
m.log.errln("Test " + name + " in module " + m.getName() + " has bad type for " + CASES);
}
if (info != null) {
this.headers = info.getStringArray(HEADERS);
}
if (this.headers == null) {
this.headers = defaultHeaders;
}
}
/**
* Get additional data related to the test.
*/
public DataMap getInfo() {
return info;
}
/**
* Returns DataMap with next settings and resets case iteration.
* If no more settings, terminates iteration and returns null.
*/
public DataMap nextSettings() {
if (settings != null && sx < settings.length) {
cx = 0;
NamedArray na = NamedArray.create((Object[][])settings[sx++]);
if (na.count() < 0) {
m.log.errln("Bad settings data for settings " + (sx-1));
return null;
} else {
return new RBDataMap(m.log, na, false);
}
}
stopIteration();
return null;
}
/**
* Returns DataMap with next case. If no next case, returns null.
*/
public DataMap nextCase() {
if (cases != null && cx < cases.length) {
NamedArray na = NamedArray.create(headers, (Object[])cases[cx++]);
if (na.count() < 0) {
m.log.errln("Bad cases data for case " + (cx-1));
} else {
return new RBDataMap(m.log, na, true);
}
}
return null;
}
/**
* Stops iteration.
*/
public void stopIteration() {
sx = cx = Integer.MAX_VALUE;
}
}
static final class RBDataMap extends DataMap {
TestLog log;
NamedArray na;
boolean required;
RBDataMap(TestLog log, NamedArray na, boolean required) {
this.log = log;
this.na = na;
this.required = required;
}
public boolean isDefined(String key) {
return na.isDefined(key);
}
public Object getObject(String key) {
Object result = na.getValue(key);
if (required && result == null) {
reportNullError(key);
}
return result;
}
public String getString(String key) {
String result = null;
try {
result = (String)getObject(key);
}
catch (ClassCastException e) {
reportTypeError("String", key);
}
return result;
}
public char getChar(String key) {
try {
String s = getString(key);
if (s != null) {
return s.charAt(0);
}
}
catch (IndexOutOfBoundsException e) {
reportTypeError("char", key);
}
return 0xffff;
}
public int getInt(String key) {
try {
String s = getString(key);
if (s != null) {
return Integer.parseInt(s);
}
}
catch (NumberFormatException e) {
reportTypeError("int", key);
}
return -1;
}
public byte getByte(String key) {
try {
String s = getString(key);
if (s != null) {
return Byte.parseByte(s);
}
}
catch (NumberFormatException e) {
reportTypeError("byte", key);
}
return (byte)-1;
}
public boolean getBoolean(String key) {
String s = getString(key);
if (s != null) {
if (s.equalsIgnoreCase("true")) {
return true;
} else if (!s.equalsIgnoreCase("false")) {
reportTypeError("boolean", key);
}
}
return false;
}
public Object[] getObjectArray(String key) {
try {
Object[] result = (Object[])na.getValue(key);
if (result == null && required) {
reportNullError(key);
}
} catch (ClassCastException e) {
reportTypeError("Object[]", key);
}
return null;
}
public String[] getStringArray(String key) {
try {
String[] result = (String[])na.getValue(key);
if (result == null && required) {
reportNullError(key);
}
return result;
}
catch (ClassCastException e) {
reportTypeError("String[]", key);
}
return null;
}
public char[] getCharArray(String key) {
try {
return (char[])na.getValue(key);
private static class UArrayResource implements DataMap{
private Map theMap;
UArrayResource(ICUResourceBundle theHeader, ICUResourceBundle theData) throws DataModuleFormatError{
assert_is (theHeader != null && theData != null);
String[] header;
String[] data;
header = getStringArrayHelper(theHeader);
data = getStringArrayHelper(theData);
if (data.length != header.length)
throw new DataModuleFormatError("The count of Header and Data is mismatch.");
theMap = new HashMap();
for (int i = 0; i < header.length; i++) {
theMap.put(header[i], data[i]);
}
catch (ClassCastException e) {
}
try{
String temp = (String)na.getValue(key);
if (temp == null) {
if (required) {
reportNullError(key);
}
} else {
return temp.toCharArray();
}
public String getString(String key) {
return (String)theMap.get(key);
}
catch (ClassCastException e) {
reportTypeError("char[]", key);
}
return null;
}
public int[] getIntArray(String key) {
try {
return (int[])na.getValue(key);
}
catch (ClassCastException e) {
}
String[] data = getStringArray(key);
if (data != null) {
try {
int[] result = new int[data.length];
for (int i = 0; i < data.length; ++i) {
result[i] = Integer.parseInt(data[i]);
}
return result;
}
catch (NumberFormatException e) {
reportTypeError("int[]", key);
}
}
return null;
}
public byte[] getByteArray(String key) {
try {
return (byte[])na.getValue(key);
}
catch (ClassCastException e) {
}
String[] data = getStringArray(key);
if (data != null) {
try {
byte[] result = new byte[data.length];
for (int i = 0; i < result.length; ++i) {
result[i] = Byte.parseByte(data[i]);
}
return result;
}
catch (NumberFormatException e) {
reportTypeError("byte[]", key);
}
}
return null;
}
public boolean[] getBooleanArray(String key) {
try {
return (boolean[])na.getValue(key);
}
catch (ClassCastException e) {
}
String[] data = getStringArray(key);
if (data != null) {
boolean[] result = new boolean[data.length];
for (int i = 0; i < result.length; ++i) {
String s = data[i];
if (s.equalsIgnoreCase("true")) {
result[i] = true;
} else if (s.equalsIgnoreCase("false")) {
result[i] = false;
} else {
reportTypeError("boolean[]", key);
return null;
}
}
return result;
}
return null;
}
private void reportNullError(String key) {
log.errln("Missing required value for '" + key + "'");
}
private void reportTypeError(String typeName, String key) {
log.errln("Could not return value of '" + key + "' (" + getObject(key) + ") as type '" + typeName + "'");
}
}
}

View file

@ -1,166 +1,103 @@
/**
*******************************************************************************
* Copyright (C) 2001-2004, International Business Machines Corporation and *
* Copyright (C) 2001-2006, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.dev.test;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Iterator;
/**
* Represents a collection of test data described in a file.
*
*/
public abstract class TestDataModule {
protected final String name;
protected final TestLog log;
public interface TestDataModule {
/**
* Looks for file with corresponding name, either xml or resource
* bundle. Returns module or null if not present.
* Return the name of this test data module.
*/
static final TestDataModule open(String name, TestLog log) {
ResourceBundle bundle = ResourceBundle.getBundle(name);
if (bundle != null) {
return new ResourceModule(name, log, bundle);
}
return null;
}
/**
* Subclass constructors call this.
*/
protected TestDataModule(String name, TestLog log) {
this.name = name;
this.log = log;
}
/**
* Return the name of this test module.
*/
public final String getName() {
return name;
}
/**
* Return the log used by this module. Errors logged to this
* log will terminate settings and cases iteration for the
* current test.
*/
public final TestLog getLog() {
return log;
}
public String getName();
/**
* Get additional data related to the module, e.g. DESCRIPTION,
* global settings. Might be null.
*/
public abstract DataMap getInfo();
/**
* A standard datum that can be found in the DataMap returned by getInfo.
* DESCRIPTION provides a one-line description. The corresponding type
* is a String.
*/
public static final String DESCRIPTION = "Description";
/**
* A standard datum that can be found in the DataMap returned by
* getInfo. LONG_DESCRIPTION provides an extended description.
* The corresponding type is a String.
*/
public static final String LONG_DESCRIPTION = "Long_Description";
/**
* The names of the columns of test data. This can be found in
* either the module INFO or the test INFO. If the test INFO
* does not define the headers then they are taken from the
* module. If test cases are used by the test, then the headers
* must be defined. Most clients will not need this information,
* since it is handled by the framework.
*/
public static final String HEADERS = "Headers";
/**
* Returns the Test object corresponding to index, or null if
* index is out of range for this module. No error logged if
* index is out of bounds, the assumption is that iteration is
* being used.
*/
public abstract TestData createTestData(int index);
public DataMap getInfo();
/**
* Returns the TestData corresponding to name, or null if name not
* found in this module. Logs error if name is not found.
* found in this module. Throw error if name is not found.
* @throws DataModuleFormatError
*/
public abstract TestData createTestData(String name);
public TestData getTestData(String name) throws DataModuleFormatError;
/**
* Return an unmodifiable list of the test data names, in index order.
* @return Iterator<TestData>
*/
public abstract List getTestDataNames();
public Iterator getTestDataIterator();
public static class Factory{
static final TestDataModule get(String baseName, String localeName) throws DataModuleFormatError {
return new ResourceModule(baseName, localeName);
}
}
public static class DataModuleFormatError extends Exception{
public DataModuleFormatError(String msg){
super(msg);
}
public DataModuleFormatError(String msg, Throwable cause){
super(msg, cause);
}
public DataModuleFormatError(Throwable cause) {
super(cause);
}
}
/**
* Represents a single test in the module.
*/
public abstract static class TestData {
String name;
protected TestData(String name) {
this.name = name;
}
public final String name() {
return name;
public static interface TestData {
public String getName();
/**
* Get additional data related to the test data, e.g. DESCRIPTION,
* global settings. Might be null.
*/
public DataMap getInfo();
/**
* @return Iterator<DataMap>
*/
public Iterator getSettingsIterator();
/**
* @return Iterator<DataMap>
*/
public Iterator getDataIterator();
}
/**
* Get additional data related to the test, e.g. DESCRIPTION,
* LONG_DESCRIPTION, HEADERS, or other test-specific
* information.
* Map-like interface for accessing key-value pairs by key.
* If the vaule is not found by given key, return null.
* The behavior is analogous the get() method of the Map interface.
*
* @author Raymond Yang
*/
public abstract DataMap getInfo();
/**
* Returns new DataMap for next settings, and resets test case
* iteration. Returns null if no more settings.
*/
public abstract DataMap nextSettings();
/**
* Returns new DataMap for next cases.
* Returns null if no more cases.
*/
public abstract DataMap nextCase();
/**
* Stops iteration of the test. Usually called if some
* condition detects an error.
*/
public abstract void stopIteration();
}
/**
* DataMap provides named access to typed data. Lookup data by
* key and attempt to cast to indicated type. If data not found
* or cast fails, log error and return null. */
public abstract static class DataMap {
public abstract boolean isDefined(String key);
public abstract Object getObject(String key);
public interface DataMap {
// public abstract boolean isDefined(String key);
//
// public abstract Object getObject(String key);
public abstract String getString(String key);
public abstract char getChar(String key);
public abstract int getInt(String key);
public abstract byte getByte(String key);
public abstract boolean getBoolean(String key);
public abstract Object[] getObjectArray(String key);
public abstract String[] getStringArray(String key);
public abstract char[] getCharArray(String key);
public abstract int[] getIntArray(String key);
public abstract byte[] getByteArray(String key);
public abstract boolean[] getBooleanArray(String key);
// public abstract char getChar(String key);
// public abstract int getInt(String key);
// public abstract byte getByte(String key);
// public abstract boolean getBoolean(String key);
//
// public abstract Object[] getObjectArray(String key);
// public abstract String[] getStringArray(String key);
// public abstract char[] getCharArray(String key);
// public abstract int[] getIntArray(String key);
// public abstract byte[] getByteArray(String key);
// public abstract boolean[] getBooleanArray(String key);
}
}

View file

@ -1,26 +1,28 @@
/**
*******************************************************************************
* Copyright (C) 2001-2004, International Business Machines Corporation and *
* Copyright (C) 2001-2006, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.dev.test.collator;
import com.ibm.icu.dev.test.ModuleTest;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.text.RuleBasedCollator;
import com.ibm.icu.text.Collator;
import com.ibm.icu.text.CollationKey;
import com.ibm.icu.text.RawCollationKey;
import com.ibm.icu.text.CollationElementIterator;
import com.ibm.icu.text.UTF16;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.impl.LocaleUtility;
import java.util.Vector;
import java.util.Iterator;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Vector;
import com.ibm.icu.dev.test.ModuleTest;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.dev.test.TestDataModule.DataMap;
import com.ibm.icu.impl.LocaleUtility;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.text.CollationElementIterator;
import com.ibm.icu.text.CollationKey;
import com.ibm.icu.text.Collator;
import com.ibm.icu.text.RawCollationKey;
import com.ibm.icu.text.RuleBasedCollator;
import com.ibm.icu.text.UTF16;
public class CollationTest extends ModuleTest
{
@ -35,13 +37,23 @@ public class CollationTest extends ModuleTest
}*/
}
public CollationTest() {
super("processModules");
// public CollationTest() {
// super("processModules");
// }
// Want to be static, why super class not?
private class DataDrivenTarget extends TestFmwk.Target{
public DataDrivenTarget(String name) {
super(name);
}
}
public void processModules() {
while (nextSettings()) {
processTest();
for (Iterator iter = t.getSettingsIterator(); iter.hasNext();) {
DataMap setting = (DataMap) iter.next();
processSetting(setting);
}
}
@ -161,7 +173,7 @@ public class CollationTest extends ModuleTest
// private methods -------------------------------------------------------
private void processTest() {
private void processSetting(DataMap settings) {
RuleBasedCollator col = null;
// ok i have to be careful here since it seems like we can have
// multiple locales for each test
@ -177,7 +189,7 @@ public class CollationTest extends ModuleTest
errln("Error creating collator for locale " + locale);
}
logln("Testing collator for locale %s\n" + locale);
processCollatorTests(col);
processSetting2(settings, col);
}
String rules = settings.getString("Rules");
// ok i have to be careful here since it seems like we can have
@ -191,26 +203,26 @@ public class CollationTest extends ModuleTest
} catch (Exception e) {
errln("Error creating collator for rules " + rules);
}
processCollatorTests(col);
processSetting2(settings, col);
}
}
private void processCollatorTests(RuleBasedCollator col)
private void processSetting2(DataMap settings,RuleBasedCollator col)
{
// ok i have to be careful here since it seems like we can have
// multiple rules for each test
String arguments = settings.getString("Arguments");
if (arguments != null) {
processArguments(col, arguments);
handleArguments(col, arguments);
}
processReadyCollator(col);
processTestCases(col);
}
/**
* Reads the options string and sets appropriate attributes in collator
*/
private void processArguments(RuleBasedCollator col, String argument) {
private void handleArguments(RuleBasedCollator col, String argument) {
int i = 0;
boolean printInfo = false;
while (i < argument.length()) {
@ -277,17 +289,11 @@ public class CollationTest extends ModuleTest
*/
}
private void processReadyCollator(RuleBasedCollator col) {
while (nextCase()) {
// this is very sad, it is alittle awkward to write the c rb
// to have an object array of an object array of a 1 element
// string array. so now we have an object array of a 1 element
// object array of string arrays.
String sequence[] = testcase.getStringArray("sequence");
for (int i = 0; i < sequence.length; i ++) {
processSequence(col, sequence[i]);
}
}
private void processTestCases(RuleBasedCollator col) {
for (Iterator iter = t.getDataIterator(); iter.hasNext();) {
DataMap e1 = (DataMap) iter.next();
processSequence(col, e1.getString("sequence"));
}
}
private void processSequence(RuleBasedCollator col, String sequence) {

View file

@ -1,6 +1,6 @@
/**
*******************************************************************************
* Copyright (C) 2001-2004, International Business Machines Corporation and *
* Copyright (C) 2001-2006, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
@ -15,42 +15,43 @@ public class ModuleTestSample extends ModuleTest {
new ModuleTestSample().run(args);
}
// standard loop, settings and cases
public void Test01() {
while (nextSettings()) {
logln("--------");
logln("String: " + settings.getString("aString"));
if (settings.isDefined("anInt")) {
logln("Int: " + settings.getInt("anInt"));
}
logln("Boolean: " + settings.getBoolean("aBoolean"));
while (nextCase()) {
logln(" ----");
logln(" StringArray: " + printArray(testcase.getStringArray("aStringArray")));
logln(" IntArray: " + printArray(testcase.getIntArray("anIntArray")));
logln(" BooleanArray: " + printArray(testcase.getBooleanArray("aBooleanArray")));
}
}
}
// loop with just cases
public void Test02() {
while (nextCase()) {
logln("----");
logln("String: " + testcase.getString("aString"));
logln("Int: " + testcase.getInt("anInt"));
logln("Boolean: " + testcase.getBoolean("aBoolean"));
}
}
// // standard loop, settings and cases
// public void Test01() {
// while (nextSettings()) {
// logln("--------");
// logln("String: " + settings.getString("aString"));
// if (settings.isDefined("anInt")) {
// logln("Int: " + settings.getInt("anInt"));
// }
// logln("Boolean: " + settings.getBoolean("aBoolean"));
//
// while (nextCase()) {
// logln(" ----");
// logln(" StringArray: " + printArray(testcase.getStringArray("aStringArray")));
// logln(" IntArray: " + printArray(testcase.getIntArray("anIntArray")));
// logln(" BooleanArray: " + printArray(testcase.getBooleanArray("aBooleanArray")));
// }
// }
// }
//
// // loop with just cases
// public void Test02() {
// while (nextCase()) {
// logln("----");
// logln("String: " + testcase.getString("aString"));
// logln("Int: " + testcase.getInt("anInt"));
// logln("Boolean: " + testcase.getBoolean("aBoolean"));
// }
// }
// no cases, just uses info for test
public void Test03() {
DataMap info = testInfo();
if (info != null) {
logln(info.getString(TestDataModule.DESCRIPTION)); // standard
logln(info.getString("Extra")); // test-specific
}
// DataMap info = testInfo();
// if (info != null) {
//// logln(info.getString(TestDataModule.DESCRIPTION)); // standard
// logln(info.getString("Extra")); // test-specific
// }
// return;
}
// no data, ModuleTest should not allow this to execute by default
@ -69,56 +70,56 @@ public class ModuleTestSample extends ModuleTest {
logln("Test05 executed.");
}
// The test data contains an error in the third case. When getInt("Data") is
// executed the error is logged and iteration stops.
public void Test06() {
while (nextCase()) {
logln("----");
logln("isGood: " + testcase.getString("IsGood"));
logln(" Data: " + testcase.getInt("Data"));
}
}
// // The test data contains an error in the third case. When getInt("Data") is
// // executed the error is logged and iteration stops.
// public void Test06() {
// while (nextCase()) {
// logln("----");
// logln("isGood: " + testcase.getString("IsGood"));
// logln(" Data: " + testcase.getInt("Data"));
// }
// }
//
// // The test using the data reports an error, which also automatically stops iteration.
// public void Test07() {
// while (nextSettings()) {
// int value = settings.getInt("Value");
// while (nextCase()) {
// int factor = testcase.getInt("Factor");
// float result = (float)value / factor;
// if (result != (int)result) {
// errln("the number '" + factor + "' is not a factor of the number '" + value + "'");
// } else {
// logln("'" + factor + "' is a factor of '" + value + "'");
// }
// }
// }
// }
// The test using the data reports an error, which also automatically stops iteration.
public void Test07() {
while (nextSettings()) {
int value = settings.getInt("Value");
while (nextCase()) {
int factor = testcase.getInt("Factor");
float result = (float)value / factor;
if (result != (int)result) {
errln("the number '" + factor + "' is not a factor of the number '" + value + "'");
} else {
logln("'" + factor + "' is a factor of '" + value + "'");
}
}
}
}
// The number of data elements is incorrect
public void Test08() {
while (nextCase()) {
int one = testcase.getInt("One");
int two = testcase.getInt("Two");
int three = testcase.getInt("Three");
logln("got: " + one + ", " + two + ", " + three);
}
}
public void Test09() {
while (nextCase()) {
int radix = testcase.getInt("Radix");
int[] pow = testcase.getIntArray("Power");
int[] val = testcase.getIntArray("Value");
logln("radix: " + radix + " pow: " + printArray(pow) + " val: " + printArray(val));
for (int i = 0; i < pow.length; ++i) {
if (val[i] != (int)Math.pow(radix, pow[i])) {
errln("radix: " + radix + " to power " + pow[i] + " != " + val[i]);
break;
}
}
}
}
// // The number of data elements is incorrect
// public void Test08() {
// while (nextCase()) {
// int one = testcase.getInt("One");
// int two = testcase.getInt("Two");
// int three = testcase.getInt("Three");
// logln("got: " + one + ", " + two + ", " + three);
// }
// }
//
// public void Test09() {
// while (nextCase()) {
// int radix = testcase.getInt("Radix");
// int[] pow = testcase.getIntArray("Power");
// int[] val = testcase.getIntArray("Value");
// logln("radix: " + radix + " pow: " + printArray(pow) + " val: " + printArray(val));
// for (int i = 0; i < pow.length; ++i) {
// if (val[i] != (int)Math.pow(radix, pow[i])) {
// errln("radix: " + radix + " to power " + pow[i] + " != " + val[i]);
// break;
// }
// }
// }
// }
// utility print functions to display the data from the resource
String printArray(String[] a) {
@ -156,4 +157,12 @@ public class ModuleTestSample extends ModuleTest {
buf.append(" }");
return buf.toString();
}
/* (non-Javadoc)
* @see com.ibm.icu.dev.test.ModuleTest#processModules()
*/
protected void processModules() {
// TODO Auto-generated method stub
}
}