/* MonkeyTalk - a cross-platform functional testing tool Copyright (C) 2012 Gorilla Logic, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.gorillalogic.monkeytalk; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import com.gorillalogic.monkeytalk.api.meta.Action; import com.gorillalogic.monkeytalk.api.meta.Arg; import com.gorillalogic.monkeytalk.finder.Finder; import com.gorillalogic.monkeytalk.parser.CSVParser; import com.gorillalogic.monkeytalk.parser.MonkeyTalkParser; import com.gorillalogic.monkeytalk.utils.FileUtils; /** * Scan the project folder to provide file system information and meta information to the processors * ({@link ScriptProcessor}, {@link SuiteProcessor}, etc.). */ public class CommandWorld { /** * MonkeyTalk script file extension. */ public static final String SCRIPT_EXT = ".mt"; /** * MonkeyTalk suite file extension. */ public static final String SUITE_EXT = ".mts"; /** * MonkeyTalk Javascript file extension. */ public static final String JS_EXT = ".js"; /** * MonkeyTalk data file extension. */ public static final String DATA_EXT = ".csv"; /** * Pattern to match files named <code><componentType>.<action>.mt</code> */ private static final String CUSTOM_PATTERN = "[^\\.]+\\.[^\\.]+\\" + SCRIPT_EXT; private File rootDir; /** * Instantiate an empty world. */ public CommandWorld() { this(null); } /** * Instantiate a command world from the given root directory. * * @param rootDir * the project root directory (aka the directory where all your scripts, suites, * datafiles, etc. are located) */ public CommandWorld(File rootDir) { if (rootDir == null || !rootDir.exists() || !rootDir.isDirectory()) { this.rootDir = null; } else { this.rootDir = rootDir.getAbsoluteFile(); } } /** * Get the root directory (aka the <i>project</i> directory where all your scripts are). * * @return the root directory */ public File getRootDir() { return rootDir; } /** * Does the given script have a Javascript override? Returns true if yes {@code script.mt} is * overridden by {@code script.js}, otherwise false. * * @return true if a Javascript override exists, otherwise false */ public boolean hasJavascriptOverride(String filename) { File f = findFileWithExt(filename, JS_EXT); return (f != null); } /** * Get the MonkeyTalk commands parsed from the given script. * * @param filename * the suite filename * @return the commands parsed from the suite */ public List<Command> getScript(String filename) { File f = findFileWithExt(filename, SCRIPT_EXT); return (f != null ? MonkeyTalkParser.parseFile(f) : null); } /** * Get the MonkeyTalk commands parsed from the given suite. * * @param filename * the suite filename * @return the commands parsed from the suite */ public List<Command> getSuite(String filename) { File f = findFileWithExt(filename, SUITE_EXT); return (f != null ? MonkeyTalkParser.parseFile(f) : null); } /** * Get the CSV data parsed from the given datafile. * * @param filename * the datafile filename * @return the CSV data parsed from the datafile */ public List<Map<String, String>> getData(String filename) { File f = findFileWithExt(filename, DATA_EXT); return (f != null ? CSVParser.parseFile(f) : null); } /** * Get the list of script files in the project (both the scripts and the custom commands). * * @return the list of script files */ public List<File> getScriptFiles() { return findFilesWithExt(SCRIPT_EXT); } /** * Get the list of suite files in the project. * * @return the list of suite files */ public List<File> getSuiteFiles() { return findFilesWithExt(SUITE_EXT); } /** * Get the list of datafiles in the project. * * @return the list of datafiles */ public List<File> getDataFiles() { return findFilesWithExt(DATA_EXT); } /** * Get the list of custom command files in the project. * * @return the list of custom command files */ public List<File> getCustomCommandFiles() { return findFilesWithRegex(CUSTOM_PATTERN); } /** * Get the list of Javascript files in the project. * * @return the list of Javascript files. */ public List<File> getJavascriptFiles() { return findFilesWithExt(JS_EXT); } /** * Get the Meta API {@link com.gorillalogic.monkeytalk.api.meta.Action} for the given script * filename. * * @param filename * the script filename * @return the action */ public Action getAPIAction(String filename) { Command cmd = null; List<Command> commands = getScript(filename); // get all arg docs Map<String, String> descArgs = new HashMap<String, String>(); cmd = Finder.findCommandByName(commands, "doc.vars"); if (cmd != null) { for (String arg : cmd.getArgs()) { int idx = arg.indexOf("="); if (idx != -1) { String key = arg.substring(0, idx); String val = arg.substring(idx + 1); if (val.startsWith("\"") && val.endsWith("\"")) { val = val.substring(1, val.length() - 1); } descArgs.put(key, val); } } } // get all args List<Arg> args = new ArrayList<Arg>(); cmd = Finder.findCommandByName(commands, "vars.define"); if (cmd != null) { for (String arg : cmd.getArgs()) { int idx = arg.indexOf("="); if (idx != -1) { String key = arg.substring(0, idx); String val = arg.substring(idx + 1); if (val.startsWith("\"") && val.endsWith("\"")) { val = val.substring(1, val.length() - 1); } args.add(new Arg(key, descArgs.get(key), "String", val)); } } } // get the script doc (aka action description) String desc = null; cmd = Finder.findCommandByName(commands, "doc.script"); if (cmd != null && cmd.getArgs().size() > 0) { String val = cmd.getArgs().get(0); if (val.startsWith("\"") && val.endsWith("\"")) { val = val.substring(1, val.length() - 1); } desc = val; } return new Action(filename, desc, (args.size() > 0 ? args : null), null, null); } /** * Search the world for a file with the given filename. * * @param filename * the filename to search for * @return true if found, otherwise false */ public boolean fileExists(String filename) { if (filename != null) { return (FileUtils.findFile(filename, rootDir) != null); } return false; } /** * Search for a file with the given filename and extension. This will append the extension to * the filename if it's not already there. NOTE: this is <b>case-insensitive</b> on both * filename and extension. * * @param filename * the suite filename * @return the file, or null if not found */ private File findFileWithExt(String filename, String ext) { if (filename != null) { if (!filename.toLowerCase().endsWith(ext.toLowerCase())) { filename += ext; } return FileUtils.findFile(filename, rootDir); } return null; } /** * Find the list of files with the given extension. Could return an empty list, but never * {@code null}. NOTE: match is <b>case-insensitive</b> on extension. * * @param ext * the file extension * @return the list of files (could be empty, but never null). */ private List<File> findFilesWithExt(String ext) { List<File> list = new ArrayList<File>(); if (rootDir != null) { ext = ext.toLowerCase(); for (File f : rootDir.listFiles()) { if (f.getName().toLowerCase().endsWith(ext)) { list.add(f); } } Collections.sort(list); } return list; } /** * Find the list of files matching the given regex pattern. Could return an empty list, but * never {@code null}. NOTE: match is <b>case-insensitive</b> on extension. * * @param pattern * the regex pattern * @return the list of files (could be empty, but never null). */ private List<File> findFilesWithRegex(String pattern) { List<File> list = new ArrayList<File>(); if (rootDir != null) { for (File f : rootDir.listFiles()) { if (f.getName().toLowerCase().matches(pattern)) { list.add(f); } } Collections.sort(list); } return list; } @Override public String toString() { StringBuilder sb = new StringBuilder("RootDir: "); sb.append(rootDir != null ? rootDir.getAbsolutePath() : "null"); sb.append("\nScripts: "); sb.append(printFiles(getScriptFiles())); sb.append("\nSuites: "); sb.append(printFiles(getSuiteFiles())); sb.append("\nCustomCommands: "); sb.append(printFiles(getCustomCommandFiles())); sb.append("\nJavascripts: "); sb.append(printFiles(getJavascriptFiles())); sb.append("\nDatafiles: "); sb.append(printFiles(getDataFiles())); sb.append("\n"); return sb.toString(); } /** * Helper to print the given list of strings. * * @param list * the list of strings * @return the strings joined by newlines */ private String printFiles(List<File> list) { if (list.size() == 0) { return "none"; } StringBuilder sb = new StringBuilder(); for (File f : list) { if (sb.length() > 0) { sb.append(", "); } sb.append(f.getName()); } return sb.toString(); } }