/* * Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute * Copyright [2016-2017] EMBL-European Bioinformatics Institute * * 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 org.ensembl.healthcheck; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.Iterator; import org.ensembl.healthcheck.testcase.EnsTestCase; /** * <p> * This is the base class for groups of tests, it provides functionality * for adding and removing tests and groups of tests using the addTest * and removeTest methods. * </p> * */ public class GroupOfTests { final protected List<GroupOfTests> sourceGroups; /** * <p> * The name of this group of tests. By default getName() will return the * name of the class, but if this is set, then the value of "name" is * returned instead. * </p> * * <p> * The name is used for the GUI. * </p> * * <p> * Setting it explicitly is necessary when groups are created on the fly. * Otherwise any group created on the fly would have the name * "GroupOfTests". * </p> */ protected String name = ""; protected String description = ""; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public void setName(String name) { this.name = name; } /** * * If name attribute is not set, will return the simple name of the class. * */ public String getName() { if (name.isEmpty()) { return this.getClass().getSimpleName(); } else { return name; } } public List<GroupOfTests> getSourceGroups() { Collections.sort(sourceGroups, new GroupOfTestsComparator()); return sourceGroups; } final protected Set<Class<? extends EnsTestCase>> setOfTests; public Set<Class<? extends EnsTestCase>> getSetOfTests() { return setOfTests; } public GroupOfTests() { this.setOfTests = new HashSet<Class<? extends EnsTestCase>>(); this.sourceGroups = new ArrayList<GroupOfTests>(); } public boolean hasTest(Class<? extends EnsTestCase> ensTestCase) { return setOfTests.contains(ensTestCase); } public Set<Class<? extends EnsTestCase>> getTestClasses() { return setOfTests; } /** * <p> * Creates and returns a list of testclasses that are in this testgroup. * </p> * * @return List<Class<? extends EnsTestCase>> * */ public List<Class<? extends EnsTestCase>> getListOfTests() { Iterator<Class<? extends EnsTestCase>> i = this.getSetOfTests().iterator(); List<Class<? extends EnsTestCase>> list = new LinkedList<Class<? extends EnsTestCase>>(); while(i.hasNext()) { Class<? extends EnsTestCase> etc = i.next(); list.add(etc); } Collections.sort(list, new EnsTestCaseComparator()); return list; } /** * @param nameOfTestClass * @throws ClassNotFoundException * * <p> * Uses the classloader to fetch the class specifies in nameOfTestClass * and remove it from the set of tests for this group. * </p> * */ public void removeTest(String... nameOfTestClass) throws ClassNotFoundException { for (String currentNameOfTestClass : nameOfTestClass) { Class<? extends EnsTestCase> testClass = (Class<? extends EnsTestCase>) Class.forName(currentNameOfTestClass); this.setOfTests.remove(testClass); } } /** * @param groupOfTests * * Adds a group of tests to the set of tests in this group. * */ public void addTest(GroupOfTests groupOfTests) { this.setOfTests.addAll(groupOfTests.getTestClasses()); } /** * <p> * Adds the tests from a single test class to the set of tests to be * run. Additionally it keeps track of which groups were added. This * may be used by the GUI in the future to show the user of which * subgroups a group comprises. * </p> * * @param groupOfTestsClass * */ public void addTest(Class<? extends GroupOfTests> groupOfTestsClass) { GroupOfTests g; try { g = groupOfTestsClass.newInstance(); } // Instantiation should always work. If not, we want to deal with // failures here rather than forcing every group definition file to // deal with these problems. // catch (InstantiationException e) { throw new RuntimeException(e); } // // This would happen, if the constructor of the healthcheck was // private or protected. // catch (IllegalAccessException e) { throw new RuntimeException(e); } sourceGroups.add(g); addTest(g); } public void addTest(List<Class<EnsTestCase>> listOfTestClasses) { addTest( listOfTestClasses.toArray(new Class[]{}) ); } /** * <p> * Use this to add tests and groups of tests to the set of tests * that you want to run. * </p> * * @param testCasesOrGroupsOfTests * */ public void addTest(Class... testCasesOrGroupsOfTests) { for (Class testCaseOrGroupOfTests : testCasesOrGroupsOfTests) { /** * The user could have passed a class that is neither a * GroupOfTests nor an EnsTestCase. In that case the class * can't be handled this variable will indicate that. */ boolean canHandleThisClass = false; if (GroupOfTests.class.isAssignableFrom(testCaseOrGroupOfTests)) { addTest(testCaseOrGroupOfTests); canHandleThisClass = true; } if (EnsTestCase.class.isAssignableFrom(testCaseOrGroupOfTests)) { // Make sure we are not adding abstract classes. We only // want tests that can be run. // if (!Modifier.isAbstract(testCaseOrGroupOfTests.getModifiers())) { setOfTests.add(testCaseOrGroupOfTests); } canHandleThisClass = true; } if (!canHandleThisClass) { throw new RuntimeException( "Don't know what to do with class " + testCaseOrGroupOfTests.getCanonicalName() ); } } } /** * @param groupOfTests * * Removes a group of tests to the set of tests in this group. * */ public void removeTest(GroupOfTests groupOfTests) { this.setOfTests.removeAll(groupOfTests.getTestClasses()); } /** * @param tests * * Removes a list of test classes from the set of tests in this group. * */ public void removeTest(Class<? extends EnsTestCase>... tests) { for(Class<? extends EnsTestCase> test: tests) { setOfTests.remove(test); } } /** * <p> * Returns instance of all the tests in this group. Tests are instantiated * on the fly. * </p> * @return set of tests in group */ public Set<EnsTestCase> getTests() { Set<EnsTestCase> tests = new HashSet<EnsTestCase>(); for(Class<? extends EnsTestCase> clazz: this.setOfTests) { try { EnsTestCase test = clazz.newInstance(); test.setTypeFromPackageName(); test.types(); tests.add(test); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return tests; } /** * <p> * Useful for debugging. Prints the name of the class and which test * classes were defined to run. * </p> * */ public String toString() { Iterator<Class<? extends EnsTestCase>> testIterator = this.getTestClasses().iterator(); StringBuffer asString = new StringBuffer(); asString.append("Class: " + this.getClass().getName() + "\n"); asString.append("Has all tests from the following groups:\n"); for (GroupOfTests sourceGroups : getSourceGroups()) { asString.append(" - " + sourceGroups.getClass().getCanonicalName() + "\n"); } asString.append("Tests defined:\n"); while (testIterator.hasNext()) { asString.append(" - " + testIterator.next().getName() + "\n"); } return asString.toString(); } } class EnsTestCaseComparator implements Comparator<Class<? extends EnsTestCase>> { public int compare(Class<? extends EnsTestCase> arg0, Class<? extends EnsTestCase> arg1) { return arg0.getSimpleName().compareTo(arg1.getSimpleName()); } }