001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase;
019
020import java.lang.reflect.Method;
021import java.lang.reflect.Modifier;
022import java.util.ArrayList;
023import java.util.List;
024import java.util.regex.Pattern;
025import org.junit.jupiter.api.Tag;
026
027/**
028 * ClassFinder that is pre-configured with filters that will only allow test classes. The name is
029 * strange because a logical name would start with "Test" and be confusing.
030 */
031public class ClassTestFinder extends ClassFinder {
032
033  public ClassTestFinder() {
034    super(new TestFileNameFilter(), new TestFileNameFilter(), new TestClassFilter());
035  }
036
037  public ClassTestFinder(Class<?> tag) {
038    super(new TestFileNameFilter(), new TestFileNameFilter(), new TestClassFilter(tag));
039  }
040
041  public static Class<?>[] getTagAnnotations(Class<?> c) {
042    // TODO handle optional Tags annotation
043    Tag[] tags = c.getAnnotationsByType(Tag.class);
044    List<Class<?>> values = new ArrayList<>();
045    if (tags != null) {
046      for (Tag tag : tags) {
047        try {
048          values.add(Class.forName(tag.value()));
049        } catch (ClassNotFoundException e) {
050          throw new RuntimeException(e);
051        }
052      }
053    }
054    return values.toArray(new Class<?>[values.size()]);
055  }
056
057  /** Filters both test classes and anything in the hadoop-compat modules */
058  public static class TestFileNameFilter implements FileNameFilter, ResourcePathFilter {
059    private static final Pattern hadoopCompactRe = Pattern.compile("hbase-hadoop\\d?-compat");
060
061    @Override
062    public boolean isCandidateFile(String fileName, String absFilePath) {
063      boolean isTestFile = fileName.startsWith("Test") || fileName.startsWith("IntegrationTest");
064      return isTestFile && !hadoopCompactRe.matcher(absFilePath).find();
065    }
066
067    @Override
068    public boolean isCandidatePath(String resourcePath, boolean isJar) {
069      return !hadoopCompactRe.matcher(resourcePath).find();
070    }
071  }
072
073  /*
074   * A class is considered as a test class if: - it's not Abstract AND - one or more of its methods
075   * is annotated with org.junit.Test OR - the class is annotated with Suite.SuiteClasses
076   */
077  public static class TestClassFilter implements ClassFilter {
078    private Class<?> tagAnnotation = null;
079
080    public TestClassFilter(Class<?> categoryAnnotation) {
081      this.tagAnnotation = categoryAnnotation;
082    }
083
084    public TestClassFilter() {
085      this(null);
086    }
087
088    @Override
089    public boolean isCandidateClass(Class<?> c) {
090      return isTestClass(c) && isTagedClass(c);
091    }
092
093    private boolean isTestClass(Class<?> c) {
094      if (Modifier.isAbstract(c.getModifiers())) {
095        return false;
096      }
097
098      for (Method met : c.getMethods()) {
099        if (met.getAnnotation(org.junit.jupiter.api.Test.class) != null) {
100          return true;
101        }
102      }
103
104      return false;
105    }
106
107    private boolean isTagedClass(Class<?> c) {
108      if (this.tagAnnotation == null) {
109        return true;
110      }
111      for (Class<?> cc : getTagAnnotations(c)) {
112        if (cc.equals(this.tagAnnotation)) {
113          return true;
114        }
115      }
116      return false;
117    }
118  }
119}