package com.googlecode.gwt.test.csv.internal; import com.googlecode.gwt.test.csv.CsvDirectory; import com.googlecode.gwt.test.csv.CsvMacros; import com.googlecode.gwt.test.csv.GwtTestCsvException; import com.googlecode.gwt.test.exceptions.GwtTestException; import com.googlecode.gwt.test.internal.GwtClassPool; import com.googlecode.gwt.test.utils.GwtReflectionUtils; import javassist.CtClass; import javassist.CtMethod; import org.apache.commons.lang3.StringEscapeUtils; import java.io.*; import java.lang.reflect.Method; import java.util.*; import java.util.Map.Entry; import java.util.regex.Pattern; public class DirectoryTestReader { static class CsvReader { private static final Pattern REPLACE_PATTERN = Pattern.compile("\\\\;"); private static final String SEPARATOR = ";"; /** * any ';' character which has not been escaped with a '\' */ private static final Pattern SEPARATOR_PATTERN = Pattern.compile("(?<!\\\\);"); public static List<List<String>> readCsv(Reader reader) throws IOException { BufferedReader br = new BufferedReader(reader); List<List<String>> l = new ArrayList<List<String>>(); while (true) { String line = br.readLine(); if (line == null) { break; } line = new String(line.getBytes(), "UTF-8").trim(); if ("".equals(line)) { l.add(new ArrayList<String>()); } else if (!line.startsWith("//")) { String[] tab = SEPARATOR_PATTERN.split(line); l.add(treatParams(tab)); } } return l; } private static List<String> treatParams(String[] csvParams) { List<String> list = new ArrayList<String>(csvParams.length); for (String csvParam : csvParams) { list.add(REPLACE_PATTERN.matcher(csvParam).replaceAll(SEPARATOR)); } return list; } } private static File getDirectory(String path) throws IOException { File directory = new File(path); if (!directory.exists()) { throw new FileNotFoundException("Directory [" + path + "] does not exist"); } else if (!directory.isDirectory()) { throw new IOException("A directory was expected for path [" + path + "] but a file has been found"); } return directory; } private Class<?> generatedClazz; private Map<String, List<List<String>>> macros; private List<Method> testMethods; private Map<String, List<List<String>>> tests; public DirectoryTestReader(Class<?> clazz) { try { CsvDirectory csvDirectoryAnnotation = GwtReflectionUtils.getAnnotation(clazz, CsvDirectory.class); if (csvDirectoryAnnotation == null) { throw new GwtTestCsvException("Missing annotation \'@" + CsvDirectory.class.getSimpleName() + "\' on class [" + clazz.getName() + "]"); } initCsvTests(csvDirectoryAnnotation); CsvMacros csvMacrosAnnotation = GwtReflectionUtils.getAnnotation(clazz, CsvMacros.class); initCsvMacros(csvMacrosAnnotation); initTestMethods(clazz, csvDirectoryAnnotation); } catch (Exception e) { if (GwtTestException.class.isInstance(e)) { throw (GwtTestException) e; } throw new GwtTestCsvException(e); } } public Object createObject() throws InstantiationException, IllegalAccessException { return generatedClazz.newInstance(); } public List<List<String>> getMacroFile(String macroName) { return macros.get(macroName); } public Set<String> getMacroFileList() { return Collections.unmodifiableSet(macros.keySet()); } public List<List<String>> getTest(String filePath) { return tests.get(filePath); } public Set<String> getTestList() { return Collections.unmodifiableSet(tests.keySet()); } public List<Method> getTestMethods() { Collections.sort(testMethods, new Comparator<Method>() { public int compare(Method o1, Method o2) { return o1.getName().compareTo(o2.getName()); } }); return testMethods; } private void collectCsvTests(File directory, String extension, Map<String, List<List<String>>> tests) throws IOException { for (File f : directory.listFiles()) { if (f.getName().endsWith(extension)) { tests.put(f.getAbsolutePath(), CsvReader.readCsv(new FileReader(f))); } else if (f.isDirectory()) { collectCsvTests(f, extension, tests); } } } private String getTestName(String csvTestFileAbsolutePath, String csvExtension) { String fileName = new File(csvTestFileAbsolutePath).getName(); String nameWithoutExtension = fileName.substring(0, fileName.length() - csvExtension.length()); return nameWithoutExtension; } private void initCsvMacros(CsvMacros csvMacros) throws FileNotFoundException, IOException { macros = new HashMap<String, List<List<String>>>(); if (csvMacros == null) { return; } Pattern macroNamePattern = csvMacros.pattern() != null ? Pattern.compile(csvMacros.pattern()) : null; File macrosDirectory = getDirectory(csvMacros.value()); for (File file : macrosDirectory.listFiles()) { String fileName = file.getName(); if (macroNamePattern == null || macroNamePattern.matcher(file.getName()).matches()) { FileReader reader = new FileReader(file); List<List<String>> sheet = CsvReader.readCsv(reader); macros.put(fileName, sheet); } } } private void initCsvTests(CsvDirectory csvDirectory) throws FileNotFoundException, IOException { File testsRoot = getDirectory(csvDirectory.value()); String extension = csvDirectory.extension(); testMethods = new ArrayList<Method>(); tests = new HashMap<String, List<List<String>>>(); collectCsvTests(testsRoot, extension, tests); } private void initTestMethods(Class<?> clazz, CsvDirectory csvDirectory) throws Exception { testMethods = new ArrayList<Method>(); CtClass newClazz = GwtClassPool.get().makeClass( clazz.getName() + ".generated" + System.nanoTime()); newClazz.setSuperclass(GwtClassPool.getCtClass(clazz)); List<String> methodList = new ArrayList<String>(); for (Entry<String, List<List<String>>> entry : tests.entrySet()) { String fileAbsolutePath = entry.getKey(); String methodName = getTestName(fileAbsolutePath, csvDirectory.extension()); CtMethod m = new CtMethod(CtClass.voidType, methodName, new CtClass[0], newClazz); methodList.add(methodName); m.setBody("launchTest(\"" + StringEscapeUtils.escapeJava(fileAbsolutePath) + "\");"); newClazz.addMethod(m); } generatedClazz = newClazz.toClass(getClass().getClassLoader(), null); for (String methodName : methodList) { Method m = generatedClazz.getMethod(methodName); testMethods.add(m); } } }