之前由于项目组的smoke test太过频繁,而且这些工具大部分功能都是通过命令行执行的,这么便利的条件下不做一套自动测试脚本真是没有天理了!
在开发过程中发现一个问题:代码中同时使用了getchar()和getch()两个函数,从自动测试角度看,getchar()好处理,它的来源是缓冲区,利用python的管道很容易搞定,而getch()就麻烦了,它是直接来源于键盘中断,管道在这里就没用了....
这怎么办呢?思来想去只能实现一个虚拟键盘来解决这个问题了,在Windows中还好,有一个现成的python模块可以利用,直接调用Windows API调用虚拟键盘,你可以在这里找到它:http://sourceforge.net/projects/pywin32/files%2Fpywin32/。在linux里比较麻烦,好像没有现成的python模块支持,没办法只能自己写个小程序实现了,简单起见,还是用c吧,这个比较熟悉...
首先是linux下虚拟键盘的C代码实现:
#include <linux/input.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
typedef struct Ascii2KeyCode{
unsigned char ascii;
int keycode_sum;
unsigned int keycode[3];
}Ascii2KeyCode_t;
#define Backspace 14
#define Shift 42
#define Control 29
#define Alt 56
#define CapsLock 58
#define Esc 1
#define PageUp 104
#define PageDown 109
#define End 107
#define Home 102
#define Insert 110
#define Delete 111
#define Help 138
#define NumLock 69
Ascii2KeyCode_t ascii2keycode_map[] = {
{'\t', 1, {15,}},
{'\n', 1, {28,}},
{'\'', 1, {40,}},
{'\\', 1, {43,}},
{'\"', 2, {Shift,40}},
{' ', 1, {57,}},
{'!', 2, {Shift,2,}},
{'#', 2, {Shift,4,}},
{'$', 2, {Shift,5,}},
{'%', 2, {Shift,6,}},
{'&', 2, {Shift,8,}},
{'(', 2, {Shift,10,}},
{')', 2, {Shift,11,}},
{'*', 2, {Shift,9,}},
{'+', 2, {Shift,13,}},
{',', 1, {51,}},
{'-', 1, {12,}},
{'.', 1, {52,}},
{'/', 1, {53,}},
{'0', 1, {11,}},
{'1', 1, {2,}},
{'2', 1, {3,}},
{'3', 1, {4,}},
{'4', 1, {5,}},
{'5', 1, {6,}},
{'6', 1, {7,}},
{'7', 1, {8,}},
{'8', 1, {9,}},
{'9', 1, {10,}},
{':', 2, {Shift,39,}},
{';', 1, {39,}},
{'<', 2, {Shift,51,}},
{'=', 1, {13,}},
{'>', 2, {Shift,52,}},
{'?', 2, {Shift,53,}},
{'@', 2, {Shift,3,}},
{'A', 2, {Shift,30,}},
{'B', 2, {Shift,48,}},
{'C', 2, {Shift,46,}},
{'D', 2, {Shift,32,}},
{'E', 2, {Shift,18,}},
{'F', 2, {Shift,33,}},
{'G', 2, {Shift,34,}},
{'H', 2, {Shift,35,}},
{'I', 2, {Shift,23,}},
{'J', 2, {Shift,36,}},
{'K', 2, {Shift,37,}},
{'L', 2, {Shift,38,}},
{'M', 2, {Shift,50,}},
{'N', 2, {Shift,49,}},
{'O', 2, {Shift,24,}},
{'P', 2, {Shift,25,}},
{'Q', 2, {Shift,16,}},
{'R', 2, {Shift,19,}},
{'S', 2, {Shift,31,}},
{'T', 2, {Shift,20,}},
{'U', 2, {Shift,22,}},
{'V', 2, {Shift,47,}},
{'W', 2, {Shift,17,}},
{'X', 2, {Shift,45,}},
{'Y', 2, {Shift,21,}},
{'Z', 2, {Shift,44,}},
{'[', 1, {26,}},
{']', 1, {27,}},
{'^', 2, {Shift,7,}},
{'_', 2, {Shift,12,}},
{'`', 1, {41,}},
{'a', 1, {30,}},
{'b', 1, {48,}},
{'c', 1, {46,}},
{'d', 1, {32,}},
{'e', 1, {18,}},
{'f', 1, {33,}},
{'g', 1, {34,}},
{'h', 1, {35,}},
{'i', 1, {23,}},
{'j', 1, {36,}},
{'k', 1, {37,}},
{'l', 1, {38,}},
{'m', 1, {50,}},
{'n', 1, {49,}},
{'o', 1, {24,}},
{'p', 1, {25,}},
{'q', 1, {16,}},
{'r', 1, {19,}},
{'s', 1, {31,}},
{'t', 1, {20,}},
{'u', 1, {22,}},
{'v', 1, {47,}},
{'w', 1, {17,}},
{'x', 1, {45,}},
{'y', 1, {21,}},
{'z', 1, {44,}},
{'{', 2, {Shift,26,}},
{'|', 2, {Shift,43,}},
{'}', 2, {Shift,27,}},
{'~', 2, {Shift,41,}},
};
void simulate_key(int fd, unsigned int keycode, int keyvalue)
{
struct input_event event,ev;
event.type = EV_KEY;
event.code = keycode;
event.value = keyvalue;
gettimeofday(&event.time, 0);
memset(&ev, 0, sizeof(ev));
if (write(fd, &event, sizeof(event)) < 0) {
printf("simulate key error\n");
write(fd, &ev, sizeof(ev));
return;
}
write(fd, &ev, sizeof(ev));
}
void input_char(int fd, char ch_input)
{
int i = 0, j = 0;
Ascii2KeyCode_t keycode = {};
for (i=0; i<sizeof(ascii2keycode_map)/sizeof(Ascii2KeyCode_t); i++){
if (ch_input == ascii2keycode_map[i].ascii){
keycode = ascii2keycode_map[i];
// Key down
for (j=0; j<keycode.keycode_sum; j++){
simulate_key(fd, keycode.keycode[j], 1);
}
// Key up
for (j=keycode.keycode_sum; j>0; j--){
simulate_key(fd, keycode.keycode[j-1], 0);
}
break;
}
}
}
int main ( int argc, char *argv[] )
{
if (argc < 3){
printf("Please input as this : ./virtkeyboard /dev/input/event1 \"12324\"\n");
return -1;
}
// Open device
int fd_kbd = open(argv[1], O_RDWR);
if(fd_kbd <= 0) {
printf("error open keyboard:%s\n", strerror(errno));
return -1;
}
// Input chars one-by-one
char *pt = argv[2];
for (pt=argv[2]; *pt!='\0'; pt++){
input_char(fd_kbd, *pt);
}
// Close device
close(fd_kbd);
return 0;
}
用gcc编译,命名为“virtkeyboard”,然后copy到/usr/local/bin下,或者把存放路径加到PATH变量中。下面是python模块,屏蔽Windows和linux差异,实现虚拟键盘:
import platform
import time
import subprocess
if (platform.system().lower().find("windows") != -1):
Backspace = 8
Shift = 16
Control = 17
Alt = 18
CapsLock = 20
Esc = 27
PageUp = 33
PageDown = 34
End = 35
Home = 36
Insert = 45
Delete = 46
Help = 47
NumLock = 144
Ascii2KeyCode = {\
b'\t':[9,], \
b'\n':[13,], \
b'\'':[222,], \
b'\\':[220,], \
b'\"':[Shift,222,],\
b' ':[32,], \
b'!':[Shift,49,], \
b'#':[Shift,51,], \
b'$':[Shift,52,], \
b'%':[Shift,53,], \
b'&':[Shift,55,], \
b'(':[Shift,57,], \
b')':[Shift,48,], \
b'*':[Shift,56,], \
b'+':[Shift,187,],\
b',':[188,], \
b'-':[189,], \
b'.':[190,], \
b'/':[191,], \
b'0':[48,], \
b'1':[49,], \
b'2':[50,], \
b'3':[51,], \
b'4':[52,], \
b'5':[53,], \
b'6':[54,], \
b'7':[55,], \
b'8':[56,], \
b'9':[57,], \
b':':[Shift,186,],\
b';':[186,], \
b'<':[Shift,188,],\
b'=':[187,], \
b'>':[Shift,190,],\
b'?':[Shift,191,],\
b'@':[Shift,50,], \
b'A':[Shift,65,], \
b'B':[Shift,66,], \
b'C':[Shift,67,], \
b'D':[Shift,68,], \
b'E':[Shift,69,], \
b'F':[Shift,70,], \
b'G':[Shift,71,], \
b'H':[Shift,72,], \
b'I':[Shift,73,], \
b'J':[Shift,74,], \
b'K':[Shift,75,], \
b'L':[Shift,76,], \
b'M':[Shift,77,], \
b'N':[Shift,78,], \
b'O':[Shift,79,], \
b'P':[Shift,80,], \
b'Q':[Shift,81,], \
b'R':[Shift,82,], \
b'S':[Shift,83,], \
b'T':[Shift,84,], \
b'U':[Shift,85,], \
b'V':[Shift,86,], \
b'W':[Shift,87,], \
b'X':[Shift,88,], \
b'Y':[Shift,89,], \
b'Z':[Shift,90,], \
b'[':[219,], \
b']':[221,], \
b'^':[Shift,54,], \
b'_':[Shift,189,],\
b'`':[192,], \
b'a':[65,], \
b'b':[66,], \
b'c':[67,], \
b'd':[68,], \
b'e':[69,], \
b'f':[70,], \
b'g':[71,], \
b'h':[72,], \
b'i':[73,], \
b'j':[74,], \
b'k':[75,], \
b'l':[76,], \
b'm':[77,], \
b'n':[78,], \
b'o':[79,], \
b'p':[80,], \
b'q':[81,], \
b'r':[82,], \
b's':[83,], \
b't':[84,], \
b'u':[85,], \
b'v':[86,], \
b'w':[87,], \
b'x':[88,], \
b'y':[89,], \
b'z':[90,], \
b'{':[Shift,219,],\
b'|':[Shift,220,],\
b'}':[Shift,221,],\
b'~':[Shift,192,],\
}
if (platform.system().lower().find("windows") != -1):
import win32api
import win32con
def KeyDown(keyCode):
if (platform.system().lower().find("windows") != -1):
win32api.keybd_event(keyCode,0,0,0)
def KeyUp(keyCode):
if (platform.system().lower().find("windows") != -1):
win32api.keybd_event(keyCode,0,win32con.KEYEVENTF_KEYUP,0)
def InputChar(char=b''):
if (len(char) < 1):
return None
keyCodeList = Ascii2KeyCode.get(char)
if (keyCodeList == None):
print('Can not found char\'s key code : ' + char)
return None
for keyCode in keyCodeList:
KeyDown(keyCode)
for keyCode in keyCodeList[::-1]:
KeyUp(keyCode)
return char
def FindKeyDevice():
if (platform.system().lower().find("linux") != -1):
commandLine = ['cat', '/proc/bus/input/devices']
proc = subprocess.Popen(commandLine, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()
devInfoLines = out.decode().splitlines()
index = 0
while (index < len(devInfoLines)):
lineStr = devInfoLines[index].strip()
if (len(lineStr) < 1):
index += 1
continue
splitArry = lineStr.split(':')
if (len(splitArry) < 2):
index += 1
continue
if (splitArry[0].strip() == 'N'):
nameSplitArry = splitArry[1].split('=', 1)
if (len(nameSplitArry) < 2) or (nameSplitArry[0].strip() != 'Name'):
index += 1
continue
if (nameSplitArry[1].lower().find('keyboard') != -1) and (nameSplitArry[1].lower().find('virtual') == -1):
index += 4
if (index >= len(devInfoLines)):
print('Can not found keyboard device !')
return None
lineStr = devInfoLines[index].strip()
splitArry = lineStr.split(':')
if (len(splitArry) < 2):
index += 1
continue
if (splitArry[0].strip() == 'H'):
handlSplitArry = splitArry[1].split('=', 1)
if (len(handlSplitArry) < 2) or (handlSplitArry[0].strip() != 'Handlers') or (handlSplitArry[1].lower().find('event') == -1):
index += 1
continue
devSplitArry = handlSplitArry[1].split(' ')
for devStr in devSplitArry:
if (devStr.lower().find('event') != -1):
return devStr
index += 1
return None
def InputString(inputStr=b''):
if (platform.system().lower().find("windows") != -1):
for char in inputStr:
keyCode = ord(char)
InputChar(char)
return inputStr
elif (platform.system().lower().find("linux") != -1):
keyDev = FindKeyDevice()
if (keyDev == None):
print('Can not found key device !')
return None
keyDev = '/dev/input/' + keyDev
keyInputStr = '' + inputStr
commandLine = ['virtkeyboard', keyDev, keyInputStr]
proc = subprocess.Popen(commandLine, shell=False)
proc.wait()
else:
return None
if __name__ == '__main__':
InputString('auto_test\"sdfsdgsdggasd')
Windows下要先安装pywin32,然后就可以随心所欲发你想要的字符了!不过这里有个问题,由于是键盘中断,linux中每个终端貌似都能收到键盘输入事件,windows好像只有当前有效窗口能收到,以后再考虑吧,至少已经满足当前需求了。