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 */
018
019package org.apache.hadoop.hbase;
020
021import java.lang.reflect.Method;
022import java.lang.reflect.Modifier;
023import java.util.regex.Pattern;
024
025import org.junit.Test;
026import org.junit.experimental.categories.Category;
027import org.junit.runners.Suite;
028
029/**
030 * ClassFinder that is pre-configured with filters that will only allow test classes.
031 * The name is strange because a logical name would start with "Test" and be confusing.
032 */
033public class ClassTestFinder extends ClassFinder {
034
035  public ClassTestFinder() {
036    super(new TestFileNameFilter(), new TestFileNameFilter(), new TestClassFilter());
037  }
038
039  public ClassTestFinder(Class<?> category) {
040    super(new TestFileNameFilter(), new TestFileNameFilter(), new TestClassFilter(category));
041  }
042
043  public static Class<?>[] getCategoryAnnotations(Class<?> c) {
044    Category category = c.getAnnotation(Category.class);
045    if (category != null) {
046      return category.value();
047    }
048    return new Class<?>[0];
049  }
050
051  /** Filters both test classes and anything in the hadoop-compat modules */
052  public static class TestFileNameFilter implements FileNameFilter, ResourcePathFilter {
053    private static final Pattern hadoopCompactRe =
054        Pattern.compile("hbase-hadoop\\d?-compat");
055
056    @Override
057    public boolean isCandidateFile(String fileName, String absFilePath) {
058      boolean isTestFile = fileName.startsWith("Test")
059          || fileName.startsWith("IntegrationTest");
060      return isTestFile && !hadoopCompactRe.matcher(absFilePath).find();
061    }
062
063    @Override
064    public boolean isCandidatePath(String resourcePath, boolean isJar) {
065      return !hadoopCompactRe.matcher(resourcePath).find();
066    }
067  }
068
069  /*
070  * A class is considered as a test class if:
071   *  - it's not Abstract AND
072   *  - one or more of its methods is annotated with org.junit.Test OR
073   *  - the class is annotated with Suite.SuiteClasses
074  * */
075  public static class TestClassFilter implements ClassFilter {
076    private Class<?> categoryAnnotation = null;
077    public TestClassFilter(Class<?> categoryAnnotation) {
078      this.categoryAnnotation = categoryAnnotation;
079    }
080
081    public TestClassFilter() {
082      this(null);
083    }
084
085    @Override
086    public boolean isCandidateClass(Class<?> c) {
087      return isTestClass(c) && isCategorizedClass(c);
088    }
089
090    private boolean isTestClass(Class<?> c) {
091      if (Modifier.isAbstract(c.getModifiers())) {
092        return false;
093      }
094
095      if (c.getAnnotation(Suite.SuiteClasses.class) != null) {
096        return true;
097      }
098
099      for (Method met : c.getMethods()) {
100        if (met.getAnnotation(Test.class) != null) {
101          return true;
102        }
103      }
104
105      return false;
106    }
107
108    private boolean isCategorizedClass(Class<?> c) {
109      if (this.categoryAnnotation == null) {
110        return true;
111      }
112      for (Class<?> cc : getCategoryAnnotations(c)) {
113        if (cc.equals(this.categoryAnnotation)) {
114          return true;
115        }
116      }
117      return false;
118    }
119  }
120}