Merge holmberg556/cmdtest into master
This commit is contained in:
.gitignoreCMDTEST_example.ymlCMakeLists.txtRakefile
bin
cmdtest.gemspecdoc
examples
files/bin
lib/cmdtest
argumentparser.rbbaselogger.rbcmdeffects.rbconsolelogger.rbfileinfo.rbfssnapshot.rbjunitfile.rbjunitlogger.rblcs.rbmethodfilter.rbnotify.rboutput.rbtestcase.rbutil.rbworkdir.rb
python
replace_strings.plt
8
python/Rakefile
Normal file
8
python/Rakefile
Normal file
@ -0,0 +1,8 @@
|
||||
# -*- ruby -*-
|
||||
|
||||
ENV["LC_ALL"] = "C" if RUBY_PLATFORM =~ /darwin/
|
||||
|
||||
desc "run unit tests"
|
||||
task "test" do
|
||||
sh "./cmdtest.py t"
|
||||
end
|
@ -74,6 +74,13 @@ def subranges(n, arr):
|
||||
for i in range(0,len(arr)-n+1):
|
||||
yield arr[i:i+n]
|
||||
|
||||
def flatten(seq):
|
||||
for item in seq:
|
||||
if isinstance(item, (list,tuple)):
|
||||
yield from flatten(item)
|
||||
else:
|
||||
yield item
|
||||
|
||||
def to_list(arg):
|
||||
return arg if isinstance(arg, list) else [arg]
|
||||
|
||||
@ -104,6 +111,15 @@ def temp_chdir(path):
|
||||
finally:
|
||||
os.chdir(starting_directory)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def extra_sys_path(dname):
|
||||
try:
|
||||
old_sys_path = sys.path
|
||||
sys.path.append(dname)
|
||||
yield
|
||||
finally:
|
||||
sys.path = old_sys_path
|
||||
|
||||
def progress(*args):
|
||||
print("###", "-" * 50, *args)
|
||||
|
||||
@ -368,6 +384,7 @@ class TestCase:
|
||||
def __init__(self, tmpdir, statistics):
|
||||
self.__tmpdir = tmpdir
|
||||
self.__statistics = statistics
|
||||
self.__always_ignored_files = set()
|
||||
|
||||
def setup(self):
|
||||
pass
|
||||
@ -383,6 +400,9 @@ class TestCase:
|
||||
os.environ['PATH'] = os.pathsep.join((os.path.join(self.__tmpdir.top, dirpath),
|
||||
os.environ['PATH']))
|
||||
|
||||
def always_ignore_file(self, fname):
|
||||
self.__always_ignored_files.add(fname)
|
||||
|
||||
def import_file(self, src, tgt):
|
||||
mkdir_for(tgt)
|
||||
shutil.copy(os.path.join(ROOT_WD, src), tgt)
|
||||
@ -390,8 +410,8 @@ class TestCase:
|
||||
def create_file(self, fname, content, encoding='utf-8'):
|
||||
mkdir_for(fname)
|
||||
with open(fname, "w", encoding=encoding) as f:
|
||||
if type(content) == list:
|
||||
for line in content:
|
||||
if isinstance(content, (list,tuple)):
|
||||
for line in flatten(content):
|
||||
print(line, file=f)
|
||||
else:
|
||||
f.write(content)
|
||||
@ -409,7 +429,7 @@ class TestCase:
|
||||
|
||||
def cmd(self, cmdline, timeout=None):
|
||||
tmpdir = self.__tmpdir
|
||||
before = tmpdir.snapshot()
|
||||
before = tmpdir.snapshot(self.__always_ignored_files)
|
||||
stdout_log = tmpdir.stdout_log()
|
||||
stderr_log = tmpdir.stderr_log()
|
||||
self._wait_for_new_second()
|
||||
@ -420,7 +440,7 @@ class TestCase:
|
||||
err = subprocess.call(cmdline, stdout=stdout, stderr=stderr, shell=True, timeout=timeout)
|
||||
else:
|
||||
err = 0
|
||||
after = tmpdir.snapshot()
|
||||
after = tmpdir.snapshot(self.__always_ignored_files)
|
||||
|
||||
return Result(err, before, after,
|
||||
File(stdout_log), File(stderr_log),
|
||||
@ -494,8 +514,9 @@ class FileInfo:
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
class FsSnapshot:
|
||||
def __init__(self, topdir):
|
||||
def __init__(self, topdir, ignored_files):
|
||||
self.topdir = topdir
|
||||
self.ignored_files = ignored_files
|
||||
self.bypath = {}
|
||||
self._collect_files(DirInfo(topdir))
|
||||
|
||||
@ -510,6 +531,8 @@ class FsSnapshot:
|
||||
|
||||
def _collect_files(self, dirinfo):
|
||||
for entry in dirinfo.entries():
|
||||
if entry.display_path in self.ignored_files:
|
||||
continue
|
||||
self.bypath[entry.display_path] = entry
|
||||
if isinstance(entry, DirInfo):
|
||||
self._collect_files(entry)
|
||||
@ -538,8 +561,8 @@ class Tmpdir:
|
||||
def timestamp_file(self):
|
||||
return os.path.join(self.logdir, "tmp.timestamp")
|
||||
|
||||
def snapshot(self):
|
||||
return FsSnapshot(self.top)
|
||||
def snapshot(self, ignored_files):
|
||||
return FsSnapshot(self.top, ignored_files)
|
||||
|
||||
def prepare_for_test(self, test_method_name):
|
||||
self.clear()
|
||||
@ -630,7 +653,9 @@ class Tfile:
|
||||
|
||||
self.glob = dict()
|
||||
self.glob['TestCase'] = TestCase
|
||||
exec(co, self.glob)
|
||||
self.glob['__file__'] = os.path.abspath(filename)
|
||||
with extra_sys_path(os.path.dirname(filename)):
|
||||
exec(co, self.glob)
|
||||
|
||||
def tclasses(self):
|
||||
for name in sorted(self.glob.keys()):
|
||||
@ -645,18 +670,20 @@ def cmdtest_in_dir(path):
|
||||
py_files = glob.glob("CMDTEST_*.py")
|
||||
return test_files(py_files)
|
||||
|
||||
def test_files(py_files, selected_methods = set()):
|
||||
def test_files(py_files, selected_methods=None, quiet=False):
|
||||
statistics = Statistics()
|
||||
tmpdir = Tmpdir()
|
||||
for py_file in py_files:
|
||||
tfile = Tfile(py_file)
|
||||
for tclass in tfile.tclasses():
|
||||
statistics.classes += 1
|
||||
progress(tclass.name())
|
||||
if not quiet:
|
||||
progress(tclass.name())
|
||||
for tmethod in tclass.tmethods():
|
||||
if not selected_methods or tmethod.name() in selected_methods:
|
||||
statistics.methods += 1
|
||||
progress(tmethod.name())
|
||||
if not quiet:
|
||||
progress(tmethod.name())
|
||||
tmethod.run(tmpdir, statistics)
|
||||
return statistics
|
||||
|
||||
@ -664,13 +691,17 @@ def parse_options():
|
||||
parser = argparse.ArgumentParser('cmdtest')
|
||||
parser.add_argument("-v", "--verbose", action="store_true",
|
||||
help="be more verbose")
|
||||
parser.add_argument("-q", "--quiet", action="store_true",
|
||||
help="be more quiet")
|
||||
parser.add_argument("arg", nargs="*",
|
||||
help="CMDTEST_*.py files / test methods")
|
||||
options = parser.parse_args()
|
||||
py_files = []
|
||||
selected_methods = set()
|
||||
for arg in options.arg:
|
||||
if re.match(r'CMDTEST_.*\.py$', arg):
|
||||
if os.path.isdir(arg):
|
||||
py_files.extend(glob.glob('%s/CMDTEST_*.py' % arg))
|
||||
elif re.match(r'CMDTEST_.*\.py$', os.path.basename(arg)):
|
||||
py_files.append(arg)
|
||||
else:
|
||||
selected_methods.add(arg)
|
||||
@ -684,10 +715,11 @@ def parse_options():
|
||||
|
||||
def main():
|
||||
options, py_files, selected_methods = parse_options()
|
||||
statistics = test_files(py_files, selected_methods)
|
||||
print()
|
||||
print(statistics)
|
||||
print()
|
||||
statistics = test_files(py_files, selected_methods, options.quiet)
|
||||
if not options.quiet:
|
||||
print()
|
||||
print(statistics)
|
||||
print()
|
||||
exit(0 if statistics.errors == 0 and statistics.fatals == 0 else 1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
34
python/t/CMDTEST_as_module.py
Normal file
34
python/t/CMDTEST_as_module.py
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
from os.path import dirname, abspath, join as path_join
|
||||
|
||||
class TC_as_module(TestCase):
|
||||
|
||||
def setup(self):
|
||||
self.always_ignore_file('subdir/tmp-cmdtest-python/')
|
||||
|
||||
def test_as_module(self):
|
||||
self.create_file("subdir/CMDTEST_foo.py", [
|
||||
'class TC_foo(TestCase):',
|
||||
' def test_01(self):',
|
||||
' with self.cmd("echo hello") as c:',
|
||||
' c.stdout_equal("hello\\n")',
|
||||
' def test_02(self):',
|
||||
' with self.cmd("echo world") as c:',
|
||||
' c.stdout_equal("world\\n")',
|
||||
' def test_03(self):',
|
||||
' with self.cmd("echo hello") as c:',
|
||||
' c.stdout_equal("world\\n")',
|
||||
])
|
||||
|
||||
dpath = dirname(abspath(__file__))
|
||||
command = path_join(dpath, 'as_module.py')
|
||||
with self.cmd(command + ' subdir') as c:
|
||||
c.stdout_match([
|
||||
r'--- ERROR: stdout_equal',
|
||||
r'actual:',
|
||||
r' hello',
|
||||
r'expect:',
|
||||
r' world',
|
||||
r'Statistics\(classes=1, methods=3, command=3, errors=1, fatals=0\)',
|
||||
])
|
||||
c.exit_nonzero()
|
42
python/t/CMDTEST_exit_nonzero.py
Normal file
42
python/t/CMDTEST_exit_nonzero.py
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
from selftest_utils import SelftestUtils
|
||||
|
||||
class TC_exit_nonzero(SelftestUtils, TestCase):
|
||||
|
||||
def test_exit_nonzero_CORRECT(self):
|
||||
self.create_CMDTEST_foo(
|
||||
'with self.cmd("false") as c:',
|
||||
' c.exit_nonzero()',
|
||||
)
|
||||
|
||||
with self.cmd_cmdtest() as c:
|
||||
c.stdout_equal([
|
||||
"### cmdline: false",
|
||||
])
|
||||
|
||||
def test_exit_nonzero_INCORRECT(self):
|
||||
self.create_CMDTEST_foo(
|
||||
'with self.cmd("true") as c:',
|
||||
' c.exit_nonzero()',
|
||||
)
|
||||
|
||||
with self.cmd_cmdtest() as c:
|
||||
c.stdout_equal([
|
||||
"### cmdline: true",
|
||||
"--- ERROR: exit_nonzero",
|
||||
"actual: 0",
|
||||
"expect: <nonzero value>",
|
||||
"",
|
||||
])
|
||||
c.exit_nonzero()
|
||||
|
||||
def test_exit_nonzero_CORRECT_18(self):
|
||||
self.create_CMDTEST_foo(
|
||||
'with self.cmd("exit 18") as c:',
|
||||
' c.exit_nonzero()',
|
||||
)
|
||||
|
||||
with self.cmd_cmdtest() as c:
|
||||
c.stdout_equal([
|
||||
"### cmdline: exit 18",
|
||||
])
|
47
python/t/CMDTEST_exit_zero.py
Normal file
47
python/t/CMDTEST_exit_zero.py
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
from selftest_utils import SelftestUtils
|
||||
|
||||
class TC_exit_zero(SelftestUtils, TestCase):
|
||||
|
||||
def test_exit_zero_CORRECT(self):
|
||||
self.create_CMDTEST_foo(
|
||||
'with self.cmd("true") as c:',
|
||||
' c.exit_zero()',
|
||||
)
|
||||
|
||||
with self.cmd_cmdtest() as c:
|
||||
c.stdout_equal([
|
||||
"### cmdline: true",
|
||||
])
|
||||
|
||||
def test_exit_zero_INCORRECT(self):
|
||||
self.create_CMDTEST_foo(
|
||||
'with self.cmd("false") as c:',
|
||||
' c.exit_zero()',
|
||||
)
|
||||
|
||||
with self.cmd_cmdtest() as c:
|
||||
c.stdout_equal([
|
||||
"### cmdline: false",
|
||||
"--- ERROR: exit_zero",
|
||||
"actual: 1",
|
||||
"expect: 0",
|
||||
"",
|
||||
])
|
||||
c.exit_nonzero()
|
||||
|
||||
def test_exit_zero_INCORRECT_18(self):
|
||||
self.create_CMDTEST_foo(
|
||||
'with self.cmd("exit 18") as c:',
|
||||
' c.exit_zero()',
|
||||
)
|
||||
|
||||
with self.cmd_cmdtest() as c:
|
||||
c.stdout_equal([
|
||||
"### cmdline: exit 18",
|
||||
"--- ERROR: exit_zero",
|
||||
"actual: 18",
|
||||
"expect: 0",
|
||||
"",
|
||||
])
|
||||
c.exit_nonzero()
|
71
python/t/CMDTEST_stdout_equal.py
Normal file
71
python/t/CMDTEST_stdout_equal.py
Normal file
@ -0,0 +1,71 @@
|
||||
|
||||
from selftest_utils import SelftestUtils
|
||||
|
||||
class TC_stdout_equal(SelftestUtils, TestCase):
|
||||
|
||||
def test_stdout_equal_CORRECT_EMPTY(self):
|
||||
self.create_CMDTEST_foo(
|
||||
'with self.cmd("true") as c:',
|
||||
' c.stdout_equal([',
|
||||
' ])',
|
||||
)
|
||||
|
||||
with self.cmd_cmdtest() as c:
|
||||
c.stdout_equal([
|
||||
"### cmdline: true",
|
||||
])
|
||||
|
||||
def test_stdout_equal_INCORRECT_EMPTY(self):
|
||||
self.create_CMDTEST_foo(
|
||||
'with self.cmd("echo hello") as c:',
|
||||
' c.stdout_equal([',
|
||||
' ])',
|
||||
)
|
||||
|
||||
with self.cmd_cmdtest() as c:
|
||||
c.stdout_equal([
|
||||
"### cmdline: echo hello",
|
||||
"--- ERROR: stdout_equal",
|
||||
"actual:",
|
||||
" hello",
|
||||
"expect:",
|
||||
" <<empty>>",
|
||||
])
|
||||
c.exit_nonzero()
|
||||
|
||||
def test_stdout_equal_CORRECT_2_LINES(self):
|
||||
self.create_CMDTEST_foo(
|
||||
'with self.cmd("echo hello && echo world") as c:',
|
||||
' c.stdout_equal([',
|
||||
' "hello",',
|
||||
' "world",',
|
||||
' ])',
|
||||
)
|
||||
|
||||
with self.cmd_cmdtest() as c:
|
||||
c.stdout_equal([
|
||||
"### cmdline: echo hello && echo world",
|
||||
])
|
||||
|
||||
def test_stdout_equal_INCORRECT_2_LINES(self):
|
||||
self.create_CMDTEST_foo(
|
||||
'with self.cmd("echo hello && echo world && echo MORE") as c:',
|
||||
' c.stdout_equal([',
|
||||
' "hello",',
|
||||
' "world",',
|
||||
' ])',
|
||||
)
|
||||
|
||||
with self.cmd_cmdtest() as c:
|
||||
c.stdout_equal([
|
||||
"### cmdline: echo hello && echo world && echo MORE",
|
||||
"--- ERROR: stdout_equal",
|
||||
"actual:",
|
||||
" hello",
|
||||
" world",
|
||||
" MORE",
|
||||
"expect:",
|
||||
" hello",
|
||||
" world",
|
||||
])
|
||||
c.exit_nonzero()
|
18
python/t/as_module.py
Executable file
18
python/t/as_module.py
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
from os.path import dirname
|
||||
|
||||
# import from 'cmdtest.py' in other directory
|
||||
sys.path.insert(0, dirname(dirname(os.path.abspath(__file__))))
|
||||
from cmdtest import cmdtest_in_dir, Statistics
|
||||
|
||||
def main():
|
||||
dirpath = sys.argv[1]
|
||||
statistics = cmdtest_in_dir(dirpath)
|
||||
print(statistics)
|
||||
exit(0 if statistics.errors == 0 and statistics.fatals == 0 else 1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
30
python/t/selftest_utils.py
Normal file
30
python/t/selftest_utils.py
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
|
||||
TOP = os.getcwd()
|
||||
|
||||
class SelftestUtils:
|
||||
|
||||
def setup(self):
|
||||
self.always_ignore_file('tmp-cmdtest-python/')
|
||||
|
||||
def create_CMDTEST_foo(self, *lines):
|
||||
self.create_file("CMDTEST_foo.py", [
|
||||
"class TC_foo(TestCase):",
|
||||
" def setup(self):",
|
||||
" #prepend_path #{BIN.inspect}",
|
||||
" #prepend_path #{PLATFORM_BIN.inspect}",
|
||||
" pass",
|
||||
"",
|
||||
" def test_foo(self):",
|
||||
[ " " + line for line in lines],
|
||||
])
|
||||
|
||||
@contextmanager
|
||||
def cmd_cmdtest(self, *args):
|
||||
cmdtest = "%s/cmdtest.py" % TOP
|
||||
command = "%s --quiet CMDTEST_foo.py" % cmdtest
|
||||
cmdline = ' '.join([command] + list(args))
|
||||
with self.cmd(cmdline) as c:
|
||||
yield c
|
Reference in New Issue
Block a user