blob: 2da24ede2598960a9a4750b855c04985045a95ac [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.icu.junit;
import android.icu.dev.test.TestFmwk;
import android.icu.util.TimeZone;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
/**
* Abstracts away reflection code that accesses various hidden fields and methods in the ICU test
* framework.
*
* <p>Assuming that this is integrated into ICU test framework itself then ideally the test
* framework will itself be modified to remove the need for this reflection at which point this
* class can be removed.
*/
public class TestFmwkUtils {
/**
* The field on TestGroup which has the list of classes in it.
*/
private static final Field classesToTestField = getField(TestFmwk.TestGroup.class, "names");
/**
* The field on TestGroup which has the default package in it.
*/
private static final Field defaultPackageField =
getField(TestFmwk.TestGroup.class, "defaultPackage");
/**
* The field on TestFmwk which has the {@link android.icu.dev.test.TestFmwk.TestParams} in it.
*/
private static final Field paramsField = getField(TestFmwk.class, "params");
private static final Method getTargetsMethod = getTargetsMethod();
/**
* The default time zone for all the tests.
*/
private final static TimeZone defaultTimeZone =
getStaticFieldValue("defaultTimeZone", TimeZone.class);
/**
* The default locale used for all the tests.
*/
private final static Locale defaultLocale = getStaticFieldValue("defaultLocale", Locale.class);
private static <T> T getStaticFieldValue(String fieldName, Class<T> fieldClass) {
Field field = getField(TestFmwk.class, fieldName);
try {
return fieldClass.cast(field.get(null));
} catch (IllegalAccessException e) {
throw new RuntimeException("Could not get field: " + fieldName, e);
}
}
private static Field getField(Class<?> theClass, String name) {
// Find the field, and complain if it is not where it's expected to be.
try {
Field field = theClass.getDeclaredField(name);
field.setAccessible(true); // It's private by default.
return field;
} catch (NoSuchFieldException e) {
throw new RuntimeException("Class structure of ICU tests have changed.", e);
}
}
private static Method getTargetsMethod() {
try {
Method method = TestFmwk.class.getDeclaredMethod("getTargets", String.class);
method.setAccessible(true);
return method;
} catch (NoSuchMethodException e) {
throw new RuntimeException("Class structure of ICU tests have changed.", e);
}
}
public static TestFmwk.Target getTargets(TestFmwk testFmwk) {
return test_for_TestFmwk_GetTargets(testFmwk);
}
/**
* A special method to avoid the TestFmwk from throwing an InternalError when an error occurs
* during execution of the test but outside the actual test method, e.g. in a
* {@link TestFmwk#validate()} method. See http://bugs.icu-project.org/trac/ticket/12183
*
* <p>DO NOT CHANGE THE NAME
*/
private static TestFmwk.Target test_for_TestFmwk_GetTargets(TestFmwk testFmwk) {
try {
// Set the default locale and time zone here as it can affect the targets returned.
// ICU4J relies on the fact that the Target.run() method of a TestGroup based test is
// called before the getTargets() method is called. Under JUnit we get the targets
// first.
Locale.setDefault(defaultLocale);
TimeZone.setDefault(defaultTimeZone);
return (TestFmwk.Target) getTargetsMethod.invoke(testFmwk, new Object[] {null});
} catch (InvocationTargetException | IllegalAccessException e) {
throw new IllegalStateException(
"Problem calling getTargets(null) on " + testFmwk, e);
}
}
public static <T extends TestFmwk> T newTestFmwkInstance(Class<? extends T> testFmwkClass)
throws IllegalAccessException, InstantiationException {
// Create a TestFmwk and make sure that it's params field is initialized.
T testFmwk = testFmwkClass.newInstance();
TestFmwk.TestParams testParams = TestFmwk.TestParams.create(new String[0], null);
if (testParams == null) {
throw new IllegalStateException("Could not create TestParams");
}
// Set a dummy stack to avoid NPE if a test logs a known error.
testParams.stack = testParams.new State(null, "DUMMY", false);
paramsField.set(testFmwk, testParams);
return testFmwk;
}
public static List<String> getClassNames(TestFmwk.TestGroup testGroup) {
try {
String[] classNames = (String[]) classesToTestField.get(testGroup);
String defaultPackage = (String) defaultPackageField.get(testGroup);
List<String> list = new ArrayList<>(classNames.length);
for (String basicName : classNames) {
// Handle relative class names.
String fullyQualifiedName;
if (basicName.contains(".")) {
fullyQualifiedName = basicName;
} else {
fullyQualifiedName = defaultPackage + basicName;
}
list.add(fullyQualifiedName);
}
// Sort to ensure consistent ordering.
Collections.sort(list);
return list;
} catch (IllegalAccessException e) {
throw new IllegalStateException("Problem getting class names from " + testGroup, e);
}
}
}