Subversion Repositories wimsdev

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  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.                
  225.