Bug 701076 - [hoooking up make] Robotium integration into birch tree. r=blassey,bear
authorJoel Maher <jmaher@mozilla.com>
Sat, 31 Dec 2011 10:03:36 -0500
changeset 84844 b73c54dfb1d0450a856725c3d1ea85bec07cb337
parent 84843 10f831bfaf08329ede947f4cbf19480a4cf62601
child 84845 905e6efc3cce0423d71912a892cd2bb85e3f3437
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey, bear
bugs701076
milestone12.0a1
Bug 701076 - [hoooking up make] Robotium integration into birch tree. r=blassey,bear
build/Makefile.in
build/mobile/robocop/Makefile.in
testing/mochitest/Makefile.in
testing/mochitest/roboextender/Makefile.in
testing/mochitest/roboextender/bootstrap.js
testing/mochitest/roboextender/install.rdf
testing/mochitest/runtestsremote.py
testing/testsuite-targets.mk
toolkit/mozapps/installer/packager.mk
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -57,16 +57,17 @@ DIRS += pgo
 
 ifdef ENABLE_TESTS
   DIRS += autoconf/test
 ifeq (android,$(MOZ_WIDGET_TOOLKIT))
   DIRS += mobile/sutagent/android \
           mobile/sutagent/android/watcher \
           mobile/sutagent/android/ffxcp \
           mobile/sutagent/android/fencp \
+          mobile/robocop \
           $(NULL)
 endif
 endif
 
 ifdef MOZ_APP_BASENAME
 DIST_FILES = application.ini
 
 ifdef LIBXUL_SDK
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -125,24 +125,21 @@ classes.dex: $(_JAVA_HARNESS)
 classes.dex: $(_JAVA_TESTS)
 	$(NSINSTALL) -D classes
 	$(JAVAC) $(JAVAC_FLAGS) -d classes $(JAVAFILES) $(_JAVA_HARNESS) $(addprefix $(DEPTH)/mobile/android/base/tests/,$(_JAVA_TESTS))
 	$(DX) --dex --output=$@ classes $(ROBOTIUM_PATH)
 
 robocop.ap_: AndroidManifest.xml
 	$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -I . -S res -F $@ -J ./
 
-robocop-unsigned-unaligned.apk: robocop.ap_ classes.dex
-	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z robocop.ap_ -f classes.dex
-
-robocop-unaligned.apk: robocop-unsigned-unaligned.apk
-	cp robocop-unsigned-unaligned.apk $@
-	jarsigner -keystore ~/.android/debug.keystore -storepass android -keypass android $@ androiddebugkey
-
-robocop.apk: robocop-unaligned.apk
-	$(ZIPALIGN) -f -v 4 robocop-unaligned.apk $@
+robocop.apk: robocop.ap_ classes.dex
+	$(APKBUILDER) robocop-raw.apk -v $(APKBUILDER_FLAGS) -z robocop.ap_ -f classes.dex
+ifdef JARSIGNER
+	$(JARSIGNER) robocop-raw.apk
+endif
+	$(ZIPALIGN) -f -v 4 robocop-raw.apk $@
 	cp $(TESTPATH)/robocop.ini robocop.ini
 	cp $(srcdir)/parse_ids.py parse_ids.py
 
 export::
 	$(NSINSTALL) -D res
 	@(cd $(srcdir)/res && tar $(TAR_CREATE_FLAGS) - *) | (cd $(DEPTH)/build/mobile/robocop/res && tar -xf -)
 
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -48,16 +48,19 @@ DIRS = \
   static \
   dynamic \
   tests \
   chrome \
   ssltunnel \
   specialpowers \
   $(NULL)
 
+ifeq ($(MOZ_BUILD_APP),mobile/android)
+DIRS += roboextender
+endif
 
 NO_JS_MANIFEST = 1
 MOZ_CHROME_FILE_FORMAT = jar
 DIST_FILES = install.rdf
 
 # Used in install.rdf
 USE_EXTENSION_MANIFEST = 1
 
@@ -77,16 +80,17 @@ include $(topsrcdir)/build/automation-bu
 		runtests.py \
 		automation.py \
 		runtestsremote.py \
 		runtestsvmware.py \
 		$(topsrcdir)/build/mobile/devicemanager.py \
 		$(topsrcdir)/build/mobile/devicemanagerADB.py \
 		$(topsrcdir)/build/mobile/devicemanagerSUT.py \
 		$(topsrcdir)/build/automationutils.py \
+		$(topsrcdir)/build/manifestparser.py \
 		$(topsrcdir)/build/mobile/remoteautomation.py \
 		gen_template.pl \
 		server.js \
 		harness-overlay.xul \
 		harness.xul \
 		browser-test-overlay.xul \
 		browser-test.js \
 		chrome-harness.js \
@@ -95,16 +99,20 @@ include $(topsrcdir)/build/automation-bu
 		$(topsrcdir)/build/pgo/server-locations.txt \
 		$(topsrcdir)/netwerk/test/httpserver/httpd.js \
 		mozprefs.js \
 		pywebsocket_wrapper.py \
  	 	plain-loop.html \
 		android.json \
 		$(NULL)	
 
+ifeq ($(MOZ_BUILD_APP),mobile/android)
+_SERV_FILES += $(topsrcdir)/mobile/android/base/tests/robocop.ini
+endif
+
 _PYWEBSOCKET_FILES = \
 		pywebsocket/standalone.py \
 		$(NULL)
 
 _MOD_PYWEBSOCKET_FILES = \
 		pywebsocket/mod_pywebsocket/__init__.py \
 		pywebsocket/mod_pywebsocket/common.py \
 		pywebsocket/mod_pywebsocket/dispatch.py \
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/roboextender/Makefile.in
@@ -0,0 +1,58 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH           = ../../..
+topsrcdir       = @top_srcdir@
+srcdir          = @srcdir@
+VPATH           = @srcdir@
+relativesrcdir  = testing/mochitest/roboextender
+
+include $(DEPTH)/config/autoconf.mk
+
+_TEST_FILES = \
+  bootstrap.js \
+  install.rdf \
+  $(NULL)
+
+TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
+
+include $(topsrcdir)/config/rules.mk
+
+libs:: $(_TEST_FILES)
+	$(MKDIR) -p $(TEST_EXTENSIONS_DIR)/roboextender@mozilla.org
+	$(INSTALL) $(foreach f,$^,"$f") $(TEST_EXTENSIONS_DIR)/roboextender@mozilla.org/
+	
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/roboextender/bootstrap.js
@@ -0,0 +1,60 @@
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+function loadIntoWindow(window) {}
+function unloadFromWindow(window) {}
+
+function _sendMessageToJava (aMsg) {
+  let bridge = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge);
+  return bridge.handleGeckoMessage(JSON.stringify(aMsg));
+};
+
+/*
+ bootstrap.js API
+*/
+var windowListener = {
+  onOpenWindow: function(aWindow) {
+    // Wait for the window to finish loading
+    let domWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
+    domWindow.addEventListener("load", function() {
+      domWindow.removeEventListener("load", arguments.callee, false);
+      if (domWindow) {
+        domWindow.addEventListener("scroll", function(e) {
+          let message = {
+            gecko: {
+              type: 'robocop:scroll',
+              y: XPCNativeWrapper.unwrap(e.target).documentElement.scrollTop,
+              height: XPCNativeWrapper.unwrap(e.target).documentElement.scrollHeight,
+              cheight: XPCNativeWrapper.unwrap(e.target).documentElement.clientHeight,
+            }
+          };
+          let retVal = _sendMessageToJava(message);
+        });
+      }
+    }, false);
+  },
+  onCloseWindow: function(aWindow) { },
+  onWindowTitleChange: function(aWindow, aTitle) { }
+};
+
+function startup(aData, aReason) {
+  let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
+
+  // Load into any new windows
+  wm.addListener(windowListener);
+}
+
+function shutdown(aData, aReason) {
+  // When the application is shutting down we normally don't have to clean up any UI changes
+  if (aReason == APP_SHUTDOWN) return;
+
+  let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
+  let obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+
+  // Stop watching for new windows
+  wm.removeListener(windowListener);
+}
+
+function install(aData, aReason) { }
+function uninstall(aData, aReason) { }
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/roboextender/install.rdf
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+    <Description about="urn:mozilla:install-manifest">
+        <em:id>roboextender@mozilla.org</em:id>
+        <em:type>2</em:type>
+        <em:name>Robocop Extender</em:name>
+        <em:version>1.0</em:version>
+        <em:bootstrap>true</em:bootstrap>
+        <em:creator>Joel Maher</em:creator>
+        <em:targetApplication>
+            <Description>
+               <em:id>toolkit@mozilla.org</em:id>
+               <em:minVersion>10.0</em:minVersion>
+               <em:maxVersion>*</em:maxVersion>
+            </Description>
+        </em:targetApplication>
+    </Description>
+</RDF>
+
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -44,16 +44,17 @@ sys.path.insert(0, os.path.abspath(os.pa
 
 from automation import Automation
 from remoteautomation import RemoteAutomation
 from runtests import Mochitest
 from runtests import MochitestOptions
 from runtests import MochitestServer
 
 import devicemanager, devicemanagerADB, devicemanagerSUT
+import manifestparser
 
 class RemoteOptions(MochitestOptions):
 
     def __init__(self, automation, scriptdir, **kwargs):
         defaults = {}
         MochitestOptions.__init__(self, automation, scriptdir)
 
         self.add_option("--remote-app-path", action="store",
@@ -101,16 +102,21 @@ class RemoteOptions(MochitestOptions):
                     help = "ip address where the remote web server is hosted at")
         defaults["sslPort"] = automation.DEFAULT_SSL_PORT
 
         self.add_option("--pidfile", action = "store",
                     type = "string", dest = "pidFile",
                     help = "name of the pidfile to generate")
         defaults["pidFile"] = ""
 
+        self.add_option("--robocop", action = "store",
+                    type = "string", dest = "robocop",
+                    help = "use when running robotium tests on native UI")
+        defaults["robocop"] = ""
+
         defaults["remoteTestRoot"] = None
         defaults["logFile"] = "mochitest.log"
         defaults["autorun"] = True
         defaults["closeWhenDone"] = True
         defaults["testPath"] = ""
         defaults["app"] = None
 
         self.set_defaults(**defaults)
@@ -307,17 +313,17 @@ class MochiRemote(Mochitest):
         return logFile
 
 def main():
     scriptdir = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
     dm_none = devicemanagerADB.DeviceManagerADB()
     auto = RemoteAutomation(dm_none, "fennec")
     parser = RemoteOptions(auto, scriptdir)
     options, args = parser.parse_args()
-    if (options.dm_trans == "adb"):
+    if (options.dm_trans == "adb" or options.robocop):
         if (options.deviceIP):
             dm = devicemanagerADB.DeviceManagerADB(options.deviceIP, options.devicePort)
         else:
             dm = dm_none
     else:
          dm = devicemanagerSUT.DeviceManagerSUT(options.deviceIP, options.devicePort)
     auto.setDeviceManager(dm)
     options = parser.verifyRemoteOptions(options, auto)
@@ -340,22 +346,54 @@ def main():
     logParent = os.path.dirname(options.remoteLogFile)
     dm.mkDir(logParent);
     auto.setRemoteLog(options.remoteLogFile)
     auto.setServerInfo(options.webServer, options.httpPort, options.sslPort)
 
     procName = options.app.split('/')[-1]
     if (dm.processExist(procName)):
       dm.killProcess(procName)
+    
+    if (options.robocop):
+      mp = manifestparser.TestManifest(strict=False)
+      # TODO: pull this in dynamically
+      mp.read('robocop.ini')
+      robocop_tests = mp.active_tests(exists=False)
 
-    try:
-      retVal = mochitest.runTests(options)
-    except:
-      print "TEST-UNEXPECTED-ERROR | | Exception caught while running tests."
+      fHandle = open("robotium.config", "w")
+      fHandle.write("profile=%s\n" % (mochitest.remoteProfile))
+      fHandle.write("logfile=%s\n" % (options.remoteLogFile))
+      fHandle.close()
+      deviceRoot = dm.getDeviceRoot()
+      
+      # Note, we are pushing to /sdcard since we have this location hard coded in robocop
+      dm.pushFile("robotium.config", "/sdcard/robotium.config")
+      dm.pushFile(os.path.abspath(options.robocop + "/fennec_ids.txt"), "/sdcard/fennec_ids.txt")
+      options.extraPrefs.append('robocop.logfile="%s/robocop.log"' % deviceRoot)
+
+      manifest = mochitest.buildProfile(options)
+      mochitest.startWebServer(options)
+
+      if (options.dm_trans == 'adb'):
+        dm.checkCmd(["install", "-r", os.path.join(options.robocop, "robocop.apk")])
+        for test in robocop_tests:
+          cmd = ["shell", "am", "instrument", "-w", "-e", "class"]
+          cmd.append("%s.tests.%s" % (options.app, test['name']))
+          cmd.append("org.mozilla.roboexample.test/android.test.InstrumentationTestRunner")
+          retVal = dm.checkCmd(cmd)
+      else:
+        # SUTAgent needs to install robocop and not crash when we launch robocop.
+        retVal = dm.launchProcess(["am", "instrument", "-w", "org.mozilla.roboexample.test/android.test.InstrumentationTestRunner"])
       mochitest.stopWebServer(options)
-      mochitest.stopWebSocketServer(options)
-      sys.exit(1)
+    else:
+      try:
+        retVal = mochitest.runTests(options)
+      except:
+        print "TEST-UNEXPECTED-ERROR | | Exception caught while running tests."
+        mochitest.stopWebServer(options)
+        mochitest.stopWebSocketServer(options)
+        sys.exit(1)
       
     sys.exit(retVal)
         
 if __name__ == "__main__":
     main()
 
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -69,16 +69,23 @@ RUN_MOCHITEST = \
 
 RUN_MOCHITEST_REMOTE = \
 	rm -f ./$@.log && \
 	$(PYTHON) _tests/testing/mochitest/runtestsremote.py --autorun --close-when-done \
 	  --console-level=INFO --log-file=./$@.log --file-level=INFO $(DM_FLAGS) --dm_trans=$(DM_TRANS) \
 	  --app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \
 	  $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
 
+RUN_MOCHITEST_ROBOTIUM = \
+  rm -f ./$@.log && \
+  $(PYTHON) _tests/testing/mochitest/runtestsremote.py --robocop ../../../build/mobile/robocop \
+    --console-level=INFO --log-file=./$@.log --file-level=INFO $(DM_FLAGS) --dm_trans=adb \
+    --app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \
+    $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
+
 ifndef NO_FAIL_ON_TEST_ERRORS
 define CHECK_TEST_ERROR
   @errors=`grep "TEST-UNEXPECTED-" $@.log` ;\
   if test "$$errors" ; then \
 	  echo "$@ failed:"; \
 	  echo "$$errors"; \
 	  exit 1; \
   else \
@@ -90,16 +97,24 @@ endif
 mochitest-remote: DM_TRANS?=adb
 mochitest-remote:
 	@if test -f ${MOZ_HOST_BIN}/xpcshell && [ "${TEST_DEVICE}" != "usb" -o "$(DM_TRANS)" = "adb" ]; \
           then $(RUN_MOCHITEST_REMOTE); \
         else \
           echo "please prepare your host with environment variables for TEST_DEVICE and MOZ_HOST_BIN"; \
         fi
 
+mochitest-robotium: DM_TRANS?=adb
+mochitest-robotium:
+	@if test -f ${MOZ_HOST_BIN}/xpcshell && [ "${TEST_DEVICE}" != "usb" -o "$(DM_TRANS)" = "adb" ]; \
+          then $(RUN_MOCHITEST_ROBOTIUM); \
+        else \
+          echo "please prepare your host with environment variables for TEST_DEVICE and MOZ_HOST_BIN"; \
+        fi
+
 mochitest-plain:
 	$(RUN_MOCHITEST)
 	$(CHECK_TEST_ERROR)
 
 # Allow mochitest-1 ... mochitest-5 for developer ease
 mochitest-1 mochitest-2 mochitest-3 mochitest-4 mochitest-5: mochitest-%:
 	echo "mochitest: $* / 5"
 	$(RUN_MOCHITEST) --chunk-by-dir=4 --total-chunks=5 --this-chunk=$*
@@ -281,16 +296,21 @@ stage-xpcshell: make-stage-dir
 stage-jstests: make-stage-dir
 	$(MAKE) -C $(DEPTH)/js/src/tests stage-package
 
 stage-android: make-stage-dir
 	$(NSINSTALL) $(DEPTH)/build/mobile/sutagent/android/sutAgentAndroid.apk $(PKG_STAGE)/bin
 	$(NSINSTALL) $(DEPTH)/build/mobile/sutagent/android/watcher/Watcher.apk $(PKG_STAGE)/bin
 	$(NSINSTALL) $(DEPTH)/build/mobile/sutagent/android/fencp/FenCP.apk $(PKG_STAGE)/bin
 	$(NSINSTALL) $(DEPTH)/build/mobile/sutagent/android/ffxcp/FfxCP.apk $(PKG_STAGE)/bin
+ifeq ($(MOZ_BUILD_APP),mobile/android)
+	$(NSINSTALL) $(DEPTH)/build/mobile/robocop/robocop.apk $(PKG_STAGE)/bin
+	$(PYTHON) $(DIST)/../build/mobile/robocop/parse_ids.py -i $(DEPTH)/mobile/android/base/R.java -o $(DEPTH)/build/mobile/robocop/fennec_ids.txt
+	$(NSINSTALL) $(DEPTH)/build/mobile/robocop/fennec_ids.txt $(PKG_STAGE)/bin
+endif
 
 stage-jetpack: make-stage-dir
 	$(NSINSTALL) $(topsrcdir)/testing/jetpack/jetpack-location.txt $(PKG_STAGE)/jetpack
 
 stage-firebug: make-stage-dir
 	$(MAKE) -C $(DEPTH)/testing/firebug stage-package
 
 stage-peptest: make-stage-dir
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -345,16 +345,17 @@ INNER_MAKE_PACKAGE	= \
     rm $(_ABS_DIST)/gecko.ap_ && \
     $(ZIP) -r9D $(_ABS_DIST)/gecko.ap_ $(DIST_FILES) -x $(NON_DIST_FILES) && \
     $(ZIP) -0 $(_ABS_DIST)/gecko.ap_ $(OMNIJAR_NAME)) && \
   rm -f $(_ABS_DIST)/gecko.apk && \
   $(APKBUILDER) $(_ABS_DIST)/gecko.apk -v $(APKBUILDER_FLAGS) -z $(_ABS_DIST)/gecko.ap_ -f $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/classes.dex && \
   cp $(_ABS_DIST)/gecko.apk $(_ABS_DIST)/gecko-unsigned-unaligned.apk && \
   $(JARSIGNER) $(_ABS_DIST)/gecko.apk && \
   $(ZIPALIGN) -f -v 4 $(_ABS_DIST)/gecko.apk $(PACKAGE)
+
 INNER_UNMAKE_PACKAGE	= \
   mkdir $(MOZ_PKG_DIR) && \
   pushd $(MOZ_PKG_DIR) && \
   $(UNZIP) $(UNPACKAGE) && \
   mv lib/$(ABI_DIR)/libmozutils.so . && \
   mv lib/$(ABI_DIR)/*plugin-container* $(MOZ_CHILD_PROCESS_NAME) && \
   rm -rf lib/$(ABI_DIR) && \
   popd