/* * 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.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Logger; import org.ensembl.healthcheck.testcase.EnsTestCase; import org.ensembl.healthcheck.autogroups.Generator; /** * * Class for automatically generating testgroups from testcases. It uses the * the group names as which the testcases register to group the tests. * Names containing underscores _ or dashes - are changed so they are valid * java classes. The characters are removed and the first letter of the next * part to be concatenated is changed to uppercase thus creating a camel * case name. * * Overview over which tests belong to which groups can be made using this: * * <pre> * /bin/bash show-groups.sh * </pre> * */ public class TestsInGroups { static final Logger log = Logger.getLogger(TestsInGroups.class.getCanonicalName()); /** * * Testcases register themselves as members of testgroups in the original * healthcheck framework. This map maps the names of these old testgroups * to a list of names of testcases which belong to them. * */ protected final Map<String,List<String>> testcasegroupToTheirTestcasemembers; /** * Creates a {@link Collection} of Testcases that were found in * the specified package * */ public static List<String> searchForTestcaseClasses(String packageToSearch) { TestInstantiator testInstantiator = new TestInstantiator(packageToSearch); Map<String,String> map = testInstantiator.getAliasToClassName(); return new ArrayList(map.values()); } /** * Delivers the full name of a class. Very simple but there are so many * names a class can have. This makes sure that everywhere the same name * is used. * */ public static String toClassName(Class c) { return c.getCanonicalName(); } /** * * Creates a map from a name of a testcase group to a list of names of * its testcase members. * * @param classNames An array of class names. * * @return A Map<String,List<String>> as described above. * */ public static Map<String,List<String>> createTestcasegroupToMembersMapping(String[] classNames) { Map<String,List<String>> testcasegroupToMembers = new HashMap<String,List<String>>(); for (String className : classNames) { try { Class c = Class.forName(className); if (EnsTestCase.class.isAssignableFrom(c)) { EnsTestCase etc = (EnsTestCase) c.newInstance(); List<String> groups = etc.getGroups(); // By default, every testcase is a member of its own // group. This leads to a multitude of groups comprising // only of a single test with the same name. // groups.remove(etc.getShortTestName()); for (String groupName : groups) { if (testcasegroupToMembers.containsKey(groupName)) { List x = testcasegroupToMembers.get(groupName); x.add(toClassName(c)); testcasegroupToMembers.put(groupName, x); } else { LinkedList<String> ll = new LinkedList<String>(); ll.add(toClassName(c)); testcasegroupToMembers.put(groupName, ll); } } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return testcasegroupToMembers; } /** * @param packageWithHealthchecks Name of packages the will be searched * for testcases. * */ TestsInGroups(String packageWithHealthchecks) { log.config( "Using classpath: \n\n" + Debug.classpathToString() ); List<String> map = searchForTestcaseClasses(packageWithHealthchecks); Map<String,List<String>> testcasegroupToMembers = createTestcasegroupToMembersMapping( map.toArray(new String[]{}) ); this.testcasegroupToTheirTestcasemembers = testcasegroupToMembers; } /** * * Creates a string representation of the mapping from testgroups to * testcases in human readable form for debugging purposes. * */ public String toString() { Map<String, List<String>> testcasegroupToMembers = this.testcasegroupToTheirTestcasemembers; StringBuffer result = new StringBuffer(); for (String groupName : testcasegroupToMembers.keySet()) { result.append(groupName); for (String memberTestcaseClassname : testcasegroupToMembers.get(groupName)) { result.append(" - " + memberTestcaseClassname + "\n"); } } return result.toString(); } public static void createTestgroupSourceFiles( Map<String,List<String>> testcasegroupToTheirTestcasemembers, Generator g, String sourceDirName, String packageName ) { //String packageName = "testgroup.legacy"; String dirForFiles = sourceDirName + "/" + packageName.replace(".", "/"); File dir = new File(dirForFiles); if (dir.exists()) { log.info("Directory " + sourceDirName + " already exists. This is ok."); } else { boolean dirCouldBeCreated = dir.mkdirs(); if (!dirCouldBeCreated) { throw new RuntimeException("Error creating directory "+sourceDirName+"!"); } } for (String currentGroupName : testcasegroupToTheirTestcasemembers.keySet()) { String currentJavaClassName = Generator.toJavaClassName(currentGroupName); //String currentGroupClassSimpleName = "All" + currentJavaClassName; String currentGroupClassSimpleName = currentJavaClassName; String currentOutFileName = dirForFiles + "/" + currentGroupClassSimpleName + ".java"; String currentClassSourceCode = g.generate( currentJavaClassName, packageName, currentGroupName, testcasegroupToTheirTestcasemembers.get(currentGroupName) ); File currentOutFile = new File(currentOutFileName); if (currentOutFile.exists()) { log.warning(currentOutFileName + " already exists, will not overwrite!"); } else { BufferedWriter out = null; try { log.info("\nCreating testgroup file "+currentOutFileName+":\n"); FileWriter fstream = new FileWriter(currentOutFileName); out = new BufferedWriter(fstream); out.write(currentClassSourceCode); } catch (IOException e) { throw new RuntimeException( "Error creating " + currentOutFileName, e ); } finally { // // If the outputstream was successfully opened, then close // it here. If it is not closed, the files will remain empty. // if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } } } public static void main(String args[]) { String packageToSearchForTestcases = args[0]; String dirNameForSourceClasses = args[1]; String packageName = args[2]; TestsInGroups tg = new TestsInGroups(packageToSearchForTestcases); Generator g = new Generator(); String dirName = dirNameForSourceClasses; boolean allWentWell = true; try { createTestgroupSourceFiles(tg.testcasegroupToTheirTestcasemembers, g, dirName, packageName); } catch(RuntimeException e) { allWentWell = false; } if (allWentWell) { TestsInGroups.log.info("Tests have been generated. Now type \"ant compile\" to compile them into tests."); } } }