/* MonkeyTalk - a cross-platform functional testing tool
Copyright (C) 2013 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.java.tools;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.gorillalogic.monkeytalk.BuildStamp;
import com.gorillalogic.monkeytalk.Command;
import com.gorillalogic.monkeytalk.api.meta.API;
import com.gorillalogic.monkeytalk.parser.MonkeyTalkParser;
import com.gorillalogic.monkeytalk.utils.FileUtils;
public class JavaMTGenerator {
private static final String TEST_TEMPLATE = "/templates/TestTemplate.txt";
private static final List<String> EXCLUDED_COMPONENTS = Arrays.asList("vars", "doc", "debug",
"suite", "test", "setup", "teardown", "script", "system");
private static boolean mods = false;
private static boolean verbose = true;
private static int timeout = Command.DEFAULT_TIMEOUT;
private static int thinktime = Command.DEFAULT_THINKTIME;
public static void main(String[] args) {
System.out.println(BuildStamp.STAMP);
if (args.length != 2) {
System.err.println("Usage: java JavaMTGenerator <input mt> <output java>");
return;
}
File mt = new File(args[0]);
if (!mt.exists()) {
System.err.println("ERROR: script '" + mt.getAbsolutePath() + "' not found");
return;
}
if (!mt.getName().endsWith(".mt")) {
System.err.println("ERROR: script file must have .mt extension");
return;
}
File java = new File(args[1]);
if (java.getParentFile() == null || !java.getParentFile().exists()) {
System.err.println("ERROR: output path must exist");
return;
}
if (!java.getName().endsWith(".java")) {
System.err.println("ERROR: output file must have .java extension");
return;
}
String testClass = java.getName().substring(0, java.getName().length() - 5);
if (verbose) {
System.out.println("generate " + java.getName() + " from " + mt.getName());
}
try {
String output = genJavaTest(mt, testClass);
FileUtils.writeFile(java, output);
} catch (IOException ex) {
System.err.println("ERROR: " + ex.getMessage());
ex.printStackTrace();
}
}
public static String genJavaTest(File mt, String testClass) throws IOException {
// load template
String tmpl = FileUtils
.readStream(JavaMTGenerator.class.getResourceAsStream(TEST_TEMPLATE));
// init generator
mods = false;
// replace vars in template
tmpl = tmpl.replace("${testClass}", testClass);
tmpl = tmpl.replace("${package}", "com.example.test");
tmpl = tmpl.replace("${driver}",
"\t\tmt = new MonkeyTalkDriver(new File(\".\"), \"iOS\");\n");
tmpl = tmpl
.replace("${timeout}", (timeout != Command.DEFAULT_TIMEOUT ? "\t\tmt.setTimeout("
+ timeout + ");\n" : ""));
tmpl = tmpl.replace("${thinktime}",
(thinktime != Command.DEFAULT_THINKTIME ? "\t\tmt.setThinktime(" + thinktime
+ ");\n" : ""));
String test = "test" + capitalize(mt.getName().substring(0, mt.getName().length() - 3));
tmpl = tmpl.replace("${test}", test);
List<String> cmds = genCommands(mt);
tmpl = tmpl.replace("${commands}", join(cmds, "\n\t\t"));
tmpl = tmpl.replace("${extra}", "");
tmpl = tmpl.replace("${importMods}",
mods ? "import com.gorillalogic.monkeytalk.java.utils.Mods;\n" : "");
return tmpl;
}
private static List<String> genCommands(File mt) {
List<Command> cmds = MonkeyTalkParser.parseFile(mt);
List<String> out = new ArrayList<String>();
for (Command cmd : cmds) {
String java = genCommand(cmd);
if (verbose) {
System.out.println(" " + java);
}
out.add(java);
}
return out;
}
private static String genCommand(Command cmd) {
if (cmd.isComment()) {
return cmd.toString().replace("#", "//");
}
if (EXCLUDED_COMPONENTS.contains(cmd.getComponentType().toLowerCase())) {
return "// " + cmd.toString();
} else if (API.getComponentTypes().contains(cmd.getComponentType())) {
return "app." + decapitalize(cmd.getComponentType()) + getMonkeyId(cmd)
+ decapitalize(cmd.getAction()) + getArgsAndMods(cmd) + ";";
} else {
return "app.raw(\"" + cmd + "\");";
}
}
private static String getMonkeyId(Command cmd) {
if (cmd.getMonkeyId().equals("*")) {
return "().";
}
return "(\"" + cmd.getMonkeyId().replaceAll("\"", "\\\"") + "\").";
}
private static String getArgsAndMods(Command cmd) {
if (cmd.getArgs().size() == 0 && cmd.getModifiers().size() == 0) {
return "()";
} else if (cmd.getArgs().size() == 0) {
// no args, only mods
mods = true;
return "(" + getMods(cmd) + ")";
} else if (cmd.getModifiers().size() == 0) {
// no mods, only args
return "(" + getArgs(cmd) + ")";
} else {
// both args and mods
mods = true;
return "(" + getArgs(cmd) + ", " + getMods(cmd) + ")";
}
}
private static String getArgs(Command cmd) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < cmd.getArgs().size(); i++) {
String arg = cmd.getArgs().get(i);
ArgType type = getArgType(cmd, i);
if (type == ArgType.STRING) {
// string args need to be quoted
arg = "\"" + arg + "\"";
} else {
arg = arg.replaceAll("[^0-9\\.-]+", "");
}
sb.append(", ").append(arg);
}
return sb.toString().substring(2);
}
private static String getMods(Command cmd) {
StringBuilder sb = new StringBuilder("new Mods.Builder().");
List<String> keys = new ArrayList<String>(cmd.getModifiers().keySet());
Collections.sort(keys);
for (String key : keys) {
sb.append(key).append("(").append(cmd.getModifiers().get(key)).append(").");
}
return sb.append("build()").toString();
}
private static String capitalize(String s) {
return (s == null || s.length() == 0 ? s : s.substring(0, 1).toUpperCase() + s.substring(1));
}
private static String decapitalize(String s) {
return (s == null || s.length() == 0 ? s : s.substring(0, 1).toLowerCase() + s.substring(1));
}
private static String join(List<String> list, String joiner) {
StringBuilder sb = new StringBuilder();
for (String item : list) {
sb.append(joiner).append(item);
}
return sb.toString().substring(joiner.length());
}
private static enum ArgType {
INT, FLOAT, STRING
};
private static ArgType getArgType(Command cmd, int idx) {
try {
String klassName = capitalize(cmd.getComponentType());
Class<?> klass = Class.forName("com.gorillalogic.monkeytalk.java.api." + klassName);
String methodName = decapitalize(cmd.getAction());
for (Method m : klass.getDeclaredMethods()) {
// ignore methods that don't match the action name
if (!m.getName().equals(methodName)) {
continue;
}
Class<?>[] params = m.getParameterTypes();
// ignore methods with no params
if (params.length == 0) {
continue;
}
// ignore methods where last param is a list
if (params[params.length - 1].equals(List.class)) {
continue;
}
// ignore methods where last param is a map
if (params[params.length - 1].equals(Map.class)) {
continue;
}
Class<?> param = null;
if (m.isVarArgs()) {
// if necessary, count backward to find the vararg param
while (idx >= params.length) {
idx--;
}
param = params[idx];
} else if (idx < params.length) {
param = params[idx];
}
if (param != null) {
if (param.getSimpleName().startsWith("int")) {
return ArgType.INT;
} else if (param.getSimpleName().startsWith("float")) {
return ArgType.FLOAT;
}
}
}
} catch (Exception ex) {
// if anything goes wrong, assume String
return ArgType.STRING;
}
return ArgType.STRING;
}
}