/* * JBoss, Home of Professional Open Source * Copyright 2009 Red Hat Inc. and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt in the distribution for a full listing of * individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.infinispan.test.fwk; import org.testng.annotations.Test; import java.io.BufferedReader; import java.io.File; import java.io.FileFilter; import java.io.FileReader; import java.io.FilenameFilter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Class that tests that test names are correctly set for each test. * * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "test.fwk.TestNameVerifier") public class TestNameVerifier { String dir = "src" + File.separator + "test" + File.separator + "java" + File.separator + "org" + File.separator + "infinispan"; public List<String> modules = new ArrayList<String>(); Pattern packageLinePattern = Pattern.compile("package org.infinispan[^;]*"); Pattern classLinePattern = Pattern.compile("(abstract\\s*)??(public\\s*)(abstract\\s*)??class [^\\s]*"); Pattern atAnnotationPattern = Pattern.compile("^\\s*@Test[^)]*"); Pattern testNamePattern = Pattern.compile("testName\\s*=\\s*\"[^\"]*\""); String fileCache; FilenameFilter javaFilter = new FilenameFilter() { public boolean accept(File dir, String name) { return !dir.getAbsolutePath().contains("testng") && name.endsWith(".java"); } }; FileFilter onlyDirs = new FileFilter() { public boolean accept(File pathname) { return pathname.isDirectory(); } }; @Test(enabled = false, description = "Do not enable this unless you want your files to be updated with test names!!!") public void process() throws Exception { File[] javaFiles = getAllJavaFiles(); for (File file : javaFiles) { if (needsUpdate(file)) { System.out.println("Updating file: " + file.getAbsolutePath()); updateFile(file); } } } private void updateFile(File file) throws Exception { String javaString = fileCache; String testName = getTestName(javaString, file.getName()); String testNameStr = ", testName = \"" + testName + "\""; javaString = replaceAtTestAnnotation(javaString, testNameStr); persistNewFile(file, javaString); } private void persistNewFile(File file, String javaString) throws Exception { if (file.delete()) { System.out.println("!!!!!!!!!! error processing file " + file.getName()); return; } file.createNewFile(); PrintWriter writter = new PrintWriter(file); writter.append(javaString); writter.close(); } private String replaceAtTestAnnotation(String javaString, String testNameStr) { Matcher matcher = atAnnotationPattern.matcher(javaString); boolean found = matcher.find(); assert found; String theMatch = matcher.group(); return matcher.replaceFirst(theMatch + testNameStr); } private String getTestName(String javaString, String filename) { String classNamePart = getClassNamePart(javaString, filename); //abstract classes do not require test names if (classNamePart.indexOf("abstract") >= 0) return null; classNamePart = classNamePart.substring("public class ".length()); String packagePart = getPackagePart(javaString, filename); //if the test is in org.infinispan package then make sure no . is prepended String packagePrepend = ((packagePart != null) && (packagePart.length() > 0)) ? packagePart + "." : ""; return packagePrepend + classNamePart; } private String getClassNamePart(String javaString, String filename) { Matcher matcher = classLinePattern.matcher(javaString); boolean found = matcher.find(); assert found : "could not determine class name for file: " + filename; return matcher.group(); } private String getPackagePart(String javaString, String filename) { Matcher matcher = packageLinePattern.matcher(javaString); boolean found = matcher.find(); assert found : "Could not determine package name for file: " + filename; String theMatch = matcher.group(); String partial = theMatch.substring("package org.infinispan".length()); if (partial.trim().length() == 0) return partial.trim(); return partial.substring(1);//drop the leading dot. } private boolean needsUpdate(File file) throws Exception { String javaFileStr = getFileAsString(file); if (javaFileStr.indexOf(" testName = \"") > 0) return false; int atTestIndex = javaFileStr.indexOf("@Test"); int classDeclarationIndex = javaFileStr.indexOf("public class"); return atTestIndex > 0 && atTestIndex < classDeclarationIndex; } private String getFileAsString(File file) throws Exception { StringBuilder builder = new StringBuilder(); BufferedReader fileReader = new BufferedReader(new FileReader(file)); String line; while ((line = fileReader.readLine()) != null) { builder.append(line + "\n"); } this.fileCache = builder.toString(); return fileCache; } // Loop through the list of module names and pass it to the getFilesFromModule() private File[] getAllJavaFiles() { populateModuleList(); List<File> listOfFiles = new ArrayList<File>(); for (String module : modules) { // Take in the list from the getFilesFromModule() and add it to the collection here to be converted into an // array and then returned. listOfFiles.addAll(getFilesFromModule(module)); } return listOfFiles.toArray(new File[listOfFiles.size()]); } private void addJavaFiles(File file, ArrayList<File> result) { assert file.isDirectory(); File[] javaFiles = file.listFiles(javaFilter); // printFiles(javaFiles); result.addAll(Arrays.asList(javaFiles)); for (File dir : file.listFiles(onlyDirs)) { addJavaFiles(dir, result); } } public void verifyTestName() throws Exception { File[] javaFiles = getAllJavaFiles(); StringBuilder errorMessage = new StringBuilder("Following test class(es) do not have an appropriate test names: \n"); boolean hasErrors = false; for (File file : javaFiles) { String expectedName = incorrectTestName(file); if (expectedName != null) { errorMessage.append(file.getAbsoluteFile()).append(" (Expected test name '").append(expectedName).append("', was '").append(existingTestName(file)).append("'\n"); hasErrors = true; } } assert !hasErrors : errorMessage.append("The rules for writing unit tests are described on http://www.jboss.org/community/wiki/ParallelTestSuite"); } private String incorrectTestName(File file) throws Exception { String fileAsStr = getFileAsString(file); boolean containsTestAnnotation = atAnnotationPattern.matcher(fileAsStr).find(); if (!containsTestAnnotation) return null; String expectedTestName = getTestName(fileAsStr, file.getName()); if (expectedTestName == null) return null; //this happens when the class is abstract String existingTestName = existingTestName(file); if (existingTestName == null || !existingTestName.equals(expectedTestName)) return expectedTestName; return null; } private String existingTestName(File file) throws Exception { String fileAsStr = getFileAsString(file); boolean containsTestAnnotation = atAnnotationPattern.matcher(fileAsStr).find(); if (!containsTestAnnotation) return null; String expectedTestName = getTestName(fileAsStr, file.getName()); if (expectedTestName == null) return null; //this happens when the class is abstract Matcher matcher = this.testNamePattern.matcher(fileAsStr); if (!matcher.find()) return expectedTestName; String name = matcher.group().trim(); int firstIndexOfQuote = name.indexOf('"'); return name.substring(firstIndexOfQuote + 1, name.length() - 1); } // method that populates the list of module names private void populateModuleList() { modules.add("core"); modules.add("cachestore" + File.separator + "cloud"); modules.add("cachestore" + File.separator + "bdbje"); modules.add("cachestore" + File.separator + "jdbc"); modules.add("cachestore" + File.separator + "jdbm"); modules.add("rhq-plugin"); modules.add("tree"); } // Old method that Mircea wrote that originally returned an array. Now returns a list and will be added to the list in getAllJavaFiles() private List<File> getFilesFromModule(String moduleName) { File file = new File(new File(dir).getAbsolutePath()); assert file.isDirectory(); ArrayList<File> result = new ArrayList<File>(); addJavaFiles(file, result); return result; } }