Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
8192 | georgesk | 1 | #!/usr/bin/python3 |
2 | |||
3 | import os, os.path, tempfile, re, copy, subprocess, locale |
||
4 | |||
5 | titlePattern=re.compile(r"^== (.*) ==$") |
||
6 | varPattern=re.compile(r"^(.*) == (.*)$") |
||
7 | rulePattern=re.compile(r"^----.*$") |
||
8 | commentPattern=re.compile(r"^\s*#.*$") |
||
9 | codingPattern=re.compile(r"^# -\*- coding: (.*) -\*-$") |
||
10 | |||
11 | locale.setlocale(locale.LC_ALL, '') |
||
12 | codeset=locale.nl_langinfo(locale.CODESET) # coding used by the user's locale |
||
13 | |||
14 | def notAtest(f): |
||
15 | """ |
||
16 | consider some filenames as not useable to perfom tests. |
||
17 | @param f a filename |
||
18 | @return True if f seems to be the name of a backup file |
||
19 | """ |
||
20 | return re.match(r".*~",f) or re.match(r".*\.bak",f) |
||
21 | |||
22 | def mkTestList(lines, cur, title, coding="utf-8"): |
||
23 | """ |
||
24 | makes a list of objects for unitary tests of Wims modtool primitives |
||
25 | @param lines a list of lines coming from a file |
||
26 | @param cur a pointer on the current line to be read |
||
27 | @param title a title which has been read from a previous line |
||
28 | @param coding the coding of the .proc file used for tests |
||
29 | @return a list of Test objects |
||
30 | """ |
||
31 | result=[] |
||
32 | proclines=[] |
||
33 | variables={} |
||
34 | currentVar=None |
||
35 | while cur < len(lines): |
||
36 | l=lines[cur].decode(coding).replace('\n','') |
||
37 | t=titlePattern.match(l) |
||
38 | v=varPattern.match(l) |
||
39 | r=rulePattern.match(l) |
||
40 | c=commentPattern.match(l) |
||
41 | if t: |
||
42 | result.append(Test(title, proclines, variables, coding)) |
||
43 | title=t.group(1) |
||
44 | proclines=[] |
||
45 | variables={} |
||
46 | currentVar=None |
||
47 | elif v: |
||
48 | currentVar=v.group(1) |
||
49 | variables[currentVar]=v.group(2) |
||
50 | elif currentVar!=None and len(l)>0: |
||
51 | variables[currentVar]+='\n'+l |
||
52 | elif not r and not c: |
||
53 | # not a title, nor variable, nor a ruler, nor a comment: |
||
54 | # these are commands |
||
55 | proclines.append(lines[cur].strip()) |
||
56 | cur = cur+1 |
||
57 | result.append(Test(title, proclines, variables, coding)) |
||
58 | return result |
||
59 | |||
60 | class Test: |
||
61 | """ |
||
62 | This class implements data for a unitary test of Wims. |
||
63 | Each test has a title, a set of lines to be pasted into |
||
64 | a .proc file for Wims, and a set assertions about |
||
65 | variables and the values which they should be assigned |
||
66 | upon completion of the test. |
||
67 | """ |
||
68 | def __init__(self, title, proclines, variables, coding="utf-8"): |
||
69 | """ |
||
70 | The constructor. |
||
71 | @param title a title for the unitary test |
||
72 | @param proclines a list of lines to be processed by Wims in a |
||
73 | .proc file. |
||
74 | @param variables a dictionary associating variable names with |
||
75 | values they should have when the test finishes. |
||
76 | @param coding the coding used for the .proc file |
||
77 | """ |
||
78 | self.title=""+title |
||
79 | self.proclines=copy.copy(proclines) |
||
80 | self.variables=copy.copy(variables) |
||
81 | self.coding=coding |
||
82 | self.gotResults={} |
||
83 | self.errors=[] |
||
84 | self.success=None |
||
85 | |||
86 | def __str__(self): |
||
87 | """ |
||
88 | Conversion to a str. |
||
89 | """ |
||
90 | result="Test {title=«%s»," %(self.title,) |
||
91 | result+=" coding=%s," %self.coding |
||
92 | result+=" proclines=%s," %self.proclines |
||
93 | result+=" variables=%s" %self.variables |
||
94 | result+="}" |
||
95 | return result |
||
96 | |||
97 | def __repr__(self): |
||
98 | """ |
||
99 | The representation is the same as the conversion to a str. |
||
100 | """ |
||
101 | return self.__str__() |
||
102 | |||
103 | def gotError(self): |
||
104 | """ |
||
105 | checks whether some error occurred |
||
106 | @return True if an error came out of the wims subprocess |
||
107 | """ |
||
108 | return len(self.errors)>1 or (len(self.errors)==1 and |
||
109 | self.errors[0]!="") |
||
110 | |||
111 | def run(self, path, fName="test.proc"): |
||
112 | """ |
||
113 | runs the unitary tests, and gather the results in |
||
114 | self.gotResults, self.errors and self.success |
||
115 | @param path a path to the .proc file |
||
116 | @param fName the name of the .proc file, defaults to "test.proc" |
||
117 | """ |
||
118 | self.success=True |
||
119 | self.failedVars=[] |
||
120 | cmd="./wims test %s %s '%s'" %(path, |
||
121 | fName, |
||
122 | ' '.join([v for v in self.variables])) |
||
123 | with open(os.path.join(path,fName), "wb") as outfile: |
||
124 | for l in self.proclines: |
||
125 | outfile.write(l+b'\n'); |
||
126 | outfile.close() |
||
127 | p=subprocess.Popen(cmd, |
||
128 | shell=True, |
||
129 | stdout=subprocess.PIPE, |
||
130 | stderr=subprocess.PIPE) |
||
131 | out, err = p.communicate() |
||
132 | currentVar=None |
||
133 | for l in out.decode(self.coding).split("\n"): |
||
134 | v=varPattern.match(l) |
||
135 | if v: |
||
136 | currentVar=v.group(1) |
||
137 | self.gotResults[currentVar]=v.group(2) |
||
138 | else: |
||
139 | if currentVar!=None and len(l)>0: |
||
140 | # add an other line to the current variable |
||
141 | self.gotResults[currentVar]+='\n'+l |
||
142 | self.errors=err.decode(codeset).split("\n") |
||
143 | if self.gotError(): |
||
144 | self.success=False |
||
145 | for v in self.variables: |
||
146 | if v not in self.gotResults or self.variables[v]!=self.gotResults[v]: |
||
147 | self.failedVars.append(v) |
||
148 | self.success=False |
||
149 | |||
150 | def showResult(self, msg="", tmpDir="/tmp"): |
||
151 | """ |
||
152 | runs the test if self.success is undefined, and |
||
153 | pretty-prints the result |
||
154 | @param msg a message string to prepend to the result (for instance, |
||
155 | a test number) |
||
156 | @param tmpDir a directory to run the tests |
||
157 | """ |
||
158 | if self.success==None: |
||
159 | self.run(tmpDir) |
||
160 | if self.success: |
||
161 | print("[%s] %s: OK." %(msg, self.title)) |
||
162 | else: |
||
163 | hrule="[%s] ========< %s >==========" %(msg, self.title) |
||
164 | print(hrule) |
||
165 | for l in self.proclines: |
||
166 | print(l) |
||
167 | hrule="="*len(hrule) |
||
168 | print(hrule) |
||
169 | if self.gotError(): |
||
170 | print ("ERROR: %s" %self.errors) |
||
171 | for v in self.failedVars: |
||
172 | if v in self.gotResults: |
||
173 | print("FAILED for variable %s, expected: «%s»; got: «%s»" |
||
174 | %(v, self.variables[v], self.gotResults[v])) |
||
175 | else: |
||
176 | print("FAILED for variable %s, expected: «%s»; got nothing" |
||
177 | %(v, self.variables[v])) |
||
178 | for v in self.variables: |
||
179 | if v not in self.failedVars: |
||
180 | print("OK for variable %s, expected: «%s»; got: «%s»" |
||
181 | %(v, self.variables[v], self.gotResults[v])) |
||
182 | print(hrule) |
||
183 | |||
184 | |||
185 | |||
186 | if __name__ == "__main__": |
||
187 | print ("Test suite for Wims.") |
||
188 | for dirpath, dirnames, filenames in os.walk("test"): |
||
189 | for f in filenames: |
||
190 | if notAtest(f): |
||
191 | continue |
||
192 | lines=open(os.path.join(dirpath, f),"rb").readlines() |
||
193 | cur=0 |
||
194 | t=titlePattern.match(lines[cur].decode("utf-8")) |
||
195 | coding = "utf-8" # default |
||
196 | while cur < len(lines) and not t: |
||
197 | # take in account coding declaration |
||
198 | c=codingPattern.match(lines[cur].decode("utf-8")) |
||
199 | if c: |
||
200 | coding=c.group(1) |
||
201 | cur+=1 |
||
202 | t=titlePattern.match(lines[cur].decode(coding)) |
||
203 | if cur < len(lines): |
||
204 | print("Running tests from {}/{} (coding: {})".format(dirpath, f, coding)) |
||
205 | title=t.group(1) |
||
206 | tests=mkTestList(lines, cur, title, coding=coding) |
||
207 | with tempfile.TemporaryDirectory(prefix='wimstest-') as tmpDir: |
||
208 | i=1 |
||
209 | ok=0 |
||
210 | nok=0 |
||
211 | for t in tests: |
||
212 | t.showResult(tmpDir=tmpDir, msg=i) |
||
213 | i += 1 |
||
214 | if t.success: |
||
215 | ok+=1 |
||
216 | else: |
||
217 | nok+=1 |
||
218 | print ("Ran {} tests; {} OK, {} WRONG.".format(i-1,ok,nok)) |
||
219 | else: |
||
220 | print("Error: the first line of the file shouldcontain some == title ==") |
||
221 | os.exit(1) |
||
222 | |||
223 | |||
224 |