blob: 288f14c63e99ac7ad2bb7139e6674f97f0a52f8a [file] [log] [blame]
/*
* 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.drill.common.scanner;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.drill.categories.SlowTest;
import org.apache.drill.common.config.DrillConfig;
import org.apache.drill.common.scanner.persistence.AnnotationDescriptor;
import org.apache.drill.common.scanner.persistence.FieldDescriptor;
import org.apache.drill.common.scanner.persistence.AnnotatedClassDescriptor;
import org.apache.drill.common.scanner.persistence.ScanResult;
import org.apache.drill.exec.expr.DrillFunc;
import org.apache.drill.exec.expr.annotations.FunctionTemplate;
import org.apache.drill.exec.fn.impl.testing.GeneratorFunctions.RandomBigIntGauss;
import org.apache.drill.exec.physical.base.PhysicalOperator;
import org.apache.drill.exec.store.SystemPlugin;
import org.apache.drill.exec.store.ischema.InfoSchemaStoragePlugin;
import org.apache.drill.exec.store.sys.SystemTablePlugin;
import org.apache.drill.test.BaseTest;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@Category({SlowTest.class})
public class TestClassPathScanner extends BaseTest {
private static ScanResult result;
@BeforeClass
public static void init() {
result = ClassPathScanner.fromPrescan(DrillConfig.create());
}
@Test
public void testFunctionTemplates() throws Exception {
List<AnnotatedClassDescriptor> functions = result.getAnnotatedClasses(FunctionTemplate.class.getName());
Set<String> scanned = new HashSet<>();
AnnotatedClassDescriptor functionRandomBigIntGauss = null;
for (AnnotatedClassDescriptor function : functions) {
assertTrue(function.getClassName() + " scanned twice", scanned.add(function.getClassName()));
if (function.getClassName().equals(RandomBigIntGauss.class.getName())) {
functionRandomBigIntGauss = function;
}
}
if (functionRandomBigIntGauss == null) {
Assert.fail("functionRandomBigIntGauss not found");
}
// TODO: use Andrew's randomized test framework to verify a subset of the functions
for (AnnotatedClassDescriptor function : functions) {
Class<?> c = Class.forName(function.getClassName(), false, this.getClass().getClassLoader());
Field[] fields = c.getDeclaredFields();
assertEquals("fields count for " + function, fields.length, function.getFields().size());
for (int i = 0; i < fields.length; i++) {
FieldDescriptor fieldDescriptor = function.getFields().get(i);
Field field = fields[i];
assertEquals(
"Class fields:\n" + Arrays.toString(fields) + "\n != \nDescriptor fields:\n" + function.getFields(),
field.getName(), fieldDescriptor.getName());
verifyAnnotations(field.getDeclaredAnnotations(), fieldDescriptor.getAnnotations());
assertEquals(field.getType(), fieldDescriptor.getFieldClass());
}
Annotation[] annotations = c.getDeclaredAnnotations();
List<AnnotationDescriptor> scannedAnnotations = function.getAnnotations();
verifyAnnotations(annotations, scannedAnnotations);
FunctionTemplate bytecodeAnnotation = function.getAnnotationProxy(FunctionTemplate.class);
assertNotNull(bytecodeAnnotation);
FunctionTemplate reflectionAnnotation = c.getAnnotation(FunctionTemplate.class);
assertEquals(reflectionAnnotation.name(), bytecodeAnnotation.name());
assertArrayEquals(reflectionAnnotation.names(), bytecodeAnnotation.names());
assertEquals(reflectionAnnotation.scope(), bytecodeAnnotation.scope());
assertEquals(reflectionAnnotation.nulls(), bytecodeAnnotation.nulls());
assertEquals(reflectionAnnotation.isBinaryCommutative(), bytecodeAnnotation.isBinaryCommutative());
assertEquals(reflectionAnnotation.desc(), bytecodeAnnotation.desc());
assertEquals(reflectionAnnotation.costCategory(), bytecodeAnnotation.costCategory());
}
for (String baseType : result.getScannedClasses()) {
validateType(result, baseType);
}
assertTrue(result.getImplementations(PhysicalOperator.class).size() > 0);
assertTrue(result.getImplementations(DrillFunc.class).size() > 0);
}
@Test
public void testSystemPlugins() {
List<AnnotatedClassDescriptor> annotatedClasses = result.getAnnotatedClasses(SystemPlugin.class.getName());
List<AnnotatedClassDescriptor> foundPlugins =
annotatedClasses.stream()
.filter(a -> InfoSchemaStoragePlugin.class.getName().equals(a.getClassName())
|| SystemTablePlugin.class.getName().equals(a.getClassName()))
.collect(Collectors.toList());
assertEquals(2, foundPlugins.size());
}
private <T> void validateType(ScanResult result, String baseType) throws ClassNotFoundException {
if (baseType.startsWith("org.apache.hadoop.hive")) {
return;
}
@SuppressWarnings("unchecked")
Class<T> baseClass = (Class<T>)Class.forName(baseType);
Set<Class<? extends T>> impls = result.getImplementations(baseClass);
if (impls != null) {
for (Class<? extends T> impl : impls) {
assertTrue(impl + " extends " + baseType, baseClass.isAssignableFrom(impl));
}
}
}
private void verifyAnnotations(Annotation[] annotations, List<AnnotationDescriptor> scannedAnnotations) throws Exception {
assertEquals(Arrays.toString(annotations) + " expected but got " + scannedAnnotations, annotations.length, scannedAnnotations.size());
for (int i = 0; i < annotations.length; i++) {
Annotation annotation = annotations[i];
AnnotationDescriptor scannedAnnotation = scannedAnnotations.get(i);
Class<? extends Annotation> annotationType = annotation.annotationType();
assertEquals(annotationType.getName(), scannedAnnotation.getAnnotationType());
if (annotation instanceof FunctionTemplate) {
FunctionTemplate ft = (FunctionTemplate) annotation;
if (!"".equals(ft.name())) {
assertEquals(ft.name(), scannedAnnotation.getSingleValue("name"));
}
}
// generally verify all properties
Annotation proxy = scannedAnnotation.getProxy(annotationType);
Method[] declaredMethods = annotationType.getDeclaredMethods();
for (Method method : declaredMethods) {
if (method.getParameterTypes().length == 0) {
Object reflectValue = method.invoke(annotation);
Object byteCodeValue = method.invoke(proxy);
String message = annotationType.getSimpleName() + "." + method.getName();
if (method.getReturnType().isArray()) {
assertArrayEquals(message, (Object[])reflectValue, (Object[])byteCodeValue);
} else {
assertEquals(message, reflectValue, byteCodeValue);
}
}
}
}
}
}