diff --git a/caps/helloworld/helloworld_local_ios_capabilities.json b/caps/helloworld/helloworld_local_ios_capabilities.json index ff4dd64e9..aa9416334 100644 --- a/caps/helloworld/helloworld_local_ios_capabilities.json +++ b/caps/helloworld/helloworld_local_ios_capabilities.json @@ -4,11 +4,11 @@ "appium:iPhoneOnly": true, "automationName": "XCUITest", "clearSystemFiles": true, - "deviceName": "iPhone 15 Pro", + "deviceName": "iPhone 16 Pro", "maxTypingFrequency": 10, "noReset": true, "platformName": "iOS", - "platformVersion": "17.4", + "platformVersion": "18.1", "preventWDAAttachments": true, "printPageSourceOnFindFailure": true, "shouldUseSingletonTestManager": false, diff --git a/package-lock.json b/package-lock.json index c6a72b929..1e23f2b4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "axios": "1.7.7", "braces": "3.0.3", "fill-range": "7.1.1", + "go-ios": "1.0.155", "ip": "2.0.1", "micromatch": "4.0.8", "rimraf": "6.0.1", @@ -18387,6 +18388,28 @@ "node": ">=4" } }, + "node_modules/go-ios": { + "version": "1.0.155", + "resolved": "https://registry.npmjs.org/go-ios/-/go-ios-1.0.155.tgz", + "integrity": "sha512-pA1rr92V+d9Zxa8wSDj43rFODtWl1sON+M7pVq/xPqSs0dqNK/ksvYlZJJ5XeNVYaLLsLvhDlDivImMvjzpm3Q==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4" + } + }, + "node_modules/go-ios/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", diff --git a/package.json b/package.json index ee9151998..622ad1624 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "appium-device-farm": "9.4.5", "appium-uiautomator2-driver": "3.8.2", "appium-xcuitest-driver": "7.29.1", + "go-ios": "1.0.155", "braces": "3.0.3", "fill-range": "7.1.1", "ws": "8.18.0", diff --git a/src/main/java/com/znsio/teswiz/aspect/AspectJMethodLoggers.java b/src/main/java/com/znsio/teswiz/aspect/AspectJMethodLoggers.java index 7de36875f..bf2e7cf82 100644 --- a/src/main/java/com/znsio/teswiz/aspect/AspectJMethodLoggers.java +++ b/src/main/java/com/znsio/teswiz/aspect/AspectJMethodLoggers.java @@ -23,7 +23,7 @@ public static void beforeAnyMethod(JoinPoint joinPoint, Level level) { Integer lineNumber = (joinPoint.getSourceLocation() != null) ? joinPoint.getSourceLocation().getLine() : -1; Object[] methodArgs = joinPoint.getArgs(); - String message = String.format("\t<<<%s>>>", + String message = String.format("\t%n<<<%s%n>>>%n", generateBeforeMethodAspectJLogger(className, methodName, lineNumber, methodArgs)); logAtLevel(level, message); @@ -53,7 +53,7 @@ public static void afterAnyMethod(JoinPoint joinPoint, Level level) { String className = joinPoint.getSignature().getDeclaringType().getSimpleName(); String methodName = joinPoint.getSignature().getName(); - String message = String.format("\t<<<%s>>>", generateAfterMethodAspectJLogger(className, methodName)); + String message = String.format("\t%n<<<%s%n>>>%n", generateAfterMethodAspectJLogger(className, methodName)); logAtLevel(level, message); } catch (Exception e) { diff --git a/src/main/java/com/znsio/teswiz/tools/cmd/AsyncCommandLineExecutor.java b/src/main/java/com/znsio/teswiz/tools/cmd/AsyncCommandLineExecutor.java new file mode 100644 index 000000000..0a021c464 --- /dev/null +++ b/src/main/java/com/znsio/teswiz/tools/cmd/AsyncCommandLineExecutor.java @@ -0,0 +1,96 @@ +package com.znsio.teswiz.tools.cmd; + +import com.znsio.teswiz.exceptions.CommandLineExecutorException; +import com.znsio.teswiz.runner.Runner; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.util.concurrent.TimeUnit; + +public class AsyncCommandLineExecutor { + private final Process process; + private final PrintWriter commandWriter; + private final BufferedReader outputReader; + private static final Logger LOGGER = LogManager.getLogger(AsyncCommandLineExecutor.class.getName()); + + public static class CommandResult { + private final String output; + + public CommandResult(String output) { + this.output = output; + } + + public String getOutput() { + return output; + } + } + + public AsyncCommandLineExecutor() { + String os = Runner.OS_NAME.toLowerCase(); + + ProcessBuilder processBuilder = os.contains("win") + ? new ProcessBuilder("cmd.exe") + : new ProcessBuilder("bash"); + + try { + process = processBuilder.start(); + } catch (IOException e) { + throw new CommandLineExecutorException("Unable to start the AsyncCommandLineExecutor", e); + } + + // Initialize writers and readers + commandWriter = new PrintWriter(process.getOutputStream(), true); + outputReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + } + + public CommandResult sendCommand(String command, int timeoutInSeconds) { + final String platformCommand = Runner.IS_WINDOWS + ? "/c " + command + : command; + + LOGGER.info("Executing command: '%s', with timeout: '%d' seconds".formatted(platformCommand, timeoutInSeconds)); + + StringBuilder output = new StringBuilder(); + + // Send the command + commandWriter.println(platformCommand); + commandWriter.flush(); + + try { + // Wait for the specified timeout + TimeUnit.SECONDS.sleep(timeoutInSeconds); + + // Read any accumulated output after the timeout + while (outputReader.ready()) { // only read if data is available + String line = outputReader.readLine(); + if (line != null) { + output.append(line).append(System.lineSeparator()); + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.debug("Command '%s' execution was interrupted.".formatted(command)); + } catch (IOException e) { + LOGGER.error("Error reading stdout: %s".formatted(e.getMessage()), e.getCause()); + throw new CommandLineExecutorException("Error reading stdout", e); + } + + return new CommandResult(output.toString().trim()); + } + + public void close() { + try { + if (process.isAlive()) { + process.destroy(); + } + commandWriter.close(); + outputReader.close(); + } catch (IOException e) { + LOGGER.debug("Error closing resources: " + e.getMessage()); + } + } +} diff --git a/src/test/java/com/znsio/teswiz/aspect/AspectJMethodLoggerTest.java b/src/test/java/com/znsio/teswiz/aspect/AspectJMethodLoggerTest.java index e0d08efba..79cc70348 100644 --- a/src/test/java/com/znsio/teswiz/aspect/AspectJMethodLoggerTest.java +++ b/src/test/java/com/znsio/teswiz/aspect/AspectJMethodLoggerTest.java @@ -8,7 +8,6 @@ import static org.junit.jupiter.api.Assertions.*; class AspectJMethodLoggerTest { - private static final String className = AspectJMethodLoggerTest.class.getSimpleName(); private static final String LOG_DIR = "./target/testLogs"; diff --git a/src/test/java/com/znsio/teswiz/tools/cmd/AsyncCommandLineExecutorTest.java b/src/test/java/com/znsio/teswiz/tools/cmd/AsyncCommandLineExecutorTest.java new file mode 100644 index 000000000..6c07509d7 --- /dev/null +++ b/src/test/java/com/znsio/teswiz/tools/cmd/AsyncCommandLineExecutorTest.java @@ -0,0 +1,132 @@ +package com.znsio.teswiz.tools.cmd; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static com.znsio.teswiz.runner.Runner.NOT_SET; +import static org.assertj.core.api.Assertions.assertThat; + +class AsyncCommandLineExecutorTest { + private static final Logger LOGGER = LogManager.getLogger(AsyncCommandLineExecutorTest.class.getName()); + private static int commmandNumber = 1; + private static final String LOG_DIR = "./target/testLogs"; + + @BeforeAll + public static void setupBefore() { + LOGGER.info("Create LOG_DIR: " + LOG_DIR); + System.setProperty("LOG_DIR", LOG_DIR); + new File(LOG_DIR).mkdirs(); + } + + + @Test + void asyncCLICalculatorTest() { + try { + AsyncCommandLineExecutor executor = new AsyncCommandLineExecutor(); + calculatorTest(executor); + executor.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static String sendCommand(AsyncCommandLineExecutor executor, String command, int timeoutInSeconds) { + LOGGER.info("Command # " + commmandNumber++); + AsyncCommandLineExecutor.CommandResult result = executor.sendCommand(command, timeoutInSeconds); + LOGGER.info("%n--> Stdout: %n%s".formatted(result.getOutput())); + return result.getOutput(); + } + + private static void calculatorTest(AsyncCommandLineExecutor executor) { + String response = NOT_SET; + + String command = "pwd"; + int timeoutInSeconds = 1; + String expectedResponse = "teswiz"; + + response = sendCommand(executor, command, timeoutInSeconds); + assertThat(response).as("Output of command: '" + command + "' is incorrect").contains(expectedResponse); + + command = "./src/test/java/com/znsio/teswiz/tools/cmd/calculator.sh"; + timeoutInSeconds = 2; + expectedResponse = """ + ---------------------------------------------------- + Welcome to the Simple Command Line Calculator + Please choose an option: + 1. Add + 2. Subtract + 3. Exit + 4. Log Errors + ----------------------------------------------------"""; + response = sendCommand(executor, command, timeoutInSeconds); + assertThat(response).as("Output of command: '" + command + "' is incorrect").isEqualTo(expectedResponse); + + command = "1"; + timeoutInSeconds = 2; + expectedResponse = """ + Addition: + + Enter first number:"""; + response = sendCommand(executor, command, timeoutInSeconds); + assertThat(response).as("Output of command: '" + command + "' is incorrect").isEqualTo(expectedResponse); + + command = "15"; + timeoutInSeconds = 2; + expectedResponse = "Enter second number:"; + response = sendCommand(executor, command, timeoutInSeconds); + assertThat(response).as("Output of command: '" + command + "' is incorrect").isEqualTo(expectedResponse); + + command = "24"; + timeoutInSeconds = 2; + expectedResponse = """ + [32mResult: 39 + ---------------------------------------------------- + Welcome to the Simple Command Line Calculator + Please choose an option: + 1. Add + 2. Subtract + 3. Exit + 4. Log Errors + ----------------------------------------------------"""; + response = sendCommand(executor, command, timeoutInSeconds); + assertThat(response).as("Output of command: '" + command + "' is incorrect").isEqualTo(expectedResponse); + + command = "4"; + expectedResponse = """ + Logging error to stderr in red. + ---------------------------------------------------- + Welcome to the Simple Command Line Calculator + Please choose an option: + 1. Add + 2. Subtract + 3. Exit + 4. Log Errors + ----------------------------------------------------"""; + response = sendCommand(executor, command, timeoutInSeconds); + assertThat(response).as("Output of command: '" + command + "' is incorrect").isEqualTo(expectedResponse); + + command = "5"; + expectedResponse = """ + Invalid option. Please try again. + ---------------------------------------------------- + Welcome to the Simple Command Line Calculator + Please choose an option: + 1. Add + 2. Subtract + 3. Exit + 4. Log Errors + ----------------------------------------------------"""; + response = sendCommand(executor, command, timeoutInSeconds); + assertThat(response).as("Output of command: '" + command + "' is incorrect").isEqualTo(expectedResponse); + + command = "3"; + expectedResponse = """ + Exiting the calculator. Goodbye!"""; + response = sendCommand(executor, command, timeoutInSeconds); + assertThat(response).as("Output of command: '" + command + "' is incorrect").isEqualTo(expectedResponse); + } +} diff --git a/src/test/java/com/znsio/teswiz/tools/cmd/calculator.sh b/src/test/java/com/znsio/teswiz/tools/cmd/calculator.sh new file mode 100755 index 000000000..a2074dbeb --- /dev/null +++ b/src/test/java/com/znsio/teswiz/tools/cmd/calculator.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# Function to display the menu +function show_menu { + echo "----------------------------------------------------" + echo "Welcome to the Simple Command Line Calculator" + echo "Please choose an option:" + echo "1. Add" + echo "2. Subtract" + echo "3. Exit" + echo "4. Log Errors" + echo "----------------------------------------------------" +} + +# Function to log errors in red to stderr +function log_error { + echo -e "\033[31mError occurred: $1\033[0m" >&2 +} + +# Function to log results in green to stdout +function log_result { + echo -e "\033[32mResult: $1\033[0m" +} + +# Set log_errors to true permanently +log_errors=true + +# Function to add two numbers +function add { + echo -e "\nAddition:" + echo -e "\nEnter first number:" + read num1 + echo -e "\nEnter second number:" + read num2 + sum=$(echo "$num1 + $num2" | bc) + log_result "$sum" +} + +# Function to subtract two numbers +function subtract { + echo -e "\nSubtraction:" + echo -e "\nEnter first number:" + read num1 + echo -e "\nEnter second number:" + read num2 + diff=$(echo "$num1 - $num2" | bc) + log_result "$diff" +} + +# Main loop to handle the user input +while true; do + show_menu + read choice + case $choice in + 1) + add + ;; + 2) + subtract + ;; + 3) + echo "Exiting the calculator. Goodbye!" + exit 0 + ;; + 4) + echo "Logging error to stderr in red." + log_error "Sample error: This is a test error message." + ;; + *) + echo "Invalid option. Please try again." + ;; + esac +done