11831e9bbSHanoh Haim## This file is part of Scapy
21831e9bbSHanoh Haim## See http://www.secdev.org/projects/scapy for more informations
31831e9bbSHanoh Haim## Copyright (C) Philippe Biondi <phil@secdev.org>
41831e9bbSHanoh Haim## This program is published under a GPLv2 license
51831e9bbSHanoh Haim
61831e9bbSHanoh Haim"""
71831e9bbSHanoh HaimUnit testing infrastructure for Scapy
81831e9bbSHanoh Haim"""
91831e9bbSHanoh Haim
101831e9bbSHanoh Haimimport sys,getopt,imp
111831e9bbSHanoh Haimimport bz2, base64, os.path, time, traceback, zlib, hashlib
121831e9bbSHanoh Haim
131831e9bbSHanoh Haim
141831e9bbSHanoh Haim#### Import tool ####
151831e9bbSHanoh Haim
161831e9bbSHanoh Haimdef import_module(name):
171831e9bbSHanoh Haim    name = os.path.realpath(name)
181831e9bbSHanoh Haim    thepath = os.path.dirname(name)
191831e9bbSHanoh Haim    name = os.path.basename(name)
201831e9bbSHanoh Haim    if name.endswith(".py"):
211831e9bbSHanoh Haim        name = name[:-3]
221831e9bbSHanoh Haim    f,path,desc = imp.find_module(name,[thepath])
231831e9bbSHanoh Haim
241831e9bbSHanoh Haim    try:
251831e9bbSHanoh Haim        return imp.load_module(name, f, path, desc)
261831e9bbSHanoh Haim    finally:
271831e9bbSHanoh Haim        if f:
281831e9bbSHanoh Haim            f.close()
291831e9bbSHanoh Haim
301831e9bbSHanoh Haim
311831e9bbSHanoh Haim#### INTERNAL/EXTERNAL FILE EMBEDDING ####
321831e9bbSHanoh Haim
331831e9bbSHanoh Haimclass File:
341831e9bbSHanoh Haim    def __init__(self, name, URL, local):
351831e9bbSHanoh Haim        self.name = name
361831e9bbSHanoh Haim        self.local = local
371831e9bbSHanoh Haim        self.URL = URL
381831e9bbSHanoh Haim    def get_local(self):
391831e9bbSHanoh Haim        return bz2.decompress(base64.decodestring(self.local))
401831e9bbSHanoh Haim    def get_URL(self):
411831e9bbSHanoh Haim        return URL
421831e9bbSHanoh Haim    def write(self, dir):
431831e9bbSHanoh Haim        if dir:
441831e9bbSHanoh Haim            dir += "/"
451831e9bbSHanoh Haim        open(dir+self.name,"w").write(self.get_local())
461831e9bbSHanoh Haim
471831e9bbSHanoh Haim
481831e9bbSHanoh Haim# Embed a base64 encoded bziped version of js and css files
491831e9bbSHanoh Haim# to work if you can't reach Internet.
501831e9bbSHanoh Haimclass External_Files:
511831e9bbSHanoh Haim    UTscapy_js = File("UTscapy.js", "http://www.secdev.org/projects/UTscapy/UTscapy.js",
521831e9bbSHanoh Haim"""QlpoOTFBWSZTWWVijKQAAXxfgERUYOvAChIhBAC/79+qQAH8AFA0poANAMjQAAAG
531831e9bbSHanoh HaimABo0NGEZNBo00BhgAaNDRhGTQaNNAYFURJinplGaKbRkJiekzSenqmpA0Gm1LFMp
541831e9bbSHanoh HaimRUklVQlK9WUTZYpNFI1IiEWEFT09Sfj5uO+qO6S5DQwKIxM92+Zku94wL6V/1KTK
551831e9bbSHanoh Haiman2c66Ug6SmVKy1ZIrgauxMVLF5xLH0lJRQuKlqLF10iatlTzqvw7S9eS3+h4lu3
561831e9bbSHanoh HaimGZyMgoOude3NJ1pQy8eo+X96IYZw+ynehsiPj73m0rnvQ3QXZ9BJQiZQYQ5/uNcl
571831e9bbSHanoh Haim2WOlC5vyQqV/BWsnr2NZYLYXQLDs/Bffk4ZfR4/SH6GfA5Xlek4xHNHqbSsRbREO
581831e9bbSHanoh HaimgueXo3kcYi94K6hSO3ldD2O/qJXOFqJ8o3TE2aQahxtQpCVUKQMvODHwu2YkaORY
591831e9bbSHanoh HaimZC6gihEallcHDIAtRPScBACAJnUggYhLDX6DEko7nC9GvAw5OcEkiyDUbLdiGCzD
601831e9bbSHanoh HaimaXWMC2DuQ2Y6sGf6NcRuON7QSbhHsPc4KKmZ/xdyRThQkGVijKQ=""")
611831e9bbSHanoh Haim    UTscapy_css = File("UTscapy.css","http://www.secdev.org/projects/UTscapy/UTscapy.css",
621831e9bbSHanoh Haim"""QlpoOTFBWSZTWTbBCNEAAE7fgHxwSB//+Cpj2QC//9/6UAR+63dxbNzO3ccmtGEk
631831e9bbSHanoh HaimpM0m1I9E/Qp6g9Q09TNQ9QDR6gMgAkiBFG9U9TEGRkGgABoABoBmpJkRAaAxD1AN
641831e9bbSHanoh HaimGh6gNADQBzAATJgATCYJhDAEYAEiQkwIyJk0n6qenpqeoaMUeo9RgIxp6pX78kfx
651831e9bbSHanoh HaimJx4MUhDHKEb2pJAYAelG1cybiZBBDipH8ocxNyHDAqTUxiQmIAEDE3ApIBUUECAT
661831e9bbSHanoh Haim7Lvlf4xA/sVK0QHkSlYtT0JmErdOjx1v5NONPYSjrIhQnbl1MbG5m+InMYmVAWJp
671831e9bbSHanoh HaimuklD9cNdmQv2YigxbEtgUrsY2pDDV/qMT2SHnHsViu2rrp2LA01YJIHZqjYCGIQN
681831e9bbSHanoh HaimsGNobFxAYHLqqMOj9TI2Y4GRpRCUGu82PnMnXUBgDSkTY4EfmygaqvUwbGMbPwyE
691831e9bbSHanoh Haim220Q4G+sDvw7+6in3CAOS634pcOEAdREUW+QqMjvWvECrGISo1piv3vqubTGOL1c
701831e9bbSHanoh HaimssrFnnSfU4T6KSCbPs98HJ2yjWN4i8Bk5WrM/JmELLNeZ4vgMkA4JVQInNnWTUTe
711831e9bbSHanoh HaimgmMSlJd/b7JuRwiM5RUzXOBTa0e3spO/rsNJiylu0rCxygdRo2koXdSJzmUVjJUm
721831e9bbSHanoh HaimBOFIkUKq8LrE+oT9h2qUqqUQ25fGV7e7OFkpmZopqUi0WeIBzlXdYY0Zz+WUJUTC
731831e9bbSHanoh HaimRC+CIPFIYh1RkopswMAop6ZjuZKRqR0WNuV+rfuF5aCXPpxAm0F14tPyhf42zFMT
741831e9bbSHanoh HaimGJUMxxowJnoauRq4xGQk+2lYFxbQ0FiC43WZSyYLHMuo5NTJ92QLAgs4FgOyZQqQ
751831e9bbSHanoh HaimxpsGKMA0cIisNeiootpnlWQvkPzNGUTPg8jqkwTvqQLguZLKJudha1hqfBib1IfO
761831e9bbSHanoh HaimLNChcU6OqF+3wyPKg5Y5oSbSJPAMcRDANwmS2i9oZm6vsD1pLkWtFGbAkEjjCuEU
771831e9bbSHanoh HaimW1ev1IsF2UVmWYFtJkqLT708ApUBK/ig3rbJWSq7RGQd3sSrOKu3lyKzTBdkXK2a
781831e9bbSHanoh HaimBGLV5dS1XURdKxaRkMplLLQxsimBYZEAa8KQkYyI+4EagMqycRR7RgwtZFxJSu0T
791831e9bbSHanoh Haim1q5wS2JG82iETHplbNj8DYo9IkmKzNAiw4FxK8bRfIYvwrbshbEagL11AQJFsqeZ
801831e9bbSHanoh HaimWeXDoWEx2FMyyZRAB5QyCFnwYtwtWAQmmITY8aIM2SZyRnHH9Wi8+Sr2qyCscFYo
811831e9bbSHanoh HaimvzM985aHXOHAxQN2UQZbQkUv3D4Vc+lyvalAffv3Tyg4ks3a22kPXiyeCGweviNX
821831e9bbSHanoh Haim0K8TKasyOhGsVamTUAZBXfQVw1zmdS4rHDnbHgtIjX3DcCt6UIr0BHTYjdV0JbPj
831831e9bbSHanoh Haimr1APYgXihjQwM2M83AKIhwQQJv/F3JFOFCQNsEI0QA==""")
841831e9bbSHanoh Haim    def get_local_dict(cls):
851831e9bbSHanoh Haim        #return dict(map(lambda (x,y): (x, y.name),  filter(lambda (x,y): isinstance(y, File), cls.__dict__.items())))
861831e9bbSHanoh Haim        return dict(map(lambda a: (a[0], a[1].name),  filter(lambda a: isinstance(a[1], File), cls.__dict__.items())))
871831e9bbSHanoh Haim    get_local_dict = classmethod(get_local_dict)
881831e9bbSHanoh Haim    def get_URL_dict(cls):
891831e9bbSHanoh Haim        #return dict(map(lambda (x,y): (x, y.URL),  filter(lambda (x,y): isinstance(y, File), cls.__dict__.items())))
901831e9bbSHanoh Haim        return dict(map(lambda a: (a[0], a[1].URL),  filter(lambda a: isinstance(a[1], File), cls.__dict__.items())))
911831e9bbSHanoh Haim    get_URL_dict = classmethod(get_URL_dict)
921831e9bbSHanoh Haim
931831e9bbSHanoh Haim
941831e9bbSHanoh Haim#### HELPER CLASSES FOR PARAMETRING OUTPUT FORMAT ####
951831e9bbSHanoh Haim
961831e9bbSHanoh Haimclass EnumClass:
971831e9bbSHanoh Haim    def from_string(cls,x):
981831e9bbSHanoh Haim        return cls.__dict__[x.upper()]
991831e9bbSHanoh Haim    from_string = classmethod(from_string)
1001831e9bbSHanoh Haim
1011831e9bbSHanoh Haimclass Format(EnumClass):
1021831e9bbSHanoh Haim    TEXT  = 1
1031831e9bbSHanoh Haim    ANSI  = 2
1041831e9bbSHanoh Haim    HTML  = 3
1051831e9bbSHanoh Haim    LATEX = 4
1061831e9bbSHanoh Haim    XUNIT = 5
1071831e9bbSHanoh Haim
1081831e9bbSHanoh Haim
1091831e9bbSHanoh Haim#### TEST CLASSES ####
1101831e9bbSHanoh Haim
1111831e9bbSHanoh Haimclass TestClass:
1121831e9bbSHanoh Haim    def __getitem__(self, item):
1131831e9bbSHanoh Haim        return getattr(self, item)
1141831e9bbSHanoh Haim    def add_keywords(self, kw):
1151831e9bbSHanoh Haim        if kw is str:
1161831e9bbSHanoh Haim            self.keywords.append(kw)
1171831e9bbSHanoh Haim        else:
1181831e9bbSHanoh Haim            self.keywords += kw
1191831e9bbSHanoh Haim
1201831e9bbSHanoh Haimclass TestCampaign(TestClass):
1211831e9bbSHanoh Haim    def __init__(self, title):
1221831e9bbSHanoh Haim        self.title = title
1231831e9bbSHanoh Haim        self.filename = None
1241831e9bbSHanoh Haim        self.headcomments = ""
1251831e9bbSHanoh Haim        self.campaign = []
1261831e9bbSHanoh Haim        self.keywords = []
1271831e9bbSHanoh Haim        self.crc = None
1281831e9bbSHanoh Haim        self.sha = None
1291831e9bbSHanoh Haim        self.preexec = None
1301831e9bbSHanoh Haim        self.preexec_output = None
1311831e9bbSHanoh Haim    def add_testset(self, testset):
1321831e9bbSHanoh Haim        self.campaign.append(testset)
1331831e9bbSHanoh Haim    def __iter__(self):
1341831e9bbSHanoh Haim        return self.campaign.__iter__()
1351831e9bbSHanoh Haim    def all_tests(self):
1361831e9bbSHanoh Haim        for ts in self:
1371831e9bbSHanoh Haim            for t in ts:
1381831e9bbSHanoh Haim                yield t
1391831e9bbSHanoh Haim
1401831e9bbSHanoh Haimclass TestSet(TestClass):
1411831e9bbSHanoh Haim    def __init__(self, name):
1421831e9bbSHanoh Haim        self.name = name
1431831e9bbSHanoh Haim        self.set = []
1441831e9bbSHanoh Haim        self.comments = ""
1451831e9bbSHanoh Haim        self.keywords = []
1461831e9bbSHanoh Haim        self.crc = None
1471831e9bbSHanoh Haim        self.expand = 1
1481831e9bbSHanoh Haim    def add_test(self, test):
1491831e9bbSHanoh Haim        self.set.append(test)
1501831e9bbSHanoh Haim    def __iter__(self):
1511831e9bbSHanoh Haim        return self.set.__iter__()
1521831e9bbSHanoh Haim
1531831e9bbSHanoh Haimclass UnitTest(TestClass):
1541831e9bbSHanoh Haim    def __init__(self, name):
1551831e9bbSHanoh Haim        self.name = name
1561831e9bbSHanoh Haim        self.test = ""
1571831e9bbSHanoh Haim        self.comments = ""
1581831e9bbSHanoh Haim        self.result = ""
1591831e9bbSHanoh Haim        self.res = True  # must be True at init to have a different truth value than None
1601831e9bbSHanoh Haim        self.output = ""
1611831e9bbSHanoh Haim        self.num = -1
1621831e9bbSHanoh Haim        self.keywords = []
1631831e9bbSHanoh Haim        self.crc = None
1641831e9bbSHanoh Haim        self.expand = 1
1651831e9bbSHanoh Haim    def __nonzero__(self):
1661831e9bbSHanoh Haim        return self.res
1671831e9bbSHanoh Haim
1681831e9bbSHanoh Haim
1691831e9bbSHanoh Haim#### PARSE CAMPAIGN ####
1701831e9bbSHanoh Haim
1711831e9bbSHanoh Haimdef parse_campaign_file(campaign_file):
1721831e9bbSHanoh Haim    test_campaign = TestCampaign("Test campaign")
1731831e9bbSHanoh Haim    test_campaign.filename=  campaign_file.name
1741831e9bbSHanoh Haim    testset = None
1751831e9bbSHanoh Haim    test = None
1761831e9bbSHanoh Haim    testnb = 0
1771831e9bbSHanoh Haim
1781831e9bbSHanoh Haim    for l in campaign_file.readlines():
1791831e9bbSHanoh Haim        if l[0] == '#':
1801831e9bbSHanoh Haim            continue
1811831e9bbSHanoh Haim        if l[0] == "~":
1821831e9bbSHanoh Haim            (test or testset or campaign_file).add_keywords(l[1:].split())
1831831e9bbSHanoh Haim        elif l[0] == "%":
1841831e9bbSHanoh Haim            test_campaign.title = l[1:].strip()
1851831e9bbSHanoh Haim        elif l[0] == "+":
1861831e9bbSHanoh Haim            testset = TestSet(l[1:].strip())
1871831e9bbSHanoh Haim            test_campaign.add_testset(testset)
1881831e9bbSHanoh Haim            test = None
1891831e9bbSHanoh Haim        elif l[0] == "=":
1901831e9bbSHanoh Haim            test = UnitTest(l[1:].strip())
1911831e9bbSHanoh Haim            test.num = testnb
1921831e9bbSHanoh Haim            testnb += 1
1931831e9bbSHanoh Haim            testset.add_test(test)
1941831e9bbSHanoh Haim        elif l[0] == "*":
1951831e9bbSHanoh Haim            if test is not None:
1961831e9bbSHanoh Haim
1971831e9bbSHanoh Haim                test.comments += l[1:]
1981831e9bbSHanoh Haim            elif testset is not None:
1991831e9bbSHanoh Haim                testset.comments += l[1:]
2001831e9bbSHanoh Haim            else:
2011831e9bbSHanoh Haim                test_campaign.headcomments += l[1:]
2021831e9bbSHanoh Haim        else:
2031831e9bbSHanoh Haim            if test is None:
2041831e9bbSHanoh Haim                if l.strip():
2051831e9bbSHanoh Haim                    print("Unknown content [%s]" % l.strip(), file = sys.stderr)
2061831e9bbSHanoh Haim            else:
2071831e9bbSHanoh Haim                test.test += l
2081831e9bbSHanoh Haim    return test_campaign
2091831e9bbSHanoh Haim
2101831e9bbSHanoh Haimdef dump_campaign(test_campaign):
2111831e9bbSHanoh Haim    print("#"*(len(test_campaign.title)+6))
2121831e9bbSHanoh Haim    print("## %(title)s ##" % test_campaign)
2131831e9bbSHanoh Haim    print("#"*(len(test_campaign.title)+6))
2141831e9bbSHanoh Haim    if test_campaign.sha and test_campaign.crc:
2151831e9bbSHanoh Haim        print("CRC=[%(crc)s] SHA=[%(sha)s]" % test_campaign)
2161831e9bbSHanoh Haim    print("from file %(filename)s" % test_campaign)
2171831e9bbSHanoh Haim    print()
2181831e9bbSHanoh Haim    for ts in test_campaign:
2191831e9bbSHanoh Haim        if ts.crc:
2201831e9bbSHanoh Haim            print("+--[%s]%s(%s)--" % (ts.name,"-"*max(2,80-len(ts.name)-18),ts.crc))
2211831e9bbSHanoh Haim        else:
2221831e9bbSHanoh Haim            print("+--[%s]%s" % (ts.name,"-"*max(2,80-len(ts.name)-6)))
2231831e9bbSHanoh Haim        if ts.keywords:
2241831e9bbSHanoh Haim            print("  kw=%s" % ",".join(ts.keywords))
2251831e9bbSHanoh Haim        for t in ts:
2261831e9bbSHanoh Haim            print("%(num)03i %(name)s" % t)
2271831e9bbSHanoh Haim            c = k = ""
2281831e9bbSHanoh Haim            if t.keywords:
2291831e9bbSHanoh Haim                k = "kw=%s" % ",".join(t.keywords)
2301831e9bbSHanoh Haim            if t.crc:
2311831e9bbSHanoh Haim                c = "[%(crc)s] " % t
2321831e9bbSHanoh Haim            if c or k:
2331831e9bbSHanoh Haim                print("    %s%s" % (c,k) )
2341831e9bbSHanoh Haim
2351831e9bbSHanoh Haim#### COMPUTE CAMPAIGN DIGESTS ####
2361831e9bbSHanoh Haim
2371831e9bbSHanoh Haimdef crc32(x):
2381831e9bbSHanoh Haim    return "%08X" % (0xffffffff & zlib.crc32(x))
2391831e9bbSHanoh Haim
2401831e9bbSHanoh Haimdef sha1(x):
2411831e9bbSHanoh Haim    return hashlib.sha1(x).hexdigest().upper()
2421831e9bbSHanoh Haim
2431831e9bbSHanoh Haimdef compute_campaign_digests(test_campaign):
2441831e9bbSHanoh Haim    dc = b""
2451831e9bbSHanoh Haim    for ts in test_campaign:
2461831e9bbSHanoh Haim        dts = b""
2471831e9bbSHanoh Haim        for t in ts:
2481831e9bbSHanoh Haim            dt = t.test.strip().encode('ascii')
2491831e9bbSHanoh Haim            t.crc = crc32(dt)
2501831e9bbSHanoh Haim            dts += b"\0"+dt
2511831e9bbSHanoh Haim        ts.crc = crc32(dts)
2521831e9bbSHanoh Haim        dc += b"\0\x01"+dts
2531831e9bbSHanoh Haim    test_campaign.crc = crc32(dc)
2541831e9bbSHanoh Haim    if type(test_campaign.filename) is str and test_campaign.filename != '<stdin>':
2551831e9bbSHanoh Haim      test = open(test_campaign.filename, 'rb').read()
2561831e9bbSHanoh Haim    elif test_campaign.filename == '<stdin>':
2571831e9bbSHanoh Haim      test = sys.stdin.read().encode('ascii')
2581831e9bbSHanoh Haim    else:
2591831e9bbSHanoh Haim      raise Exception("Unknown test source %s" % test_campaign.filename)
2601831e9bbSHanoh Haim    test_campaign.sha = sha1(test)
2611831e9bbSHanoh Haim
2621831e9bbSHanoh Haim
2631831e9bbSHanoh Haim#### FILTER CAMPAIGN #####
2641831e9bbSHanoh Haim
2651831e9bbSHanoh Haimdef filter_tests_on_numbers(test_campaign, num):
2661831e9bbSHanoh Haim    if num:
2671831e9bbSHanoh Haim        for ts in test_campaign:
2681831e9bbSHanoh Haim            #ts.set = filter(lambda t: t.num in num, ts.set)
2691831e9bbSHanoh Haim            ts.set = [ t for t in ts.set if t.num in num ]
2701831e9bbSHanoh Haim        #test_campaign.campaign = filter(lambda ts: len(ts.set) > 0, test_campaign.campaign)
2711831e9bbSHanoh Haim        test_campaign.campaign = [ ts for ts in test_campaign.campaign if len(ts.set) > 0 ]
2721831e9bbSHanoh Haim
2731831e9bbSHanoh Haimdef filter_tests_keep_on_keywords(test_campaign, kw):
2741831e9bbSHanoh Haim    def kw_match(lst, kw):
2751831e9bbSHanoh Haim        for k in lst:
2761831e9bbSHanoh Haim            if k in kw:
2771831e9bbSHanoh Haim                return True
2781831e9bbSHanoh Haim        return False
2791831e9bbSHanoh Haim
2801831e9bbSHanoh Haim    if kw:
2811831e9bbSHanoh Haim        for ts in test_campaign:
2821831e9bbSHanoh Haim            #ts.set = filter(lambda t: kw_match(t.keywords, kw), ts.set)
2831831e9bbSHanoh Haim            ts.set = [ t for t in ts.set if kw_match(t.keywords, kw) ]
2841831e9bbSHanoh Haim
2851831e9bbSHanoh Haimdef filter_tests_remove_on_keywords(test_campaign, kw):
2861831e9bbSHanoh Haim    def kw_match(lst, kw):
2871831e9bbSHanoh Haim        for k in kw:
2881831e9bbSHanoh Haim            if k not in lst:
2891831e9bbSHanoh Haim                return False
2901831e9bbSHanoh Haim        return True
2911831e9bbSHanoh Haim
2921831e9bbSHanoh Haim    if kw:
2931831e9bbSHanoh Haim        for ts in test_campaign:
2941831e9bbSHanoh Haim            #ts.set = filter(lambda t: not kw_match(t.keywords, kw), ts.set)
2951831e9bbSHanoh Haim            ts.set = [ t for t in ts.set if not kw_match(t.keywords, kw) ]
2961831e9bbSHanoh Haim
2971831e9bbSHanoh Haim
2981831e9bbSHanoh Haimdef remove_empty_testsets(test_campaign):
2991831e9bbSHanoh Haim    #test_campaign.campaign = filter(lambda ts: len(ts.set) > 0, test_campaign.campaign)
3001831e9bbSHanoh Haim    test_campaign.campaign = [ ts for ts in test_campaign.campaign if len(ts.set) > 0 ]
3011831e9bbSHanoh Haim
3021831e9bbSHanoh Haim
3031831e9bbSHanoh Haim#### RUN CAMPAIGN #####
3041831e9bbSHanoh Haim
3051831e9bbSHanoh Haimdef run_campaign(test_campaign, get_interactive_session, verb=2):
3061831e9bbSHanoh Haim    passed=failed=0
3071831e9bbSHanoh Haim    if test_campaign.preexec:
3081831e9bbSHanoh Haim        test_campaign.preexec_output = get_interactive_session(test_campaign.preexec.strip())[0]
3091831e9bbSHanoh Haim    for testset in test_campaign:
3101831e9bbSHanoh Haim        for t in testset:
3111831e9bbSHanoh Haim            t.output,res = get_interactive_session(t.test.strip())
3121831e9bbSHanoh Haim            the_res = False
3131831e9bbSHanoh Haim            try:
3141831e9bbSHanoh Haim                if res is None or res:
3151831e9bbSHanoh Haim                    the_res= True
3161831e9bbSHanoh Haim            except Exception as msg:
3171831e9bbSHanoh Haim                t.output+="UTscapy: Error during result interpretation:\n"
3181831e9bbSHanoh Haim                t.output+="".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback,))
3191831e9bbSHanoh Haim            if the_res:
3201831e9bbSHanoh Haim                t.res = True
3211831e9bbSHanoh Haim                res = "passed"
3221831e9bbSHanoh Haim                passed += 1
3231831e9bbSHanoh Haim            else:
3241831e9bbSHanoh Haim                t.res = False
3251831e9bbSHanoh Haim                res = "failed"
3261831e9bbSHanoh Haim                failed += 1
3271831e9bbSHanoh Haim            t.result = res
3281831e9bbSHanoh Haim            if verb > 1:
3291831e9bbSHanoh Haim                print("%(result)6s %(crc)s %(name)s" % t, file = sys.stderr)
3301831e9bbSHanoh Haim    test_campaign.passed = passed
3311831e9bbSHanoh Haim    test_campaign.failed = failed
3321831e9bbSHanoh Haim    if verb:
3331831e9bbSHanoh Haim        print("Campaign CRC=%(crc)s  SHA=%(sha)s" % test_campaign, file = sys.stderr)
3341831e9bbSHanoh Haim        print("PASSED=%i FAILED=%i" % (passed, failed), file = sys.stderr)
3351831e9bbSHanoh Haim
3361831e9bbSHanoh Haim
3371831e9bbSHanoh Haim#### INFO LINES ####
3381831e9bbSHanoh Haim
3391831e9bbSHanoh Haimdef info_line(test_campaign):
3401831e9bbSHanoh Haim    filename = test_campaign.filename
3411831e9bbSHanoh Haim    if filename is None:
3421831e9bbSHanoh Haim        return "Run %s by UTscapy" % time.ctime()
3431831e9bbSHanoh Haim    else:
3441831e9bbSHanoh Haim        return "Run %s from [%s] by UTscapy" % (time.ctime(), filename)
3451831e9bbSHanoh Haim
3461831e9bbSHanoh Haimdef html_info_line(test_campaign):
3471831e9bbSHanoh Haim    filename = test_campaign.filename
3481831e9bbSHanoh Haim    if filename is None:
3491831e9bbSHanoh Haim        return """Run %s by <a href="http://www.secdev.org/projects/UTscapy/">UTscapy</a><br>""" % time.ctime()
3501831e9bbSHanoh Haim    else:
3511831e9bbSHanoh Haim        return """Run %s from [%s] by <a href="http://www.secdev.org/projects/UTscapy/">UTscapy</a><br>""" % (time.ctime(), filename)
3521831e9bbSHanoh Haim
3531831e9bbSHanoh Haim
3541831e9bbSHanoh Haim#### CAMPAIGN TO something ####
3551831e9bbSHanoh Haim
3561831e9bbSHanoh Haimdef campaign_to_TEXT(test_campaign):
3571831e9bbSHanoh Haim    output="%(title)s\n" % test_campaign
3581831e9bbSHanoh Haim    output += "-- "+info_line(test_campaign)+"\n\n"
3591831e9bbSHanoh Haim    output += "Passed=%(passed)i\nFailed=%(failed)i\n\n%(headcomments)s\n" % test_campaign
3601831e9bbSHanoh Haim
3611831e9bbSHanoh Haim    for testset in test_campaign:
3621831e9bbSHanoh Haim        output += "######\n## %(name)s\n######\n%(comments)s\n\n" % testset
3631831e9bbSHanoh Haim        for t in testset:
3641831e9bbSHanoh Haim            if t.expand:
3651831e9bbSHanoh Haim                output += "###(%(num)03i)=[%(result)s] %(name)s\n%(comments)s\n%(output)s\n\n" % t
3661831e9bbSHanoh Haim
3671831e9bbSHanoh Haim    return output
3681831e9bbSHanoh Haim
3691831e9bbSHanoh Haimdef campaign_to_ANSI(test_campaign):
3701831e9bbSHanoh Haim    output="%(title)s\n" % test_campaign
3711831e9bbSHanoh Haim    output += "-- "+info_line(test_campaign)+"\n\n"
3721831e9bbSHanoh Haim    output += "Passed=%(passed)i\nFailed=%(failed)i\n\n%(headcomments)s\n" % test_campaign
3731831e9bbSHanoh Haim
3741831e9bbSHanoh Haim    for testset in test_campaign:
3751831e9bbSHanoh Haim        output += "######\n## %(name)s\n######\n%(comments)s\n\n" % testset
3761831e9bbSHanoh Haim        for t in testset:
3771831e9bbSHanoh Haim            if t.expand:
3781831e9bbSHanoh Haim                output += "###(%(num)03i)=[%(result)s] %(name)s\n%(comments)s\n%(output)s\n\n" % t
3791831e9bbSHanoh Haim
3801831e9bbSHanoh Haim    return output
3811831e9bbSHanoh Haim
3821831e9bbSHanoh Haimdef campaign_to_xUNIT(test_campaign):
3831831e9bbSHanoh Haim    output='<?xml version="1.0" encoding="UTF-8" ?>\n<testsuite>\n'
3841831e9bbSHanoh Haim    for testset in test_campaign:
3851831e9bbSHanoh Haim        for t in testset:
3861831e9bbSHanoh Haim            output += ' <testcase classname="%s"\n' % testset.name.encode("string_escape").replace('"',' ')
3871831e9bbSHanoh Haim            output += '           name="%s"\n' % t.name.encode("string_escape").replace('"',' ')
3881831e9bbSHanoh Haim            output += '           duration="0">\n' % t
3891831e9bbSHanoh Haim            if not t.res:
3901831e9bbSHanoh Haim                output += '<error><![CDATA[%(output)s]]></error>\n' % t
3911831e9bbSHanoh Haim            output += "</testcase>\n"
3921831e9bbSHanoh Haim    output += '</testsuite>'
3931831e9bbSHanoh Haim    return output
3941831e9bbSHanoh Haim
3951831e9bbSHanoh Haim
3961831e9bbSHanoh Haimdef campaign_to_HTML(test_campaign, local=0):
3971831e9bbSHanoh Haim    output = """<html>
3981831e9bbSHanoh Haim<head>
3991831e9bbSHanoh Haim<title>%(title)s</title>
4001831e9bbSHanoh Haim<link rel="stylesheet" href="%%(UTscapy_css)s" type="text/css">
4011831e9bbSHanoh Haim<script language="JavaScript" src="%%(UTscapy_js)s" type="text/javascript"></script>
4021831e9bbSHanoh Haim</head>
4031831e9bbSHanoh Haim<body>
4041831e9bbSHanoh Haim
4051831e9bbSHanoh Haim<h1>%(title)s</h1>
4061831e9bbSHanoh Haim
4071831e9bbSHanoh Haim<span class=button onClick="hide_all('tst')">Shrink All</span>
4081831e9bbSHanoh Haim<span class=button onClick="show_all('tst')">Expand All</span>
4091831e9bbSHanoh Haim<span class=button onClick="show_passed('tst')">Expand Passed</span>
4101831e9bbSHanoh Haim<span class=button onClick="show_failed('tst')">Expand Failed</span>
4111831e9bbSHanoh Haim<p>
4121831e9bbSHanoh Haim""" % test_campaign
4131831e9bbSHanoh Haim
4141831e9bbSHanoh Haim    if local:
4151831e9bbSHanoh Haim        External_Files.UTscapy_js.write(os.path.dirname(test_campaign.output_file.name))
4161831e9bbSHanoh Haim        External_Files.UTscapy_css.write(os.path.dirname(test_campaign.output_file.name))
4171831e9bbSHanoh Haim        output %= External_Files.get_local_dict()
4181831e9bbSHanoh Haim    else:
4191831e9bbSHanoh Haim        output %= External_Files.get_URL_dict()
4201831e9bbSHanoh Haim
4211831e9bbSHanoh Haim    if test_campaign.crc is not None and test_campaign.sha is not None:
4221831e9bbSHanoh Haim        output += "CRC=<span class=crc>%(crc)s</span> SHA=<span class=crc>%(sha)s</span><br>" % test_campaign
4231831e9bbSHanoh Haim    output += "<small><em>"+html_info_line(test_campaign)+"</em></small>"
4241831e9bbSHanoh Haim    output += test_campaign.headcomments +  "\n<p>PASSED=%(passed)i FAILED=%(failed)i<p>\n\n" % test_campaign
4251831e9bbSHanoh Haim    for ts in test_campaign:
4261831e9bbSHanoh Haim        for t in ts:
4271831e9bbSHanoh Haim            output += """<span class=button%(result)s onClick="goto_id('tst%(num)il')">%(num)03i</span>\n""" % t
4281831e9bbSHanoh Haim    output += "\n\n"
4291831e9bbSHanoh Haim
4301831e9bbSHanoh Haim    for testset in test_campaign:
4311831e9bbSHanoh Haim        output += "<h2>" % testset
4321831e9bbSHanoh Haim        if testset.crc is not None:
4331831e9bbSHanoh Haim            output += "<span class=crc>%(crc)s</span> " % testset
4341831e9bbSHanoh Haim        output += "%(name)s</h2>\n%(comments)s\n<ul>\n" % testset
4351831e9bbSHanoh Haim        for t in testset:
4361831e9bbSHanoh Haim            output += """<li class=%(result)s id="tst%(num)il">\n""" % t
4371831e9bbSHanoh Haim            if t.expand == 2:
4381831e9bbSHanoh Haim                output +="""
4391831e9bbSHanoh Haim<span id="tst%(num)i+" class="button%(result)s" onClick="show('tst%(num)i')" style="POSITION: absolute; VISIBILITY: hidden;">+%(num)03i+</span>
4401831e9bbSHanoh Haim<span id="tst%(num)i-" class="button%(result)s" onClick="hide('tst%(num)i')">-%(num)03i-</span>
4411831e9bbSHanoh Haim""" % t
4421831e9bbSHanoh Haim            else:
4431831e9bbSHanoh Haim                output += """
4441831e9bbSHanoh Haim<span id="tst%(num)i+" class="button%(result)s" onClick="show('tst%(num)i')">+%(num)03i+</span>
4451831e9bbSHanoh Haim<span id="tst%(num)i-" class="button%(result)s" onClick="hide('tst%(num)i')" style="POSITION: absolute; VISIBILITY: hidden;">-%(num)03i-</span>
4461831e9bbSHanoh Haim""" % t
4471831e9bbSHanoh Haim            if t.crc is not None:
4481831e9bbSHanoh Haim                output += "<span class=crc>%(crc)s</span>\n" % t
4491831e9bbSHanoh Haim            output += """%(name)s\n<span class="comment %(result)s" id="tst%(num)i" """ % t
4501831e9bbSHanoh Haim            if t.expand < 2:
4511831e9bbSHanoh Haim                output += """ style="POSITION: absolute; VISIBILITY: hidden;" """
4521831e9bbSHanoh Haim            output += """><br>%(comments)s
4531831e9bbSHanoh Haim<pre>
4541831e9bbSHanoh Haim%(output)s</pre></span>
4551831e9bbSHanoh Haim""" % t
4561831e9bbSHanoh Haim        output += "\n</ul>\n\n"
4571831e9bbSHanoh Haim
4581831e9bbSHanoh Haim    output += "</body></html>"
4591831e9bbSHanoh Haim    return output
4601831e9bbSHanoh Haim
4611831e9bbSHanoh Haimdef campaign_to_LATEX(test_campaign):
4621831e9bbSHanoh Haim    output = r"""\documentclass{report}
4631831e9bbSHanoh Haim\usepackage{alltt}
4641831e9bbSHanoh Haim\usepackage{xcolor}
4651831e9bbSHanoh Haim\usepackage{a4wide}
4661831e9bbSHanoh Haim\usepackage{hyperref}
4671831e9bbSHanoh Haim
4681831e9bbSHanoh Haim\title{%(title)s}
4691831e9bbSHanoh Haim\date{%%s}
4701831e9bbSHanoh Haim
4711831e9bbSHanoh Haim\begin{document}
4721831e9bbSHanoh Haim\maketitle
4731831e9bbSHanoh Haim\tableofcontents
4741831e9bbSHanoh Haim
4751831e9bbSHanoh Haim\begin{description}
4761831e9bbSHanoh Haim\item[Passed:] %(passed)i
4771831e9bbSHanoh Haim\item[Failed:] %(failed)i
4781831e9bbSHanoh Haim\end{description}
4791831e9bbSHanoh Haim
4801831e9bbSHanoh Haim%(headcomments)s
4811831e9bbSHanoh Haim
4821831e9bbSHanoh Haim""" % test_campaign
4831831e9bbSHanoh Haim    output %= info_line(test_campaign)
4841831e9bbSHanoh Haim
4851831e9bbSHanoh Haim    for testset in test_campaign:
4861831e9bbSHanoh Haim        output += "\\chapter{%(name)s}\n\n%(comments)s\n\n" % testset
4871831e9bbSHanoh Haim        for t in testset:
4881831e9bbSHanoh Haim            if t.expand:
4891831e9bbSHanoh Haim                output += r"""\section{%(name)s}
4901831e9bbSHanoh Haim
4911831e9bbSHanoh Haim[%(num)03i] [%(result)s]
4921831e9bbSHanoh Haim
4931831e9bbSHanoh Haim%(comments)s
4941831e9bbSHanoh Haim\begin{alltt}
4951831e9bbSHanoh Haim%(output)s
4961831e9bbSHanoh Haim\end{alltt}
4971831e9bbSHanoh Haim
4981831e9bbSHanoh Haim""" % t
4991831e9bbSHanoh Haim
5001831e9bbSHanoh Haim    output += "\\end{document}\n"
5011831e9bbSHanoh Haim    return output
5021831e9bbSHanoh Haim
5031831e9bbSHanoh Haim
5041831e9bbSHanoh Haim
5051831e9bbSHanoh Haim#### USAGE ####
5061831e9bbSHanoh Haim
5071831e9bbSHanoh Haimdef usage():
5081831e9bbSHanoh Haim    print("""Usage: UTscapy [-m module] [-f {text|ansi|HTML|LaTeX}] [-o output_file]
5091831e9bbSHanoh Haim               [-t testfile] [-k keywords [-k ...]] [-K keywords [-K ...]]
5101831e9bbSHanoh Haim               [-l] [-d|-D] [-F] [-q[q]] [-P preexecute_python_code]
5111831e9bbSHanoh Haim               [-s /path/to/scpay]
5121831e9bbSHanoh Haim-l\t\t: generate local files
5131831e9bbSHanoh Haim-F\t\t: expand only failed tests
5141831e9bbSHanoh Haim-d\t\t: dump campaign
5151831e9bbSHanoh Haim-D\t\t: dump campaign and stop
5161831e9bbSHanoh Haim-C\t\t: don't calculate CRC and SHA
5171831e9bbSHanoh Haim-s\t\t: path to scapy.py
5181831e9bbSHanoh Haim-q\t\t: quiet mode
5191831e9bbSHanoh Haim-qq\t\t: [silent mode]
5201831e9bbSHanoh Haim-n <testnum>\t: only tests whose numbers are given (eg. 1,3-7,12)
5211831e9bbSHanoh Haim-m <module>\t: additional module to put in the namespace
5221831e9bbSHanoh Haim-k <kw1>,<kw2>,...\t: include only tests with one of those keywords (can be used many times)
5231831e9bbSHanoh Haim-K <kw1>,<kw2>,...\t: remove tests with one of those keywords (can be used many times)
5241831e9bbSHanoh Haim-P <preexecute_python_code>
5251831e9bbSHanoh Haim""", file = sys.stderr)
5261831e9bbSHanoh Haim    raise SystemExit
5271831e9bbSHanoh Haim
5281831e9bbSHanoh Haim
5291831e9bbSHanoh Haim#### MAIN ####
5301831e9bbSHanoh Haim
5311831e9bbSHanoh Haimdef main(argv):
5321831e9bbSHanoh Haim    import builtins
5331831e9bbSHanoh Haim
5341831e9bbSHanoh Haim    # Parse arguments
5351831e9bbSHanoh Haim
5361831e9bbSHanoh Haim    FORMAT = Format.ANSI
5371831e9bbSHanoh Haim    TESTFILE = sys.stdin
5381831e9bbSHanoh Haim    OUTPUTFILE = sys.stdout
5391831e9bbSHanoh Haim    LOCAL = 0
5401831e9bbSHanoh Haim    NUM=None
5411831e9bbSHanoh Haim    KW_OK = []
5421831e9bbSHanoh Haim    KW_KO = []
5431831e9bbSHanoh Haim    DUMP = 0
5441831e9bbSHanoh Haim    CRC = 1
5451831e9bbSHanoh Haim    ONLYFAILED = 0
5461831e9bbSHanoh Haim    VERB=2
5471831e9bbSHanoh Haim    PREEXEC=""
5481831e9bbSHanoh Haim    SCAPY="scapy"
5491831e9bbSHanoh Haim    MODULES = []
5501831e9bbSHanoh Haim    try:
5511831e9bbSHanoh Haim        opts = getopt.getopt(argv, "o:t:f:hln:m:k:K:DdCFqP:s:")
5521831e9bbSHanoh Haim        for opt,optarg in opts[0]:
5531831e9bbSHanoh Haim            if opt == "-h":
5541831e9bbSHanoh Haim                usage()
5551831e9bbSHanoh Haim            elif opt == "-F":
5561831e9bbSHanoh Haim                ONLYFAILED = 1
5571831e9bbSHanoh Haim            elif opt == "-q":
5581831e9bbSHanoh Haim                VERB -= 1
5591831e9bbSHanoh Haim            elif opt == "-D":
5601831e9bbSHanoh Haim                DUMP = 2
5611831e9bbSHanoh Haim            elif opt == "-d":
5621831e9bbSHanoh Haim                DUMP = 1
5631831e9bbSHanoh Haim            elif opt == "-C":
5641831e9bbSHanoh Haim                CRC = 0
5651831e9bbSHanoh Haim            elif opt == "-s":
5661831e9bbSHanoh Haim                SCAPY = optarg
5671831e9bbSHanoh Haim            elif opt == "-P":
5681831e9bbSHanoh Haim                PREEXEC += "\n"+optarg
5691831e9bbSHanoh Haim            elif opt == "-f":
5701831e9bbSHanoh Haim                try:
5711831e9bbSHanoh Haim                    FORMAT = Format.from_string(optarg)
5721831e9bbSHanoh Haim                except KeyError as msg:
5731831e9bbSHanoh Haim                    raise getopt.GetoptError("Unknown output format %s" % msg)
5741831e9bbSHanoh Haim            elif opt == "-t":
5751831e9bbSHanoh Haim                TESTFILE = open(optarg)
5761831e9bbSHanoh Haim            elif opt == "-o":
5771831e9bbSHanoh Haim                OUTPUTFILE = open(optarg, "w")
5781831e9bbSHanoh Haim            elif opt == "-l":
5791831e9bbSHanoh Haim                LOCAL = 1
5801831e9bbSHanoh Haim            elif opt == "-n":
5811831e9bbSHanoh Haim                NUM = []
5821831e9bbSHanoh Haim                for v in map( lambda x: x.strip(), optarg.split(",") ):
5831831e9bbSHanoh Haim                    try:
5841831e9bbSHanoh Haim                        NUM.append(int(v))
5851831e9bbSHanoh Haim                    except ValueError:
5861831e9bbSHanoh Haim                        v1,v2 = map(int, v.split("-"))
5871831e9bbSHanoh Haim                        for vv in range(v1,v2+1):
5881831e9bbSHanoh Haim                            NUM.append(vv)
5891831e9bbSHanoh Haim            elif opt == "-m":
5901831e9bbSHanoh Haim                MODULES.append(optarg)
5911831e9bbSHanoh Haim            elif opt == "-k":
5921831e9bbSHanoh Haim                KW_OK.append(optarg.split(","))
5931831e9bbSHanoh Haim            elif opt == "-K":
5941831e9bbSHanoh Haim                KW_KO.append(optarg.split(","))
5951831e9bbSHanoh Haim
5961831e9bbSHanoh Haim
5971831e9bbSHanoh Haim        try:
5981831e9bbSHanoh Haim            from scapy import all as scapy
5991831e9bbSHanoh Haim        except ImportError as e:
6001831e9bbSHanoh Haim            raise getopt.GetoptError("cannot import [%s]: %s" % (SCAPY,e))
6011831e9bbSHanoh Haim
6021831e9bbSHanoh Haim        for m in MODULES:
6031831e9bbSHanoh Haim            try:
6041831e9bbSHanoh Haim                mod = import_module(m)
6051831e9bbSHanoh Haim                builtins.__dict__.update(mod.__dict__)
6061831e9bbSHanoh Haim            except ImportError as e:
6071831e9bbSHanoh Haim                raise getopt.GetoptError("cannot import [%s]: %s" % (m,e))
6081831e9bbSHanoh Haim
6091831e9bbSHanoh Haim    except getopt.GetoptError as msg:
6101831e9bbSHanoh Haim        print("ERROR:",msg, file = sys.stderr)
6111831e9bbSHanoh Haim        raise SystemExit
6121831e9bbSHanoh Haim
6131831e9bbSHanoh Haim    autorun_func = {
6141831e9bbSHanoh Haim        Format.TEXT: scapy.autorun_get_text_interactive_session,
6151831e9bbSHanoh Haim        Format.ANSI: scapy.autorun_get_ansi_interactive_session,
6161831e9bbSHanoh Haim        Format.HTML: scapy.autorun_get_html_interactive_session,
6171831e9bbSHanoh Haim        Format.LATEX: scapy.autorun_get_latex_interactive_session,
6181831e9bbSHanoh Haim        Format.XUNIT: scapy.autorun_get_text_interactive_session,
6191831e9bbSHanoh Haim        }
6201831e9bbSHanoh Haim
6211831e9bbSHanoh Haim    # Parse test file
6221831e9bbSHanoh Haim    test_campaign = parse_campaign_file(TESTFILE)
6231831e9bbSHanoh Haim
6241831e9bbSHanoh Haim    # Report parameters
6251831e9bbSHanoh Haim    if PREEXEC:
6261831e9bbSHanoh Haim        test_campaign.preexec = PREEXEC
6271831e9bbSHanoh Haim
6281831e9bbSHanoh Haim
6291831e9bbSHanoh Haim    # Compute campaign CRC and SHA
6301831e9bbSHanoh Haim    if CRC:
6311831e9bbSHanoh Haim        compute_campaign_digests(test_campaign)
6321831e9bbSHanoh Haim
6331831e9bbSHanoh Haim    # Filter out unwanted tests
6341831e9bbSHanoh Haim    filter_tests_on_numbers(test_campaign, NUM)
6351831e9bbSHanoh Haim    for k in KW_OK:
6361831e9bbSHanoh Haim        filter_tests_keep_on_keywords(test_campaign, k)
6371831e9bbSHanoh Haim    for k in KW_KO:
6381831e9bbSHanoh Haim        filter_tests_remove_on_keywords(test_campaign, k)
6391831e9bbSHanoh Haim
6401831e9bbSHanoh Haim    remove_empty_testsets(test_campaign)
6411831e9bbSHanoh Haim
6421831e9bbSHanoh Haim
6431831e9bbSHanoh Haim    # Dump campaign
6441831e9bbSHanoh Haim    if DUMP:
6451831e9bbSHanoh Haim        dump_campaign(test_campaign)
6461831e9bbSHanoh Haim        if DUMP > 1:
6471831e9bbSHanoh Haim            sys.exit()
6481831e9bbSHanoh Haim
6491831e9bbSHanoh Haim    # Run tests
6501831e9bbSHanoh Haim    test_campaign.output_file = OUTPUTFILE
6511831e9bbSHanoh Haim    run_campaign(test_campaign, autorun_func[FORMAT], verb=VERB)
6521831e9bbSHanoh Haim
6531831e9bbSHanoh Haim    # Shrink passed
6541831e9bbSHanoh Haim    if ONLYFAILED:
6551831e9bbSHanoh Haim        for t in test_campaign.all_tests():
6561831e9bbSHanoh Haim            if t:
6571831e9bbSHanoh Haim                t.expand = 0
6581831e9bbSHanoh Haim            else:
6591831e9bbSHanoh Haim                t.expand = 2
6601831e9bbSHanoh Haim
6611831e9bbSHanoh Haim    # Generate report
6621831e9bbSHanoh Haim    if FORMAT == Format.TEXT:
6631831e9bbSHanoh Haim        output = campaign_to_TEXT(test_campaign)
6641831e9bbSHanoh Haim    elif FORMAT == Format.ANSI:
6651831e9bbSHanoh Haim        output = campaign_to_ANSI(test_campaign)
6661831e9bbSHanoh Haim    elif FORMAT == Format.HTML:
6671831e9bbSHanoh Haim        output = campaign_to_HTML(test_campaign, local=LOCAL)
6681831e9bbSHanoh Haim    elif FORMAT == Format.LATEX:
6691831e9bbSHanoh Haim        output = campaign_to_LATEX(test_campaign)
6701831e9bbSHanoh Haim    elif FORMAT == Format.XUNIT:
6711831e9bbSHanoh Haim        output = campaign_to_xUNIT(test_campaign)
6721831e9bbSHanoh Haim
6731831e9bbSHanoh Haim    OUTPUTFILE.write(output)
6741831e9bbSHanoh Haim    OUTPUTFILE.close()
6751831e9bbSHanoh Haim
6761831e9bbSHanoh Haimif __name__ == "__main__":
6771831e9bbSHanoh Haim    main(sys.argv[1:])
678