]>
git.proxmox.com Git - mirror_frr.git/blob - tests/helpers/python/frrtest.py
1 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Copyright (C) 2017 by David Lamparter & Christian Franke,
6 # Open Source Routing / NetDEF Inc.
8 # This file is part of FRRouting (FRR)
21 # These are the gritty internals of the TestMultiOut implementation.
22 # See below for the definition of actual TestMultiOut tests.
25 srcbase
= os
.path
.abspath(inspect
.getsourcefile(frrsix
))
27 srcbase
= os
.path
.dirname(srcbase
)
31 return os
.path
.relpath(os
.path
.abspath(srcpath
), srcbase
)
34 class MultiTestFailure(Exception):
38 class MetaTestMultiOut(type):
39 def __getattr__(cls
, name
):
40 if name
.startswith("_"):
43 internal_name
= "_{}".format(name
)
44 if internal_name
not in dir(cls
):
47 def registrar(*args
, **kwargs
):
48 cls
._add
_test
(getattr(cls
, internal_name
), *args
, **kwargs
)
53 @frrsix.add_metaclass(MetaTestMultiOut
)
54 class _TestMultiOut(object):
56 if "tests_run" in dir(self
.__class
__) and self
.tests_run
:
58 self
.__class
__.tests_run
= True
59 basedir
= os
.path
.dirname(inspect
.getsourcefile(type(self
)))
60 program
= os
.path
.join(basedir
, self
.program
)
61 proc
= subprocess
.Popen([binpath(program
)], stdout
=subprocess
.PIPE
)
62 self
.output
, _
= proc
.communicate("")
63 self
.exitcode
= proc
.wait()
65 self
.__class
__.testresults
= {}
66 for test
in self
.tests
:
69 except MultiTestFailure
:
70 self
.testresults
[test
] = sys
.exc_info()
72 self
.testresults
[test
] = None
74 def _exit_cleanly(self
):
75 if self
.exitcode
!= 0:
76 raise MultiTestFailure("Program did not terminate with exit code 0")
79 def _add_test(cls
, method
, *args
, **kwargs
):
80 if "tests" not in dir(cls
):
81 setattr(cls
, "tests", [])
82 if method
is not cls
._exit
_cleanly
:
83 cls
._add
_test
(cls
._exit
_cleanly
)
85 def matchfunction(self
):
86 method(self
, *args
, **kwargs
)
88 cls
.tests
.append(matchfunction
)
90 def testfunction(self
):
92 result
= self
.testresults
[matchfunction
]
93 if result
is not None:
94 frrsix
.reraise(*result
)
96 testname
= re
.sub(r
"[^A-Za-z0-9]", "_", "%r%r" % (args
, kwargs
))
97 testname
= re
.sub(r
"__*", "_", testname
)
98 testname
= testname
.strip("_")
100 testname
= method
.__name
__.strip("_")
101 if "test_%s" % testname
in dir(cls
):
103 while "test_%s_%d" % (testname
, index
) in dir(cls
):
105 testname
= "%s_%d" % (testname
, index
)
106 setattr(cls
, "test_%s" % testname
, testfunction
)
110 # This class houses the actual TestMultiOut tests types.
111 # If you want to add a new test type, you probably do it here.
113 # Say you want to add a test type called foobarlicious. Then define
114 # a function _foobarlicious here that takes self and the test arguments
115 # when called. That function should check the output in self.output
116 # to see whether it matches the expectation of foobarlicious with the
117 # given arguments and should then adjust self.output according to how
118 # much output it consumed.
119 # If the output doesn't meet the expectations, MultiTestFailure can be
120 # raised, however that should only be done after self.output has been
121 # modified according to consumed content.
124 re_okfail
= re
.compile(r
"(?:[3[12]m|^)?(?P<ret>OK|failed)".encode("utf8"), re
.MULTILINE
)
127 class TestMultiOut(_TestMultiOut
):
128 def _onesimple(self
, line
):
129 if type(line
) is str:
130 line
= line
.encode("utf8")
131 idx
= self
.output
.find(line
)
133 self
.output
= self
.output
[idx
+ len(line
) :]
135 raise MultiTestFailure("%r could not be found" % line
)
137 def _okfail(self
, line
, okfail
=re_okfail
):
138 self
._onesimple
(line
)
140 m
= okfail
.search(self
.output
)
142 raise MultiTestFailure("OK/fail not found")
143 self
.output
= self
.output
[m
.end() :]
145 if m
.group("ret") != "OK".encode("utf8"):
146 raise MultiTestFailure("Test output indicates failure")
150 # This class implements a test comparing the output of a program against
151 # an existing reference output
155 class TestRefMismatch(Exception):
156 def __init__(self
, _test
, outtext
, reftext
):
157 self
.outtext
= outtext
158 self
.reftext
= reftext
161 rv
= "Expected output and actual output differ:\n"
163 difflib
.unified_diff(
164 self
.reftext
.splitlines(),
165 self
.outtext
.splitlines(),
174 class TestExitNonzero(Exception):
178 class TestRefOut(object):
179 def test_refout(self
):
180 basedir
= os
.path
.dirname(inspect
.getsourcefile(type(self
)))
181 program
= os
.path
.join(basedir
, self
.program
)
183 if getattr(self
, "built_refin", False):
184 refin
= binpath(program
) + ".in"
186 refin
= program
+ ".in"
187 if getattr(self
, "built_refout", False):
188 refout
= binpath(program
) + ".refout"
190 refout
= program
+ ".refout"
193 if os
.path
.exists(refin
):
194 with
open(refin
, "rb") as f
:
196 with
open(refout
, "rb") as f
:
199 proc
= subprocess
.Popen(
200 [binpath(program
)], stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
202 outtext
, _
= proc
.communicate(intext
)
204 # Get rid of newline problems (Windows vs Unix Style)
205 outtext_str
= outtext
.decode("utf8").replace("\r\n", "\n").replace("\r", "\n")
206 reftext_str
= reftext
.decode("utf8").replace("\r\n", "\n").replace("\r", "\n")
208 if outtext_str
!= reftext_str
:
209 raise TestRefMismatch(self
, outtext_str
, reftext_str
)
211 raise TestExitNonzero(self
)