prashant-ramcharan / courgette-jvm

Multiprocess | Parallel Cucumber-JVM | Parallelize your Java Cucumber tests on a feature level or on a scenario level.
MIT License
130 stars 38 forks source link

Mobile device allocation plugin is not working as expected when running test in parallel #316

Closed vsan6545 closed 2 years ago

vsan6545 commented 2 years ago

TC1 : Login with Native customer TC2 : Login with Hybrid customer (password field is part of webview) TC3 : Login and validate place order

If i run above 3 test cases in parallel on 2 device and lets say TC1 and TC2 have started execution on 2 devices, TC1 has finished execution and waiting on TC3 to start, before TC3 start TC2 on other device which is still executing will immediately failed with error "org.openqa.selenium.WebDriverException: Connection refused (Connection refused)"

Note : Running above all 3 test cases on 1 device using same runner class works fine.

What i noticed is when running more than 3 test cases on 2 devices and 1 device has finished execution on 1 device and 2nd device still running test case will immediately failed waiting on 3rd test case to start execution on any 1 of this device.

Solution : When 2 test cases running in parallel on 2 devices, 1 has finished execution and another one still running should not affect waiting on 3rd test case to start execution on available device 1

====== If not Connection Refused this error will throw ======

io.appium.java_client.NoSuchContextException: An unknown server-side error occurred while processing the command. Original error: The process has received SIGTERM signal Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:17:03' System info: host: '341-MBC02FG14MMD6N.local', ip: '127.0.0.1', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.16', java.version: '1.8.0_202' Driver info: io.appium.java_client.android.AndroidDriver Capabilities {app: /Users/vsan6545/Desktop/Sys..., appPackage: com.syscocorp.mss.enterpris..., autoLaunch: false, automationName: UiAutomator2, avd: Pixel4XL, chromedriverExecutable: /Users/vsan6545/.cache/sele..., chromedriverUseSystemExecutable: true, databaseEnabled: false, desired: {app: /Users/vsan6545/Desktop/Sys..., autoLaunch: false, automationName: UiAutomator2, avd: Pixel4XL, chromedriverExecutable: /Users/vsan6545/.cache/sele..., chromedriverUseSystemExecutable: true, newCommandTimeout: 10000, platformName: android, systemPort: 64078}, deviceApiLevel: 32, deviceManufacturer: Google, deviceModel: sdk_gphone64_x86_64, deviceName: emulator-5558, deviceScreenDensity: 560, deviceScreenSize: 1440x3040, deviceUDID: emulator-5558, javascriptEnabled: true, locationContextEnabled: false, networkConnectionEnabled: true, newCommandTimeout: 10000, pixelRatio: 3.5, platform: LINUX, platformName: Android, platformVersion: 12, statBarHeight: 84, systemPort: 64078, takesScreenshot: true, viewportRect: {height: 2900, left: 0, top: 84, width: 1440}, warnings: {}, webStorageEnabled: false} Session ID: fd630814-4b2a-4a8c-ab1f-4681c903f2e3 at io.appium.java_client.AppiumDriver.context(AppiumDriver.java:232) at com.sysolab.mobile.automation.MobileAppsPages.UserAction.AndroidSwitchToWebViewContext(UserAction.java:78) at com.sysolab.mobile.automation.MobileAppsPages.AndroidAndiOSPages.ContinueLoginPage.VerifyContinueLoginPage(ContinueLoginPage.java:41) at com.sysolab.mobile.automation.steps.AndroidAndiOSSteps.ContinueLoginPageSteps.lambda$new$2(ContinueLoginPageSteps.java:26) at ✽.It should redirect to Continue login page whose unique identifier is "ContinueLoginPage"(file:///Users/vsan6545/Desktop/Syscolab-Mobile-Apps-Automation/src/test/resources/features/Login.feature:42) Caused by: org.openqa.selenium.WebDriverException: An unknown server-side error occurred while processing the command. Original error: The process has received SIGTERM signal Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:17:03' System info: host: '341-MBC02FG14MMD6N.local', ip: '127.0.0.1', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.16', java.version: '1.8.0_202' Driver info: io.appium.java_client.android.AndroidDriver Capabilities {app: /Users/vsan6545/Desktop/Sys..., appPackage: com.syscocorp.mss.enterpris..., autoLaunch: false, automationName: UiAutomator2, avd: Pixel4XL, chromedriverExecutable: /Users/vsan6545/.cache/sele..., chromedriverUseSystemExecutable: true, databaseEnabled: false, desired: {app: /Users/vsan6545/Desktop/Sys..., autoLaunch: false, automationName: UiAutomator2, avd: Pixel4XL, chromedriverExecutable: /Users/vsan6545/.cache/sele..., chromedriverUseSystemExecutable: true, newCommandTimeout: 10000, platformName: android, systemPort: 64078}, deviceApiLevel: 32, deviceManufacturer: Google, deviceModel: sdk_gphone64_x86_64, deviceName: emulator-5558, deviceScreenDensity: 560, deviceScreenSize: 1440x3040, deviceUDID: emulator-5558, javascriptEnabled: true, locationContextEnabled: false, networkConnectionEnabled: true, newCommandTimeout: 10000, pixelRatio: 3.5, platform: LINUX, platformName: Android, platformVersion: 12, statBarHeight: 84, systemPort: 64078, takesScreenshot: true, viewportRect: {height: 2900, left: 0, top: 84, width: 1440}, warnings: {}, webStorageEnabled: false} Session ID: fd630814-4b2a-4a8c-ab1f-4681c903f2e3 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at org.openqa.selenium.remote.http.W3CHttpResponseCodec.createException(W3CHttpResponseCodec.java:187) at org.openqa.selenium.remote.http.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:122) at org.openqa.selenium.remote.http.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:49) at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:158) at io.appium.java_client.remote.AppiumCommandExecutor.execute(AppiumCommandExecutor.java:250) at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:552) at io.appium.java_client.DefaultGenericMobileDriver.execute(DefaultGenericMobileDriver.java:41) at io.appium.java_client.AppiumDriver.execute(AppiumDriver.java:1) at io.appium.java_client.android.AndroidDriver.execute(AndroidDriver.java:1) at io.appium.java_client.AppiumDriver.context(AppiumDriver.java:229) at com.sysolab.mobile.automation.MobileAppsPages.UserAction.AndroidSwitchToWebViewContext(UserAction.java:78) at com.sysolab.mobile.automation.MobileAppsPages.AndroidAndiOSPages.ContinueLoginPage.VerifyContinueLoginPage(ContinueLoginPage.java:41) at com.sysolab.mobile.automation.steps.AndroidAndiOSSteps.ContinueLoginPageSteps.lambda$new$2(ContinueLoginPageSteps.java:26) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at io.cucumber.java8.Invoker.invoke(Invoker.java:20) at io.cucumber.java8.AbstractGlueDefinition.invokeMethod(AbstractGlueDefinition.java:48) at io.cucumber.java8.Java8StepDefinition.execute(Java8StepDefinition.java:45) at io.cucumber.core.runner.CoreStepDefinition.execute(CoreStepDefinition.java:66) at io.cucumber.core.runner.PickleStepDefinitionMatch.runStep(PickleStepDefinitionMatch.java:63) at io.cucumber.core.runner.ExecutionMode$1.execute(ExecutionMode.java:10) at io.cucumber.core.runner.TestStep.executeStep(TestStep.java:86) at io.cucumber.core.runner.TestStep.run(TestStep.java:57) at io.cucumber.core.runner.PickleStepTestStep.run(PickleStepTestStep.java:51) at io.cucumber.core.runner.TestCase.run(TestCase.java:95) at io.cucumber.core.runner.Runner.runPickle(Runner.java:75) at io.cucumber.core.runtime.Runtime.lambda$executePickle$6(Runtime.java:128) at io.cucumber.core.runtime.CucumberExecutionContext.lambda$runTestCase$3(CucumberExecutionContext.java:110) at io.cucumber.core.runtime.RethrowingThrowableCollector.executeAndThrow(RethrowingThrowableCollector.java:23) at io.cucumber.core.runtime.CucumberExecutionContext.runTestCase(CucumberExecutionContext.java:110) at io.cucumber.core.runtime.Runtime.lambda$executePickle$7(Runtime.java:128) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at io.cucumber.core.runtime.Runtime$SameThreadExecutorService.execute(Runtime.java:249) at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112) at io.cucumber.core.runtime.Runtime.lambda$runFeatures$3(Runtime.java:110) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.stream.SliceOps$1$1.accept(SliceOps.java:204) at java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1359) at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126) at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) at io.cucumber.core.runtime.Runtime.runFeatures(Runtime.java:111) at io.cucumber.core.runtime.Runtime.lambda$run$0(Runtime.java:82) at io.cucumber.core.runtime.Runtime.execute(Runtime.java:94) at io.cucumber.core.runtime.Runtime.run(Runtime.java:80) at io.cucumber.core.cli.Main.run(Main.java:87) at io.cucumber.core.cli.Main.main(Main.java:30)

prashant-ramcharan commented 2 years ago

The Courgette mobile device allocator plugin does not manage connections to external servers (i.e. Appium). Your test framework should still do this. Any connection exception should be handled by your test framework.

This plugin just tracks when a device is been used in a test. The device is only unallocated when the Java process running each test is complete.

To assist further, please provide a project to replicate this issue.

LearningPur commented 2 years ago

Thank you so much @prashant-ramcharan appreciated for quick reply back. Since i am working for company don't want to share sample project here in github, would you please mind to provide your gmail id where i can send you to take a look at it.

prashant-ramcharan commented 2 years ago

You can share a private git repo and add me as a collborator if that's better?

Or email github@prashant-ramcharan.co.uk

Please provide clear instructions on how to run the project to replicate the issue.

LearningPur commented 2 years ago

Thanks, i will email you or create new repo and add you as collaborator.

As per appium pro if apps is hybrid we also need to set unique chromedriverport if so in that case can you please create another static variable like this "CourgetteMobileDeviceAllocator.PARALLEL_PORT" for chromedriverport

https://appiumpro.com/editions/28-running-multiple-appium-tests-in-parallel

Screen Shot 2022-02-22 at 2 35 57 PM

This issue is mostly happening when apps is Hybrid and 1 instance (thread) finish execution it kills the chromedriver and at the same time if another instance is still running with same chromedriver it also gets killed before it finish the execution.

prashant-ramcharan commented 2 years ago

You can try this in your framework:


 capabilities.setCapability("chromeDriverPort", getChromeDriverPort());

     private int getChromeDriverPort() {
        int parallelPort = 0;
        try {
            ServerSocket socket = new ServerSocket(0);
            parallelPort = socket.getLocalPort();
            socket.close();
        } catch (IOException e) {
            System.out.err("Unable to find a free port");
        }
        return parallelPort;
    }
LearningPur commented 2 years ago

Okay thank you so much @prashant-ramcharan but i am still getting connection refused error when 2 TC running in parallel on 2 device and 1 has finished execution, killed the session, 2nd device also killed the session with connection refused error.

I will create sample project and will share it to you to take a look at it.

As always thank you so much for spreading helping hands.

LearningPur commented 2 years ago

Hi @prashant-ramcharan I have created sample private repo below is the url of it and invited you as collborator

https://github.com/LearningPur/Syscolab-Mobile-Apps-Automation

I have written clear instruction for you to run this code.

Requesting you from bottom of heart if you can please look at it and help me resolve an issue i am facing running test in parallel and getting either "Connection refused" or "Session terminated" error.

prashant-ramcharan commented 2 years ago

@LearningPur I had a quick look and this is not an issue with Courgette.

The issue is you killing all Appium servers before each test so this will conflict when you have an active server running a test.

Remove the before all method killRunningAppiumServer as this is where the issue is.

If you want to kill all appium servers after all tests are complete then you should move this code to a @CourgetteAfterAll hook.

https://user-images.githubusercontent.com/2563149/155517820-83f054e0-aae9-4386-8af3-cdfffa2b4fd7.mp4

Some additional tips:

Remove these caps

chromedriverUseSystemExecutable
chromedriverExecutable
chromeDriverPort

Update the service builder

        serviceBuilder
                .usingAnyFreePort()
                .withArgument(() -> "--allow-insecure","chromedriver_autodownload");
LearningPur commented 2 years ago

Thank you so much @prashant-ramcharan for giving your golden time to look into it and help me identify the silly mistake i made which took me few days though couldn't resolved it.

I made the changes what you mentioned in your comment above and it has resolved an issue.

Thanks a ton, you don't know how much you helped me and saved me in this project.

In future if i will be given a chance i would love to meet you in person and touch your feet.