/* * Copyright (C) 2013 Ustream Inc. * author chaotx <lombai.ferenc@ustream.tv> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ package com.robin.testcase; import java.io.File; import java.lang.reflect.Method; import org.safs.android.auto.lib.DUtilities; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Optional; import org.testng.annotations.Parameters; import com.android.ddmlib.IDevice; import com.jayway.android.robotium.remotecontrol.solo.Solo; import com.robin.BaseFunctionality; import com.robin.device.DevicePool; import com.robin.reporter.Reporter; import com.robin.reporter.logcat.LogCatHandler; import com.robin.reporter.logcat.LogCatTimeFormatDebugFilterBuilder; import com.robin.utilities.Utilities; import com.robin.utilities.android.AndroidUtilities; import com.robin.utilities.config.ConfigParams; /** * This classes purpose is to be the Base for all Test classes. Contains setup * and teardown phase. Also it opens and reads the local configuration file. */ public class BaseTest extends BaseFunctionality { private static final String APK_EXT = ".apk"; private static Object testRunnerApkLock = new Object(); private static Object messengerApkLock = new Object(); private static Object soloInitLock = new Object(); /** * Performing setup for tests. * 1. Rebuild / install apks * 2. Starting aut * The following @Optional parameters can be given (if not specified by the * testng.xml, then the Robin.properties are used): * @param method the current method that testng is running * @param autFilePath path to the application under test .apk file * @param deviceSelectorString regular exp. to match device names */ @Parameters({"aut", "device" }) @BeforeMethod(alwaysRun = true) public void setUp(final Method method, @Optional("") final String autFilePath, @Optional("") final String deviceSelectorString) { genericSetup(method, autFilePath, deviceSelectorString); TestCaseSetup testSetup = test().setup(); TestExecutionManager.registerMethod( method, testSetup.getDeviceSelectorRegexp()); createFilesForTest(testSetup.getAutApk()); startRobotium( testSetup.getAutApk(), testSetup.getDeviceSelectorRegexp()); } private void createFilesForTest(final File autFile) { final boolean isDebug = true; ApkResigner.resignAUT(autFile); File autAPK = ApkResigner.getReSignedAUT(autFile); File messengerApk = getDesiredMessengerApk(isDebug); if (!messengerApk.exists()) { synchronized (messengerApkLock) { Reporter.log( "Building messenger apk: '" + messengerApk.getAbsolutePath() + "'", true); final String messengerSourcePath = config().getValue(ConfigParams.MESSENGER_SOURCE); boolean success = DUtilities.buildAPK(messengerSourcePath, isDebug); Assert.assertTrue( success, "Could not build '" + messengerApk.getAbsolutePath() + "'."); Utilities.moveFileAndReplace( new File(messengerSourcePath + File.separator + "bin" + File.separator + messengerApk.getName()), messengerApk); } } // Robotium test runner apk build and install File desiredRunnerApk = getDesiredRunnerApkFile(autAPK); if (!desiredRunnerApk.exists()) { synchronized (testRunnerApkLock) { final String runnerSourcePath = config().getValue(ConfigParams.TEST_RUNNER_SOURCE_DIR); final String runnerSourceBinPath = runnerSourcePath + File.separator + "bin" + File.separator; final String runnerAPKPath = runnerSourceBinPath + "RobotiumTestRunner" + getFilenameDebugPostfix(isDebug) + APK_EXT; Reporter.log("Attempt to rebuild testrunner source at '" + runnerSourcePath + "'", true); final String runnerApk = DUtilities.rebuildTestRunnerApk( runnerSourcePath, autAPK.getAbsolutePath(), runnerAPKPath, DUtilities.TEST_RUNNER_INSTRUMENT, isDebug); Assert.assertNotNull(runnerApk, "Could not build " + runnerAPKPath); if (!isFilePresent(runnerApk)) { Reporter.log(runnerApk + "' runner apk not found -> force rebuilding!'", true); boolean rebildSuccess = DUtilities.buildAPK(runnerSourcePath, isDebug); Assert.assertTrue( rebildSuccess, "Could not rebuild test runner apk."); } Utilities.moveFileAndReplace( new File(runnerApk), desiredRunnerApk); } } } private String getFilenameDebugPostfix(final boolean isDebug) { return isDebug ? "-debug" : ""; } private File getDesiredMessengerApk(final boolean isDebug) { String messengerFileName = "SAFSTCPMessenger" + getFilenameDebugPostfix(isDebug) + APK_EXT; final String messengerAPKPath = config().getValue(ConfigParams.RESIGNED_AUT_PATH) + File.separator + messengerFileName; return new File(messengerAPKPath); } private void genericSetup(final Method method, final String authFilePath, final String deviceSelectorRegexp) { TestCaseSetup testCaseSetup = RobinTestCaseSetup.createFromParametersAndFallbackConfig( authFilePath, deviceSelectorRegexp, config()); test().addSetup(testCaseSetup); testCaseSetup.setTestClassName(getClass().getSimpleName()); testCaseSetup.setMethodName(method.getName()); checkAutFile(test().setup().getAutApk().getAbsolutePath()); checkDeviceAvailable(test().setup().getDeviceSelectorRegexp()); } private void checkDeviceAvailable(final String deviceName) { Assert.assertTrue(DevicePool.isDeviceExists(deviceName), deviceName + " is not in the Device Pool."); } private void checkAutFile(final String path) { Assert.assertTrue(new File(path).exists(), path + " is not found!"); } /** * Performing a tear down after each test * 1. Capturing the last screen for logging * 2. Stoping solo * @param method the method that was run before this teardown */ @AfterMethod(alwaysRun = true) public void tearDown(final Method method) { try { genericTearDown(); } finally { for (int i = 0; i <= test().setup().getLastSoloIndex(); i++) { DevicePool.unlockDevice(method, test() .setup() .getDevice(i)); } TestExecutionManager.unRegisterMethod(method); } } private void genericTearDown() { test().setup().getReporter().log( "Closing solo(s).", Reporter.CONFIG_EVENT_STYLE); for (int i = 0; i <= test().lastSoloIndex(); i++) { stopRobotium(i); } } /** * Performing setup and start of a new solo session on the current host * and port setup with current device type. * @return the index of the solo object stored in the current * TestCaseSetup */ protected int startNewDevice() { TestCaseSetup testSetup = test().setup(); startRobotium( testSetup.getAutApk(), testSetup.getDeviceSelectorRegexp()); return test().lastSoloIndex(); } private void logTestcaseSetup() { final String deviceReportString = log().valueStyleString(test().setup().getDeviceSelectorRegexp()); test().setup() .getReporter() .log( String.format( "Testcase setup for device %s.", deviceReportString), Reporter.CONFIG_EVENT_STYLE); } protected void startRobotium(final File autFile, final String deviceSelector) { IDevice device = DevicePool.getDeviceForExecution(deviceSelector); //TODO: Investigate how to handle device language change. //core().setDeviceLanguage(device, Language.en_US); test().setup().addDevice(device); int newSoloIndex = test().lastSoloIndex() + 1; addLogCat(newSoloIndex); logTestcaseSetup(); final boolean isDebugBuild = true; final boolean isRobotiumDebug = Boolean.parseBoolean(config() .getValue(ConfigParams.ROBOTIUM_LOGGING)); final String serial = device.getSerialNumber(); AndroidUtilities androidUtils = new AndroidUtilities(); File autAPK = ApkResigner.getReSignedAUT(autFile); androidUtils.installReplaceApk(autAPK, device); File messengerApk = getDesiredMessengerApk(isDebugBuild); androidUtils.installReplaceApk(messengerApk, device); // Robotium test runner apk build and install File desiredRunnerApk = getDesiredRunnerApkFile(autAPK); androidUtils.installReplaceApk(desiredRunnerApk, device); androidUtils.unlockDeviceScreen(serial); androidUtils.launchTestInstrumentation( DUtilities.TEST_RUNNER_INSTRUMENT, serial); test().setup().addSolo(new Solo()); Solo solo = test().solo(test().lastSoloIndex()); solo.setPortForwarding(true); try { synchronized (soloInitLock) { DUtilities.USE_DEVICE_SERIAL = "-s " + serial; solo.initialize(); } } catch (Exception e) { Assert.fail("Solo initialization failed.", e); } solo.turnProtocolDebug(isRobotiumDebug); solo.turnRunnerDebug(isRobotiumDebug); try { solo.startMainLauncher(); } catch (Exception e) { Assert.fail("Solo main launcher failed.", e); } String mainActivityUID = ""; try { mainActivityUID = solo.getCurrentActivity(); } catch (Exception e) { Assert.fail("Solo read current activity failed.", e); } test() .setup() .getReporter() .log( String.format( "Robin started on '%s' device. MainActivityUID: %s", log().valueStyleString( DevicePool.getDeviceDescriptionString(test() .setup() .getDevice(newSoloIndex))), log().valueStyleString(mainActivityUID)), Reporter.CONFIG_EVENT_STYLE); } private File getDesiredRunnerApkFile(final File autApk) { return new File(autApk.getPath().replace(APK_EXT, "") + "_" + "RobotiumTestRunner.apk"); } private boolean isFilePresent(final String pathToFile) { return new File(pathToFile).exists(); } protected void stopRobotium(final int... indexOfSolo) { Assert.assertTrue( test().solo(indexOfSolo).shutdownRemote(), "Fail to shutdown remote service."); test().solo(indexOfSolo).shutdown(); } private void addLogCat(final int... indexOfSolo) { constructDebugLevelLongFormatLogCat(indexOfSolo); } private void constructDebugLevelLongFormatLogCat(final int... indexOfSolo) { LogCatHandler.clearLogCat(indexOfSolo); LogCatHandler logCatHandler = new LogCatHandler(); logCatHandler .setLogCatBuilder(new LogCatTimeFormatDebugFilterBuilder()); logCatHandler.constructLogCat(); test().setup().addLogCatHandler(logCatHandler); } }