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

com.axway.ats.uiengine.utilities.mobile.MobileDeviceUtils Maven / Gradle / Ivy

/*
 * Copyright 2017 Axway Software
 * 
 * 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.axway.ats.uiengine.utilities.mobile;

import java.lang.reflect.Constructor;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.axway.ats.common.filesystem.Md5SumMode;
import com.axway.ats.core.filesystem.LocalFileSystemOperations;
import com.axway.ats.core.filesystem.model.IFileSystemOperations;
import com.axway.ats.core.process.LocalProcessExecutor;
import com.axway.ats.core.process.model.IProcessExecutor;
import com.axway.ats.core.system.LocalSystemOperations;
import com.axway.ats.core.system.model.ISystemOperations;
import com.axway.ats.core.utils.IoUtils;
import com.axway.ats.core.utils.HostUtils;
import com.axway.ats.core.utils.StringUtils;
import com.axway.ats.uiengine.MobileDriver;
import com.axway.ats.uiengine.exceptions.MobileOperationException;
import com.axway.ats.uiengine.exceptions.NotSupportedOperationException;

public class MobileDeviceUtils {

    private static final Pattern          LS_ENTRY_PATTERN = Pattern.compile( "[^\\s]{9,15}\\s+\\d+\\s+\\d+\\s+(\\d*)\\s+([\\d\\-]+\\s+[\\d:]+)\\s+(.*)" );
    private static final SimpleDateFormat LS_DATE_FORMAT   = new SimpleDateFormat( "yyyy-MM-dd HH:mm" );

    private MobileDriver                  mobileDriver;

    private String                        iOSUserHomeDir;
    private String                        iOSDeviceId;
    private String                        iOSApplicationId;
    private String                        iOSApplicationDataPath;

    public MobileDeviceUtils( MobileDriver uiDriver ) {

        this.mobileDriver = uiDriver;
    }

    /**
    *
    * @param path a path on the mobile device. It can be a directory or file (if you want to check
    * for specific file existence)
    * 
* Note for iOS: You have to specify a relative path to the application data folder
* For example: "Documents/MyAppFiles"
* and we'll internally search for files in: * "/Users/<username>/Library/Developer/CoreSimulator/Devices/<device_id>/data/Containers/Data/Application/<app_id>/Documents/MyAppFiles/"
* which is the iOS Simulator application data folder path * * @return {@link FileInfo} array with all the files and folders from the target path */ public FileInfo[] listFiles( String path ) { List files = new ArrayList(); if( this.mobileDriver.isAndroidAgent() ) { String[] commandArguments = new String[]{ "shell", "ls", "-lan", path }; try { IProcessExecutor processExecutor = executeAdbCommand( commandArguments, true ); String result = processExecutor.getStandardOutput(); if( !StringUtils.isNullOrEmpty( result ) ) { String[] lines = result.split( "[\r\n]+" ); for( String line : lines ) { Matcher m = LS_ENTRY_PATTERN.matcher( line ); if( m.matches() ) { /* * group 1 -> file size * group 2 -> modification date and time * group 3 -> file name */ String fileName = m.group( 3 ); String absolutePath = new String( IoUtils.normalizeUnixDir( path ) + fileName ).replace( "//", "/" ); if( line.startsWith( "l" ) && line.contains( "->" ) ) { // a link absolutePath = fileName.substring( fileName.lastIndexOf( "->" ) + 2 ).trim(); // after '->' fileName = fileName.substring( 0, fileName.indexOf( "->" ) ).trim(); // before '->' } String fileSize = m.group( 1 ); FileInfo file = new FileInfo( fileName, absolutePath, line.startsWith( "d" ) || fileSize.isEmpty() /* OR because second option is for symbolic links */ ); if( !fileSize.isEmpty() ) { file.setSize( Long.parseLong( fileSize ) ); } file.setModificationDate( LS_DATE_FORMAT.parse( m.group( 2 ) ) ); files.add( file ); } } } } catch( Exception e ) { throw new MobileOperationException( "Unable to list files for path '" + path + "'", e ); } } else { // iOS case supposed IFileSystemOperations fileSystemOperations = getFileSystemOperatoinsImpl(); String[] filePaths = fileSystemOperations.findFiles( getiOSApplicationDataPath() + path, ".*", true, true, false ); for( String filePath : filePaths ) { boolean isDirectory = filePath.endsWith( "/" ); if( isDirectory ) { filePath = filePath.substring( 0, filePath.length() - 1 ); } String fileName = filePath.substring( filePath.lastIndexOf( '/' ) + 1 ); FileInfo file = new FileInfo( fileName, filePath, isDirectory ); file.setSize( fileSystemOperations.getFileSize( filePath ) ); files.add( file ); } } return files.toArray( new FileInfo[files.size()] ); } /** * * @param filePath file absolute path *
* Note for iOS: You can specify relative path too, by skipping the root slash '/' at the beginning * and pass the path relative to the application data folder
* For example: "Documents/MyAppFiles/IMG_0001.PNG"
* and we'll internally search for: * "/Users/<username>/Library/Developer/CoreSimulator/Devices/<device_id>/data/Containers/Data/Application/<app_id>/Documents/MyAppFiles/IMG_0001.PNG"
* which is the iOS Simulator application data folder path * * @return the MD5 sum of the specified file */ public String getMD5Sum( String filePath ) { if( this.mobileDriver.isAndroidAgent() ) { String[] commandArguments = new String[]{ "shell", "md5", filePath }; try { IProcessExecutor processExecutor = executeAdbCommand( commandArguments, true ); String result = processExecutor.getStandardOutput(); if( !StringUtils.isNullOrEmpty( result ) ) { result = result.trim(); return result.substring( 0, result.indexOf( ' ' ) ); } } catch( Exception e ) { throw new MobileOperationException( "Unable to calculate md5 sum of file '" + filePath + "'", e ); } } else { // iOS app. supposed if( !filePath.startsWith( "/" ) ) { filePath = getiOSApplicationDataPath() + filePath; } return getFileSystemOperatoinsImpl().computeMd5Sum( filePath, Md5SumMode.BINARY ); } return null; } /** * * @param directoryPath the directory for deletion * @param recursively whether to delete the internal directories recursively * * Note for iOS: You can specify relative directory path too, by skipping the root slash '/' at the beginning * and pass the path relative to the application data folder
* For example: "Documents/MyAppFiles"
* and we'll internally search for: * "/Users/<username>/Library/Developer/CoreSimulator/Devices/<device_id>/data/Containers/Data/Application/<app_id>/Documents/MyAppFiles/"
* which is the iOS Simulator application data folder path */ public void deleteDirectory( String directoryPath, boolean recursively ) { if( this.mobileDriver.isAndroidAgent() ) { String[] commandArguments = null; if( recursively ) { commandArguments = new String[]{ "shell", "rm", "-rf", directoryPath }; } else { commandArguments = new String[]{ "shell", "rm", "-f", directoryPath }; } try { executeAdbCommand( commandArguments, true ); } catch( Exception e ) { throw new MobileOperationException( "Unable to " + ( recursively ? "recursively " : "" ) + "delete directory '" + directoryPath + "'", e ); } } else { // iOS app. supposed if( !directoryPath.startsWith( "/" ) ) { directoryPath = getiOSApplicationDataPath() + directoryPath; } getFileSystemOperatoinsImpl().deleteDirectory( directoryPath, recursively ); } } /** * * @param filePath the file for deletion * * Note for iOS: You can specify relative file path too, by skipping the root slash '/' at the beginning * and pass the path relative to the application data folder
* For example: "Documents/MyAppFiles/fileToDelete"
* and we'll internally search for: * "/Users/<username>/Library/Developer/CoreSimulator/Devices/<device_id>/data/Containers/Data/Application/<app_id>/Documents/MyAppFiles/fileToDelete"
* which is the iOS Simulator application data folder path */ public void deleteFile( String filePath ) { if( this.mobileDriver.isAndroidAgent() ) { String[] commandArguments = new String[]{ "shell", "rm", filePath }; try { executeAdbCommand( commandArguments, true ); } catch( Exception e ) { throw new MobileOperationException( "Unable to delete file '" + filePath + "'", e ); } } else { // iOS app. supposed if( !filePath.startsWith( "/" ) ) { filePath = getiOSApplicationDataPath() + filePath; } getFileSystemOperatoinsImpl().deleteFile( filePath ); } } /** * * @param directoryPath the directory for creation */ public void createDirectory( String directoryPath ) { if( this.mobileDriver.isAndroidAgent() ) { String[] commandArguments = new String[]{ "shell", "mkdir", "-p", directoryPath }; try { executeAdbCommand( commandArguments, true ); } catch( Exception e ) { throw new MobileOperationException( "Unable to create directory '" + directoryPath + "'", e ); } } else { throw new NotSupportedOperationException( "Currently 'createDirectory' operation for iOS is not implemented" ); } } /** * * @param localFilePath local file path * @param deviceFilePath file path on the device */ public void copyFileTo( String localFilePath, String deviceFilePath ) { if( this.mobileDriver.isAndroidAgent() ) { String[] commandArguments = new String[]{ "push", localFilePath, deviceFilePath }; try { executeAdbCommand( commandArguments, true ); } catch( Exception e ) { throw new MobileOperationException( "Unable to copy file '" + localFilePath + "' to '" + deviceFilePath + "'", e ); } } else { throw new NotSupportedOperationException( "Currently 'copyFileTo' operation for iOS is not implemented" ); } } /** * * @param deviceFilePath file path on the device * @param localFilePath local file path */ public void copyFileFrom( String deviceFilePath, String localFilePath ) { if( this.mobileDriver.isAndroidAgent() ) { String[] commandArguments = new String[]{ "pull", deviceFilePath, localFilePath }; try { executeAdbCommand( commandArguments, true ); } catch( Exception e ) { throw new MobileOperationException( "Unable to copy file '" + deviceFilePath + "' to '" + localFilePath + "'", e ); } } else { throw new NotSupportedOperationException( "Currently 'copyFileFrom' operation for iOS is not implemented" ); } } public boolean isAdbOnWindows() { return getSystemOperationsImpl().getOperatingSystemType().isWindows(); } public IProcessExecutor executeAdbCommand( String[] commandArguments, boolean verifyExitCode ) { IProcessExecutor pe = null; try { if( getSystemOperationsImpl().getOperatingSystemType().isWindows() ) { pe = getProcessExecutorImpl( this.mobileDriver.getAdbLocation() + "adb.exe", commandArguments ); } else { pe = getProcessExecutorImpl( this.mobileDriver.getAdbLocation() + "adb", commandArguments ); } pe.setWorkDirectory( this.mobileDriver.getAdbLocation() ); pe.execute(); if( verifyExitCode && pe.getExitCode() != 0 ) { throw new MobileOperationException( "Adb command failed (STDOUT: '" + pe.getStandardOutput() + "', STDERR: '" + pe.getErrorOutput() + "')" ); } } catch( Exception e ) { throw new MobileOperationException( "Adb command failed", e ); } return pe; } private String getiOSApplicationDataPath() { if( iOSApplicationDataPath == null && !this.mobileDriver.isAndroidAgent() ) { // get user home dir iOSUserHomeDir = getSystemOperationsImpl().getSystemProperty( "user.home" ); if( !iOSUserHomeDir.startsWith( "/" ) ) { iOSUserHomeDir = "/" + iOSUserHomeDir; } iOSUserHomeDir = IoUtils.normalizeUnixDir( iOSUserHomeDir ); // get booted device UUID IProcessExecutor pe = getProcessExecutorImpl( "/bin/bash", new String[]{ "-c", "xcrun simctl list | grep Booted | cut -d \\( -f2 | tr -d \\)" } ); pe.execute(); iOSDeviceId = pe.getStandardOutput().trim(); //FIXME: The next variables are retrieved for iOS Simulator, // so when we add support for iOS real devices, we have to touch this code // get current application ID in the simulator data structure // we suppose that the current application ID is the last modified directory in the device Applications folder pe = getProcessExecutorImpl( "/bin/bash", new String[]{ "-c", "ls -t " + iOSUserHomeDir + "Library/Developer/CoreSimulator/Devices/" + iOSDeviceId + "/data/Containers/Data/Application | head -1" } ); pe.execute(); iOSApplicationId = pe.getStandardOutput().trim(); // now we can build the application data path using the device and application retrieved identifiers iOSApplicationDataPath = iOSUserHomeDir + "Library/Developer/CoreSimulator/Devices/" + iOSDeviceId + "/data/Containers/Data/Application/" + iOSApplicationId + "/"; } return iOSApplicationDataPath; } /** * * @return {@link ISystemOperations} implementation instance */ private ISystemOperations getSystemOperationsImpl() { if( this.mobileDriver.isWorkingRemotely() ) { String remoteSystemOperationsClassName = "com.axway.ats.action.system.RemoteSystemOperations"; try { Class remoteSOClass = Class.forName( remoteSystemOperationsClassName ); Constructor constructor = remoteSOClass.getDeclaredConstructors()[0]; return ( ISystemOperations ) constructor.newInstance( mobileDriver.getHost() ); } catch( Exception e ) { throw new RuntimeException( "Unable to instantiate RemoteSystemOperations. Check whether the Action Library component is in the classpath.", e ); } } return new LocalSystemOperations(); } /** * * @return {@link IFileSystemOperations} implementation instance */ private IFileSystemOperations getFileSystemOperatoinsImpl() { if( this.mobileDriver.isWorkingRemotely() ) { String remoteFileSystemOperationsClassName = "com.axway.ats.action.filesystem.RemoteFileSystemOperations"; try { Class remoteFSOClass = Class.forName( remoteFileSystemOperationsClassName ); Constructor constructor = remoteFSOClass.getDeclaredConstructors()[0]; return ( IFileSystemOperations ) constructor.newInstance( mobileDriver.getHost() ); } catch( Exception e ) { throw new RuntimeException( "Unable to instantiate RemoteFileSystemOperations. Check whether the Action Library component is in the classpath.", e ); } } return new LocalFileSystemOperations(); } /** * * @return {@link IProcessExecutor} implementation instance */ private IProcessExecutor getProcessExecutorImpl( String command, String... commandArguments ) { if( this.mobileDriver.isWorkingRemotely() ) { String remoteProcessExecutorClassName = "com.axway.ats.action.processes.RemoteProcessExecutor"; try { Class remotePEOClass = Class.forName( remoteProcessExecutorClassName ); Constructor constructor = remotePEOClass.getDeclaredConstructors()[0]; return ( IProcessExecutor ) constructor.newInstance( mobileDriver.getHost(), command, commandArguments ); } catch( Exception e ) { throw new RuntimeException( "Unable to instantiate RemoteProcessExecutor. Check whether the Action Library component is in the classpath.", e ); } } return new LocalProcessExecutor( HostUtils.LOCAL_HOST_NAME, command, commandArguments ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy