run_android.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #!/usr/bin/env python
  2. import os
  3. import re
  4. import getpass
  5. from run_utils import Err, log, execute, isColorEnabled, hostos
  6. from run_suite import TestSuite
  7. def exe(program):
  8. return program + ".exe" if hostos == 'nt' else program
  9. class ApkInfo:
  10. def __init__(self):
  11. self.pkg_name = None
  12. self.pkg_target = None
  13. self.pkg_runner = None
  14. def forcePackage(self, package):
  15. if package:
  16. if package.startswith("."):
  17. self.pkg_target += package
  18. else:
  19. self.pkg_target = package
  20. class Tool:
  21. def __init__(self):
  22. self.cmd = []
  23. def run(self, args=[], silent=False):
  24. cmd = self.cmd[:]
  25. cmd.extend(args)
  26. return execute(self.cmd + args, silent)
  27. class Adb(Tool):
  28. def __init__(self, sdk_dir):
  29. Tool.__init__(self)
  30. exe_path = os.path.join(sdk_dir, exe("platform-tools/adb"))
  31. if not os.path.isfile(exe_path) or not os.access(exe_path, os.X_OK):
  32. exe_path = None
  33. # fix adb tool location
  34. if not exe_path:
  35. exe_path = "adb"
  36. self.cmd = [exe_path]
  37. def init(self, serial):
  38. # remember current device serial. Needed if another device is connected while this script runs
  39. if not serial:
  40. serial = self.detectSerial()
  41. if serial:
  42. self.cmd.extend(["-s", serial])
  43. def detectSerial(self):
  44. adb_res = self.run(["devices"], silent=True)
  45. # assume here that device name may consists of any characters except newline
  46. connected_devices = re.findall(r"^[^\n]+[ \t]+device\r?$", adb_res, re.MULTILINE)
  47. if not connected_devices:
  48. raise Err("Can not find Android device")
  49. elif len(connected_devices) != 1:
  50. raise Err("Too many (%s) devices are connected. Please specify single device using --serial option:\n\n%s", len(connected_devices), adb_res)
  51. else:
  52. return connected_devices[0].split("\t")[0]
  53. def getOSIdentifier(self):
  54. return "Android" + self.run(["shell", "getprop ro.build.version.release"], silent=True).strip()
  55. class Aapt(Tool):
  56. def __init__(self, sdk_dir):
  57. Tool.__init__(self)
  58. aapt_fn = exe("aapt")
  59. aapt = None
  60. for r, ds, fs in os.walk(os.path.join(sdk_dir, 'build-tools')):
  61. if aapt_fn in fs:
  62. aapt = os.path.join(r, aapt_fn)
  63. break
  64. if not aapt:
  65. raise Err("Can not find aapt tool: %s", aapt_fn)
  66. self.cmd = [aapt]
  67. def dump(self, exe):
  68. res = ApkInfo()
  69. output = self.run(["dump", "xmltree", exe, "AndroidManifest.xml"], silent=True)
  70. if not output:
  71. raise Err("Can not dump manifest from %s", exe)
  72. tags = re.split(r"[ ]+E: ", output)
  73. # get package name
  74. manifest_tag = [t for t in tags if t.startswith("manifest ")]
  75. if not manifest_tag:
  76. raise Err("Can not read package name from: %s", exe)
  77. res.pkg_name = re.search(r"^[ ]+A: package=\"(?P<pkg>.*?)\" \(Raw: \"(?P=pkg)\"\)\r?$", manifest_tag[0], flags=re.MULTILINE).group("pkg")
  78. # get test instrumentation info
  79. instrumentation_tag = [t for t in tags if t.startswith("instrumentation ")]
  80. if not instrumentation_tag:
  81. raise Err("Can not find instrumentation details in: %s", exe)
  82. res.pkg_runner = re.search(r"^[ ]+A: android:name\(0x[0-9a-f]{8}\)=\"(?P<runner>.*?)\" \(Raw: \"(?P=runner)\"\)\r?$", instrumentation_tag[0], flags=re.MULTILINE).group("runner")
  83. res.pkg_target = re.search(r"^[ ]+A: android:targetPackage\(0x[0-9a-f]{8}\)=\"(?P<pkg>.*?)\" \(Raw: \"(?P=pkg)\"\)\r?$", instrumentation_tag[0], flags=re.MULTILINE).group("pkg")
  84. if not res.pkg_name or not res.pkg_runner or not res.pkg_target:
  85. raise Err("Can not find instrumentation details in: %s", exe)
  86. return res
  87. class AndroidTestSuite(TestSuite):
  88. def __init__(self, options, cache, id, android_env={}):
  89. TestSuite.__init__(self, options, cache, id)
  90. sdk_dir = options.android_sdk or os.environ.get("ANDROID_SDK", False) or os.path.dirname(os.path.dirname(self.cache.android_executable))
  91. log.debug("Detecting Android tools in directory: %s", sdk_dir)
  92. self.adb = Adb(sdk_dir)
  93. self.aapt = Aapt(sdk_dir)
  94. self.env = android_env
  95. def isTest(self, fullpath):
  96. if os.path.isfile(fullpath):
  97. if fullpath.endswith(".apk") or os.access(fullpath, os.X_OK):
  98. return True
  99. return False
  100. def getOS(self):
  101. return self.adb.getOSIdentifier()
  102. def checkPrerequisites(self):
  103. self.adb.init(self.options.serial)
  104. def runTest(self, module, path, logfile, workingDir, args=[]):
  105. args = args[:]
  106. exe = os.path.abspath(path)
  107. if exe.endswith(".apk"):
  108. info = self.aapt.dump(exe)
  109. if not info:
  110. raise Err("Can not read info from test package: %s", exe)
  111. info.forcePackage(self.options.package)
  112. self.adb.run(["uninstall", info.pkg_name])
  113. output = self.adb.run(["install", exe], silent=True)
  114. if not (output and "Success" in output):
  115. raise Err("Can not install package: %s", exe)
  116. params = ["-e package %s" % info.pkg_target]
  117. ret = self.adb.run(["shell", "am instrument -w %s %s/%s" % (" ".join(params), info.pkg_name, info.pkg_runner)])
  118. return None, ret
  119. else:
  120. device_dir = getpass.getuser().replace(" ", "") + "_" + self.options.mode + "/"
  121. if isColorEnabled(args):
  122. args.append("--gtest_color=yes")
  123. tempdir = "/data/local/tmp/"
  124. android_dir = tempdir + device_dir
  125. exename = os.path.basename(exe)
  126. android_exe = android_dir + exename
  127. self.adb.run(["push", exe, android_exe])
  128. self.adb.run(["shell", "chmod 777 " + android_exe])
  129. env_pieces = ["export %s=%s" % (a, b) for a, b in self.env.items()]
  130. pieces = ["cd %s" % android_dir, "./%s %s" % (exename, " ".join(args))]
  131. log.warning("Run: %s" % " && ".join(pieces))
  132. ret = self.adb.run(["shell", " && ".join(env_pieces + pieces)])
  133. # try get log
  134. hostlogpath = os.path.join(workingDir, logfile)
  135. self.adb.run(["pull", android_dir + logfile, hostlogpath])
  136. # cleanup
  137. self.adb.run(["shell", "rm " + android_dir + logfile])
  138. self.adb.run(["shell", "rm " + tempdir + "__opencv_temp.*"], silent=True)
  139. if os.path.isfile(hostlogpath):
  140. return hostlogpath, ret
  141. return None, ret
  142. if __name__ == "__main__":
  143. log.error("This is utility file, please execute run.py script")