runtime-test
一般来说,我们的测试代码是测试自己写的代码,测试模块的功能,但如果我们想自动化测试一个.py文件或者exe程序,就完全不一样了
如何自己写一个简易的测评姬?就是实现自动读取样例和输出样例对比来确定程序是否正确
思路:启动一个子线程,将子线程的输入输出流重定向方便我们获取,然后样例标志答案保存至文件,读取即可。
需要用到Python的subprocess模块
subprocess模块
运行python的时候,我们都是在创建并运行一个进程。像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序。在Python中,我们通过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序。
subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用。另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。
-
subprocess.call()
父进程等待子进程完成
返回退出信息(return code,相当于Linux exit code) -
subprocess.check_call()
父进程等待子进程完成并返回0
检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查 -
subprocess.Popen()
启动一个子线程,它有一个复杂的构造函数
1 | def __init__(self, args, bufsize=-1, executable=None, |
经常用到的参数也不多:
参数 | 解释 |
---|---|
args | 要执行的shell命令,可以是字符串,也可以是命令各个参数组成的序列。当该参数的值是一个字符串时,该命令的解释过程是与平台相关的,因此通常建议将args参数作为一个序列传递。 |
bufsize | 指定缓存策略,0表示不缓冲,1表示行缓冲,其他大于1的数字表示缓冲区大小,负数 表示使用系统默认缓冲策略。 |
stdin,stdout | 用于重定向标准的流。 |
shell | 该参数用于标识是否使用shell作为要执行的程序,如果shell值为True,则建议将args参数作为一个字符串传递而不要作为一个序列传递。 |
universal_newlines | 不同系统的换行符不同。若True,则该文件对象的stdin,stdout和stderr将会以文本流方式打开;否则以二进制流方式打开。 |
比较特殊的地方是,我们可以指定使用subprocess.PIPE
并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe),这实际就是建立了一块缓冲区
Popen的方法:
函数 | 解释 |
---|---|
Popen.poll() | 用于检查子进程是否已经结束。设置并返回returncode属性。 |
Popen.wait() | 等待子进程结束。设置并返回returncode属性。 |
Popen.send_signal(signal) | 向子进程发送信号。 |
Popen.terminate() | 停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。 |
Popen.kill() | 杀死子进程。 |
Popen.stdin() | 如果在创建Popen对象时,参数stdin被设置为PIPE,Popen.stdin将返回一个文件对象用于向子进程发送指令。否则返回None。 |
Popen.stdout | 类似上 |
Popen.pid | 获取子进程的进程ID。 |
Popen.returncode | 获取进程的返回值。如果进程还没有结束,返回None。 |
Popen.communicate()
用于获取缓冲区的数据并且避免死锁情况:
如果 stdout或 stderr 参数是 pipe,并且程序输出超过操作系统的 pipe size时,如果使用 Popen.wait() 方式等待程序结束获取返回值,会导致死锁,程序卡在 wait() 调用上。
需要注意的是,communicate获取到的是一个元组,包含stdout和stderr
同时communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成,所以不需要wait了
重定向标志流
sys.stdin = 文件流即可
获取文件的路径
使用sys.getcwd(),得到的就是你文件的所在文件夹路径
简单测评姬的代码
1 | class VirtualTestGirl: |
- 需要注意的是,不同平台下的换行符不同,MAC下是\r, Windows下默认是\r\n,Linux下是\n
- 有神奇的一个地方是,print函数打印出来的是\n, 但定向输出到PIPE里然后再读出时变成了\r\n(可以split切一下\n看看会发现每行多了一个\r),推测为写入内存里的时候发生了替换,因为Windows下是\r\n
- rstrip()会去除字符串末尾的指定字符,而strip()是删去字符串中所有匹配到的指定字符(默认删除\r\n)