Skip to content

Commit

Permalink
Support to test Interactive CLI (#878)
Browse files Browse the repository at this point in the history
* added go-ios in package.json

* updated ios capabilitiles file

* added AsyncCommandLineExecutor and a calculator test for it

* made AspectJ auto logging better

* updated AsyncCommandLineExecutor to get all stdout in one shot. updated test

* Added a new option in calculator.sh to an log error message to console. Updated tests accordingly

* Throw CommandLineExecutorException where required from AsyncCommandLineExecutor class

* fixed test
  • Loading branch information
anandbagmar authored Nov 15, 2024
1 parent 1608e37 commit 46fab45
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 5 deletions.
4 changes: 2 additions & 2 deletions caps/helloworld/helloworld_local_ios_capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
23 changes: 23 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
73 changes: 73 additions & 0 deletions src/test/java/com/znsio/teswiz/tools/cmd/calculator.sh
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 46fab45

Please sign in to comment.