Skip to content

Commit

Permalink
feature: selenium 4
Browse files Browse the repository at this point in the history
  • Loading branch information
akamarouski committed Feb 25, 2024
1 parent 69d539b commit beb0dd2
Show file tree
Hide file tree
Showing 29 changed files with 1,864 additions and 50 deletions.
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.project
target
bin
.classpath
/test-output
/.settings
**/.settings
/com
/application.log
/sql.log
*.checkstyle
.idea
*.iml
test-output
*.log
/reports
/out
dependency-reduced-pom.xml
.DS_Store
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ ENV DEVICE_BUS=/dev/bus/usb/003/011
# Usbmuxd settings "host:port"
ENV USBMUXD_SOCKET_ADDRESS=

ENV GRID_BROWSER_TIMEOUT 180

#Setup libimobile device, usbmuxd and some tools
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get -y install iputils-ping nano jq telnet netcat curl ffmpeg libimobiledevice-utils libimobiledevice6 usbmuxd socat

Expand All @@ -82,6 +84,16 @@ COPY files/check-wda.sh /opt
COPY files/zbr-config-gen.sh /opt
COPY files/zbr-default-caps-gen.sh /opt

COPY target/mcloud-node-1.0.jar \
/opt
COPY target/mcloud-node.jar \
/opt

COPY agent/target/mcloud-node-agent-1.0.jar \
/opt
COPY agent/target/mcloud-node-agent.jar \
/opt

ENV ENTRYPOINT_DIR=/opt/entrypoint
RUN mkdir -p ${ENTRYPOINT_DIR}
COPY entrypoint.sh ${ENTRYPOINT_DIR}
Expand Down
82 changes: 82 additions & 0 deletions agent/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zebrunner</groupId>
<artifactId>mcloud-node-agent</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>Zebrunner Device Farm (Selenium Grid Node Agent)</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven-shade-plugin.version>3.5.0</maven-shade-plugin.version>
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
</properties>
<dependencies>
<dependency>
<!--we need it to reuse benefits of zebrunner testng agent for webdriver
sesssion(s) declaration -->
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.14.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven-shade-plugin.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<transformers>
<!-- Don't do this: Avoid adding anything that makes shade create or modify a manifest file.
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.mypackage.MyMainClass</mainClass>
</transformer>
-->
<!-- Add a transformer to exclude any other manifest files (possibly from dependencies). -->
<transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
<resource>MANIFEST.MF</resource>
</transformer>

<!-- Add a transformer to include your custom manifest file. -->
<transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer">
<resource>META-INF/MANIFEST.MF</resource>
<file>src/main/resources/META-INF/MANIFEST.MF</file>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<finalName>mcloud-node-agent</finalName>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
53 changes: 53 additions & 0 deletions agent/src/main/java/com/zebrunner/mcloud/grid/agent/NodeAgent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.zebrunner.mcloud.grid.agent;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.NameMatcher;
import net.bytebuddy.pool.TypePool;

import java.lang.instrument.Instrumentation;
import java.util.logging.Logger;

import static net.bytebuddy.implementation.MethodDelegation.to;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;

public class NodeAgent {
private static final Logger LOGGER = Logger.getLogger(NodeAgent.class.getName());
private static final String RELAY_SESSION_FACTORY_CLASS = "org.openqa.selenium.grid.node.relay.RelaySessionFactory";
private static final String TEST_METHOD_NAME = "test";

public static void premain(String args, Instrumentation instrumentation) {
try {
new AgentBuilder.Default()
.with(new AgentBuilder.InitializationStrategy.SelfInjection.Eager())
.type(named(RELAY_SESSION_FACTORY_CLASS))
.transform((builder, type, classloader, module, protectionDomain) -> addTestMethodInterceptor(builder))
.installOn(instrumentation);
} catch (Exception e) {
LOGGER.warning(() -> "Could not init instrumentation.");
}
}

private static DynamicType.Builder<?> addTestMethodInterceptor(DynamicType.Builder<?> builder) {
return builder.method(isTestMethod())
.intercept(to(testMethodInterceptor()));
}

public static ElementMatcher<? super MethodDescription> isTestMethod() {
return isPublic()
.and(not(isStatic()))
.and(new NameMatcher<>(TEST_METHOD_NAME::equals));
}

private static TypeDescription testMethodInterceptor() {
return TypePool.Default.ofSystemLoader()
.describe(RelaySessionFactoryInterceptor.class.getName())
.resolve();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.zebrunner.mcloud.grid.agent;

import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.implementation.bind.annotation.This;

import java.util.concurrent.Callable;

public class RelaySessionFactoryInterceptor {

@RuntimeType
public static Object onTestMethodInvocation(@This final Object factory,
@SuperCall final Callable<Object> proxy) throws Exception {
return true;
}
}
4 changes: 4 additions & 0 deletions agent/src/main/resources/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Manifest-Version: 1.0
Premain-Class: com.zebrunner.mcloud.grid.agent.NodeAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
10 changes: 6 additions & 4 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

NODE_CONFIG_JSON="/root/nodeconfig.json"
NODE_CONFIG_JSON="/root/nodeconfig.toml"
DEFAULT_CAPABILITIES_JSON="/root/defaultcapabilities.json"

# show list of plugins including installed ones
Expand All @@ -12,7 +12,7 @@ if [[ -n $APPIUM_PLUGINS ]]; then
echo "plugins_cli: $plugins_cli"
fi

CMD="xvfb-run appium --log-no-colors --log-timestamp -pa /wd/hub --port $APPIUM_PORT --log $TASK_LOG --log-level $LOG_LEVEL $APPIUM_CLI $plugins_cli"
CMD="xvfb-run appium --log-no-colors --log-timestamp -pa /wd/hub --port ${APPIUM_PORT} --log $TASK_LOG --log-level $LOG_LEVEL $APPIUM_CLI $plugins_cli"
#--use-plugins=relaxed-caps

share() {
Expand Down Expand Up @@ -341,7 +341,7 @@ if [ "$CONNECT_TO_GRID" = true ]; then
else
/root/generate_config.sh $NODE_CONFIG_JSON
fi
CMD+=" --nodeconfig $NODE_CONFIG_JSON"
# CMD+=" --nodeconfig $NODE_CONFIG_JSON"
fi

if [ "$DEFAULT_CAPABILITIES" = true ]; then
Expand All @@ -365,8 +365,10 @@ rm -rf /tmp/.X99-lock

touch ${TASK_LOG}
echo $CMD
$CMD &

$CMD &
java ${JAVA_OPTS} -cp /opt/mcloud-node-1.0.jar:/opt/mcloud-node.jar -javaagent:/opt/mcloud-node-agent.jar org.openqa.selenium.grid.Bootstrap node \
--config $NODE_CONFIG_JSON &
trap 'finish' SIGTERM

# start in background video artifacts capturing
Expand Down
5 changes: 0 additions & 5 deletions files/android.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,7 @@ else
export DEVICETYPE='Phone'
fi

if [[ ${PLATFORM_VERSION} == 4* ]] || [[ ${PLATFORM_VERSION} == 5* ]] || [[ ${PLATFORM_VERSION} == 6* ]]
then
export AUTOMATION_NAME='Appium'
else
export AUTOMATION_NAME='uiautomator2'
fi

# there is no sense to clean something for scalable one-time redroid container
if [ "$ANDROID_DEVICE" != "device:5555" ]; then
Expand Down
87 changes: 46 additions & 41 deletions files/zbr-config-gen.sh
Original file line number Diff line number Diff line change
@@ -1,47 +1,52 @@
#!/bin/bash

#IMPORTANT!!! Don't do any echo otherwise you corrupt generated nodeconfig.json
#IMPORTANT!!! Don't do any echo otherwise you corrupt generated nodeconfig.toml
# convert to lower case using Linux/Mac compatible syntax (bash v3.2)
PLATFORM_NAME=`echo "$PLATFORM_NAME" | tr '[:upper:]' '[:lower:]'`
cat << EndOfMessage
{
"capabilities":
[
{
"maxInstances": 1,
"deviceName": "${DEVICE_NAME}",
"deviceType": "${DEVICETYPE}",
"platformName":"${PLATFORM_NAME}",
"platformVersion":"${PLATFORM_VERSION}",
"udid": "${DEVICE_UDID}",
"adb_port": ${ADB_PORT},
"proxy_port": ${PROXY_PORT},
"automationName": "${AUTOMATION_NAME}"
}
],
"configuration":
{
"proxy": "com.zebrunner.mcloud.grid.MobileRemoteProxy",
"url":"http://${STF_PROVIDER_HOST}:${APPIUM_PORT}/wd/hub",
"host": "${STF_PROVIDER_HOST}",
"port": ${APPIUM_PORT},
"hubHost": "${SELENIUM_HOST}",
"hubPort": ${SELENIUM_PORT},
"maxSession": 1,
"register": true,
"registerCycle": 300000,
"cleanUpCycle": 5000,
"timeout": 180,
"browserTimeout": 0,
"nodeStatusCheckTimeout": 5000,
"nodePolling": 5000,
"role": "node",
"unregisterIfStillDownAfter": ${UNREGISTER_IF_STILL_DOWN_AFTER},
"downPollingLimit": 2,
"debug": false,
"servlets" : [],
"withoutServlets": [],
"custom": {}
}
}
[node]
# Autodetect which drivers are available on the current system, and add them to the Node.
detect-drivers = false
# Maximum number of concurrent sessions. Default value is the number of available processors.
max-sessions = 1
# Full classname of non-default Node implementation. This is used to manage a session’s lifecycle.
implementation = "com.zebrunner.mcloud.grid.MobileRemoteProxy"
# The address of the Hub in a Hub-and-Node configuration.
hub = "http://${SELENIUM_HOST}:${SELENIUM_PORT}"
# How often, in seconds, the Node will try to register itself for the first time to the Distributor.
register-cycle = 300
# How long, in seconds, will the Node try to register to the Distributor for the first time.
# After this period is completed, the Node will not attempt to register again.
register-period = 1000
# How often, in seconds, will the Node send heartbeat events to the Distributor to inform it that the Node is up.
heartbeat-period = 5
# Let X be the session-timeout in seconds.
# The Node will automatically kill a session that has not had any activity in the last X seconds.
# This will release the slot for other tests.
session-timeout = $GRID_BROWSER_TIMEOUT
[relay]
# URL for connecting to the service that supports WebDriver commands like an Appium server or a cloud service.
url = "http://localhost:${APPIUM_PORT}/wd/hub"
# Optional, endpoint to query the WebDriver service status, an HTTP 200 response is expected
status-endpoint = "/status"
# Stereotypes supported by the service. The initial number is "max-sessions", and will allocate
# that many test slots to that particular configuration
configs = [
"1", "{\"platformName\": \"${PLATFORM_NAME}\", \"appium:platformVersion\": \"${PLATFORM_VERSION}\", \"appium:deviceName\": \"${DEVICE_NAME}\", \"appium:automationName\": \"${AUTOMATION_NAME}\", \"zebrunner:deviceType\": \"${DEVICETYPE}\", \"appium:udid\": \"${DEVICE_UDID}\", \"zebrunner:adb_port\": \"${ADB_PORT}\", \"zebrunner:proxy_port\": \"${PROXY_PORT}\" }"
]
[logging]
# Log level. Default logging level is INFO. Log levels are described here
# https://docs.oracle.com/javase/7/docs/api/java/util/logging/Level.html
log-level = "INFO"
EndOfMessage
Loading

0 comments on commit beb0dd2

Please sign in to comment.