Mobile phone Automation test: Appium Source analysis of Bootstrap one

Source: Internet
Author: User

Objective:

Poptest is the only training institute for developing Test and development engineers in China, aiming at the ability of the trainees to be competent in automated testing, performance testing and testing tools development. Poptest launches the mobile phone automation Test course, explains the Appuim practical application, the training entire use commercial project, we may add QQ group to Exchange: 195983133

The biggest benefit of open source projects is that you can get the source code, we can modify the code on the basis of the source code, compile, develop the projects that meet our requirements. If you want to be able to modify the source code, first read the code to understand the code. Some domestic companies in the open-source software based on a two-time development package, and then play their own logo, in sales to customers. Some internet companies also use a lot of open source code in their product code. So let's see what the Appium code looks like.

Get ready:

Appium is an open source project, can get Source: Appium-master, in Eclipse with MAVEN import 2 items: Bootstrap and Sauce_appium_junit.
Sauce_appium_junit is a collection of test cases that help you learn if you have time to read them well. Bootstrap is the server that Appium runs on the phone side.

Begin

I. Bootstrap role
The bootstrap is in the form of a jar package, which is actually a case packet written by Uiautomator, which is executed on the phone by a PC-side command.

Two. Bootstrap Source analysis
Program entry is bootstrap class
Below is the Bootstrap.java source code

Package Io.appium.android.bootstrap;import Io.appium.android.bootstrap.exceptions.socketserverexception;import com.android.uiautomator.testrunner.uiautomatortestcase;/** * The Bootstrap class runs the socket server. Uiautomator developed scripts can be launched directly on the PC side */public class Bootstrap extends Uiautomatortestcase {public  void Testrunserver () {    socketserver server;    try {      //start the socket server and listen on port 4724.      Server = new Socketserver (4724);      Server.listenforever ();    } catch (Final socketserverexception e) {      logger.error (E.geterror ());      System.exit (1);}}  

This class is a boot thread that listens on port 4724 and communicates with Appium through that port. This class inherits from Uiautomatortestcase. The PC side sends instructions via ADB adb shell uiautomator runtest appiumbootstrap.jar-c, io.appium.android.bootstrap.Bootstrap execution.

Then execute the Server.listenforever () method
Please see Socketserver.java source code

/** * listens on the socket for data, and calls {@link #handleClientData ()} when * it ' s available. * * @throws socketserverexception */public void Listenforever () throws Socketserverexception {Logger.debug ("App    Ium Socket Server ready ");    Read Strings.json file Data Updatestrings.loadstringsjson ();    Register two types of listeners: and and Crash Dismisscrashalerts (); Final TimerTask updatewatchers = new TimerTask () {@Override public void run () {try {//check whether the system        There is abnormal watchers.check ();    } catch (Final Exception e) {}}};    Timer, starts 0.1 seconds, executes every 0.1 seconds.    Timer.scheduleatfixedrate (updatewatchers, 100, 100);      try {client = server.accept ();      Logger.debug ("Client connected");      in = new BufferedReader (New InputStreamReader (Client.getinputstream (), "UTF-8"));      out = new BufferedWriter (New OutputStreamWriter (Client.getoutputstream (), "UTF-8")); while (keeplistening) {//Get client data HandlecLientdata ();      } in.close ();      Out.close ();      Client.close ();    Logger.debug ("Closed Client Connection");    } catch (Final IOException e) {throw new Socketserverexception ("Error when client is trying to connect"); }  }
Call Updatestrings.loadstringsjson () in the method, and the code is as follows (updatestrings):
/** * Strings.json files are saved in the strings.xml of the APK, parsed and push to the device side by the Appium server before bootstrap boot * * @return */public static Boo    Lean Loadstringsjson () {logger.debug ("Loading json ...");      try {final String FilePath = "/data/local/tmp/strings.json";      Final file Jsonfile = new file (FilePath);      JSON will isn't exist for APKs that is only on device//Your case must specify the path of the APK, if you start an application that is already on the device and there is no app path in cases, the JSON file does not exist      Because the node server can ' t extract the JSON from the APK.      if (!jsonfile.exists ()) {return false;      } final DataInputStream datainput = new DataInputStream (new FileInputStream (Jsonfile));      Final byte[] Jsonbytes = new byte[(int) jsonfile.length ()];      Datainput.readfully (jsonbytes);      This closes FileInputStream datainput.close ();      Final String jsonstring = new String (jsonbytes, "UTF-8");      Assign the read information to a property in the Find class to do a post-use find.apkstrings = new Jsonobject (jsonstring); Logger.debug ("JSON loading compLete. ");      catch (Final Exception e) {logger.error ("Error loading JSON:" + e.getmessage ());    return false;  } return true; }
After execution, return to the Listenforever () of the ServerSocket class and execute to Dismisscrashalerts (); This method registers the listener to see if there is a popup or an and and crash exception.
public void Dismisscrashalerts () {    try {      new uiwatchers (). Registeranrandcrashwatchers ();      Logger.debug ("Registered crash watchers.");    catch (Final Exception e) {      logger.debug ("Unable to register crash watchers.");    }  }
The Listenforever () method executes to the registered heartbeat program, starting every 0.1 seconds with the listener registered above to check the system for any exceptions.
Final TimerTask updatewatchers = new TimerTask () {      @Override public      void Run () {        try {          //Check the system for exceptions 
   
    watchers.check ();        } catch (Final Exception e) {        }}}    ;    Timer, starts 0.1 seconds, executes every 0.1 seconds.    timer.scheduleatfixedrate (updatewatchers, 100, 100);
    
The data channel is then started, accepting the data sent by the client and returning the results to the client.
 client = Server.accept ();      Logger.debug ("Client connected");      in = new BufferedReader (New InputStreamReader (Client.getinputstream (), "UTF-8")); out = new BufferedWriter (New OutputStreamWriter (Client.getoutputstream (), "UTF-8")); The 
Next is the Handleclientdata () method, and the effect of this listenforever () is complete. The Handleclientdata () method code is as follows.

/** * When data was available on the socket, this method is called to run the * command or throw an error if it can ' t.      * * @throws socketserverexception */private void Handleclientdata () throws Socketserverexception {try { Input.setlength (0);      Clear String Res;      int A;      (char)-1 is not equal to-1.      Checked to ensure the read call doesn ' t block.      while ((A = In.read ())! =-1 && in.ready ()) {input.append ((char) a);      } final String inputstring = input.tostring ();      Logger.debug ("Got Data from client:" + inputstring);        try {final Androidcommand cmd = GetCommand (inputstring);        Logger.debug ("Got command of type" + Cmd.commandtype (). toString ());        res = RunCommand (cmd);      Logger.debug ("Returning Result:" + res);            } catch (Final Commandtypeexception e) {res = new Androidcommandresult (Wdstatus.unknown_error, E.getmessage ())      . toString (); } catch (FinAl jsonexception e) {res = new Androidcommandresult (Wdstatus.unknown_error, "ERROR running and parsing      Command "). ToString ();      } out.write (res);    Out.flush (); } catch (Final IOException e) {throw new Socketserverexception ("Error Processing data to/from socket (" + E    . toString () + ")"); }  }
The method receives the client data, obtains the Androidcommand object with the GetCommand () method, and then executes the RunCommand () method to obtain the result.
/**
 * When {@link #handleClientData ()} have valid data, this method delegates the * command.  * * * @param cmd * androidcommand * @return Result */Private String RunCommand (final androidcommand cmd)    {Androidcommandresult res;      if (cmd.commandtype () = = Androidcommandtype.shutdown) {keeplistening = false;    res = new Androidcommandresult (wdstatus.success, "OK, shutting down");      } else if (cmd.commandtype () = = Androidcommandtype.action) {try {res = executor.execute (cmd);      } catch (Final Exception e) {res = new Androidcommandresult (Wdstatus.unknown_error, E.getmessage ()); }} else {//This code should never is executed, here for future-proofing res = new Androidcommandresult (WDS Tatus.    Unknown_error, "UNKNOWN command type, could not execute!");  } return res.tostring (); }}

This method determines which type of command data it belongs to, commands the command and action commands, and has a lot of attention to action. The focus depends on the Androidcommandexecutor.execute () method in the first else if. Androidcommandexecutor.java

/**   * Gets The handler out of the map, and executes the command.   *   * @param command   * The          {@link Androidcommand}   * @return {@link Androidcommandresult}  */ Public Androidcommandresult Execute (final androidcommand command) {    try {      logger.debug ("Got Command action:" + Command.action ());      if (Map.containskey (Command.action ())) {        return Map.get (Command.action ()). Execute (command);      } else {        return new Androidcommandresult (Wdstatus.unknown_command,            "UNKNOWN COMMAND:" + command.action ());      }    } catch (Final Jsonexception e) {      logger.error ("Could not decode Action/params of command");      return new Androidcommandresult (Wdstatus.json_decoder_error,          "Could not decode action/params of command, please Check format! ");    }  }
After the command is executed, all of your actions are started.
 if (Map.containskey (Command.action ())) {return Map.get (Command.action ()). Execute (command); } else {return new Androidcommandresult (Wdstatus.unknown_command, "UNKNOWN COMMAND:" + command.action (      )); 
Note Map.get (Command.action ()). Execute (Command). Locate the code for the map in this class:
static {Map.put ("Waitforidle", New Waitforidle ());    Map.put ("Clear", new Clear ());    Map.put ("orientation", new orientation ());    Map.put ("Swipe", new swipe ());    Map.put ("Flick", New Flick ());    Map.put ("Drag", new drag ());    Map.put ("Pinch", New Pinch ());    Map.put ("Click", New Click ());    Map.put ("Touchlongclick", New Touchlongclick ());    Map.put ("TouchDown", New TouchDown ());    Map.put ("TouchUp", New TouchUp ());    Map.put ("TouchMove", New TouchMove ());    Map.put ("GetText", New GetText ());    Map.put ("SetText", New SetText ());    Map.put ("GetName", New GetName ());    Map.put ("GetAttribute", New GetAttribute ());    Map.put ("Getdevicesize", New Getdevicesize ());    Map.put ("ScrollTo", New ScrollTo ());    Map.put ("Find", new Find ());    Map.put ("GetLocation", New GetLocation ());    Map.put ("GetSize", New GetSize ());    Map.put ("Wake", New Wake ());    Map.put ("Pressback", New Pressback ());    Map.put ("Dumpwindowhierarchy", New Dumpwindowhierarchy ()); Map.put ("PresskeYcode ", New Presskeycode ());    Map.put ("Longpresskeycode", New Longpresskeycode ());    Map.put ("Takescreenshot", New Takescreenshot ());    Map.put ("Updatestrings", New Updatestrings ());    Map.put ("Getdatadir", New Getdatadir ());    Map.put ("Performmultipointergesture", New Multipointergesture ());  Map.put ("Opennotification", New Opennotification ()); }
Map is a map of the <String,CommandHandler> form. The value values correspond to objects, which inherit from CommandHandler and have the Execute method, which is to invoke different objects depending on the command to get the result of the relevant code. From the definition of map, we can see that the command of Appium Mobile is very rich, we can add the operation code we need here,

Let's take a look at the Click method, under study (Click.java).

Package Io.appium.android.bootstrap.handler;import Com.android.uiautomator.core.uidevice;import Com.android.uiautomator.core.uiobjectnotfoundexception;import Io.appium.android.bootstrap.*;import Org.json.jsonexception;import Java.util.arraylist;import java.util.hashtable;/** * This handler are used to click Elements in the Android UI. * * Based on the element Id, click the element.  * */public class Click extends CommandHandler {/* * @param command the {@link Androidcommand} * * @return {@link Androidcommandresult} * * @throws jsonexception * * @see io.appium.android.bootstrap.commandhandler#execute (IO. Appium.android. * Bootstrap. Androidcommand) */@Override public Androidcommandresult Execute (Final Androidcommand command) throws Jsonexcepti        on {if (Command.iselementcommand ()) {try {final androidelement el = command.getelement ();        El.click ();      Return Getsuccessresult (TRUE);   } catch (Final Uiobjectnotfoundexception e) {     return new Androidcommandresult (Wdstatus.no_such_element, E.getmessage ());      } catch (Final Exception e) {//Handle nullpointerexception return Geterrorresult ("Unknown error");      }} else {final hashtable<string, object> params = Command.params (); Final double[] coords = {double.parsedouble (Params.get ("X"). ToString ()), Double.parsedouble (Params.get ("Y"). toSt      Ring ())};      Final arraylist<integer> posvals = Absposfromcoords (coords);      Final Boolean res = Uidevice.getinstance (). Click (Posvals.get (0), Posvals.get (1));    Return Getsuccessresult (RES); }  }}


The class is an Execute method, the Execute method first to determine whether the passed parameter object is a coordinate value or an element value, is the element value directly call Androidelement in the click Method, or the coordinates, will call Uidevice's click Method, It is a class in the Uiautomator package. Appium Use the uiautomator mechanism above Android API16. Then parse a touchdown command, and if the command suffix passed is touchdown, then it calls the Execute method of the touchdown object.

Map.put ("TouchDown", New TouchDown ());

Touchdown.java

Package Io.appium.android.bootstrap.handler;import Com.android.uiautomator.common.reflectionutils;import Com.android.uiautomator.core.uiobjectnotfoundexception;import Io.appium.android.bootstrap.logger;import java.lang.reflect.method;/** * This handler was used to perform a touchDown event on an element in the * Android UI. *  */public class TouchDown extends TouchEvent {  @Override  protected Boolean executetouchevent () throws uiobjectnotfoundexception {    printeventdebugline ("TouchDown");    try {      final reflectionutils utils = new Reflectionutils ();      Final Method TouchDown = Utils.getcontrollermethod ("TouchDown", Int.class,          int.class);      Return (Boolean) Touchdown.invoke (Utils.getcontroller (), Clickx, clicky);    } catch (Final Exception e) {      logger.debug ("Problem invoking TouchDown:" + e);      return false;}}  }
This method uses the reflection mechanism to invoke the Uiautomator hidden API to perform the operation

Mobile phone Automation test: Appium Source analysis of Bootstrap one

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.