Skip to content

Commit

Permalink
Download app if URL is provided (#236)
Browse files Browse the repository at this point in the history
* Handling appPath if URL is provided (create repo, download file, install downloaded file) (#226)

* Resolved TODO loggingPrefs for firefox.
Added setLogLevel, loggingPrefs for createChromeDriver and createFirefoxDriver.

* Added 2 methods isURLValid(String) and downloadFile(String, String).
Both of these are used in verifyAppExistsAtMentionedPath() where if URL exist we are creating repository, downloading file, then installing the downloaded file on device.

* Removed isURLValid(appPath), used isAppPathAUrl(appPath) instead.
Added createDirectoryIfNotExist(directoryPath).
Rewritten code for simplification

* Added Logger to createDirectoryIfNotExist() based on condition.
Added Logger, exception throwing in downloadFileIfNotExist().
Create method convertAppPathToFilePathIfNeeded(), and called from verifyAppExistsAtMentionedPath().
Added Logger to isAppPathAUrl() based on condition

* made changes according to PR comments

* pushing after pulling changes from forked

* made convertAppPathToFilePathIfNeeded() public,
created unit tests in AppPathTest for convertAppPathToFilePathIfNeeded()

* modified method names and code as per comments

* changed method naming using given, when, then. Changed to single imports.
modified code as per comments

* Changed to single imports. Modified code as per comments. Created separate method to open file

* made directoryPath as second argument

* made changes to directoryPath in variable names, for unit test using temp/unitTests directory.

---------

Co-authored-by: Mukund1 Gupta <[email protected]>

* updated download app functionality

* renamed method from convertAppPathToFilePathIfNeeded to downloadAppAndGetFilePath

* Removed openFile() method, added and modified Unit Tests  (#240)

* Resolved TODO loggingPrefs for firefox.
Added setLogLevel, loggingPrefs for createChromeDriver and createFirefoxDriver.

* Added 2 methods isURLValid(String) and downloadFile(String, String).
Both of these are used in verifyAppExistsAtMentionedPath() where if URL exist we are creating repository, downloading file, then installing the downloaded file on device.

* Removed isURLValid(appPath), used isAppPathAUrl(appPath) instead.
Added createDirectoryIfNotExist(directoryPath).
Rewritten code for simplification

* Added Logger to createDirectoryIfNotExist() based on condition.
Added Logger, exception throwing in downloadFileIfNotExist().
Create method convertAppPathToFilePathIfNeeded(), and called from verifyAppExistsAtMentionedPath().
Added Logger to isAppPathAUrl() based on condition

* made changes according to PR comments

* pushing after pulling changes from forked

* made convertAppPathToFilePathIfNeeded() public,
created unit tests in AppPathTest for convertAppPathToFilePathIfNeeded()

* modified method names and code as per comments

* changed method naming using given, when, then. Changed to single imports.
modified code as per comments

* Changed to single imports. Modified code as per comments. Created separate method to open file

* made directoryPath as second argument

* made changes to directoryPath in variable names, for unit test using temp/unitTests directory.

* removed openFile() method and its calling from convertAppPathToFilePathIfNeeded() method

* modified code, created all 12 possible unit tests

* changed unit test method name

* modified code as per downloadApp branch and called createDirectory() inside downloadFile()

* modified method names for unit tests and method called for downloading file.

* add isAppPathAUrl() method to confirm url is valid or invalid after confirming it is an url

* change assertions to assertThrows and replace Exception

* add conditional code for handling file does not exist or incorrect file path by throwing exception

* made changes to unit test assertions, replaced with assertThrows to catch exceptions

* add methods checkEitherFilePathIsIncorrectOrFileIsMissing(), downloadFileIfDoesNotExist()

---------

Co-authored-by: Mukund1 Gupta <[email protected]>

* fixed and optimised downloadApp if URL is given functionality. Updated tests.

---------

Co-authored-by: Mukund Gupta <[email protected]>
Co-authored-by: Mukund1 Gupta <[email protected]>
  • Loading branch information
3 people authored Jul 8, 2023
1 parent 96f7de8 commit b9a5cef
Show file tree
Hide file tree
Showing 2 changed files with 312 additions and 29 deletions.
192 changes: 163 additions & 29 deletions src/main/java/com/znsio/teswiz/runner/DeviceSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@
import com.znsio.teswiz.tools.cmd.CommandLineExecutor;
import com.znsio.teswiz.tools.cmd.CommandLineResponse;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
Expand All @@ -31,6 +38,9 @@

class DeviceSetup {
private static final Logger LOGGER = Logger.getLogger(DeviceSetup.class.getName());
private static final String DEFAULT_TEMP_SAMPLE_APP_DIRECTORY =
System.getProperty("user.dir") + File.separator +
"temp" + File.separator + "sampleApps";

private DeviceSetup() {
LOGGER.debug("DeviceSetup - private constructor");
Expand Down Expand Up @@ -65,10 +75,10 @@ static String getPathForFileInLogDir(String fullFilePath) {

static ArrayList<String> setupAndroidExecution() {
ArrayList<String> androidCukeArgs = new ArrayList<>();
if(Setup.getPlatform().equals(Platform.android)) {
if (Setup.getPlatform().equals(Platform.android)) {
verifyAppExistsAtMentionedPath();
fetchAndroidAppVersion();
if(Setup.getBooleanValueFromConfigs(RUN_IN_CI)) {
if (Setup.getBooleanValueFromConfigs(RUN_IN_CI)) {
setupCloudExecution();
} else {
LocalDevicesSetup.setupLocalExecution();
Expand All @@ -86,22 +96,121 @@ static ArrayList<String> setupAndroidExecution() {
static void verifyAppExistsAtMentionedPath() {
String appPath = Setup.getFromConfigs(APP_PATH);
LOGGER.info(String.format("Update path to Apk: %s", appPath));
if(appPath.equals(NOT_SET)) {
if (appPath.equals(NOT_SET)) {
appPath = getAppPathFromCapabilities();
appPath = downloadAppToDirectoryIfNeeded(appPath, DEFAULT_TEMP_SAMPLE_APP_DIRECTORY);
Setup.addToConfigs(APP_PATH, appPath);
String capabilitiesFileName = Setup.getFromConfigs(CAPS);
checkIfAppExistsAtTheMentionedPath(appPath, capabilitiesFileName);
} else {
appPath = downloadAppToDirectoryIfNeeded(appPath, DEFAULT_TEMP_SAMPLE_APP_DIRECTORY);
LOGGER.info(String.format("\tUsing AppPath provided as environment variable - %s",
appPath));
appPath));
}
}

public static String downloadAppToDirectoryIfNeeded(String appPath, String saveToLocalDirectory) {
String fileName = appPath.split(File.separator)[appPath.split(File.separator).length - 1];
String localFilePath = saveToLocalDirectory + File.separator + fileName;
if (isAppPathAUrl(appPath)) {
LOGGER.info(String.format("App url '%s' is provided in capabilities. Download it, if " +
"not already available at '%s'", appPath, localFilePath));
downloadFileIfDoesNotExist(appPath, localFilePath, saveToLocalDirectory);
LOGGER.info("Changing value of appPath from URL to file path");
LOGGER.info(String.format("Before change, appPath value: %s", appPath));
appPath = localFilePath;
LOGGER.info(String.format("After change, appPath value: %s", localFilePath));
} else {
LOGGER.info(String.format("App file path '%s' is provided in capabilities.", appPath));
if (!(new File(appPath).exists())) {
throw new InvalidTestDataException(String.format("App file path '%s' provided in capabilities is incorrect", appPath));
// checkEitherFilePathIsIncorrectOrFileIsMissing(appPath, localFilePath);
}
}
LOGGER.info(String.format("App file path '%s' is provided in capabilities.", appPath));
LOGGER.info(String.format("File available at App file path '%s'", appPath));
return appPath;
}

private static void createDirectory(String directoryPath) {
try {
LOGGER.info("Directory doesn't exist, Creating directory");
Files.createDirectories(Path.of(directoryPath));
} catch (IOException e) {
throw new RuntimeException(String.format("Failed to create directory: %s, error occurred%s", directoryPath, e));
}
}

private static void downloadFile(String url, String filePath, String saveToDirectory) {
LOGGER.info(String.format("Downloading App from url: '%s'", url));
try {
URL fileUrl = new URL(url);
HttpURLConnection connection = getHttpURLConnection(fileUrl);
downloadFileFromHttpURL(filePath, saveToDirectory, connection);
String formattedSize = getDownloadedAppSize(Path.of(filePath));
LOGGER.info(String.format("App downloaded at path: '%s', having size: '%s MB'", filePath, formattedSize));
} catch (IOException e) {
throw new InvalidTestDataException("An error occurred while opening the URL/downloading file: " + e.getMessage());
}
}

private static String getDownloadedAppSize(Path filePath) {
long fileSizeBytes = 0;
try {
fileSizeBytes = Files.size(filePath);
} catch (IOException e) {
throw new InvalidTestDataException("Unable to get downloaded app file size. Download " +
"may be corrupt. Check and fix before " +
"rerunning the test.", e);
}
double fileSizeMB = (double) fileSizeBytes / (1024 * 1024);
return new DecimalFormat("#.##").format(fileSizeMB);
}

private static void downloadFileFromHttpURL(String filePath, String saveToDirectory, HttpURLConnection connection) {
try (InputStream inputStream = connection.getInputStream()) {
createDirectoryIfNotExists(saveToDirectory);
Files.copy(inputStream, Path.of(filePath), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new InvalidTestDataException(String.format("Unable to download file '%s'", connection.getURL().toString()), e);
}
}

@NotNull
private static HttpURLConnection getHttpURLConnection(URL fileUrl) {
try {
HttpURLConnection connection = (HttpURLConnection) fileUrl.openConnection();
connection.setRequestMethod("GET");

int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new InvalidTestDataException(String.format("Unable to connect to url: '%s'. Got connection error '%d'", fileUrl, responseCode));
}
return connection;
} catch (IOException e) {
throw new InvalidTestDataException(String.format("Unable to connect to url: '%s'.", fileUrl));
}
}

private static void createDirectoryIfNotExists(String directory) throws IOException {
Path directoryPath = Path.of(directory);
if (!Files.exists(directoryPath)) {
Files.createDirectories(directoryPath);
}
}

private static void downloadFileIfDoesNotExist(String appPath, String filePath, String saveToDirectory){
if (!(new File(filePath).exists())) {
LOGGER.info(String.format("App is not available at path: '%s'. Download it.", appPath));
downloadFile(appPath, filePath, saveToDirectory);
} else {
LOGGER.info(String.format("App is already available at path: '%s'. No need to download it.", appPath));
}
}

private static void fetchAndroidAppVersion() {
Pattern versionNamePattern = Pattern.compile("versionName='(\\d+(\\.\\d+)+)'",
Pattern.MULTILINE);
Pattern.MULTILINE);
String searchPattern = "grep";
if(Runner.IS_WINDOWS) {
if (Runner.IS_WINDOWS) {
searchPattern = "findstr";
}

Expand All @@ -115,11 +224,11 @@ private static void fetchAndroidAppVersion() {
File aaptExecutable = new File(buildVersionFolder, "aapt").getAbsoluteFile();

String[] commandToGetAppVersion = new String[]{aaptExecutable.toString(), "dump",
"badging", appFilePath, "|",
searchPattern, "versionName"};
"badging", appFilePath, "|",
searchPattern, "versionName"};
fetchAppVersion(commandToGetAppVersion, versionNamePattern);
}
} catch(Exception e) {
} catch (Exception e) {
LOGGER.info(
String.format("fetchAndroidAppVersion: Exception: %s", e.getLocalizedMessage()));
}
Expand All @@ -128,7 +237,7 @@ private static void fetchAndroidAppVersion() {
static void setupCloudExecution() {
String cloudName = getCloudNameFromCapabilities();
String deviceLabURL = NOT_SET;
switch(cloudName.toLowerCase()) {
switch (cloudName.toLowerCase()) {
case "headspin":
deviceLabURL = getCloudUrlFromCapabilities();
HeadSpinSetup.updateHeadspinCapabilities(deviceLabURL);
Expand All @@ -152,16 +261,15 @@ static void setupCloudExecution() {

private static String getAppPathFromCapabilities() {
String capabilityFile = Setup.getFromConfigs(CAPS);
return JsonFile.getNodeValueAsStringFromJsonFile(capabilityFile,
new String[]{Setup.getPlatform().name(), "app"});
return JsonFile.getNodeValueAsStringFromJsonFile(capabilityFile, new String[]{Setup.getPlatform().name(), "app"});
}

private static void checkIfAppExistsAtTheMentionedPath(String appPath,
String capabilitiesFileName) {
if(!isAppPathAUrl(appPath)) {
if(Files.exists(Paths.get(appPath))) {
if (!isAppPathAUrl(appPath)) {
if (Files.exists(Paths.get(appPath))) {
LOGGER.info(String.format("\tUsing AppPath: %s in file: %s:: %s", appPath,
capabilitiesFileName, Setup.getPlatform()));
capabilitiesFileName, Setup.getPlatform()));
} else {
LOGGER.info(String.format("\tAppPath: %s not found!", appPath));
throw new InvalidTestDataException(
Expand All @@ -170,19 +278,45 @@ private static void checkIfAppExistsAtTheMentionedPath(String appPath,
}
}

private static boolean isAppPathAUrl(String appPath) {
boolean isUrl = appPath.toLowerCase().startsWith("http");
LOGGER.info(String.format("\tAppPath refers to a url: %s", appPath));
return isUrl;
private static boolean isAppPathAUrl(String appPathUrl) {
URL url;
try {
url = new URL(appPathUrl);
LOGGER.info(String.format("'%s' is a URL.", appPathUrl));
isAppUrlValid(appPathUrl);
return true;
} catch (MalformedURLException e) {
LOGGER.info(String.format("'%s' is not a URL.", appPathUrl));
return false;
}
}

private static void isAppUrlValid(String appPathUrl) {
int responseCode;
HttpURLConnection connection;
try {
connection = (HttpURLConnection) new URL(appPathUrl).openConnection();
connection.setRequestMethod("HEAD");
responseCode = connection.getResponseCode();
connection.disconnect();
} catch (IOException e) {
throw new InvalidTestDataException(String.format("Failed to make a connection using url: '%s'", appPathUrl) + e);
}

if (responseCode != HttpURLConnection.HTTP_OK) {
LOGGER.info(String.format("'%s' is an invalid URL.", appPathUrl));
throw new InvalidTestDataException("URL is not accessible: " + appPathUrl);
}
LOGGER.info(String.format("'%s' is a valid URL.", appPathUrl));
}

private static void fetchAppVersion(String[] commandToGetAppVersion, Pattern pattern) {
CommandLineResponse commandResponse = CommandLineExecutor.execCommand(
commandToGetAppVersion);
String commandOutput = commandResponse.getStdOut();
if(!(null == commandOutput || commandOutput.isEmpty())) {
if (!(null == commandOutput || commandOutput.isEmpty())) {
Matcher matcher = pattern.matcher(commandOutput);
if(matcher.find()) {
if (matcher.find()) {
Setup.addToConfigs(APP_VERSION, matcher.group(1));
LOGGER.info(String.format("APP_VERSION: %s", matcher.group(1)));
}
Expand Down Expand Up @@ -214,7 +348,7 @@ private static String getCloudApiUrlFromCapabilities() {

static ArrayList<String> setupWindowsExecution() {
ArrayList<String> windowsCukeArgs = new ArrayList<>();
if(Setup.getPlatform().equals(Platform.windows)) {
if (Setup.getPlatform().equals(Platform.windows)) {
verifyAppExistsAtMentionedPath();
fetchWindowsAppVersion();
windowsCukeArgs.add(PLUGIN);
Expand All @@ -231,20 +365,20 @@ private static void fetchWindowsAppVersion() {
try {
File appFile = new File(Setup.getFromConfigs(APP_PATH));
String nameVariable = "name=\"" + appFile.getCanonicalPath()
.replace("\\", "\\\\") + "\"";
.replace("\\", "\\\\") + "\"";
String[] commandToGetAppVersion = new String[]{"wmic", "datafile", "where",
nameVariable, "get", "Version",
"/value"};
nameVariable, "get", "Version",
"/value"};
fetchAppVersion(commandToGetAppVersion, versionNamePattern);
} catch(IOException e) {
} catch (IOException e) {
LOGGER.info(
String.format("fetchWindowsAppVersion: Exception: %s", e.getLocalizedMessage()));
}
}

static void cleanupCloudExecution() {
String cloudName = getCloudNameFromCapabilities();
switch(cloudName.toLowerCase()) {
switch (cloudName.toLowerCase()) {
case "browserstack":
BrowserStackSetup.cleanUp();
break;
Expand Down
Loading

0 comments on commit b9a5cef

Please sign in to comment.