基于 Appium 的移动测试框架,Appium 的 Wrapper 很关键。Talk is cheap, Java 版本代码如下:
package com.ctrip.cap.lanucher;
/**
* A service wrapper for Appium Server
*
* @author ltyao
*
*/
public class AppiumServer {
private static final Logger logger = LoggerFactory
.getLogger(AppiumServer.class);
private static final long START_TIMEOUT_MILLISECONDS = 30000;
private static final HttpClient httpClient = HttpClients.createDefault();
private static final String STATUS_PATH = "/wd/hub/status";
private static final String PATH = "/wd/hub";
private String ip = "localhost";
// private volatile boolean started = false;
private Process process;
private int appiumPort = -1;
private int bootstrapPort = -1;
private int selendroidPort = -1;
private int chromeDriverPort = -1;
private int robotPort = -1;
private String appiumLog;
private AndroidDevice device;
public AppiumServer() {
}
public AppiumServer(AndroidDevice device) {
this.device = device;
}
public void stopAppium() {
try {
WinProcess winp = new WinProcess(process);
logger.warn("try to kill process {} Recursively", winp.getPid());
winp.killRecursively();
} catch (Exception e) {
logger.warn("stopAppium", e);
}
}
public void startAppium() {
startAppium(START_TIMEOUT_MILLISECONDS);
}
public void startAppium(long milliseconds) {
List<String> cmds = buildCmds();
final ProcessBuilder pb = new ProcessBuilder(cmds)
.redirectErrorStream(true);
logger.debug("start appium with {}", cmds.toString());
new Thread(new Runnable() {
@Override
public void run() {
try {
process = pb.start();
int exitvalue = process.waitFor();
logger.warn("appium server exitvalue {}", exitvalue);
} catch (Exception e) {
logger.warn("startAppium", e);
}
}
}).start();
long start = System.currentTimeMillis();
boolean state = isRunning();
while (!state) {
long end = System.currentTimeMillis();
if (end - start > milliseconds) {
this.stopAppium();
throw new AppiumTimeoutException("Appium can't be lanuched in "
+ milliseconds + " seconds");
}
state = isRunning();
}
logger.warn("started appium server {}", this);
}
public void restartAppium() {
this.stopAppium();
this.startAppium();
}
public boolean isRunning() {
try {
URI uri = new URIBuilder().setScheme("http").setHost(ip)
.setPort(appiumPort).setPath(STATUS_PATH).build();
HttpGet httpget;
HttpResponse response;
httpget = new HttpGet(uri);
response = httpClient.execute(httpget);
HttpEntity entity = response.getEntity();
String rs = EntityUtils.toString(entity);
JsonElement json = new JsonParser().parse(rs);
int status = json.getAsJsonObject().get("status").getAsInt();
return status == 0;
} catch (Exception e) {
// logger.warn("isRunning", e);
return false;
}
}
public URL getURL() {
URI uri;
try {
uri = new URIBuilder().setScheme("http").setHost(ip)
.setPort(appiumPort).setPath(PATH).build();
return uri.toURL();
} catch (URISyntaxException | MalformedURLException e) {
throw new CapException("getURL", e);
}
}
public int getPid() {
try {
WinProcess winp = new WinProcess(process);
return winp.getPid();
} catch (Exception e) {
logger.warn("", e);
return -1;
}
}
/**
* need to be checked
*
* @return
*/
private List<String> buildCmds() {
appiumPort = AvailablePortFinder.getNextAvailable();
chromeDriverPort = AvailablePortFinder.getNextAvailable();
bootstrapPort = AvailablePortFinder.getNextAvailable();
selendroidPort = AvailablePortFinder.getNextAvailable();
List<String> cmds = new LinkedList<>();
cmds.add("appium.cmd");
cmds.add(String.format("--port=%d", appiumPort));
cmds.add(String.format("--chromedriver-port=%d", chromeDriverPort));
cmds.add(String.format("--selendroid-port=%d", selendroidPort));
cmds.add(String.format("--bootstrap-port=%d", bootstrapPort));
this.appiumLog = Environment.appiumLog(device.getSerialNumber());
String qappiumLog = StringUtils.quoteArgument(this.appiumLog);
cmds.add(String.format("--log=%s", qappiumLog));
cmds.add("--log-timestamp");
// switch (config.getDriverType()) {
// case Selendroid:
// selendroidPort = AvailablePortFinder.getNextAvailable();
// cmds.add(String.format("--selendroid-port=%d", selendroidPort));
//
// break;
// case ChromeDriver:
// chromeDriverPort = AvailablePortFinder.getNextAvailable();
// cmds.add(String.format("--chromedriver-port=%d", chromeDriverPort));
//
// default:
// break;
// }
return cmds;
}
public int getAppiumPort() {
return appiumPort;
}
public void setAppiumPort(int appiumPort) {
this.appiumPort = appiumPort;
}
public int getBootstrapPort() {
return bootstrapPort;
}
public void setBootstrapPort(int bootstrapPort) {
this.bootstrapPort = bootstrapPort;
}
public int getSelendroidPort() {
return selendroidPort;
}
public void setSelendroidPort(int selendroidPort) {
this.selendroidPort = selendroidPort;
}
public int getChromeDriverPort() {
return chromeDriverPort;
}
public void setChromeDriverPort(int chromeDriverPort) {
this.chromeDriverPort = chromeDriverPort;
}
public int getRobotPort() {
return robotPort;
}
public void setRobotPort(int robotPort) {
this.robotPort = robotPort;
}
public String getAppiumLog() {
return appiumLog;
}
public Device getDevice() {
return device;
}
public void setDevice(AndroidDevice device) {
this.device = device;
}
@Override
public String toString() {
return "AppiumServer [pid=" + getPid() + ",ip=" + ip + ", appiumPort="
+ appiumPort + ", bootstrapPort=" + bootstrapPort
+ ", selendroidPort=" + selendroidPort + ", chromeDriverPort="
+ chromeDriverPort + ", appiumLog=" + appiumLog + "]";
}
}