From 1b100c531d6abeefd0a0377773f961eb2de890fc Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 3 Dec 2022 21:31:55 +0100 Subject: [PATCH 1/4] feat: Add a possibility to connect to a running session --- .../io/appium/java_client/AppiumDriver.java | 52 ++++++++++++++--- .../appium/java_client/AppiumFluentWait.java | 22 +++---- .../java_client/android/AndroidDriver.java | 14 +++++ .../appium/java_client/gecko/GeckoDriver.java | 14 +++++ .../internal/ReflectionHelpers.java | 44 ++++++++++++++ .../java_client/internal/SessionHelpers.java | 58 +++++++++++++++++++ .../io/appium/java_client/ios/IOSDriver.java | 13 +++++ .../io/appium/java_client/mac/Mac2Driver.java | 13 +++++ .../remote/AppiumCommandExecutor.java | 22 ++----- .../java_client/safari/SafariDriver.java | 13 +++++ .../local/AppiumDriverLocalService.java | 20 +++---- .../java_client/windows/WindowsDriver.java | 5 +- .../internal/SessionConnectTest.java | 39 +++++++++++++ 13 files changed, 278 insertions(+), 51 deletions(-) create mode 100644 src/main/java/io/appium/java_client/internal/ReflectionHelpers.java create mode 100644 src/main/java/io/appium/java_client/internal/SessionHelpers.java create mode 100644 src/test/java/io/appium/java_client/internal/SessionConnectTest.java diff --git a/src/main/java/io/appium/java_client/AppiumDriver.java b/src/main/java/io/appium/java_client/AppiumDriver.java index 0c72f9c86..debd404d3 100644 --- a/src/main/java/io/appium/java_client/AppiumDriver.java +++ b/src/main/java/io/appium/java_client/AppiumDriver.java @@ -16,9 +16,13 @@ package io.appium.java_client; +import com.google.common.collect.ImmutableMap; import io.appium.java_client.internal.CapabilityHelpers; +import io.appium.java_client.internal.ReflectionHelpers; +import io.appium.java_client.internal.SessionHelpers; import io.appium.java_client.remote.AppiumCommandExecutor; import io.appium.java_client.remote.AppiumNewSessionCommandPayload; +import io.appium.java_client.remote.AppiumW3CHttpCommandCodec; import io.appium.java_client.remote.MobileCapabilityType; import io.appium.java_client.remote.options.BaseOptions; import io.appium.java_client.service.local.AppiumDriverLocalService; @@ -36,11 +40,11 @@ import org.openqa.selenium.remote.HttpCommandExecutor; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.Response; +import org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec; import org.openqa.selenium.remote.html5.RemoteLocationContext; import org.openqa.selenium.remote.http.HttpClient; import org.openqa.selenium.remote.http.HttpMethod; -import java.lang.reflect.Field; import java.net.URL; import java.util.Arrays; import java.util.Collections; @@ -128,6 +132,42 @@ public AppiumDriver(Capabilities capabilities) { this(AppiumDriverLocalService.buildDefaultService(), capabilities); } + /** + * This is a special constructor used to connect to a running driver instance. + * It does not do any necessary verifications, but rather assumes the given + * driver session is already running at `remoteSessionAddress`. + * The maintenance of driver state(s) is the caller's responsibility. + * !!! This API is supposed to be used for **debugging purposes only**. + * + * @param remoteSessionAddress The address of the **running** session including the session identifier. + * @param platformName The name of the target platform. + * @param automationName The name of the target automation. + */ + public AppiumDriver(URL remoteSessionAddress, String platformName, String automationName) { + super(); + ReflectionHelpers.setPrivateFieldValue( + RemoteWebDriver.class, this, "capabilities", new ImmutableCapabilities( + ImmutableMap.of( + PLATFORM_NAME, platformName, + APPIUM_PREFIX + AUTOMATION_NAME, automationName + ) + ) + ); + SessionHelpers.SessionAddress sessionAddress = SessionHelpers.parseSessionAddress(remoteSessionAddress); + AppiumCommandExecutor executor = new AppiumCommandExecutor( + MobileCommand.commandRepository, sessionAddress.getServerUrl() + ); + executor.setCommandCodec(new AppiumW3CHttpCommandCodec()); + executor.setResponseCodec(new W3CHttpResponseCodec()); + setCommandExecutor(executor); + this.executeMethod = new AppiumExecutionMethod(this); + locationContext = new RemoteLocationContext(executeMethod); + super.setErrorHandler(errorHandler); + this.remoteAddress = executor.getAddressOfRemoteServer(); + + setSessionId(sessionAddress.getId()); + } + /** * Changes platform name if it is not set and returns merged capabilities. * @@ -252,13 +292,9 @@ && isBlank((String) rawCapabilities.get(CapabilityType.BROWSER_NAME))) { rawCapabilities.remove(CapabilityType.BROWSER_NAME); } MutableCapabilities returnedCapabilities = new BaseOptions<>(rawCapabilities); - try { - Field capsField = RemoteWebDriver.class.getDeclaredField("capabilities"); - capsField.setAccessible(true); - capsField.set(this, returnedCapabilities); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new WebDriverException(e); - } + ReflectionHelpers.setPrivateFieldValue( + RemoteWebDriver.class, this, "capabilities", returnedCapabilities + ); setSessionId(response.getSessionId()); } diff --git a/src/main/java/io/appium/java_client/AppiumFluentWait.java b/src/main/java/io/appium/java_client/AppiumFluentWait.java index d914f6c16..2ea101daf 100644 --- a/src/main/java/io/appium/java_client/AppiumFluentWait.java +++ b/src/main/java/io/appium/java_client/AppiumFluentWait.java @@ -17,6 +17,7 @@ package io.appium.java_client; import com.google.common.base.Throwables; +import io.appium.java_client.internal.ReflectionHelpers; import lombok.AccessLevel; import lombok.Getter; import org.openqa.selenium.TimeoutException; @@ -24,7 +25,6 @@ import org.openqa.selenium.support.ui.FluentWait; import org.openqa.selenium.support.ui.Sleeper; -import java.lang.reflect.Field; import java.time.Clock; import java.time.Duration; import java.time.Instant; @@ -99,23 +99,15 @@ public AppiumFluentWait(T input, Clock clock, Sleeper sleeper) { } private B getPrivateFieldValue(String fieldName, Class fieldType) { - try { - final Field f = getClass().getSuperclass().getDeclaredField(fieldName); - f.setAccessible(true); - return fieldType.cast(f.get(this)); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new WebDriverException(e); - } + return ReflectionHelpers.getPrivateFieldValue( + getClass().getSuperclass(), this, fieldName, fieldType + ); } private Object getPrivateFieldValue(String fieldName) { - try { - final Field f = getClass().getSuperclass().getDeclaredField(fieldName); - f.setAccessible(true); - return f.get(this); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new WebDriverException(e); - } + return ReflectionHelpers.getPrivateFieldValue( + getClass().getSuperclass(), this, fieldName, Object.class + ); } protected Clock getClock() { diff --git a/src/main/java/io/appium/java_client/android/AndroidDriver.java b/src/main/java/io/appium/java_client/android/AndroidDriver.java index f3b812584..0fc1c5917 100644 --- a/src/main/java/io/appium/java_client/android/AndroidDriver.java +++ b/src/main/java/io/appium/java_client/android/AndroidDriver.java @@ -240,6 +240,20 @@ public AndroidDriver(Capabilities capabilities) { super(ensurePlatformName(capabilities, ANDROID_PLATFORM)); } + /** + * This is a special constructor used to connect to a running driver instance. + * It does not do any necessary verifications, but rather assumes the given + * driver session is already running at `remoteSessionAddress`. + * The maintenance of driver state(s) is the caller's responsibility. + * !!! This API is supposed to be used for **debugging purposes only**. + * + * @param remoteSessionAddress The address of the **running** session including the session identifier. + * @param automationName The name of the target automation. + */ + public AndroidDriver(URL remoteSessionAddress, String automationName) { + super(remoteSessionAddress, ANDROID_PLATFORM, automationName); + } + /** * Get test-coverage data. * diff --git a/src/main/java/io/appium/java_client/gecko/GeckoDriver.java b/src/main/java/io/appium/java_client/gecko/GeckoDriver.java index 6a7a55cab..07cb9e3e7 100644 --- a/src/main/java/io/appium/java_client/gecko/GeckoDriver.java +++ b/src/main/java/io/appium/java_client/gecko/GeckoDriver.java @@ -75,6 +75,20 @@ public GeckoDriver(HttpClient.Factory httpClientFactory, Capabilities capabiliti super(httpClientFactory, ensureAutomationName(capabilities, AUTOMATION_NAME)); } + /** + * This is a special constructor used to connect to a running driver instance. + * It does not do any necessary verifications, but rather assumes the given + * driver session is already running at `remoteSessionAddress`. + * The maintenance of driver state(s) is the caller's responsibility. + * !!! This API is supposed to be used for **debugging purposes only**. + * + * @param remoteSessionAddress The address of the **running** session including the session identifier. + * @param platformName The name of the target platform. + */ + public GeckoDriver(URL remoteSessionAddress, String platformName) { + super(remoteSessionAddress, platformName, AUTOMATION_NAME); + } + /** * Creates a new instance based on the given ClientConfig and {@code capabilities}. * The HTTP client is default client generated by {@link HttpCommandExecutor#getDefaultClientFactory}. diff --git a/src/main/java/io/appium/java_client/internal/ReflectionHelpers.java b/src/main/java/io/appium/java_client/internal/ReflectionHelpers.java new file mode 100644 index 000000000..62b1e9088 --- /dev/null +++ b/src/main/java/io/appium/java_client/internal/ReflectionHelpers.java @@ -0,0 +1,44 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.appium.java_client.internal; + +import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.remote.CommandExecutor; + +import java.lang.reflect.Field; + +public class ReflectionHelpers { + public static T setPrivateFieldValue(Class cls, T target, String fieldName, Object newValue) { + try { + final Field f = cls.getDeclaredField(fieldName); + f.setAccessible(true); + f.set(target, newValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new WebDriverException(e); + } + return target; + } + + public static T getPrivateFieldValue(Class cls, Object target, String fieldName, Class fieldType) { + try { + final Field f = cls.getDeclaredField(fieldName); + f.setAccessible(true); + return fieldType.cast(f.get(target)); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new WebDriverException(e); + } + } +} diff --git a/src/main/java/io/appium/java_client/internal/SessionHelpers.java b/src/main/java/io/appium/java_client/internal/SessionHelpers.java new file mode 100644 index 000000000..f49715509 --- /dev/null +++ b/src/main/java/io/appium/java_client/internal/SessionHelpers.java @@ -0,0 +1,58 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.appium.java_client.internal; + +import lombok.Data; +import org.openqa.selenium.InvalidArgumentException; +import org.openqa.selenium.WebDriverException; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SessionHelpers { + private static final Pattern SESSION = Pattern.compile("/session/([^/]+)"); + + @Data public static class SessionAddress { + private final URL serverUrl; + private final String id; + } + + /** + * Parses the address of a running remote session. + * + * @param address The address string containing /session/id suffix. + * @return Parsed address object. + * @throws InvalidArgumentException If no session identifier could be parsed. + */ + public static SessionAddress parseSessionAddress(URL address) { + String addressString = address.toString(); + Matcher matcher = SESSION.matcher(addressString); + if (!matcher.find()) { + throw new InvalidArgumentException( + String.format("The server URL '%s' must include /session/ suffix", addressString) + ); + } + try { + return new SessionAddress( + new URL(addressString.replace(matcher.group(), "")), matcher.group(1) + ); + } catch (MalformedURLException e) { + throw new WebDriverException(e); + } + } +} diff --git a/src/main/java/io/appium/java_client/ios/IOSDriver.java b/src/main/java/io/appium/java_client/ios/IOSDriver.java index d450c1b0f..231d721c4 100644 --- a/src/main/java/io/appium/java_client/ios/IOSDriver.java +++ b/src/main/java/io/appium/java_client/ios/IOSDriver.java @@ -220,6 +220,19 @@ public IOSDriver(AppiumClientConfig appiumClientConfig, Capabilities capabilitie super(appiumClientConfig, ensurePlatformName(capabilities, PLATFORM_NAME)); } + /** + * This is a special constructor used to connect to a running driver instance. + * It does not do any necessary verifications, but rather assumes the given + * driver session is already running at `remoteSessionAddress`. + * The maintenance of driver state(s) is the caller's responsibility. + * !!! This API is supposed to be used for **debugging purposes only**. + * + * @param remoteSessionAddress The address of the **running** session including the session identifier. + * @param automationName The name of the target automation. + */ + public IOSDriver(URL remoteSessionAddress, String automationName) { + super(remoteSessionAddress, PLATFORM_NAME, automationName); + } /** * Creates a new instance based on {@code capabilities}. diff --git a/src/main/java/io/appium/java_client/mac/Mac2Driver.java b/src/main/java/io/appium/java_client/mac/Mac2Driver.java index 905cbec77..46911c314 100644 --- a/src/main/java/io/appium/java_client/mac/Mac2Driver.java +++ b/src/main/java/io/appium/java_client/mac/Mac2Driver.java @@ -86,6 +86,19 @@ public Mac2Driver(HttpClient.Factory httpClientFactory, Capabilities capabilitie capabilities, PLATFORM_NAME, AUTOMATION_NAME)); } + /** + * This is a special constructor used to connect to a running driver instance. + * It does not do any necessary verifications, but rather assumes the given + * driver session is already running at `remoteSessionAddress`. + * The maintenance of driver state(s) is the caller's responsibility. + * !!! This API is supposed to be used for **debugging purposes only**. + * + * @param remoteSessionAddress The address of the **running** session including the session identifier. + */ + public Mac2Driver(URL remoteSessionAddress) { + super(remoteSessionAddress, PLATFORM_NAME, AUTOMATION_NAME); + } + /** * Creates a new instance based on the given ClientConfig and {@code capabilities}. * The HTTP client is default client generated by {@link HttpCommandExecutor#getDefaultClientFactory}. diff --git a/src/main/java/io/appium/java_client/remote/AppiumCommandExecutor.java b/src/main/java/io/appium/java_client/remote/AppiumCommandExecutor.java index b3acea991..0085f457d 100644 --- a/src/main/java/io/appium/java_client/remote/AppiumCommandExecutor.java +++ b/src/main/java/io/appium/java_client/remote/AppiumCommandExecutor.java @@ -21,6 +21,7 @@ import com.google.common.net.HttpHeaders; import io.appium.java_client.AppiumClientConfig; import io.appium.java_client.AppiumUserAgentFilter; +import io.appium.java_client.internal.ReflectionHelpers; import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.remote.Command; @@ -42,7 +43,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; -import java.lang.reflect.Field; import java.net.ConnectException; import java.net.MalformedURLException; import java.net.URL; @@ -128,25 +128,13 @@ public AppiumCommandExecutor(Map additionalCommands, @SuppressWarnings("SameParameterValue") protected B getPrivateFieldValue( Class cls, String fieldName, Class fieldType) { - try { - final Field f = cls.getDeclaredField(fieldName); - f.setAccessible(true); - return fieldType.cast(f.get(this)); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new WebDriverException(e); - } + return ReflectionHelpers.getPrivateFieldValue(cls, this, fieldName, fieldType); } @SuppressWarnings("SameParameterValue") protected void setPrivateFieldValue( Class cls, String fieldName, Object newValue) { - try { - final Field f = cls.getDeclaredField(fieldName); - f.setAccessible(true); - f.set(this, newValue); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new WebDriverException(e); - } + ReflectionHelpers.setPrivateFieldValue(cls, this, fieldName, newValue); } protected Map getAdditionalCommands() { @@ -159,11 +147,11 @@ protected CommandCodec getCommandCodec() { return getPrivateFieldValue(HttpCommandExecutor.class, "commandCodec", CommandCodec.class); } - protected void setCommandCodec(CommandCodec newCodec) { + public void setCommandCodec(CommandCodec newCodec) { setPrivateFieldValue(HttpCommandExecutor.class, "commandCodec", newCodec); } - protected void setResponseCodec(ResponseCodec codec) { + public void setResponseCodec(ResponseCodec codec) { setPrivateFieldValue(HttpCommandExecutor.class, "responseCodec", codec); } diff --git a/src/main/java/io/appium/java_client/safari/SafariDriver.java b/src/main/java/io/appium/java_client/safari/SafariDriver.java index 97d1f96e4..f8f10bb01 100644 --- a/src/main/java/io/appium/java_client/safari/SafariDriver.java +++ b/src/main/java/io/appium/java_client/safari/SafariDriver.java @@ -83,6 +83,19 @@ public SafariDriver(HttpClient.Factory httpClientFactory, Capabilities capabilit capabilities, PLATFORM_NAME, AUTOMATION_NAME)); } + /** + * This is a special constructor used to connect to a running driver instance. + * It does not do any necessary verifications, but rather assumes the given + * driver session is already running at `remoteSessionAddress`. + * The maintenance of driver state(s) is the caller's responsibility. + * !!! This API is supposed to be used for **debugging purposes only**. + * + * @param remoteSessionAddress The address of the **running** session including the session identifier. + */ + public SafariDriver(URL remoteSessionAddress) { + super(remoteSessionAddress, PLATFORM_NAME, AUTOMATION_NAME); + } + /** * Creates a new instance based on the given ClientConfig and {@code capabilities}. * The HTTP client is default client generated by {@link HttpCommandExecutor#getDefaultClientFactory}. diff --git a/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java b/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java index 62d2a98ce..b58191ec2 100644 --- a/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java +++ b/src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java @@ -17,6 +17,7 @@ package io.appium.java_client.service.local; import com.google.common.annotations.VisibleForTesting; +import io.appium.java_client.internal.ReflectionHelpers; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.openqa.selenium.net.UrlChecker; @@ -31,7 +32,6 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.lang.reflect.Field; import java.net.MalformedURLException; import java.net.URL; import java.time.Duration; @@ -226,15 +226,15 @@ private int destroyProcess(Duration timeout) { // it does not exit after two seconds, which is in most cases not enough for // Appium try { - Field processField = process.getClass().getDeclaredField("process"); - processField.setAccessible(true); - Object osProcess = processField.get(process); - Field watchdogField = osProcess.getClass().getDeclaredField("executeWatchdog"); - watchdogField.setAccessible(true); - Object watchdog = watchdogField.get(osProcess); - Field nativeProcessField = watchdog.getClass().getDeclaredField("process"); - nativeProcessField.setAccessible(true); - Process nativeProcess = (Process) nativeProcessField.get(watchdog); + Object osProcess = ReflectionHelpers.getPrivateFieldValue( + process.getClass(), process, "process", Object.class + ); + Object watchdog = ReflectionHelpers.getPrivateFieldValue( + osProcess.getClass(), osProcess, "executeWatchdog", Object.class + ); + Process nativeProcess = ReflectionHelpers.getPrivateFieldValue( + watchdog.getClass(), watchdog, "process", Process.class + ); nativeProcess.destroy(); nativeProcess.waitFor(timeout.toMillis(), TimeUnit.MILLISECONDS); } catch (Exception e) { diff --git a/src/main/java/io/appium/java_client/windows/WindowsDriver.java b/src/main/java/io/appium/java_client/windows/WindowsDriver.java index 9a441d68a..7c6e68a7a 100644 --- a/src/main/java/io/appium/java_client/windows/WindowsDriver.java +++ b/src/main/java/io/appium/java_client/windows/WindowsDriver.java @@ -46,7 +46,6 @@ public WindowsDriver(HttpCommandExecutor executor, Capabilities capabilities) { super(executor, ensurePlatformAndAutomationNames(capabilities, PLATFORM_NAME, AUTOMATION_NAME)); } - public WindowsDriver(URL remoteAddress, Capabilities capabilities) { super(remoteAddress, ensurePlatformAndAutomationNames( capabilities, PLATFORM_NAME, AUTOMATION_NAME)); @@ -82,6 +81,10 @@ public WindowsDriver(HttpClient.Factory httpClientFactory, Capabilities capabili capabilities, PLATFORM_NAME, AUTOMATION_NAME)); } + public WindowsDriver(URL remoteSessionAddress) { + super(remoteSessionAddress, PLATFORM_NAME, AUTOMATION_NAME); + } + /** * Creates a new instance based on the given ClientConfig and {@code capabilities}. * The HTTP client is default client generated by {@link HttpCommandExecutor#getDefaultClientFactory}. diff --git a/src/test/java/io/appium/java_client/internal/SessionConnectTest.java b/src/test/java/io/appium/java_client/internal/SessionConnectTest.java new file mode 100644 index 000000000..f72c41ea8 --- /dev/null +++ b/src/test/java/io/appium/java_client/internal/SessionConnectTest.java @@ -0,0 +1,39 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.appium.java_client.internal; + +import io.appium.java_client.ios.IOSDriver; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebDriverException; + +import java.net.MalformedURLException; +import java.net.URL; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class SessionConnectTest { + + @Test + void canConnectToASession() throws MalformedURLException { + IOSDriver driver = new IOSDriver( + new URL("http://localhost:4723/session/1234"), "xcuitest" + ); + assertEquals(driver.getSessionId().toString(), "1234"); + assertThrows(WebDriverException.class, driver::quit); + } + +} From ff9fc1a26ee434208abaf41c4a0f5959836c03ff Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 3 Dec 2022 21:38:52 +0100 Subject: [PATCH 2/4] Remove automation name --- src/main/java/io/appium/java_client/ios/IOSDriver.java | 6 +++--- .../io/appium/java_client/internal/SessionConnectTest.java | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/appium/java_client/ios/IOSDriver.java b/src/main/java/io/appium/java_client/ios/IOSDriver.java index 231d721c4..ad00a5f90 100644 --- a/src/main/java/io/appium/java_client/ios/IOSDriver.java +++ b/src/main/java/io/appium/java_client/ios/IOSDriver.java @@ -31,6 +31,7 @@ import io.appium.java_client.PushesFiles; import io.appium.java_client.SupportsLegacyAppManagement; import io.appium.java_client.battery.HasBattery; +import io.appium.java_client.remote.AutomationName; import io.appium.java_client.remote.SupportsContextSwitching; import io.appium.java_client.remote.SupportsLocation; import io.appium.java_client.remote.SupportsRotation; @@ -228,10 +229,9 @@ public IOSDriver(AppiumClientConfig appiumClientConfig, Capabilities capabilitie * !!! This API is supposed to be used for **debugging purposes only**. * * @param remoteSessionAddress The address of the **running** session including the session identifier. - * @param automationName The name of the target automation. */ - public IOSDriver(URL remoteSessionAddress, String automationName) { - super(remoteSessionAddress, PLATFORM_NAME, automationName); + public IOSDriver(URL remoteSessionAddress) { + super(remoteSessionAddress, PLATFORM_NAME, AutomationName.IOS_XCUI_TEST); } /** diff --git a/src/test/java/io/appium/java_client/internal/SessionConnectTest.java b/src/test/java/io/appium/java_client/internal/SessionConnectTest.java index f72c41ea8..52b4b051a 100644 --- a/src/test/java/io/appium/java_client/internal/SessionConnectTest.java +++ b/src/test/java/io/appium/java_client/internal/SessionConnectTest.java @@ -29,9 +29,7 @@ public class SessionConnectTest { @Test void canConnectToASession() throws MalformedURLException { - IOSDriver driver = new IOSDriver( - new URL("http://localhost:4723/session/1234"), "xcuitest" - ); + IOSDriver driver = new IOSDriver(new URL("http://localhost:4723/session/1234")); assertEquals(driver.getSessionId().toString(), "1234"); assertThrows(WebDriverException.class, driver::quit); } From 1a29c1fc62ba4b68238360951505bc498aa3e5e7 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 3 Dec 2022 21:48:14 +0100 Subject: [PATCH 3/4] Fix checkstyle --- .../internal/ReflectionHelpers.java | 21 ++++++++++++++++++- .../java_client/internal/SessionHelpers.java | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/appium/java_client/internal/ReflectionHelpers.java b/src/main/java/io/appium/java_client/internal/ReflectionHelpers.java index 62b1e9088..cbaa7c796 100644 --- a/src/main/java/io/appium/java_client/internal/ReflectionHelpers.java +++ b/src/main/java/io/appium/java_client/internal/ReflectionHelpers.java @@ -13,14 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.appium.java_client.internal; import org.openqa.selenium.WebDriverException; -import org.openqa.selenium.remote.CommandExecutor; import java.lang.reflect.Field; public class ReflectionHelpers { + + /** + * Sets the given value to a private instance field. + * + * @param cls The target class or a superclass. + * @param target Target instance. + * @param fieldName Target field name. + * @param newValue The value to be set. + * @return The same instance for chaining. + */ public static T setPrivateFieldValue(Class cls, T target, String fieldName, Object newValue) { try { final Field f = cls.getDeclaredField(fieldName); @@ -32,6 +42,15 @@ public static T setPrivateFieldValue(Class cls, T target, String fieldNam return target; } + /** + * Fetches the value of a private instance field. + * + * @param cls The target class or a superclass. + * @param target Target instance. + * @param fieldName Target field name. + * @param fieldType Field type. + * @return The retrieved field value. + */ public static T getPrivateFieldValue(Class cls, Object target, String fieldName, Class fieldType) { try { final Field f = cls.getDeclaredField(fieldName); diff --git a/src/main/java/io/appium/java_client/internal/SessionHelpers.java b/src/main/java/io/appium/java_client/internal/SessionHelpers.java index f49715509..b3b9f0eca 100644 --- a/src/main/java/io/appium/java_client/internal/SessionHelpers.java +++ b/src/main/java/io/appium/java_client/internal/SessionHelpers.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.appium.java_client.internal; import lombok.Data; From aed6c8f5223bf7c441d70b246093758356e40574 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sun, 4 Dec 2022 14:07:27 +0100 Subject: [PATCH 4/4] Simplify calls --- src/main/java/io/appium/java_client/AppiumFluentWait.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/appium/java_client/AppiumFluentWait.java b/src/main/java/io/appium/java_client/AppiumFluentWait.java index 2ea101daf..9061600a0 100644 --- a/src/main/java/io/appium/java_client/AppiumFluentWait.java +++ b/src/main/java/io/appium/java_client/AppiumFluentWait.java @@ -99,15 +99,11 @@ public AppiumFluentWait(T input, Clock clock, Sleeper sleeper) { } private B getPrivateFieldValue(String fieldName, Class fieldType) { - return ReflectionHelpers.getPrivateFieldValue( - getClass().getSuperclass(), this, fieldName, fieldType - ); + return ReflectionHelpers.getPrivateFieldValue(FluentWait.class, this, fieldName, fieldType); } private Object getPrivateFieldValue(String fieldName) { - return ReflectionHelpers.getPrivateFieldValue( - getClass().getSuperclass(), this, fieldName, Object.class - ); + return getPrivateFieldValue(fieldName, Object.class); } protected Clock getClock() {