org.jboss.arquillian.android.drone.impl.SelendroidHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.arquillian.android.drone.impl.SelendroidHelper.java

Source

/*
 * JBoss, Home of Professional Open Source
 * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual
 * contributors by the @authors tag. See the copyright.txt in the
 * distribution for a full listing of individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * 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 org.jboss.arquillian.android.drone.impl;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.jboss.arquillian.android.drone.configuration.Command;
import org.jboss.arquillian.android.drone.configuration.Validate;
import org.jboss.arquillian.container.android.api.AndroidDevice;
import org.jboss.arquillian.container.android.api.AndroidDeviceOutputReciever;
import org.jboss.arquillian.container.android.api.AndroidExecutionException;

/**
 * Provides various helper methods for Selendroid server.
 *
 * @author <a href="mailto:smikloso@redhat.com">Stefan Miklosovic</a>
 *
 */
public class SelendroidHelper {

    private static final Logger logger = Logger.getLogger(SelendroidHelper.class.getName());

    private AndroidDevice androidDevice;

    private File serverLogFile;

    private static final String TOP_CMD = "top -n 1";

    private static final int SOCKET_TIME_OUT_SECONDS = 10;

    private static final int CONNECTION_TIME_OUT_SECONDS = 10;

    private static final int NUM_CONNECTION_RETIRES = 5;

    public SelendroidHelper(AndroidDevice androidDevice, File serverLogFile) {
        Validate.notNull(androidDevice, "Android Device for SelendroidHelper can't be null object!");
        Validate.notNull(serverLogFile, "Server log file for SelendroidHelper can't be null object!");

        this.androidDevice = androidDevice;
        this.serverLogFile = serverLogFile;
    }

    /**
     * Waits for Selendroid start. After installation and execution of instrumentation command, we repeatedly send http request
     * to status page to get response code of 200 - server is up and running and we can proceed safely.
     */
    public void waitForServerHTTPStart() {
        HttpClient httpClient = new DefaultHttpClient();

        httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, SOCKET_TIME_OUT_SECONDS * 1000);
        httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,
                CONNECTION_TIME_OUT_SECONDS * 1000);

        HttpGet httpGet = new HttpGet(getSelendroidStatusURI());

        boolean connectionSuccess = false;

        for (int i = NUM_CONNECTION_RETIRES; i > 0; i--) {
            try {
                HttpResponse response = httpClient.execute(httpGet);
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == 200) {
                    connectionSuccess = true;
                    break;
                }
                logger.log(Level.INFO,
                        "Response was not 200, response was: " + statusCode + ". Repeating " + i + " times.");
            } catch (ClientProtocolException e) {
                logger.log(Level.WARNING, e.getMessage());
            } catch (IOException e) {
                logger.log(Level.WARNING, e.getMessage());
            }
        }

        httpClient.getConnectionManager().shutdown();

        if (!connectionSuccess) {
            throw new AndroidExecutionException("Unable to get successful connection from Selendroid http server.");
        }
    }

    /**
     * Stop instrumentation.
     *
     * @param stopApplicationInstrumentationCommand
     */
    public void stopInstrumentation(Command stopApplicationInstrumentationCommand) {
        androidDevice.executeShellCommand(stopApplicationInstrumentationCommand.getAsString());
    }

    /**
     * Uninstalls application under test
     *
     * @param applicationUninstallCommand
     */
    public void uninstallApplicationUnderTest(Command applicationUninstallCommand) {
        androidDevice.executeShellCommand(applicationUninstallCommand.getAsString());
    }

    /**
     * Uninstalls modified Selendroid server
     *
     * @param uninstallSelendroidCommand
     */
    public void uninstallSelendroid(Command uninstallSelendroidCommand) {
        androidDevice.executeShellCommand(uninstallSelendroidCommand.getAsString());
    }

    /**
     * Starts instrumentation of underlying application under test.
     *
     * @param startApplicationInstrumentationCommand
     * @param applicationBasePackage
     */
    public void startInstrumentation(Command startApplicationInstrumentationCommand,
            String applicationBasePackage) {
        try {
            ServerMonkey monkey = new ServerMonkey(serverLogFile, applicationBasePackage);
            androidDevice.executeShellCommand(startApplicationInstrumentationCommand.getAsString(), monkey);
            waitUntilServerIsStarted(androidDevice, monkey);
        } catch (IOException ex) {
            throw new AndroidExecutionException(ex.getMessage());
        }
    }

    /**
     * @author <a href="mailto:smikloso@redhat.com">Stefan Miklosovic</a>
     *
     */
    private static class ServerMonkey implements AndroidDeviceOutputReciever {
        private static final Logger logger = Logger.getLogger(ServerMonkey.class.getName());

        private final Writer output;

        private boolean active = false;

        private String waitForString;

        public ServerMonkey(File output, String waitForString) throws IOException {
            Validate.notNull(output, "File to write logs for ServerMonkey can't be null!");
            Validate.notNullOrEmpty(waitForString, "String to wait for in ServerMonkey can't be null nor empty!");
            this.output = new FileWriter(output);
            this.waitForString = waitForString;
        }

        @Override
        public void processNewLines(String[] lines) {
            for (String line : lines) {
                logger.finest(line);
                try {
                    output.append(line).append("\n").flush();
                } catch (IOException e) {
                    // ignore output
                }
                if (line.contains(waitForString)) {
                    this.active = true;
                    return;
                }
            }
        }

        @Override
        public boolean isCancelled() {
            return false;
        }

        public boolean isActive() {
            return active;
        }
    }

    /**
     * Waits until Selendroid is started by looking at the output of Android "top" command.
     *
     * @param device
     * @param monkey
     * @throws IOException
     */
    private void waitUntilServerIsStarted(AndroidDevice device, ServerMonkey monkey) throws IOException {

        logger.info("Starting Selendroid instrumentation on Android device.");
        for (int i = 0; i < 5; i++) {
            device.executeShellCommand(TOP_CMD, monkey);
            if (monkey.isActive()) {
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        throw new AndroidExecutionException("Unable to start Selendroid instrumentation on Android device.");
    }

    /**
     *
     * @return Selendroid URL where status code is got from.
     */
    private URI getSelendroidStatusURI() {
        try {
            return new URI("http://localhost:" + androidDevice.getDroneHostPort() + "/wd/hub/status");
        } catch (URISyntaxException e) {
            return null;
        }
    }
}