#!/usr/bin/env python import sys, os, platform, xml, re, tempfile, glob, datetime, getpass, shutil from optparse import OptionParser from subprocess import Popen, PIPE hostos = os.name # 'nt', 'posix' hostmachine = platform.machine() # 'x86', 'AMD64', 'x86_64' errorCode = 0 SIMD_DETECTION_PROGRAM=""" #if __SSE5__ # error SSE5 #endif #if __AVX2__ # error AVX2 #endif #if __AVX__ # error AVX #endif #if __SSE4_2__ # error SSE4.2 #endif #if __SSE4_1__ # error SSE4.1 #endif #if __SSSE3__ # error SSSE3 #endif #if __SSE3__ # error SSE3 #endif #if __AES__ # error AES #endif #if __SSE2__ # error SSE2 #endif #if __SSE__ # error SSE #endif #if __3dNOW__ # error 3dNOW #endif #if __MMX__ # error MMX #endif #if __ARM_NEON__ # error NEON #endif #error NOSIMD """ parse_patterns = ( {'name': "has_perf_tests", 'default': "OFF", 'pattern': re.compile("^BUILD_PERF_TESTS:BOOL=(ON)$")}, {'name': "has_accuracy_tests", 'default': "OFF", 'pattern': re.compile("^BUILD_TESTS:BOOL=(ON)$")}, {'name': "cmake_home", 'default': None, 'pattern': re.compile("^CMAKE_HOME_DIRECTORY:INTERNAL=(.+)$")}, {'name': "opencv_home", 'default': None, 'pattern': re.compile("^OpenCV_SOURCE_DIR:STATIC=(.+)$")}, {'name': "tests_dir", 'default': None, 'pattern': re.compile("^EXECUTABLE_OUTPUT_PATH:PATH=(.+)$")}, {'name': "build_type", 'default': "Release", 'pattern': re.compile("^CMAKE_BUILD_TYPE:STRING=(.*)$")}, {'name': "svnversion_path", 'default': None, 'pattern': re.compile("^SVNVERSION_PATH:FILEPATH=(.*)$")}, {'name': "git_executable", 'default': None, 'pattern': re.compile("^GIT_EXECUTABLE:FILEPATH=(.*)$")}, {'name': "cxx_flags", 'default': "", 'pattern': re.compile("^CMAKE_CXX_FLAGS:STRING=(.*)$")}, {'name': "cxx_flags_debug", 'default': "", 'pattern': re.compile("^CMAKE_CXX_FLAGS_DEBUG:STRING=(.*)$")}, {'name': "cxx_flags_release", 'default': "", 'pattern': re.compile("^CMAKE_CXX_FLAGS_RELEASE:STRING=(.*)$")}, {'name': "opencv_cxx_flags", 'default': "", 'pattern': re.compile("^OPENCV_EXTRA_C_FLAGS:INTERNAL=(.*)$")}, {'name': "opencv_cxx_flags_debug", 'default': "", 'pattern': re.compile("^OPENCV_EXTRA_C_FLAGS_DEBUG:INTERNAL=(.*)$")}, {'name': "opencv_cxx_flags_release", 'default': "", 'pattern': re.compile("^OPENCV_EXTRA_C_FLAGS_RELEASE:INTERNAL=(.*)$")}, {'name': "cxx_flags_android", 'default': None, 'pattern': re.compile("^ANDROID_CXX_FLAGS:INTERNAL=(.*)$")}, {'name': "ndk_path", 'default': None, 'pattern': re.compile("^(?:ANDROID_NDK|ANDROID_STANDALONE_TOOLCHAIN)?:PATH=(.*)$")}, {'name': "android_abi", 'default': None, 'pattern': re.compile("^ANDROID_ABI:STRING=(.*)$")}, {'name': "android_executable", 'default': None, 'pattern': re.compile("^ANDROID_EXECUTABLE:FILEPATH=(.*android.*)$")}, {'name': "ant_executable", 'default': None, 'pattern': re.compile("^ANT_EXECUTABLE:FILEPATH=(.*ant.*)$")}, {'name': "java_test_binary_dir", 'default': None, 'pattern': re.compile("^opencv_test_java_BINARY_DIR:STATIC=(.*)$")}, {'name': "is_x64", 'default': "OFF", 'pattern': re.compile("^CUDA_64_BIT_DEVICE_CODE:BOOL=(ON)$")},#ugly( {'name': "cmake_generator", 'default': None, 'pattern': re.compile("^CMAKE_GENERATOR:INTERNAL=(.+)$")}, {'name': "cxx_compiler", 'default': None, 'pattern': re.compile("^CMAKE_CXX_COMPILER:FILEPATH=(.+)$")}, {'name': "cxx_compiler_arg1", 'default': None, 'pattern': re.compile("^CMAKE_CXX_COMPILER_ARG1:[A-Z]+=(.+)$")}, {'name': "with_cuda", 'default': "OFF", 'pattern': re.compile("^WITH_CUDA:BOOL=(ON)$")}, {'name': "cuda_library", 'default': None, 'pattern': re.compile("^CUDA_CUDA_LIBRARY:FILEPATH=(.+)$")}, {'name': "core_dependencies", 'default': None, 'pattern': re.compile("^opencv_core_LIB_DEPENDS:STATIC=(.+)$")}, ) def query_yes_no(stdout, question, default="yes"): valid = {"yes":True, "y":True, "ye":True, "no":False, "n":False} if default == None: prompt = " [y/n] " elif default == "yes": prompt = " [Y/n] " elif default == "no": prompt = " [y/N] " else: raise ValueError("invalid default answer: '%s'" % default) while True: stdout.write(os.linesep + question + prompt) choice = raw_input().lower() if default is not None and choice == '': return valid[default] elif choice in valid: return valid[choice] else: stdout.write("Please respond with 'yes' or 'no' "\ "(or 'y' or 'n').\n") def getRunningProcessExePathByName_win32(name): from ctypes import windll, POINTER, pointer, Structure, sizeof from ctypes import c_long , c_int , c_uint , c_char , c_ubyte , c_char_p , c_void_p class PROCESSENTRY32(Structure): _fields_ = [ ( 'dwSize' , c_uint ) , ( 'cntUsage' , c_uint) , ( 'th32ProcessID' , c_uint) , ( 'th32DefaultHeapID' , c_uint) , ( 'th32ModuleID' , c_uint) , ( 'cntThreads' , c_uint) , ( 'th32ParentProcessID' , c_uint) , ( 'pcPriClassBase' , c_long) , ( 'dwFlags' , c_uint) , ( 'szExeFile' , c_char * 260 ) , ( 'th32MemoryBase' , c_long) , ( 'th32AccessKey' , c_long ) ] class MODULEENTRY32(Structure): _fields_ = [ ( 'dwSize' , c_long ) , ( 'th32ModuleID' , c_long ), ( 'th32ProcessID' , c_long ), ( 'GlblcntUsage' , c_long ), ( 'ProccntUsage' , c_long ) , ( 'modBaseAddr' , c_long ) , ( 'modBaseSize' , c_long ) , ( 'hModule' , c_void_p ) , ( 'szModule' , c_char * 256 ), ( 'szExePath' , c_char * 260 ) ] TH32CS_SNAPPROCESS = 2 TH32CS_SNAPMODULE = 0x00000008 ## CreateToolhelp32Snapshot CreateToolhelp32Snapshot= windll.kernel32.CreateToolhelp32Snapshot CreateToolhelp32Snapshot.reltype = c_long CreateToolhelp32Snapshot.argtypes = [ c_int , c_int ] ## Process32First Process32First = windll.kernel32.Process32First Process32First.argtypes = [ c_void_p , POINTER( PROCESSENTRY32 ) ] Process32First.rettype = c_int ## Process32Next Process32Next = windll.kernel32.Process32Next Process32Next.argtypes = [ c_void_p , POINTER(PROCESSENTRY32) ] Process32Next.rettype = c_int ## CloseHandle CloseHandle = windll.kernel32.CloseHandle CloseHandle.argtypes = [ c_void_p ] CloseHandle.rettype = c_int ## Module32First Module32First = windll.kernel32.Module32First Module32First.argtypes = [ c_void_p , POINTER(MODULEENTRY32) ] Module32First.rettype = c_int hProcessSnap = c_void_p(0) hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS , 0 ) pe32 = PROCESSENTRY32() pe32.dwSize = sizeof( PROCESSENTRY32 ) ret = Process32First( hProcessSnap , pointer( pe32 ) ) path = None while ret : if name + ".exe" == pe32.szExeFile: hModuleSnap = c_void_p(0) me32 = MODULEENTRY32() me32.dwSize = sizeof( MODULEENTRY32 ) hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pe32.th32ProcessID ) ret = Module32First( hModuleSnap, pointer(me32) ) path = me32.szExePath CloseHandle( hModuleSnap ) if path: break ret = Process32Next( hProcessSnap, pointer(pe32) ) CloseHandle( hProcessSnap ) return path def getRunningProcessExePathByName_posix(name): pids= [pid for pid in os.listdir('/proc') if pid.isdigit()] for pid in pids: try: path = os.readlink(os.path.join('/proc', pid, 'exe')) if path and path.endswith(name): return path except: pass def getRunningProcessExePathByName(name): try: if hostos == "nt": return getRunningProcessExePathByName_win32(name) elif hostos == "posix": return getRunningProcessExePathByName_posix(name) else: return None except: return None class TestSuite(object): def __init__(self, options, path = None): self.options = options self.path = path self.error = None self.setUp = None self.tearDown = None self.adb = None self.targetos = None self.nameprefix = "opencv_" + self.options.mode + "_" for p in parse_patterns: setattr(self, p["name"], p["default"]) if self.path: cachefile = open(os.path.join(self.path, "CMakeCache.txt"), "rt") try: for l in cachefile.readlines(): ll = l.strip() if not ll or ll.startswith("#"): continue for p in parse_patterns: match = p["pattern"].match(ll) if match: value = match.groups()[0] if value and not value.endswith("-NOTFOUND"): setattr(self, p["name"], value) except: pass cachefile.close() # detect target platform if self.android_executable or self.android_abi or self.ndk_path: self.targetos = "android" else: self.targetos = hostos self.initialize() def initialize(self): # fix empty tests dir if not self.tests_dir: self.tests_dir = self.path self.tests_dir = os.path.normpath(self.tests_dir) # compute path to adb if self.android_executable: self.adb = os.path.join(os.path.dirname(os.path.dirname(self.android_executable)), ("platform-tools/adb","platform-tools/adb.exe")[hostos == 'nt']) if not os.path.isfile(self.adb) or not os.access(self.adb, os.X_OK): self.adb = None else: self.adb = None if self.targetos == "android": # fix adb tool location if not self.adb: self.adb = getRunningProcessExePathByName("adb") if not self.adb: self.adb = "adb" if self.options.adb_serial: self.adb = [self.adb, "-s", self.options.adb_serial] else: self.adb = [self.adb] try: output = Popen(self.adb + ["shell", "ls"], stdout=PIPE, stderr=PIPE).communicate() except OSError: self.adb = [] # remember current device serial. Needed if another device is connected while this script runs if self.adb and not self.options.adb_serial: adb_res = self.runAdb("devices") if not adb_res: self.error = "Could not run adb command: %s (for %s)" % (self.error, self.path) self.adb = [] else: # assume here that device name may consists of any characters except newline connected_devices = re.findall(r"^[^\n]+[ \t]+device\r?$", adb_res, re.MULTILINE) if not connected_devices: self.error = "Android device not found" self.adb = [] elif len(connected_devices) != 1: self.error = "Too many (%s) devices are connected. Please specify single device using --serial option:\n\n" % (len(connected_devices)) + adb_res self.adb = [] else: self.options.adb_serial = connected_devices[0].split("\t")[0] self.adb = self.adb + ["-s", self.options.adb_serial] if self.adb: # construct name for aapt tool self.aapt = [os.path.join(os.path.dirname(self.adb[0]), ("aapt","aapt.exe")[hostos == 'nt'])] if not os.path.isfile(self.aapt[0]): # it's moved in SDK r22 sdk_dir = os.path.dirname( os.path.dirname(self.adb[0]) ) aapt_fn = ("aapt", "aapt.exe")[hostos == 'nt'] for r, ds, fs in os.walk( os.path.join(sdk_dir, 'build-tools') ): if aapt_fn in fs: self.aapt = [ os.path.join(r, aapt_fn) ] break else: self.error = "Can't find '%s' tool!" % aapt_fn # fix has_perf_tests param self.has_perf_tests = self.has_perf_tests == "ON" self.has_accuracy_tests = self.has_accuracy_tests == "ON" # fix is_x64 flag self.is_x64 = self.is_x64 == "ON" if not self.is_x64 and ("X64" in "%s %s %s" % (self.cxx_flags, self.cxx_flags_release, self.cxx_flags_debug) or "Win64" in self.cmake_generator): self.is_x64 = True # fix test path if "Visual Studio" in self.cmake_generator: if self.options.configuration: self.tests_dir = os.path.join(self.tests_dir, self.options.configuration) else: self.tests_dir = os.path.join(self.tests_dir, self.build_type) elif not self.is_x64 and self.cxx_compiler: #one more attempt to detect x64 compiler try: compiler = [self.cxx_compiler] if self.cxx_compiler_arg1: compiler.append(self.cxx_compiler_arg1) output = Popen(compiler + ["-v"], stdout=PIPE, stderr=PIPE).communicate() if not output[0] and "x86_64" in output[1]: self.is_x64 = True except OSError: pass # detect target arch if self.targetos == "android": if "armeabi-v7a" in self.android_abi: self.targetarch = "armv7a" elif "armeabi-v6" in self.android_abi: self.targetarch = "armv6" elif "armeabi" in self.android_abi: self.targetarch = "armv5te" elif "x86" in self.android_abi: self.targetarch = "x86" elif "mips" in self.android_abi: self.targetarch = "mips" else: self.targetarch = "ARM" elif self.is_x64 and hostmachine in ["AMD64", "x86_64"]: self.targetarch = "x64" elif hostmachine in ["x86", "AMD64", "x86_64"]: self.targetarch = "x86" else: self.targetarch = "unknown" # fix CUDA attributes self.with_cuda = self.with_cuda == "ON" if self.cuda_library and self.cuda_library.endswith("-NOTFOUND"): self.cuda_library = None self.has_cuda = self.with_cuda and self.cuda_library and self.targetarch in ["x86", "x64"] self.hardware = None self.cmake_home_vcver = self.getVCVersion(self.cmake_home) if self.opencv_home == self.cmake_home: self.opencv_home_vcver = self.cmake_home_vcver else: self.opencv_home_vcver = self.getVCVersion(self.opencv_home) self.tests = self.getAvailableTestApps() def getVCVersion(self, root_path): if not root_path: return None if os.path.isdir(os.path.join(root_path, ".svn")): return self.getSvnVersion(root_path) elif os.path.isdir(os.path.join(root_path, ".git")): return self.getGitHash(root_path) return None def getGitHash(self, path): if not path or not self.git_executable: return None try: output = Popen([self.git_executable, "rev-parse", "--short", "HEAD"], stdout=PIPE, stderr=PIPE, cwd = path).communicate() if not output[1]: return output[0].strip() else: return None except OSError: return None def getSvnVersion(self, path): if not path: val = None elif not self.svnversion_path and hostos == 'nt': val = self.tryGetSvnVersionWithTortoise(path) else: svnversion = self.svnversion_path if not svnversion: svnversion = "svnversion" try: output = Popen([svnversion, "-n", path], stdout=PIPE, stderr=PIPE).communicate() if not output[1]: val = output[0] else: val = None except OSError: val = None if val: val = val.replace(" ", "_") return val def tryGetSvnVersionWithTortoise(self, path): try: wcrev = "SubWCRev.exe" dir = tempfile.mkdtemp() #print dir tmpfilename = os.path.join(dir, "svn.tmp") tmpfilename2 = os.path.join(dir, "svn_out.tmp") tmpfile = open(tmpfilename, "w") tmpfile.write("$WCRANGE$$WCMODS?M:$") tmpfile.close(); output = Popen([wcrev, path, tmpfilename, tmpfilename2, "-f"], stdout=PIPE, stderr=PIPE).communicate() if "is not a working copy" in output[0]: version = "exported" else: tmpfile = open(tmpfilename2, "r") version = tmpfile.read() tmpfile.close() return version except: return None finally: if dir: shutil.rmtree(dir) def isTest(self, fullpath): if not os.path.isfile(fullpath): return False if self.targetos == "nt" and not fullpath.endswith(".exe"): return False if hostos == self.targetos: return os.access(fullpath, os.X_OK) if self.targetos == "android" and fullpath.endswith(".apk"): return True return True def getAvailableTestApps(self): if self.tests_dir and os.path.isdir(self.tests_dir): files = glob.glob(os.path.join(self.tests_dir, self.nameprefix + "*")) files = [f for f in files if self.isTest(f)] if self.ant_executable and self.java_test_binary_dir: files.append("java") return files return [] def getLogName(self, app, timestamp): app = os.path.basename(app) if app.endswith(".exe"): if app.endswith("d.exe"): app = app[:-5] else: app = app[:-4] if app.startswith(self.nameprefix): app = app[len(self.nameprefix):] if self.cmake_home_vcver: if self.cmake_home_vcver == self.opencv_home_vcver: rev = self.cmake_home_vcver elif self.opencv_home_vcver: rev = self.cmake_home_vcver + "-" + self.opencv_home_vcver else: rev = self.cmake_home_vcver else: rev = None if rev: rev = rev.replace(":","to") else: rev = "" if self.options.useLongNames: if not rev: rev = "unknown" tstamp = timestamp.strftime("%Y%m%d-%H%M%S") features = [] #OS _os = "" if self.targetos == "android": _os = "Android" + self.runAdb("shell", "getprop ro.build.version.release").strip() else: mv = platform.mac_ver() if mv[0]: _os = "Darwin" + mv[0] else: wv = platform.win32_ver() if wv[0]: _os = "Windows" + wv[0] else: lv = platform.linux_distribution() if lv[0]: _os = lv[0] + lv[1] else: _os = self.targetos features.append(_os) #HW(x86, x64, ARMv7a) if self.targetarch: features.append(self.targetarch) #TBB if ";tbb;" in self.core_dependencies: features.append("TBB") #CUDA if self.has_cuda: #TODO: determine compute capability features.append("CUDA") #SIMD compiler_output = "" try: tmpfile = tempfile.mkstemp(suffix=".cpp", text = True) fd = os.fdopen(tmpfile[0], "w+b") fd.write(SIMD_DETECTION_PROGRAM) fd.close(); options = [self.cxx_compiler] if self.cxx_compiler_arg1: options.append(self.cxx_compiler_arg1) cxx_flags = self.cxx_flags + " " + self.cxx_flags_release + " " + self.opencv_cxx_flags + " " + self.opencv_cxx_flags_release if self.targetos == "android" and self.cxx_flags_android: cxx_flags = self.cxx_flags_android + " " + cxx_flags prev_option = None for opt in cxx_flags.split(" "): if opt.count('\"') % 2 == 1: if prev_option is None: prev_option = opt else: options.append(prev_option + " " + opt) prev_option = None elif prev_option is None: options.append(opt) else: prev_option = prev_option + " " + opt options.append(tmpfile[1]) output = Popen(options, stdout=PIPE, stderr=PIPE).communicate() compiler_output = output[1] os.remove(tmpfile[1]) except OSError: pass if compiler_output: m = re.search("#error\W+(\w+)", compiler_output) if m: features.append(m.group(1)) #fin return "%s__%s__%s__%s.xml" % (app, rev, tstamp, "_".join(features)) else: if rev: rev = rev + "_" if self.hardware: hw = str(self.hardware).replace(" ", "_") + "_" elif self.has_cuda: hw = "CUDA_" else: hw = "" tstamp = timestamp.strftime("%Y%m%d-%H%M%S") lname = "%s_%s_%s_%s%s%s.xml" % (app, self.targetos, self.targetarch, hw, rev, tstamp) lname = str.replace(lname, '(', '_') lname = str.replace(lname, ')', '_') return lname def getTest(self, name): # full path if self.isTest(name): return name # name only fullname = os.path.join(self.tests_dir, name) if self.isTest(fullname): return fullname # name without extension fullname += ".exe" if self.isTest(fullname): return fullname if self.targetos == "android": fullname += ".apk" if self.isTest(fullname): return fullname # short name for OpenCV tests for t in self.tests: if t == name: return t fname = os.path.basename(t) if fname == name: return t if fname.endswith(".exe") or (self.targetos == "android" and fname.endswith(".apk")): fname = fname[:-4] if fname == name: return t if self.options.configuration == "Debug" and fname == name + 'd': return t if fname.startswith(self.nameprefix): fname = fname[len(self.nameprefix):] if fname == name: return t if self.options.configuration == "Debug" and fname == name + 'd': return t return None def runAdb(self, *args): cmd = self.adb[:] cmd.extend(args) try: output = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() if not output[1]: return output[0] self.error = output[1] except OSError: pass return None def isRunnable(self): if self.error: return False if self.targetarch == "x64" and hostmachine == "x86": self.error = "Target architecture is incompatible with current platform (at %s)" % self.path return False if self.targetos == "android": if not self.adb: self.error = "Could not find adb executable (for %s)" % self.path return False if "armeabi-v7a" in self.android_abi: adb_res = self.runAdb("shell", "cat /proc/cpuinfo") if not adb_res: self.error = "Could not get info about Android platform: %s (for %s)" % (self.error, self.path) return False if "ARMv7" not in adb_res: self.error = "Android device does not support ARMv7 commands, but tests are built for armeabi-v7a (for %s)" % self.path return False if "NEON" in self.android_abi and "neon" not in adb_res: self.error = "Android device has no NEON, but tests are built for %s (for %s)" % (self.android_abi, self.path) return False hw = re.search(r"^Hardware[ \t]*:[ \t]*(.*?)$", adb_res, re.MULTILINE) if hw: self.hardware = hw.groups()[0].strip() return True def runTest(self, path, workingDir, _stdout, _stderr, args = []): global errorCode if self.error: return args = args[:] timestamp = datetime.datetime.now() logfile = self.getLogName(path, timestamp) exe = os.path.abspath(path) userlog = [a for a in args if a.startswith("--gtest_output=")] if len(userlog) == 0: args.append("--gtest_output=xml:" + logfile) else: logfile = userlog[0][userlog[0].find(":")+1:] if self.targetos == "android" and exe.endswith(".apk"): print "Run java tests:", exe try: # get package info output = Popen(self.aapt + ["dump", "xmltree", exe, "AndroidManifest.xml"], stdout=PIPE, stderr=_stderr).communicate() if not output[0]: print >> _stderr, "fail to dump manifest from", exe return tags = re.split(r"[ ]+E: ", output[0]) # get package name manifest_tag = [t for t in tags if t.startswith("manifest ")] if not manifest_tag: print >> _stderr, "fail to read package name from", exe return pkg_name = re.search(r"^[ ]+A: package=\"(?P.*?)\" \(Raw: \"(?P=pkg)\"\)\r?$", manifest_tag[0], flags=re.MULTILINE).group("pkg") # get test instrumentation info instrumentation_tag = [t for t in tags if t.startswith("instrumentation ")] if not instrumentation_tag: print >> _stderr, "can not find instrumentation detials in", exe return pkg_runner = re.search(r"^[ ]+A: android:name\(0x[0-9a-f]{8}\)=\"(?P.*?)\" \(Raw: \"(?P=runner)\"\)\r?$", instrumentation_tag[0], flags=re.MULTILINE).group("runner") pkg_target = re.search(r"^[ ]+A: android:targetPackage\(0x[0-9a-f]{8}\)=\"(?P.*?)\" \(Raw: \"(?P=pkg)\"\)\r?$", instrumentation_tag[0], flags=re.MULTILINE).group("pkg") if not pkg_name or not pkg_runner or not pkg_target: print >> _stderr, "can not find instrumentation detials in", exe return if self.options.junit_package: if self.options.junit_package.startswith("."): pkg_target += self.options.junit_package else: pkg_target = self.options.junit_package # uninstall previously installed package print >> _stderr, "Uninstalling old", pkg_name, "from device..." Popen(self.adb + ["uninstall", pkg_name], stdout=PIPE, stderr=_stderr).communicate() print >> _stderr, "Installing new", exe, "to device...", output = Popen(self.adb + ["install", exe], stdout=PIPE, stderr=PIPE).communicate() if output[0] and output[0].strip().endswith("Success"): print >> _stderr, "Success" else: print >> _stderr, "Failure" print >> _stderr, "Failed to install", exe, "to device" return print >> _stderr, "Running jUnit tests for ", pkg_target if self.setUp: self.setUp() Popen(self.adb + ["shell", "am instrument -w -e package " + pkg_target + " " + pkg_name + "/" + pkg_runner], stdout=_stdout, stderr=_stderr).wait() if self.tearDown: self.tearDown() except OSError: pass return elif self.targetos == "android": hostlogpath = "" usercolor = [a for a in args if a.startswith("--gtest_color=")] if len(usercolor) == 0 and _stdout.isatty() and hostos != "nt": args.append("--gtest_color=yes") try: tempdir = "/data/local/tmp/" andoidcwd = tempdir + getpass.getuser().replace(" ","") + "_" + self.options.mode +"/" exename = os.path.basename(exe) androidexe = andoidcwd + exename # upload _stderr.write("Uploading... ") output = Popen(self.adb + ["push", exe, androidexe], stdout=_stdout, stderr=_stderr).wait() if output != 0: print >> _stderr, "adb finishes unexpectedly with error code", output return # chmod output = Popen(self.adb + ["shell", "chmod 777 " + androidexe], stdout=_stdout, stderr=_stderr).wait() if output != 0: print >> _stderr, "adb finishes unexpectedly with error code", output return # run if self.options.help: command = exename + " --help" else: command = exename + " " + " ".join(args) print >> _stderr, "Run command:", command if self.setUp: self.setUp() env = self.options.android_env.copy() env['OPENCV_TEST_DATA_PATH'] = self.options.test_data_path if self.options.android_propagate_opencv_env: for k, v in os.environ.items(): if k.startswith('OPENCV') and not k in env: env[k] = v print >> _stderr, "Android environment variables: \n", '\n'.join([' %s=%s' % (k, v) for k, v in env.items()]) commandPrefix = ''.join(['export %s=%s && ' % (k, v) for k, v in env.items()]) Popen(self.adb + ["shell", commandPrefix + "cd " + andoidcwd + "&& ./" + command], stdout=_stdout, stderr=_stderr).wait() if self.tearDown: self.tearDown() # try get log if not self.options.help: #_stderr.write("Pull log... ") hostlogpath = os.path.join(workingDir, logfile) output = Popen(self.adb + ["pull", andoidcwd + logfile, hostlogpath], stdout=_stdout, stderr=PIPE).wait() if output != 0: print >> _stderr, "adb finishes unexpectedly with error code", output return #rm log Popen(self.adb + ["shell", "rm " + andoidcwd + logfile], stdout=PIPE, stderr=PIPE).wait() # clean temporary files Popen(self.adb + ["shell", "rm " + tempdir + "__opencv_temp.*"], stdout=PIPE, stderr=PIPE).wait() except OSError: pass if os.path.isfile(hostlogpath): return hostlogpath return None elif path == "java": cmd = [self.ant_executable, "-Dopencv.build.type=" + (self.options.configuration if self.options.configuration else self.build_type), "buildAndTest"] print >> _stderr, "Run command:", " ".join(cmd) try: errorCode = Popen(cmd, stdout=_stdout, stderr=_stderr, cwd = self.java_test_binary_dir + "/.build").wait() except: print "Unexpected error:", sys.exc_info()[0] return None else: cmd = [exe] if self.options.help: cmd.append("--help") else: cmd.extend(args) orig_temp_path = os.environ.get('OPENCV_TEMP_PATH') temp_path = tempfile.mkdtemp(prefix="__opencv_temp.", dir=orig_temp_path or None) os.environ['OPENCV_TEMP_PATH'] = temp_path print >> _stderr, "Run command:", " ".join(cmd) try: errorCode = Popen(cmd, stdout=_stdout, stderr=_stderr, cwd = workingDir).wait() except: print "Unexpected error:", sys.exc_info()[0] # clean temporary files if orig_temp_path: os.environ['OPENCV_TEMP_PATH'] = orig_temp_path else: del os.environ['OPENCV_TEMP_PATH'] try: shutil.rmtree(temp_path) pass except: pass logpath = os.path.join(workingDir, logfile) if os.path.isfile(logpath): return logpath return None def runTests(self, tests, _stdout, _stderr, workingDir, args = []): if not self.isRunnable(): print >> _stderr, "Error:", self.error if self.error: return [] if self.adb and self.targetos == "android": print "adb command:", " ".join(self.adb) if not tests: tests = self.tests logs = [] for test in tests: t = self.getTest(test) if t: logfile = self.runTest(t, workingDir, _stdout, _stderr, args) if logfile: logs.append(os.path.relpath(logfile, ".")) else: print >> _stderr, "Error: Test \"%s\" is not found in %s" % (test, self.tests_dir) return logs def getRunArgs(args): run_args = [] for path in args: path = os.path.abspath(path) while (True): if os.path.isdir(path) and os.path.isfile(os.path.join(path, "CMakeCache.txt")): run_args.append(path) break npath = os.path.dirname(path) if npath == path: break path = npath return run_args if hostos == "nt": def moveTests(instance, destination): src = os.path.dirname(instance.tests_dir) # new binaries path newBinPath = os.path.join(destination, "bin") try: # copy binaries and CMakeCache.txt to the specified destination shutil.copytree(src, newBinPath) shutil.copy(os.path.join(instance.path, "CMakeCache.txt"), os.path.join(destination, "CMakeCache.txt")) except Exception, e: print "Copying error occurred:", str(e) exit(e.errno) # pattern of CMakeCache.txt string to be replaced replacePattern = re.compile("EXECUTABLE_OUTPUT_PATH:PATH=(.+)") with open(os.path.join(destination, "CMakeCache.txt"), "r") as cachefile: try: cachedata = cachefile.read() if hostos == 'nt': # fix path slashes on nt systems newBinPath = re.sub(r"\\", r"/", newBinPath) # replace old binaries path in CMakeCache.txt cachedata = re.sub(re.search(replacePattern, cachedata).group(1), newBinPath, cachedata) except Exception, e: print "Reading error occurred:", str(e) exit(e.errno) with open(os.path.join(destination, "CMakeCache.txt"), "w") as cachefile: try: cachefile.write(cachedata) except Exception, e: print "Writing error occurred:", str(e) exit(e.errno) exit() if __name__ == "__main__": test_args = [a for a in sys.argv if a.startswith("--perf_") or a.startswith("--gtest_")] argv = [a for a in sys.argv if not(a.startswith("--perf_") or a.startswith("--gtest_"))] parser = OptionParser(usage="run.py [options] [build_path]", description="Note: build_path is required if running not from CMake build directory") parser.add_option("-t", "--tests", dest="tests", help="comma-separated list of modules to test", metavar="SUITS", default="") if hostos == "nt": parser.add_option("-m", "--move_tests", dest="move", help="location to move current tests build", metavar="PATH", default="") parser.add_option("-w", "--cwd", dest="cwd", help="working directory for tests", metavar="PATH", default=".") parser.add_option("-a", "--accuracy", dest="accuracy", help="look for accuracy tests instead of performance tests", action="store_true", default=False) parser.add_option("-l", "--longname", dest="useLongNames", action="store_true", help="generate log files with long names", default=False) parser.add_option("", "--android_test_data_path", dest="test_data_path", help="OPENCV_TEST_DATA_PATH for Android run", metavar="PATH", default="/sdcard/opencv_testdata/") parser.add_option("", "--android_env", dest="android_env_array", help="Environment variable for Android run (NAME=VALUE)", action='append') parser.add_option("", "--android_propagate_opencv_env", dest="android_propagate_opencv_env", help="Propagate OPENCV* environment variables for Android run", action="store_true", default=False) parser.add_option("", "--configuration", dest="configuration", help="force Debug or Release configuration", metavar="CFG", default="") parser.add_option("", "--serial", dest="adb_serial", help="Android: directs command to the USB device or emulator with the given serial number", metavar="serial number", default="") parser.add_option("", "--package", dest="junit_package", help="Android: run jUnit tests for specified package", metavar="package", default="") parser.add_option("", "--help-tests", dest="help", help="Show help for test executable", action="store_true", default=False) parser.add_option("", "--check", dest="check", help="Shortcut for '--perf_min_samples=1 --perf_force_samples=1'", action="store_true", default=False) parser.add_option("", "--list", dest="list", help="List available tests", action="store_true", default=False) (options, args) = parser.parse_args(argv) if options.accuracy: options.mode = "test" else: options.mode = "perf" run_args = getRunArgs(args[1:] or ['.']) if len(run_args) == 0: print >> sys.stderr, "Usage:", os.path.basename(sys.argv[0]), "[options] [build_path]" print >> sys.stderr, "Please specify build_path or run script from CMake build directory" exit(1) options.android_env = {} if options.android_env_array: for entry in options.android_env_array: k, v = entry.split("=", 1) options.android_env[k] = v tests = [s.strip() for s in options.tests.split(",") if s] if len(tests) != 1 or len(run_args) != 1: # remove --gtest_output from params test_args = [a for a in test_args if not a.startswith("--gtest_output=")] if options.check: if not [a for a in test_args if a.startswith("--perf_min_samples=")] : test_args.extend(["--perf_min_samples=1"]) if not [a for a in test_args if a.startswith("--perf_force_samples=")] : test_args.extend(["--perf_force_samples=1"]) if not [a for a in test_args if a.startswith("--perf_verify_sanity")] : test_args.extend(["--perf_verify_sanity"]) logs = [] test_list = [] for path in run_args: suite = TestSuite(options, path) if hostos == "nt": if(options.move): moveTests(suite, options.move) #print vars(suite),"\n" if options.list: test_list.extend(suite.tests) else: logs.extend(suite.runTests(tests, sys.stdout, sys.stderr, options.cwd, test_args)) if options.list: print os.linesep.join(test_list) or "No tests found" if logs: print >> sys.stderr, "Collected: ", " ".join(logs) if errorCode != 0: print "Error code: ", errorCode, (" (0x%x)" % (errorCode & 0xffffffff)) exit(errorCode)