/*
* Funambol is a mobile platform developed by Funambol, Inc.
* Copyright (C) 2010 Funambol, Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*
* You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
* 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by Funambol" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by Funambol".
*/
package com.funambol.client.test.basic;
import java.util.Vector;
import com.funambol.client.test.ClientTestException;
import com.funambol.client.test.CommandRunner;
import com.funambol.client.test.IgnoreScriptException;
import com.funambol.client.test.Robot;
import com.funambol.util.Log;
/**
* Implementation of the CommandRunner class that define the full commands set
* available to the tester in order to create automatic test script.
*/
public abstract class BasicCommandRunner extends CommandRunner implements BasicUserCommands {
private static final String TAG_LOG = "BasicCommandRunner";
// Key events used by KeyPress command
public static final String DOWN_KEY_NAME = "KeyDown";
public static final String UP_KEY_NAME = "KeyUp";
public static final String LEFT_KEY_NAME = "KeyLeft";
public static final String RIGHT_KEY_NAME = "KeyRight";
public static final String FIRE_KEY_NAME = "KeyFire";
public static final String MENU_KEY_NAME = "KeyMenu";
public static final String BACK_KEY_NAME = "KeyBack";
public static final String DEL_KEY_NAME = "KeyDelete";
public String currentTestName;
private String globalFilterByName = null;
private String globalFilterBySourceType = null;
private String globalFilterByDirection = null;
private String globalFilterByLocality = null;
/**
* Constructor
* @param robot the BasicRobot object that runs the commands on the given
* client implementation. This robot should be defined into the high level
* as it is architecture specific.
*/
public BasicCommandRunner( BasicRobot robot, String filterByName, String filterBySourceType,
String filterByDirection, String filterByLocality)
{
super(robot);
globalFilterByName = filterByName;
globalFilterBySourceType = filterBySourceType;
globalFilterByDirection = filterByDirection;
globalFilterByLocality = filterByLocality;
if (globalFilterByName == null) {
globalFilterByName = "*";
}
if (globalFilterBySourceType == null) {
globalFilterBySourceType = "*";
}
if (globalFilterByDirection == null) {
globalFilterByDirection = "*";
}
if (globalFilterByLocality == null) {
globalFilterByLocality = "*";
}
}
/**
* Core method of this class. It parses the command line command and
* arguments to realize the actions defined in the high level provided
* script
* @param command the String formatted command to be parsed and given to the
* robot that is defined to execute it
* @param pars the command string formatted arguments
* @return boolean true if the command is valid, false otherwise
* @throws Throwable if anything goes wrong when the command is run
*/
public boolean runCommand(String command, Vector pars) throws Throwable {
if (WAIT_COMMAND.equals(command)) {
wait(command, pars);
} else if (BEGIN_TEST_COMMAND.equals(command)) {
beginTest(command, pars);
} else if (END_TEST_COMMAND.equals(command)) {
endTest(command, pars);
} else if (KEY_PRESS_COMMAND.equals(command)) {
keyPress(command, pars);
} else if (REFRESH_ALL_COMMAND.equals(command)) {
refreshAll(command, pars);
} else if (REFRESH_SOURCE_COMMAND.equals(command)) {
refreshSource(command, pars);
} else if (WRITE_STRING_COMMAND.equals(command)) {
writeString(command, pars);
} else if (WAIT_FOR_SYNC_TO_COMPLETE_COMMAND.equals(command)) {
waitForSyncToComplete(command, pars);
} else if (CHECK_EXCHANGED_DATA_COMMAND.equals(command)) {
checkExchangedData(command, pars);
} else if (CHECK_SYNC_ERRORS_COMMAND.equals(command)) {
checkSyncErrors(command, pars);
} else if (CHECK_SYNC_STATUS_CODE_COMMAND.equals(command)) {
checkSyncStatusCode(command, pars);
} else if (CHECK_RESUMED_DATA_COMMAND.equals(command)) {
checkResumedData(command, pars);
} else if (CHECK_REQUESTED_SYNC_MODE_COMMAND.equals(command)) {
checkLastSyncRequestedSyncMode(command, pars);
} else if (CHECK_ALERTED_SYNC_MODE_COMMAND.equals(command)) {
checkLastSyncAlertedSyncMode(command, pars);
} else if (CHECK_REMOTE_URI_COMMAND.equals(command)) {
checkLastSyncRemoteUri(command, pars);
} else if (CHECK_LAST_NOTIFICATION.equals(command)) {
checkLastNotification(command, pars);
} else if (FORCE_SLOW_SYNC_COMMAND.equals(command)) {
resetSourceAnchor(command, pars);
} else if (START_MAIN_APP_COMMAND.equals(command)) {
startMainApp(command, pars);
} else if (CLOSE_MAIN_APP_COMMAND.equals(command)) {
closeMainApp(command, pars);
} else if (INTERRUPT_SYNC_AFTER_PHASE_COMMAND.equals(command)) {
interruptSyncAfterPhase(command, pars);
} else if (SET_DEVICE_DATE.equals(command)) {
//setDeviceTime(command, pars);
} else if (RESET_FIRST_RUN_TIMESTAMP.equals(command)) {
resetFirstRunTimestamp(command, pars);
} else if (SET_VARIABLE.equals(command)) {
setVariable(command, pars);
} else {
return false;
}
return true;
}
/**
* Return the name of the test that is executing.
* @return String the String formatted test name
*/
protected String getCurrentTestName() {
return currentTestName;
}
/**
* Accessor method
* @return BasicRobot the BasicRobot instance reference.
*/
protected BasicRobot getBasicRobot() {
return (BasicRobot)robot;
}
/**
* The automatic test common method to start the main application.
* See implementation for further details.
* @param command the String formatted command to be executed
* @param args the command related and String formatted arguments
* @throws Throwable if anything goes wrong when the application starts.
*/
protected void startMainApp(String command, Vector args) throws Throwable {
getBasicRobot().startMainApp();
}
/**
* The automatic test common method to close the main application.
* See implementation for further details.
* @param command the String formatted command to be executed
* @param args the command related and String formatted arguments
* @throws Throwable if anything goes wrong when the application starts.
*/
protected void closeMainApp(String command, Vector args) throws Throwable {
getBasicRobot().closeMainApp();
}
/**
* Uses the SyncMonitor object to wait that for specific sync action and
* validate it as completed after a given amount of time. Use the BasicRobot
* to perform the sync action.
* @param command the String representation of the command
* @param args the command's related and String formatted arguments.
* In particular the script commad must contain the sync startup time and
* the maximum time for the sync to be completed.
* @throws Throwable if anything went wrong during the sync
*/
protected void waitForSyncToComplete(String command, Vector args) throws Throwable {
String minStart = getParameter(args, 0);
String maxWait = getParameter(args, 1);
checkArgument(minStart, "Missing min start in " + command);
checkArgument(maxWait, "Missing max wait in " + command);
int min = Integer.parseInt(minStart)*1000;
int max = Integer.parseInt(maxWait)*1000;
getBasicRobot().waitForSyncToComplete(min, max);
}
/**
* a command that interrupts the current sync action
* @param command the command related to the cancel sync action
* @param args the command related and String formatted arguments
* @throws Throwable if an error occurred
*/
private void interruptSyncAfterPhase(String command, Vector args) throws Throwable {
String phase = getParameter(args, 0);
String num = getParameter(args, 1);
String reason = getParameter(args, 2);
checkArgument(phase, "Missing phase name in " + command);
checkArgument(num, "Missing num in " + command);
checkArgument(reason, "Missing reason in " + command);
int n = Integer.parseInt(num);
getBasicRobot().interruptSyncAfterPhase(phase, n, reason);
}
/**
* Begin test declaration command. Useful to address the current test name
* @param command the String fomatted command to declare that a test was
* begun
* @param args the command String formatted parameter. In particular the
* tester must provide the test name when declaring the begin of a test
* @throws Throwable if an error occurred
*/
private void beginTest(String command, Vector args) throws Throwable {
currentTestName = getParameter(args, 0);
if (currentTestName == null) {
Log.error(TAG_LOG, "Syntax error in script, missing test name in begin "
+ BEGIN_TEST_COMMAND);
} else {
if (Log.isLoggable(Log.INFO)) {
Log.info(TAG_LOG, "Starting test " + currentTestName);
}
}
// Grab the test filters if they are set
Vector testFilters = new Vector();
String filter;
int i = 1;
do {
filter = getParameter(args, i++);
if (filter != null) {
testFilters.addElement(filter);
}
} while(filter != null);
// Now check if this test shall be executed
// The filters are defined this way:
// 1) filter by name
// 2) filter by source type
// 3) filter by direction
// 4) filter by locality type
if (testFilters.size() == 4) {
String byName = ((String)testFilters.elementAt(0)).trim();
String bySourceType = ((String)testFilters.elementAt(1)).trim();
String byDirection = ((String)testFilters.elementAt(2)).trim();
String byLocality = ((String)testFilters.elementAt(3)).trim();
if (!globalFilterByName.equals("*") && !byName.equals("*")) {
if (!globalFilterByName.equals(byName)) {
if (Log.isLoggable(Log.INFO)) {
Log.info(TAG_LOG, "Ignoring script " + currentTestName);
Log.info(TAG_LOG, "because byName is " + byName + " and globalFilterByName is " + globalFilterByName);
}
throw new IgnoreScriptException();
}
}
if (!globalFilterBySourceType.equals("*") && !bySourceType.equals("*")) {
if (!globalFilterBySourceType.equals(bySourceType)) {
if (Log.isLoggable(Log.INFO)) {
Log.info(TAG_LOG, "Ignoring script " + currentTestName);
Log.info(TAG_LOG, "because bySourceType is " + bySourceType + " and globalFilterBySourceType is " + globalFilterBySourceType);
}
throw new IgnoreScriptException();
}
}
if (!globalFilterByDirection.equals("*") && !byDirection.equals("*")) {
if (!globalFilterByDirection.equals(byDirection)) {
if (Log.isLoggable(Log.INFO)) {
Log.info(TAG_LOG, "Ignoring script " + currentTestName);
Log.info(TAG_LOG, "because byDirection is " + byDirection + " and globalFilterByDirection is " + globalFilterByDirection);
}
throw new IgnoreScriptException();
}
}
if (!globalFilterByLocality.equals("*") && !byLocality.equals("*")) {
if (!globalFilterByLocality.equals(byLocality)) {
if (Log.isLoggable(Log.INFO)) {
Log.info(TAG_LOG, "Ignoring script " + currentTestName);
Log.info(TAG_LOG, "because byLocality is " + byLocality + " and globalFilterByLocality is " + globalFilterByLocality);
}
throw new IgnoreScriptException();
}
}
} else {
if (Log.isLoggable(Log.INFO)) {
Log.info(TAG_LOG, "Test has invalid filters, execute it " + currentTestName);
}
}
}
/**
* End test declaration command.
* @param command the end test related Stirng formatted representation
* @param args the command's related String arguments. Not required for this
* command
* @throws Throwable if an error occurred
*/
protected void endTest(String command, Vector args) throws Throwable {
currentTestName = null;
}
/**
* checks the final data exchanged after a sync
* @param command the check data command related Stirng formatted
* representation
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void checkExchangedData(String command, Vector args) throws Throwable {
String source = getParameter(args, 0);
String sentAdd = getParameter(args, 1);
String sentReplace = getParameter(args, 2);
String sentDelete = getParameter(args, 3);
String receivedAdd = getParameter(args, 4);
String receivedReplace = getParameter(args, 5);
String receivedDelete = getParameter(args, 6);
checkArgument(source, "Missing source name in " + command);
checkArgument(sentAdd, "Missing sentAdd in " + command);
checkArgument(sentReplace, "Missing sentReplace in " + command);
checkArgument(sentDelete, "Missing sentDelete in " + command);
checkArgument(receivedAdd, "Missing receivedAdd in " + command);
checkArgument(receivedReplace, "Missing receivedReplace in " + command);
checkArgument(receivedDelete, "Missing receivedDelete in " + command);
getBasicRobot().checkLastSyncExchangedData(source,
Integer.parseInt(sentAdd),
Integer.parseInt(sentReplace),
Integer.parseInt(sentDelete),
Integer.parseInt(receivedAdd),
Integer.parseInt(receivedReplace),
Integer.parseInt(receivedDelete));
}
/**
* checks the final data exchanged after a sync
* @param command the check data command related Stirng formatted
* representation
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void checkSyncErrors(String command, Vector args) throws Throwable {
String source = getParameter(args, 0);
String sendingErrors = getParameter(args, 1);
String receivingErrors = getParameter(args, 2);
checkArgument(source, "Missing source name in " + command);
checkArgument(sendingErrors, "Missing sendingErrors in " + command);
checkArgument(receivingErrors, "Missing receivingErrors in " + command);
getBasicRobot().checkLastSyncErrors(source,
Integer.parseInt(sendingErrors),
Integer.parseInt(receivingErrors));
}
/**
* Checks the final status code after a sync
* @param command the check data command related String formatted
* representation
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void checkSyncStatusCode(String command, Vector args) throws Throwable {
String source = getParameter(args, 0);
String code = getParameter(args, 1);
checkArgument(source, "Missing source name in " + command);
checkArgument(code, "Missing code in " + command);
getBasicRobot().checkLastSyncStatusCode(source, Integer.parseInt(code));
}
/**
* Checks the last notification's content
*
* @param command the check data command related String formatted
* representation
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void checkLastNotification(String command, Vector args) throws Throwable {
String id = getParameter(args, 0);
if (id == null) {
id = "-1";
}
String severity = getParameter(args, 1);
if (severity == null) {
severity = "-1";
}
String ticker = getParameter(args, 2);
if (ticker.length() == 0) {
ticker = null;
}
String title = getParameter(args, 3);
if (title.length() == 0) {
title = null;
}
String message = getParameter(args, 4);
if (message.length() == 0) {
message = null;
}
getBasicRobot().checkLastNotification(
Integer.parseInt(id),
Integer.parseInt(severity),
ticker,
title,
message);
}
/**
* Checks the resumed data of the last sync.
* @param command the check data command related Stirng formatted
* representation
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void checkResumedData(String command, Vector args) throws Throwable {
String source = getParameter(args, 0);
String sentResumed = getParameter(args, 1);
String receivedResumed = getParameter(args, 2);
checkArgument(source, "Missing source name in " + command);
checkArgument(sentResumed, "Missing sentResumed in " + command);
checkArgument(receivedResumed, "Missing receivedResumed in " + command);
getBasicRobot().checkLastSyncResumedData(source,
Integer.parseInt(sentResumed),
Integer.parseInt(receivedResumed));
}
/**
* Check that the latest sync request was requested with a specific sync
* mode
* @param command the check sync mode command related String formatted
* representation
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void checkLastSyncRequestedSyncMode(String command, Vector args) throws Throwable {
String source = getParameter(args, 0);
String mode = getParameter(args, 1);
checkArgument(source, "Missing source in " + command);
checkArgument(mode, "Missing mode in " + command);
int modeValue = Integer.parseInt(mode);
getBasicRobot().checkLastSyncRequestedSyncMode(source, modeValue);
}
/**
* Check that the latest sync request was requested with a specific sync
* alert mode
* @param command the check sync alert command related String formatted
* representation
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void checkLastSyncAlertedSyncMode(String command, Vector args) throws Throwable {
String source = getParameter(args, 0);
String mode = getParameter(args, 1);
checkArgument(source, "Missing source in " + command);
checkArgument(mode, "Missing mode in " + command);
int modeValue = Integer.parseInt(mode);
getBasicRobot().checkLastSyncAlertedSyncMode(source, modeValue);
}
/**
* Check that the latest sync request was requested with a specific sync
* uri
* @param command the check sync uri command related String formatted
* representation
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void checkLastSyncRemoteUri(String command, Vector args) throws Throwable {
String source = getParameter(args, 0);
String remoteUri = getParameter(args, 1);
checkArgument(source, "Missing source in " + command);
checkArgument(remoteUri, "Missing remoteUri in " + command);
getBasicRobot().checkLastSyncRemoteUri(source, remoteUri);
}
/**
* Command to reset a specific source anchors in order to provoke a slow
* sync next time
* @param command the String formatted command to break the anchors
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void resetSourceAnchor(String command, Vector args) throws Throwable {
String source = getParameter(args, 0);
checkArgument(source, "Missing source in " + command);
getBasicRobot().resetSourceAnchor(source);
}
/**
* Command to simulate a keypress
* @param command the String formatted command to simualte the key press
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void keyPress(String command, Vector args) throws Throwable {
String keyName = getParameter(args, 0);
String count = getParameter(args, 1);
checkArgument(keyName, "Missing key name in " + command);
int pressCount = 1;
if(count != null) {
try {
pressCount = Integer.parseInt(count);
} catch(NumberFormatException ex) {
throw new ClientTestException("Invalid count: " + count
+ " in command: " + command);
}
}
getBasicRobot().keyPress(keyName, pressCount);
}
/**
* Command to start a refresh for all the sources.
*
* @param command the String formatted command
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void refreshAll(String command, Vector args) throws Throwable {
getBasicRobot().refreshAll();
}
/**
* Command to start a refresh for the given source.
*
* @param command the String formatted command
* @param args the command's related String arguments.
* @throws Throwable if an error occurred
*/
private void refreshSource(String command, Vector args) throws Throwable {
String source = getParameter(args, 0);
checkArgument(source, "Missing source in " + command);
getBasicRobot().refreshSource(source);
}
/**
* Command to simulate a input text action into an editable text field
* @param command the String formatted command to edit text
* @param args the command's arguments string formatted
* @throws Throwable if anything goes wrong
*/
private void writeString(String command, Vector args) throws Throwable {
String text = getParameter(args, 0);
checkArgument(text, "Missing string in " + command);
getBasicRobot().writeString(text);
}
private void setVariable(String command, Vector args) throws Throwable {
String variable = getParameter(args, 0);
String value = getParameter(args, 1);
getBasicRobot().setVariable(variable, value);
}
/**
* A generic wait request command. Useful if the tester must wait for a
* limited amount of time before continuing the test execution.
* @param command the String formatted command to tell tes runner to wait
* @param args the command's arguments string formatted
* @throws Throwable if anything goes wrong
*/
private void wait(String command, Vector args) throws Throwable {
String delay = getParameter(args, 0);
checkArgument(delay, "Missing delay in " + command);
int d = Integer.parseInt(delay);
if(d > 0) {
Robot.waitDelay(d);
} else {
// Wait forever
Robot.waitDelay(10000);
}
}
private void resetFirstRunTimestamp(String command, Vector args) throws Throwable {
getBasicRobot().resetFirstRunTimestamp();
}
}