最近开始学习整理python在windows自动化测试中的使用,觉得挺有意思的
主要思路,在windows下,主要通过启进程,然后查找进程的句柄,然后再操作这个句柄,包括点击,填写文字,关闭,获取文字等操作
下面以一个简单的校验文件md5值的操作来介绍一个python的应用,当然python中有校验md5的函数,不用非要使用工具来校验,这里只是练习使用python来自动化操作
所用的工具有SpyLite,用于查看窗口ID,句柄等信息
工具下载
http://www.xiazaiba.com/html/358.html
http://www.xiazaiba.com/html/5861.html
我们要达到的目的是通过md5校验工具将文件的md5值保存到一个log文档中
测试的目录结构
--automd5--
--needCheck--
checkmd5.py
SpyLite24.exe
Hash.exe
needCheck目录放需要检查md5的文件
005 | import os,sys,subprocess,win32gui,win32con,win32api |
009 | def createProcess(cmd, wait = False): |
011 | proc = tryExcept(subprocess.Popen, cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE) |
013 | proc = tryExcept(subprocess.Popen, cmd) |
021 | def tryExcept(func, *params, **paramMap): |
023 | return func(*params, **paramMap) |
026 | def isExcept(e, eType = Exception): |
027 | return isinstance(e, eType) |
032 | def parseClickConfig(clkCfg): |
034 | return None, None, False, 0 |
035 | if type(clkCfg) == bool: |
036 | return None, None, clkCfg, 0 |
037 | if type(clkCfg) == int: |
038 | return None, None, False, clkCfg |
040 | if type(clkCfg[0]) == int and type(clkCfg[1]) == int: |
041 | return clkCfg[0], clkCfg[1], False, 0 |
042 | return None, None, clkCfg[0], clkCfg[1] |
044 | if type(clkCfg[2]) == bool: |
045 | return clkCfg[0], clkCfg[1], clkCfg[2], 0 |
046 | return clkCfg[0], clkCfg[1], False, clkCfg[2] |
052 | def clickWindow(hwnd, clkCfg = None): |
056 | rect = getWindowRect(hwnd) |
059 | x, y, byDrv, mode = BaseWindow.parseClickConfig(clkCfg) |
061 | x = (rect[0] + rect[2]) / 2 |
067 | y = (rect[1] + rect[3]) / 2 |
072 | clickMouse(x, y, byDrv, mode) |
076 | CLICK_MOUSE_DOUBLE = 1 |
079 | def clickMouse(x, y, byDrv = False, mode = CLICK_MOUSE): |
080 | moveMouse(x, y, byDrv) |
081 | downMsg, upMsg = win32con.MOUSEEVENTF_LEFTDOWN, win32con.MOUSEEVENTF_LEFTUP |
082 | if mode == CLICK_MOUSE_RIGHT: |
083 | downMsg, upMsg = win32con.MOUSEEVENTF_RIGHTDOWN, win32con.MOUSEEVENTF_RIGHTUP |
084 | win32api.mouse_event(downMsg, 0, 0, 0, 0) |
085 | win32api.mouse_event(upMsg, 0, 0, 0, 0) |
086 | if mode == CLICK_MOUSE_DOUBLE: |
087 | win32api.mouse_event(downMsg, 0, 0, 0, 0) |
088 | win32api.mouse_event(upMsg, 0, 0, 0, 0) |
091 | def moveMouse(x, y, byDrv = False): |
092 | w, h = win32api.GetSystemMetrics(0), win32api.GetSystemMetrics(1) |
093 | x, y = int(float(x) / w * 65535), int(float(y) / h * 65535) |
094 | win32api.mouse_event(win32con.MOUSEEVENTF_MOVE | win32con.MOUSEEVENTF_ABSOLUTE, x, y, 0, 0) |
098 | def getWindowRect(hwnd): |
099 | rect = tryExcept(win32gui.GetWindowRect, hwnd) |
100 | if not isExcept(rect): |
105 | fgHwnd = win32gui.GetForegroundWindow() |
108 | rst = tryExcept(win32gui.SetForegroundWindow, hwnd) |
111 | return getWindowClass(fgHwnd) == 'Progman' and getWindowText(fgHwnd) == 'Program Manager' |
115 | def getWindowText(hwnd, buf = ctypes.c_buffer(1024)): |
116 | size = ctypes.sizeof(buf) |
117 | ctypes.memset(buf, 0, size) |
118 | tryExcept(win32gui.SendMessageTimeout, hwnd, win32con.WM_GETTEXT, size, buf, win32con.SMTO_ABORTIFHUNG, 10) |
119 | return buf.value.strip() |
122 | def getWindowClass(hwnd, buf = ctypes.c_buffer(1024)): |
123 | size = ctypes.sizeof(buf) |
124 | ctypes.memset(buf, 0, size) |
125 | ctypes.windll.user32.GetClassNameA(hwnd, ctypes.addressof(buf), size) |
126 | return buf.value.strip() |
131 | def findWindow(title, parentTitle = None, isRaw = False): |
132 | hwndList = findWindows(title, parentTitle, isRaw) |
135 | def findRawWindows(title, parentTitle = None): |
136 | return findWindows(title, parentTitle, True) |
139 | def isRawWindow(hwnd): |
140 | return not win32gui.IsWindowVisible(hwnd) or not win32gui.IsWindowEnabled(hwnd) or ctypes.windll.user32.IsHungAppWindow(hwnd) |
145 | def findWindows(title, parentTitle = None, isRaw = False): |
146 | def __enumWindowHandler__(hwnd, wndList): |
147 | text = re.split('[\r|\n]+', getWindowText(hwnd))[0].strip() |
148 | clazz = getWindowClass(hwnd).strip() |
149 | ctrlId = win32gui.GetDlgCtrlID(hwnd) |
150 | wndList.append((hwnd, text, clazz, ctrlId)) |
152 | parentHwndList = [None] |
153 | elif type(parentTitle) == int: |
154 | parentHwndList = [parentTitle] |
156 | parentHwndList = findRawWindows(parentTitle) |
158 | for parentHwnd in parentHwndList: |
162 | tryExcept(win32gui.EnumChildWindows, parentHwnd, __enumWindowHandler__, wndList) |
164 | win32gui.EnumWindows(__enumWindowHandler__, wndList) |
166 | hwnd, foundHwndList = None, [] |
168 | hwnd = tryExcept(win32gui.FindWindowEx, parentHwnd, hwnd, None, None) |
169 | if not hwnd or isExcept(hwnd) or hwnd in foundHwndList: |
171 | __enumWindowHandler__(hwnd, wndList) |
172 | foundHwndList.append(hwnd) |
175 | hwnd = tryExcept(win32gui.GetWindow, parentHwnd, win32con.GW_CHILD) |
177 | hwnd = tryExcept(win32gui.GetWindow, win32gui.GetDesktopWindow(), win32con.GW_CHILD) |
178 | while hwnd and not isExcept(hwnd): |
179 | __enumWindowHandler__(hwnd, wndList) |
180 | hwnd = tryExcept(win32gui.GetWindow, hwnd, win32con.GW_HWNDNEXT) |
182 | for hwnd, text, clazz, ctrlId in set(wndList): |
183 | if type(title) == int: |
186 | elif text == title or re.match('^' + title + '$', text, re.S) or clazz == title or re.match('^' + title + '$', clazz, re.S): |
189 | hwndSet.update(findRawWindows(title, hwnd)) |
194 | if not isRawWindow(hwnd): |
199 | def setWindowText(hwnd, text): |
200 | rst = tryExcept(win32gui.SendMessageTimeout, hwnd, win32con.WM_SETTEXT, 0, text, win32con.SMTO_ABORTIFHUNG, 10) |
201 | return not isExcept(rst) |
204 | def killProcessByName(pname, user = None): |
205 | killProcessByNames([pname], user) |
207 | def listFile(path, isDeep=True): |
211 | for root, dirs, files in os.walk(path): |
213 | _list.append('%s\%s' % (root, fl)) |
217 | for fn in glob.glob( path + os.sep + '*' ): |
218 | if not os.path.isdir(fn): |
219 | _list.append('%s' % path + os.sep + fn[fn.rfind('\\')+1:]) |
224 | def killProcessByNames(pnameList, user = None): |
225 | cmd = 'taskkill /F /T' |
227 | cmd += ' /FI "USERNAME eq %s"' % user |
228 | for pname in pnameList: |
229 | cmd += ' /IM %s' % pname |
230 | createProcess(cmd, True) |
233 | def handleTimeout(func, timeout, *params, **paramMap): |
235 | if type(timeout) == tuple: |
236 | timeout, interval = timeout |
239 | rst = func(*params, **paramMap) |
240 | if rst and not isExcept(rst): |
243 | timeout -= time.time() - t |
247 | def setFileData(filename, data, mode): |
248 | f = open(filename, mode) |
253 | if __name__ == '__main__': |
254 | if os.path.isfile('md5.log'): |
255 | os.system('del md5.log') |
257 | parentHwnd = r'Hash.*?' |
259 | filedir = os.path.join(os.getcwd(),'needCheck') |
260 | filelist = listFile(filedir) |
265 | createProcess('Hash.exe') |
267 | browse_button = findWindow(r'浏览.*?',parentHwnd) |
269 | clickWindow(browse_button) |
270 | textblack = findWindow(0x47C,'#32770') |
271 | handleTimeout(setWindowText,10,textblack,file) |
272 | open_hwnd = findWindow('打开.*?','#32770') |
274 | clickWindow(open_hwnd) |
277 | expect_content_id = [0,4] |
278 | content_hwnd = findWindow(0x3EA,r'Hash.*?') |
279 | content_text = handleTimeout(getWindowText,20,content_hwnd) |
280 | content = content_text.split('\r\n') |
281 | md5content = [i.split(': ')[1].strip() for ind, i in enumerate(content) if ind in expect_content_id] |
283 | filename,md5value = md5content |
284 | setFileData('md5.log','文件名:'+filename+'\n'+'md5:'+ md5value+'\n\n','a') |
285 | killProcessByName('Hash.exe') |