Skip to content

Instantly share code, notes, and snippets.

@KillerGoldFisch
Last active December 18, 2015 19:28
Show Gist options
  • Save KillerGoldFisch/5832668 to your computer and use it in GitHub Desktop.
Save KillerGoldFisch/5832668 to your computer and use it in GitHub Desktop.
DocTest for .NET AssemblyTest
#!/usr/bin/env ipy
# coding: utf8
"""
DocTest for .NET AssemblyTest
=============================
Usage:
------
ipy doctestnet.py -a <Assembly-File> [-s <Sinature-Pattern>] [-v] [-p]
-a : The Assembly File to test, there have to be an XML File named <Assembly>.XML in the same Directory
-s : A Pattern for the Signature, excample: *.Test*
-v : Verbose, Prin additional informations
-p : Pause the script, if any Test goes wrong
Before you use this Tool, you have to activate the XML Documentation for the Project and rebuild it:
http://msdn.microsoft.com/en-us/library/vstudio/x4sa0ak0(v=vs.100).aspx
Dependencies:
-------------
- BeautifulSoup
- IronPython
Sample C# Class with doctest:
-----------------------------
.. code-block:: csharp
/// <summary>
/// <doctest>
/// >>> t = Test()
/// </doctest>
/// </summary>
public class Test
{
/// <summary>
/// <doctest>
/// >>> t.test() <para/>
/// 'test'
/// </doctest>
/// </summary>
/// <returns></returns>
public string test() { return "test"; }
}
"""
__author__ = "Kevin Gliewe aka KillerGoldFisch"
__copyright__ = "Copyright 2013, Kevin Gliewe"
__credits__ = ["kevin Gliewe",]
__license__ = "GPL"
__version__ = "1.0.0"
__date__ = "2013-06-21"
__maintainer__ = "Kevin Gliewe"
__email__ = "kevingliewe@gmail,com"
__status__ = "Production"
import doctest, os, sys, fnmatch
from bs4 import BeautifulSoup
verbose = False
#Testchunk for one Module
class TestChunk:
def __init__(self, membersoup, verbose = False):
#signature of Chunk <namespace>.<type>(.<method>|.<property>)
self._signature = membersoup["name"].split(":")[1]
self._doctests = []
def addDocTest(self, doctestsoup):
self._doctests.append(doctestsoup)
def writeDocTest(self, stream):
#import namespace
stream.write(">>> from %s import *\n"%self._namespace)
for doctest in self._doctests:
#Add each line of <doctest/> to testfile
content = doctest.contents[0]
lines = [line.strip() for line in content.split("\n")]
#Add the Sgnature in a Comment at the End of the line if it starts with ">>>"
lines = [line + "\t\t# " + self._signature if line[:3] == ">>>" else line for line in lines]
content = "\n".join([line for line in lines if len(line) > 0])
content = content.replace("<para/>", "")
stream.write(content + "\n")
#Testchunk for Types (Classes, Interfaces...)
class TestType(TestChunk):
def __init__(self, membersoup, verbose = False):
TestChunk.__init__(self, membersoup, verbose)
self._namespace = ".".join(self._signature.split(".")[:-1])
self._class = self._signature.split(".")[-1]
if verbose:
print "New test for Type : %s"%self._signature
#Testchunk for Methods
class TestMethod(TestChunk):
def __init__(self, membersoup, verbose = False):
TestChunk.__init__(self, membersoup, verbose)
splited_signature = self._signature.split("(")[0].split(".")
self._namespace = ".".join(splited_signature[:-2])
self._class = splited_signature[-2]
self._method = splited_signature[-1]
if verbose:
print "New test for Method : %s"%self._signature
#Testchunk for Propertys
class TestProperty(TestChunk):
def __init__(self, membersoup, verbose = False):
TestChunk.__init__(self, membersoup, verbose)
splited_signature = self._signature.split(".")
self._namespace = ".".join(splited_signature[:-2])
self._class = splited_signature[-2]
self._property = splited_signature[-1]
if verbose:
print "New test for Property : %s"%self._signature
_testTypes = {
"T" : TestType,
"M" : TestMethod,
"P" : TestProperty,
}
class AssemblyTest:
def __init__(self, assemblyPath, signature = "*", verbose = False):
self._signature = signature
self._verbose = verbose
self._assembly = os.path.abspath(assemblyPath)
self._assemblyBasedir = os.path.dirname(self._assembly)
self._assemblyName = os.path.splitext(os.path.basename(self._assembly))[0]
self._assemblyXMLFile = os.path.join(self._assemblyBasedir, self._assemblyName + os.path.extsep + "XML")
self._assemblyDocTestFile = os.path.join(self._assemblyBasedir, self._assemblyName + os.path.extsep + "doctest")
if verbose:
print ""
print "Handle Assebmly : '%s'"%self._assembly
print " Basedir : '%s'"%self._assemblyBasedir
print " Name : '%s'"%self._assemblyName
print " XML File : '%s'"%self._assemblyXMLFile
print " DocTest File : '%s'"%self._assemblyDocTestFile
print ""
def findDoctests(self):
soup = BeautifulSoup(open(self._assemblyXMLFile))
self._tests = []
for member in soup.find_all('member'):
#All <doctest/> entrys in XML file
doctests = member.find_all('doctest')
#If there where no entry, continue on next member
if len(doctests) == 0:
continue
#The type of the Test (Type, Method of Property)
testtype = member["name"][0]
#The Signature of the entry -> <namespace>.<type>(.<method>|.<property>)
typesignature = member["name"][2:]
#If the Signature dues not match, continue next member
if not fnmatch.fnmatch(typesignature, self._signature):
continue
#If the type fo member is not Supportet, continue on next member
if not _testTypes.has_key(testtype):
if self._verbose:
print "No handler found for '%s'"%testtype
continue
#Get TestChunk
testtype = _testTypes[testtype]
#Generate TestChunk
tester = testtype(member, self._verbose)
for doctest in doctests:
tester.addDocTest(doctest)
self._tests.append(tester)
def writeDocTest(self, stream):
#Add a reference to the assembly
stream.write(">>> import clr\n")
stream.write(">>> clr.AddReference('%s')\n"%os.path.basename(self._assembly))
for test in self._tests:
test.writeDocTest(stream)
def writeDocTestToFile(self, filename):
stream = open(filename, "w")
self.writeDocTest(stream)
stream.close()
def saveDocTest(self):
self.writeDocTestToFile(self._assemblyDocTestFile)
def runDocTest(self):
self.saveDocTest()
sys.path.append(self._assemblyBasedir)
return doctest.testfile(self._assemblyDocTestFile, verbose=self._verbose)
if __name__ == "__main__":
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-a", "--assembly", dest="filename",
help="Assembly-File to test.", metavar="FILE")
parser.add_option("-s", "--signature", dest="signature",
help="Matchpattern for type signature", default="*")
parser.add_option("-v", action="store_true", dest="verbose", default=False)
parser.add_option("-p", action="store_true", dest="pause", default=False)
(options, args) = parser.parse_args()
verbose = options.verbose
assemblyFile = options.filename
signature = options.signature
assembly = AssemblyTest(assemblyFile, signature, verbose)
assembly.findDoctests()
result = assembly.runDocTest()
if options.pause and result.failed > 0:
sys.stdin.readline()
sys.exit(result.failed)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment