Last active
December 18, 2015 19:28
-
-
Save KillerGoldFisch/5832668 to your computer and use it in GitHub Desktop.
DocTest for .NET AssemblyTest
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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