Skip to content

Commit d89e15c

Browse files
author
Andrei Kamarouski
committed
feature: selenium 4
1 parent 69d539b commit d89e15c

39 files changed

+2538
-45
lines changed

.gitignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
.project
2+
target
3+
bin
4+
.classpath
5+
/test-output
6+
/.settings
7+
**/.settings
8+
/com
9+
/application.log
10+
/sql.log
11+
*.checkstyle
12+
.idea
13+
*.iml
14+
test-output
15+
*.log
16+
/reports
17+
/out
18+
dependency-reduced-pom.xml
19+
.DS_Store

Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ ENV DEVICE_BUS=/dev/bus/usb/003/011
6464
# Usbmuxd settings "host:port"
6565
ENV USBMUXD_SOCKET_ADDRESS=
6666

67+
ENV GRID_BROWSER_TIMEOUT 180
68+
6769
#Setup libimobile device, usbmuxd and some tools
6870
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
6971

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

87+
COPY target/mcloud-node-1.0.jar \
88+
/opt
89+
COPY target/mcloud-node.jar \
90+
/opt
91+
92+
COPY agent/target/mcloud-node-agent-1.0.jar \
93+
/opt
94+
COPY agent/target/mcloud-node-agent.jar \
95+
/opt
96+
8597
ENV ENTRYPOINT_DIR=/opt/entrypoint
8698
RUN mkdir -p ${ENTRYPOINT_DIR}
8799
COPY entrypoint.sh ${ENTRYPOINT_DIR}

agent/pom.xml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="http://maven.apache.org/POM/4.0.0"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<groupId>com.zebrunner</groupId>
7+
<artifactId>mcloud-node-agent</artifactId>
8+
<version>1.0</version>
9+
<packaging>jar</packaging>
10+
<name>Zebrunner Device Farm (Selenium Grid Node Agent)</name>
11+
<properties>
12+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13+
<maven-shade-plugin.version>3.5.0</maven-shade-plugin.version>
14+
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
15+
</properties>
16+
<dependencies>
17+
<dependency>
18+
<!--we need it to reuse benefits of zebrunner testng agent for webdriver
19+
sesssion(s) declaration -->
20+
<groupId>net.bytebuddy</groupId>
21+
<artifactId>byte-buddy</artifactId>
22+
<version>1.14.5</version>
23+
</dependency>
24+
</dependencies>
25+
<build>
26+
<plugins>
27+
<plugin>
28+
<groupId>org.apache.maven.plugins</groupId>
29+
<artifactId>maven-shade-plugin</artifactId>
30+
<version>${maven-shade-plugin.version}</version>
31+
<executions>
32+
<execution>
33+
<phase>package</phase>
34+
<goals>
35+
<goal>shade</goal>
36+
</goals>
37+
<configuration>
38+
<minimizeJar>false</minimizeJar>
39+
<transformers>
40+
<!-- Don't do this: Avoid adding anything that makes shade create or modify a manifest file.
41+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
42+
<mainClass>com.mypackage.MyMainClass</mainClass>
43+
</transformer>
44+
-->
45+
<!-- Add a transformer to exclude any other manifest files (possibly from dependencies). -->
46+
<transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
47+
<resource>MANIFEST.MF</resource>
48+
</transformer>
49+
50+
<!-- Add a transformer to include your custom manifest file. -->
51+
<transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer">
52+
<resource>META-INF/MANIFEST.MF</resource>
53+
<file>src/main/resources/META-INF/MANIFEST.MF</file>
54+
</transformer>
55+
</transformers>
56+
<filters>
57+
<filter>
58+
<artifact>*:*</artifact>
59+
<excludes>
60+
<exclude>META-INF/*.SF</exclude>
61+
<exclude>META-INF/*.DSA</exclude>
62+
<exclude>META-INF/*.RSA</exclude>
63+
</excludes>
64+
</filter>
65+
</filters>
66+
<finalName>mcloud-node-agent</finalName>
67+
</configuration>
68+
</execution>
69+
</executions>
70+
</plugin>
71+
<plugin>
72+
<groupId>org.apache.maven.plugins</groupId>
73+
<artifactId>maven-compiler-plugin</artifactId>
74+
<version>${maven-compiler-plugin.version}</version>
75+
<configuration>
76+
<source>11</source>
77+
<target>11</target>
78+
</configuration>
79+
</plugin>
80+
</plugins>
81+
</build>
82+
</project>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.zebrunner.mcloud.grid.agent;
2+
3+
import net.bytebuddy.agent.builder.AgentBuilder;
4+
import net.bytebuddy.description.method.MethodDescription;
5+
import net.bytebuddy.description.type.TypeDescription;
6+
import net.bytebuddy.dynamic.DynamicType;
7+
import net.bytebuddy.matcher.ElementMatcher;
8+
import net.bytebuddy.matcher.NameMatcher;
9+
import net.bytebuddy.pool.TypePool;
10+
11+
import java.lang.instrument.Instrumentation;
12+
import java.util.logging.Logger;
13+
14+
import static net.bytebuddy.implementation.MethodDelegation.to;
15+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
16+
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
17+
import static net.bytebuddy.matcher.ElementMatchers.named;
18+
import static net.bytebuddy.matcher.ElementMatchers.not;
19+
20+
public class NodeAgent {
21+
private static final Logger LOGGER = Logger.getLogger(NodeAgent.class.getName());
22+
private static final String RELAY_SESSION_FACTORY_CLASS = "org.openqa.selenium.grid.node.relay.RelaySessionFactory";
23+
private static final String TEST_METHOD_NAME = "test";
24+
25+
public static void premain(String args, Instrumentation instrumentation) {
26+
try {
27+
new AgentBuilder.Default()
28+
.with(new AgentBuilder.InitializationStrategy.SelfInjection.Eager())
29+
.type(named(RELAY_SESSION_FACTORY_CLASS))
30+
.transform((builder, type, classloader, module, protectionDomain) -> addTestMethodInterceptor(builder))
31+
.installOn(instrumentation);
32+
} catch (Exception e) {
33+
LOGGER.warning(() -> "Could not init instrumentation.");
34+
}
35+
}
36+
37+
private static DynamicType.Builder<?> addTestMethodInterceptor(DynamicType.Builder<?> builder) {
38+
return builder.method(isTestMethod())
39+
.intercept(to(testMethodInterceptor()));
40+
}
41+
42+
public static ElementMatcher<? super MethodDescription> isTestMethod() {
43+
return isPublic()
44+
.and(not(isStatic()))
45+
.and(new NameMatcher<>(TEST_METHOD_NAME::equals));
46+
}
47+
48+
private static TypeDescription testMethodInterceptor() {
49+
return TypePool.Default.ofSystemLoader()
50+
.describe(RelaySessionFactoryInterceptor.class.getName())
51+
.resolve();
52+
}
53+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.zebrunner.mcloud.grid.agent;
2+
3+
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
4+
import net.bytebuddy.implementation.bind.annotation.SuperCall;
5+
import net.bytebuddy.implementation.bind.annotation.This;
6+
7+
import java.util.concurrent.Callable;
8+
9+
public class RelaySessionFactoryInterceptor {
10+
11+
@RuntimeType
12+
public static Object onTestMethodInvocation(@This final Object factory,
13+
@SuperCall final Callable<Object> proxy) throws Exception {
14+
return true;
15+
}
16+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Manifest-Version: 1.0
2+
Premain-Class: com.zebrunner.mcloud.grid.agent.NodeAgent
3+
Can-Redefine-Classes: true
4+
Can-Retransform-Classes: true

entrypoint.sh

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash
22

3-
NODE_CONFIG_JSON="/root/nodeconfig.json"
3+
NODE_CONFIG_JSON="/root/nodeconfig.toml"
44
DEFAULT_CAPABILITIES_JSON="/root/defaultcapabilities.json"
55

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

15-
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"
15+
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"
1616
#--use-plugins=relaxed-caps
1717

1818
share() {
@@ -341,7 +341,7 @@ if [ "$CONNECT_TO_GRID" = true ]; then
341341
else
342342
/root/generate_config.sh $NODE_CONFIG_JSON
343343
fi
344-
CMD+=" --nodeconfig $NODE_CONFIG_JSON"
344+
# CMD+=" --nodeconfig $NODE_CONFIG_JSON"
345345
fi
346346

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

366366
touch ${TASK_LOG}
367367
echo $CMD
368-
$CMD &
369368

369+
$CMD &
370+
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 \
371+
--config $NODE_CONFIG_JSON &
370372
trap 'finish' SIGTERM
371373

372374
# start in background video artifacts capturing

files/zbr-config-gen.sh

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,55 @@
11
#!/bin/bash
22

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

0 commit comments

Comments
 (0)