#前言

因为必须要给同学们的C程序看代码,但是挨个手工检查也太麻烦且耗时间了,所以还是弄个小小的测试系统吧。

#系统流程

大体的系统流程如图1所示,将学生们编写的C程序文件收集到服务器上后,编译并测试程序,将信息记录并最终展示给学生,方便他们反复提交和修改。

系统流程图

图1 系统流程图

#收集代码

但凡一种网页语言都有文件上传接口,所以只需要网页表单即可完成代码的收集工作,但我为了偷懒😂,选择了用腾讯微云的文件收集功能,具体可以自行了解。

在收集代码的时候,规定了文件的命名格式为 学号(题号) 的方式,所以可以得到代码的成员信息和题号信息而不必额外附加说明。

#编译

因为想用命令行的c语言工具,最方便的大概就是 gcc 了。
window下gcc安装比较麻烦,相关方法可以参照 MinGW | Minimalist GNU for Windows 进行安装。
MacOS和Linux都直接命令行自带gcc,直接使用即可,但后两者在编译时会默认编译为 .o 结尾的文件,而windows是 .exe 结尾的文件,不过问题不大,指定一下即可。

不过需要注意的是在windows命令行下运行可执行文件的时候,不需要加 ./ ,所以在系统上需要加以区分。在命名中的一些诸如 () 之类的符号在MacOS下需要转义加 \,但是如果在windows下进行转义又会出错,也要加以区分。

#测试

测试是测试C语言程序有没有正确的必要步骤,只要和学生们约定好输入输出的格式,到时候只需要将程序执行结果与标准答案进行字符匹配即可。
这里需要注意一个问题是,MacOS下的换行一般以 \n 为主,而windows下则以 \r\n 为主,在匹配前需要将 \r 移除。

#展示

这个展示就因人而异了,我选择的是bootstrap的table样式表进行展示。

#具体实现

整个流程控制是用python完成的,一个 pipe.py 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import subprocess
import os
import time
import re
import json
import platform

# 文件命名格式: 学号(题目).cpp
# 例子: 23020181154229(1).cpp

dir = os.path.abspath(os.path.dirname(__file__))
os_type = '1'

if(platform.system() == 'Windows'):
os_type = '2'


def match(name):
res = re.search(r'^([0-9]+)\(([0-9]+)\)', name)
if(res):
if(name.endswith(".c") or name.endswith(".cpp")):
return True
return False


def untest(name):
res = re.search(r'^0-([0-9]{14})-([0-9]+)\(([0-9]+)\)', name)
if(res):
return True
return False


def edit(dir):
for home, dirs, files in os.walk(dir):
for filename in files:
if match(filename):

os.rename(os.path.join(home, filename), os.path.join(
home, time.strftime("0-%Y%m%d%H%M%S-", time.localtime()) + filename))


def generate(dir):
for home, dirs, files in os.walk(dir):
for filename in files:
if untest(filename):
newname = '1' + filename[1:]

os.rename(os.path.join(home, filename),
os.path.join(home, newname))
return newname
return ''


def build(cname, oname):
try:
res = subprocess.check_output("gcc \"%s\" -o %s" % (cname, oname),
stderr=subprocess.STDOUT,
shell=True)
return True
except subprocess.CalledProcessError as e:
return False


def run(oname, idata, odata):
r, w = os.pipe()
os.write(w, bytes(idata, "utf-8"))
os.close(w)

if(os_type == '1'):
try:
res = subprocess.check_output("./%s" % (oname), stdin=r, timeout=5)
except subprocess.TimeoutExpired as e:
return False
except subprocess.CalledProcessError as e:
return False
if(os_type == '2'):
try:
res = subprocess.check_output("%s" % (oname), stdin=r, timeout=5)
except subprocess.TimeoutExpired as e:
return False
except subprocess.CalledProcessError as e:
return False
try:
result = res.decode("utf-8").replace('\r', '')
except:
return False

if result.strip().replace(" ","") == odata.strip().replace(" ",""):
return True
print(result)
return False


def test(oname, question):
question_list = {
'1': [['24', '0'], ['11', '1'], ['0', '0'], ['-2', '0'], ['-21', '1']],
'2': [['a', 'a'], ['A', 'a'], ['Z', 'z'], ['0', '0'], ['Q', 'q']],
'3': [['12/14/16\n10/10/15', '10/10/15'], ['13/12/16\n10/10/15', '-1']],
'4': [['1 2 3 4', '4 1']],
'5': [['3 4 5', '1\n1'], ['1.5 1.5 1.5', '1\n0'], ['0 0 0', '0\n0'], ['-3 -4 -5', '0\n0']],
'6': [['2019 1 1', '1'], ['1900 3 1', '60'], ['2000 3 1', '61']],
'7': [['1 2 3 4 5 6 7 8 9 10', '1 1\n'], ['89 56 23 14 789 54 1223 78 12 65', '12 9\n'], ['3 2 1 1 5 6 7 8 9 10', '1 3\n']],
'8':[['1 2 3 4 5 6 7 8 9 10', '55.00 5.50\n'],['1 1 1 1 1 1 1 1 1 1', '10.00 1.00\n'],['0.6798 0.5958 0.5754 0.4854 0.3895 0.2901 0.1942 0.1053 0.045 0.003','3.40 0.34\n']],
'9':[['1 2 3 4 5\n2 3 4 5 1\n3 4 5 1 2\n4 5 1 2 3\n5 1 2 3 4','15 15 15 15 15\n3.00 3.00 3.00 3.00 3.00\n3.00 3.00 3.00 3.00 3.00\n5 5 5 5 5\n1 1 1 1 1'],['24 14 8 0 46\n36 20 20 20 96\n34 20 20 2 76\n40 20 20 18 98\n36 20 20 20 96','92 192 152 196 192\n18.40 38.40 30.40 39.20 38.40\n34.00 18.80 17.60 12.00 82.40\n40 20 20 20 98\n24 14 8 0 46']],
'10':[['ASJFKLSJFLSJFLSJFIIQJKZVNIRUQ','13\n'],['ABCDEFGHIJKLMNOPQRSTUVWXY','25\n'],['ONNSISSTUPID','8\n']],
'11':[['dzldzldzl\nd','3\n'],['13757769913\n3','2\n'],['2302018dzllp\nl','2\n'],['QCHZK\nq','0\n']],
'12':[['ASJFKLSJFLSJFLSJFIIQJKZVNIRUQ','ASJFKLSJFLSJFLSJFIIQJKZVNIRUQ\n'],['ABCDEFGHIJKLMNOPQRSTUVWXY','ABCDEFGHIJKLMNOPQRSTUVWXY\n']]
}
if(not question in question_list):
return False
for test_data in question_list[question]:
if(not run(oname, test_data[0], test_data[1])):
print(test_data[0])
print(test_data[1])
return False
return True


def write(data):
f = open(os.path.join(dir, 'result.json'), 'r')
old_data = json.loads(f.read()[9:-1])
f.close()
print(data)
old_data.append(data)
f = open(os.path.join(dir, 'result.json'), 'w')
f.write('callback('+json.dumps(old_data) + ')')
f.close


if __name__ == "__main__":
os.chdir(dir)
while(True):
homework_dir = os.path.join(dir, 'homework2')
edit(homework_dir)
while(True):
cname = generate(homework_dir)
if(cname == ''):
break

res = re.search(r'^1-([0-9]{14})-([0-9]+)\(([0-9]+)\)', cname)
if(res):
date = res.group(1)
xmuid = res.group(2)
question = res.group(3)
cname = os.path.join(homework_dir, cname)
status = '0'
if build(cname, 'raw.o'):
if(test('raw.o', question)):
status = '1'
else:
status = '3'
else:
status = '2'
write({'date': date[:8], 'time': date[8:10] + ':' + date[10:12] + ':' + date[12:14],
'xmuid': xmuid, 'question': question, 'status': status})
time.sleep(5)