All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.seleniumtests.browserfactory.mobile.LocalAppiumLauncher Maven / Gradle / Ivy

There is a newer version: 4.23.18
Show newest version
/**
 * Orignal work: Copyright 2015 www.seleniumtests.com
 * Modified work: Copyright 2016 www.infotel.com
 * 				Copyright 2017-2019 B.Hecquet
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.seleniumtests.browserfactory.mobile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.log4j.Logger;
import org.json.JSONObject;
import org.openqa.selenium.Platform;

import com.seleniumtests.customexception.ConfigurationException;
import com.seleniumtests.customexception.ScenarioException;
import com.seleniumtests.util.helper.WaitHelper;
import com.seleniumtests.util.logging.SeleniumRobotLogger;
import com.seleniumtests.util.osutility.OSCommand;
import com.seleniumtests.util.osutility.OSUtility;
import com.seleniumtests.util.osutility.OSUtilityFactory;
import com.seleniumtests.util.osutility.ProcessInfo;
import com.vdurmont.semver4j.Semver;

public class LocalAppiumLauncher implements AppiumLauncher {

	private String appiumVersion;
	private String appiumHome;
	private String nodeVersion;
	private String nodeCommand;
	private Process appiumProcess;
	private ProcessInfo appiumNodeProcess;
	private long appiumPort;
	private String logFile = null;
	private String optionString = "";
	private static Object appiumLauncherLock = new Object();

	private static Logger logger = SeleniumRobotLogger.getLogger(LocalAppiumLauncher.class);
	
	public LocalAppiumLauncher() {
		this(null);
	}

	public LocalAppiumLauncher(String logDirectory) {

		appiumPort = 4723 + Math.round(Math.random() * 1000);
		if (logDirectory != null) {
			new File(logDirectory).mkdirs();
			if (new File(logDirectory).isDirectory()) {
				logFile = Paths.get(logDirectory, String.format("appium-%d.log", appiumPort)).toString();
			}
		}
		
		checkInstallation();
		generateOptions();
		
	}
	
	public Process getAppiumProcess() {
		return appiumProcess;
	}

	public String getNodeVersion() {
		return nodeVersion;
	}
	
	public long getAppiumPort() {
		return appiumPort;
	}
	
	public void setAppiumPort(long appiumPort) {
		this.appiumPort = appiumPort;
	}
	
	/**
	 * Method for generating options passed to appium (e.g: logging)
	 */
	private void generateOptions() {
		if (logFile != null) {
			optionString += String.format(" --log %s --log-level debug:debug", logFile);
		}
	}

	private void checkAppiumVersion() {
		try {
			File packageFile = Paths.get(appiumHome, "node_modules", "appium", "package.json").toFile();
			String appiumConfig = FileUtils.readFileToString(packageFile);
			JSONObject packages = new JSONObject(appiumConfig);
			if (!"appium".equals(packages.getString("name"))) {
				throw new ConfigurationException(String.format("package.json file found in %s is not for appium, check path", packageFile.getAbsolutePath()));
			}
			
			appiumVersion = packages.getString("version");

		} catch (IOException e) {
			throw new ConfigurationException("File package.json not found, appium does not seem to be installed in " + appiumHome, e);
		}
	}

	/**
	 * Check that node and appium are installed
	 */
	private void checkInstallation() {
		appiumHome = System.getenv("APPIUM_HOME");
		if (appiumHome != null) {
			if (Paths.get(appiumHome, "node").toFile().exists()
					|| Paths.get(appiumHome, "node.exe").toFile().exists()) {
				nodeCommand = Paths.get(appiumHome, "node").toString();
			} else {
				nodeCommand = "node";
			}
		} else {
			throw new ConfigurationException("APPIUM_HOME environment variable not set");
		}
		
		// get appium version
		checkAppiumVersion();
		
		// get version for node
		String reply = OSCommand.executeCommandAndWait(nodeCommand + " -v").trim();
		if (!reply.matches("v\\d++\\.\\d++.*")) {
			throw new ConfigurationException("Node does not seem to be installed, is environment variable APPIUM_HOME set ?");
		} else {
			nodeVersion = reply;
		}
		
		if (OSUtility.getCurrentPlatorm() == Platform.WINDOWS) {
			nodeCommand = "cmd /c start /MIN cmd /C " + nodeCommand;
		}
	}
	
	/**
	 * Call /wd/hub/sessions to see if appium is started
	 */
	private void waitAppiumAlive() {
		
		for (int i=0; i< 60; i++) {
			try (CloseableHttpClient client = HttpClients.createDefault();) {
				HttpGet request = new HttpGet(getAppiumServerUrl() + "sessions"); 
		        CloseableHttpResponse response = client.execute(request);

		        if (response.getStatusLine().getStatusCode() == 200) {
		        	logger.info("appium has started");
		        	break;
		        }
			} catch (IOException e) {
				logger.info("appium not started");
			}
			WaitHelper.waitForSeconds(1);
		}
	}
	
	/**
	 * Returns the local appium URL
	 * @return
	 */
	public String getAppiumServerUrl() {
		return String.format("http://localhost:%d/wd/hub/", appiumPort);
	}
	
	/**
	 * Start appium and wait for availability
	 * To work around windows launching, which spawns a new cmd (we cannot stop the underlying node process), 
	 * get the node process PID associated to the newly created appium
	 */
	public void startAppiumWithWait() {
		
		synchronized(appiumLauncherLock) {
			
			List nodeProcessesInitial = OSUtilityFactory.getInstance().getRunningProcesses("node");
		
			startAppiumWithoutWait();
			
			// wait for startup
			waitAppiumAlive();
			
			for (ProcessInfo nodeProcess: OSUtilityFactory.getInstance().getRunningProcesses("node")) {
				if (!nodeProcessesInitial.contains(nodeProcess)) {
					appiumNodeProcess = nodeProcess;
					break;
				}
			}
		}
	}
	
	public void startAppiumWithoutWait() {
		
		// correction for "socket hang up" error when starting test
		// TODO: this fix does not handle multiple tests in parallel, but for now, only one mobile test can be done on mac on one session
		if (OSUtility.isMac()) {
			OSCommand.executeCommand("killall iproxy xcodebuild XCTRunner");
		}

		Semver appiumVers = new Semver(appiumVersion);
		if (appiumVers.isGreaterThan("1.6.0") || appiumVers.isEqualTo("1.6.0")) {
			appiumProcess = OSCommand.executeCommand(String.format("%s %s/node_modules/appium/ --port %d %s", 
					nodeCommand, 
					appiumHome, 
					appiumPort,
					optionString));
		} else {
			appiumProcess = OSCommand.executeCommand(String.format("%s %s/node_modules/appium/bin/appium.js --port %d %s", 
										nodeCommand, 
										appiumHome, 
										appiumPort,
										optionString));
		}	
	}
	
	/**
	 * Start appium process
	 */
	@Override
	public void startAppium() {
		startAppiumWithWait();
	}

	/**
	 * Stops appium process if it has been started, else, raise a ScenarioException
	 */
	@Override
	public void stopAppium() {
		if (appiumProcess == null) {
			throw new ScenarioException("Appium process has never been started");
		}
		appiumProcess.destroy();
		
		if (appiumNodeProcess != null) {
			OSUtilityFactory.getInstance().killProcess(appiumNodeProcess.getPid(), true);
		}
		
	}
	
	public String getAppiumVersion() {
		return appiumVersion;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy