How to keep Appium server to its assigned AppiumDriver when multiple devices are connected

1.2k Views Asked by At

I trying to set up a framework automation setup with multiple devices with each creating an Appium server depending on how many devices are connected. I'm able to create an Appium server and assign it to each device, but when it starts and at first show the correct form of the Capabilities written for each device. However, when I start to run tests, the second server's session gets overridden and connects to the first device, and as a results, no test get run on second device for the second server.

Is there a function to keep the Appium servers stick to their assigned device and never get overridden?

Here is a bit of the logs when the session get overridden by another device:

 [Appium] Welcome to Appium v1.8.1
    [Appium] Non-default server args:
    [Appium]   address: 127.0.0.1
    [Appium]   port: 5001
    [Appium]   defaultCapabilities: {
    [Appium]     noReset: false
    [Appium]   }
    [Appium] Default capabilities, which will be added to each request unless overridden by desired capabilities:
    [Appium]   noReset: false
    [Appium] Appium REST http interface listener started on 127.0.0.1:5001
    [HTTP] --> POST /wd/hub/session
    [HTTP] {"desiredCapabilities":{"browserName":"Chrome","deviceId":"192.168.174.101:5555\t","deviceName":"device2","newCommandTimeout":"4000","noReset":false,"platformName":"Android"},"capabilities":{"desiredCapabilities":{"browserName":"Chrome","deviceId":"192.168.174.101:5555\t","deviceName":"device2","newCommandTimeout":"4000","noReset":false,"platformName":"Android"},"firstMatch":[{"browserName":"Chrome","platformName":"android"}]}}
    [debug] [MJSONWP] Calling AppiumDriver.createSession() with args: [{"browserName":"Chrome","deviceId":"192.168.174.101:5555\t","deviceName":"device2","newCommandTimeout":"4000","noReset":false,"platformName":"Android"},null,{"desiredCapabilities":{"browserName":"Chrome","deviceId":"192.168.174.101:5555\t","deviceName":"device2","newCommandTimeout":"4000","noReset":false,"platformName":"Android"},"firstMatch":[{"browserName":"Chrome","platformName":"android"}]}]
    [debug] [BaseDriver] Event 'newSessionRequested' logged at 1537146899868 (18:14:59 GMT-0700 (Pacific Daylight Time))
    [BaseDriver] The capabilities ["noReset"] are not standard capabilities and should have an extension prefix
    [BaseDriver] Boolean capability passed in as string. Functionality may be compromised.
    [Appium] Could not parse W3C capabilities: 'deviceName' can't be blank. Falling back to JSONWP protocol.
    [Appium] The following capabilities were provided in the JSONWP desired capabilities that are missing in W3C capabilities: ["noReset","browserName","deviceId","deviceName","newCommandTimeout","platformName"]. Falling back to JSONWP protocol.
    [Appium] Creating new AndroidDriver (v2.7.0) session
    [Appium] Capabilities:
    [Appium]   noReset: false
    [Appium]   browserName: Chrome
    [Appium]   deviceId: 192.168.174.101:5555
    [Appium]   deviceName: device2
    [Appium]   newCommandTimeout: 4000
    [Appium]   platformName: Android
    [debug] [BaseDriver] Creating session with MJSONWP desired capabilities: {"noReset":false,"browserNa...
    [BaseDriver] Capability 'newCommandTimeout' changed from string ('4000') to integer (4000). This may cause unexpected behavior
    [BaseDriver] The following capabilities were provided, but are not recognized by appium: deviceId.
    [BaseDriver] Session created with session id: 68b1e594-54d4-4241-befc-a6b8beefe2f9
    [debug] [AndroidDriver] Getting Java version
    [AndroidDriver] Java version is: 1.8.0_181
    [AndroidDriver] We're going to run a Chrome-based session
    [AndroidDriver] Chrome-type package and activity are com.android.chrome and com.google.android.apps.chrome.Main
    [ADB] Checking whether adb is present
    [ADB] Found 3 'build-tools' folders under 'C:\Users\Rigat\AppData\Local\Android\Sdk' (newest first):
    [ADB]     C:/Users/Rigat/AppData/Local/Android/Sdk/build-tools/28.0.0-rc1
    [ADB]     C:/Users/Rigat/AppData/Local/Android/Sdk/build-tools/27.0.3
    [ADB]     C:/Users/Rigat/AppData/Local/Android/Sdk/build-tools/26.0.2
    [ADB] Using adb.exe from C:\Users\Rigat\AppData\Local\Android\Sdk\platform-tools\adb.exe
    [AndroidDriver] Retrieving device list
    [debug] [ADB] Trying to find a connected android device
    [debug] [ADB] Getting connected devices...
    [debug] [ADB] 2 device(s) connected
    [AndroidDriver] Using device: 192.168.174.102:5555
    [debug] [ADB] Setting device id to 192.168.174.102:5555
    [AndroidDriver] App file was not listed, instead we're going to run com.android.chrome directly on the device
    [debug] [AndroidDriver] Checking whether package is present on the device
    [debug] [ADB] Running 'C:\Users\Rigat\AppData\Local\Android\Sdk\platform-tools\adb.exe -P 5037 -s 192.168.174.102\:5555 shell pm list packages com.android.chrome'
    [AndroidDriver] Starting Android session
    [debug] [ADB] Running 'C:\Users\Rigat\AppData\Local\Android\Sdk\platform-tools\adb.exe -P 5037 -s 192.168.174.102\:5555 wait-for-device'
    [debug] [ADB] Running 'C:\Users\Rigat\AppData\Local\Android\Sdk\platform-tools\adb.exe -P 5037 -s 192.168.174.102\:5555 shell echo ping'
    [debug] [AndroidDriver] Pushing settings apk to device...
    [debug] [ADB] Getting install status for io.appium.settings
    [debug] [ADB] Running 'C:\Users\Rigat\AppData\Local\Android\Sdk\platform-tools\adb.exe -P 5037 -s 192.168.174.102\:5555 shell pm list packages io.appium.settings'

Here is the code when the devices and servers are setup:

public PageObject pageObject = null;

    public AppiumDriverSetup createDrivers = new AppiumDriverSetup();


    @BeforeClass(alwaysRun=true)
    public void setupDrivers() throws IOException {
        createDrivers.makeList();
        for(AppiumDriver<MobileElement> driver : createDrivers.getActiveList()){

            this.driver = driver;
            TLDriverFactory.setTLDriver(driver);
            System.out.println("Calling for driver: " + driver.getCapabilities());

            wait = new WebDriverWait(TLDriverFactory.getTLDriver(), 10);
            testActivation();
        }
    }

    @Test
    public void testActivation() {
        assertTrue(driver != null);

        pageObject = new PageObject(TLDriverFactory.getTLDriver());
        pageObject.driver.get("https://www.google.com");

    }


public class AppiumDriverSetup {

    enum Devices {
        ANDROID, IOS;
    }

    public static int deviceNum = 0;

    public AppiumDriver<MobileElement> driver;

    private List<Object> newDevice = new ArrayList<Object>();

    private static List<AppiumDriver<MobileElement>> activeList = Collections.synchronizedList(new ArrayList<AppiumDriver<MobileElement>>());

    AppOrBrowser determinePlatType = new AppOrBrowser();

    ActiveAppiumPorts activePorts = new ActiveAppiumPorts();

    public void makeList () throws IOException {
        Runtime rt = Runtime.getRuntime();
        Process p = rt.exec("adb devices");

        BufferedReader output = new BufferedReader(new InputStreamReader(p.getInputStream()));
        String line ="";

        while ((line = output.readLine()) != null) {
            if(StringUtils.isEmpty(line) || line.startsWith("List of devices attached")) {
                continue;
            }
            System.out.println(line);
            String [] serialNum = line.replace(" ", "").split("device");
            newDevice.add(Devices.ANDROID);
            newDevice.add(serialNum[0]);
            driver = setCaps((Devices)newDevice.get(0), (String)newDevice.get(1));
            activeList.add(driver);
            newDevice.clear();
        }

        //Implement the IOS Process
    }

    private AppiumDriver<MobileElement> setCaps(Devices d, String udid) throws MalformedURLException{
        AppiumDriver<MobileElement> driver = null;
        DesiredCapabilities cap = new DesiredCapabilities();

        String deviceName = "device"+ ++deviceNum;
        cap.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName);
        if(d == Devices.ANDROID) {
            cap.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
        }
        if(d == Devices.IOS) {
            cap.setCapability(MobileCapabilityType.PLATFORM_NAME, "IOS");
        }
        cap.setCapability("deviceId", udid);
        cap.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, "4000");
        cap.setCapability("noReset", false);

        cap = determinePlatType.determinePlat(cap);

        //Generate AppiumPorts Class and retrieve generated port number.
        activePorts.generateServer();
        System.out.println("Argument to driver object : " + activePorts.getAppiumURL() + "\n");
        switch (d) {
            case ANDROID: 
                driver = new AndroidDriver<MobileElement>(new URL(activePorts.getAppiumURL()), cap);
                break;
            case IOS:
                driver = new IOSDriver<MobileElement>(new URL(activePorts.getAppiumURL()), cap);
                break;
        }
        activePorts.increasePortNum();

        return driver;
    }

    public List<AppiumDriver<MobileElement>> getActiveList(){
        return activeList;
    }

    public void tearDown() {
        driver.quit();
    }

}


public class ActiveAppiumPorts {

    private int portNum = 5000;

    private String appiumPort = "500" + portNum;
    private String serverIp = "127.0.0.1";


    public String getAppiumURL() {
        String serverURL = "http://" + serverIp + ":" + portNum + "/wd/hub";
        System.out.println(serverURL);
        return serverURL;
    }

    public void increasePortNum() {
        portNum++;
        System.out.println("Next Port will be: " + portNum);
    }

    public String getPort() {
        return ""+portNum;
    }

    public void generateServer() {
        Runtime runtime = Runtime.getRuntime();
        try {
            runtime.exec("cmd.exe /c start cmd.exe /k \"appium -a " + serverIp + " -p " + portNum + 
                    "--session-override -dc \" {\"\"noReset\"\": \"\"false\"\"}\"\"");
            Thread.sleep(20000);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException r) {
            r.printStackTrace();
        }

    }

    public void stopServer() {
        Runtime runtime = Runtime.getRuntime();
        try {
            runtime.exec("taskkill /F /IM node.exe");
            runtime.exec("taskkill /F /IM cmd.exe");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

This is what the logs showed for the driver when I ran the test:

Calling for driver: Capabilities {browserName: Chrome, databaseEnabled: false, desired: {browserName: Chrome, deviceId: 192.168.174.102:5555 , deviceName: device1, newCommandTimeout: 4000, noReset: false, platformName: Android}, deviceId: 192.168.174.102:5555       , deviceManufacturer: Genymotion, deviceModel: Samsung Galaxy S7 - 6.0.0 -..., deviceName: 192.168.174.102:5555, deviceScreenSize: 1440x2560, deviceUDID: 192.168.174.102:5555, javascriptEnabled: true, locationContextEnabled: false, networkConnectionEnabled: true, newCommandTimeout: 4000, noReset: false, platform: LINUX, platformName: LINUX, platformVersion: 6.0, takesScreenshot: true, warnings: {}, webStorageEnabled: false}
Calling for driver: Capabilities {browserName: Chrome, databaseEnabled: false, desired: {browserName: Chrome, deviceId: 192.168.174.101:5555 , deviceName: device2, newCommandTimeout: 4000, noReset: false, platformName: Android}, deviceId: 192.168.174.101:5555       , deviceManufacturer: Genymotion, deviceModel: Samsung Galaxy S7 - 6.0.0 -..., deviceName: 192.168.174.102:5555, deviceScreenSize: 1440x2560, deviceUDID: 192.168.174.102:5555, javascriptEnabled: true, locationContextEnabled: false, networkConnectionEnabled: true, newCommandTimeout: 4000, noReset: false, platform: LINUX, platformName: LINUX, platformVersion: 6.0, takesScreenshot: true, warnings: {}, webStorageEnabled: false}
1

There are 1 best solutions below

0
On

If you are running test in parallel you need to provide the systemPort, more details here