223 lines
6.2 KiB
Python
223 lines
6.2 KiB
Python
#!/usr/bin/env python
|
|
# -*-mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
|
|
|
"""
|
|
Runs doctests locallly
|
|
doctest files are in the tests/ directory.
|
|
|
|
Note that when writing new test files, it will be convenient to use the command-line flags to avoid time-consuming reprovisioning or to target particular boxes or tests.
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
from sys import stderr
|
|
|
|
import argparse
|
|
import doctest
|
|
import glob
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
|
|
OPTIONS = doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE
|
|
|
|
# Convenience items for testing.
|
|
# We'll pass these as globals to the doctests.
|
|
|
|
if os.path.exists('/dev/null'):
|
|
DEV_NULL = open('/dev/null', 'w')
|
|
EXE='vagrant'
|
|
else:
|
|
DEV_NULL = open('NUL:', 'w')
|
|
EXE='sh /i/bin/vagrant.msys'
|
|
|
|
# find all our available boxes
|
|
#with open('Vagrantfile', 'r') as f:
|
|
# avail_boxes = re.findall(r'^\s+config.vm.define "(.+?)"', f.read(), re.MULTILINE)
|
|
# unused because it could be a Ruby variable
|
|
|
|
parser = argparse.ArgumentParser(description='Run playbook tests.')
|
|
parser.add_argument(
|
|
'-f', '--force',
|
|
action='store_true',
|
|
help="Force tests to proceed if box already exists. Do not destroy box at end of tests."
|
|
)
|
|
parser.add_argument(
|
|
'-n', '--no-provision',
|
|
action='store_true',
|
|
help="Skip provisioning."
|
|
)
|
|
parser.add_argument(
|
|
'-F', '--fail-fast',
|
|
action='store_true',
|
|
help="REPORT_ONLY_FIRST_FAILURE."
|
|
)
|
|
parser.add_argument(
|
|
'-o', '--options',
|
|
help=""
|
|
)
|
|
parser.add_argument(
|
|
'--haltonfail',
|
|
action='store_true',
|
|
help="Stop multibox tests after a fail; leave box running."
|
|
)
|
|
parser.add_argument(
|
|
'--file',
|
|
help="Specify a single doctest file (default tests/*.txt).",
|
|
)
|
|
parser.add_argument(
|
|
'--box',
|
|
help="Specify a particular target box",
|
|
action="append",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
if args.box:
|
|
lBoxes = args.box
|
|
else:
|
|
# find all our available running boxes
|
|
# sed -e 's/ .*//'
|
|
try:
|
|
s = os.system("vagrant global-status 2>&1| grep running | cut -f 1 -d ' ' ")
|
|
except StandardError as e:
|
|
print("ERROR: Unable to find any running boxes. Rerun with the --box argument.", file=sys.stderr)
|
|
raise
|
|
assert s, "ERROR: Unable to find a running box. Rerun with the --box argument."
|
|
lBoxes = s.split(' ')
|
|
|
|
# mplatform = None
|
|
# def get_mplatform():
|
|
# global mplatform
|
|
# # Linux-4.14.80-gentoo-x86_64-Intel-R-_Pentium-R-_CPU_N3700_@_1.60GHz-with-gentoo-2.2.1
|
|
# if mplatform is None:
|
|
# mplatform = subprocess.check_output(
|
|
# """vagrant ssh %s -c 'python -mplatform'""" % box,
|
|
# shell=True,
|
|
# stderr=DEV_NULL
|
|
# )
|
|
# return mplatform
|
|
|
|
print (repr(args))
|
|
|
|
def ssh_run(cmd):
|
|
"""
|
|
Run a command line in a vagrant box via vagrant ssh.
|
|
Return the output.
|
|
"""
|
|
|
|
return subprocess.check_output(
|
|
"""%s ssh %s -c '%s'""" % (EXE, box, cmd),
|
|
shell=True,
|
|
stderr=DEV_NULL
|
|
).replace('^@', '')
|
|
|
|
|
|
def run(cmd):
|
|
"""
|
|
Run a command in the host.
|
|
Stop the tests with a useful message if it fails.
|
|
"""
|
|
|
|
if sys.platform.startswith('win'):
|
|
p = subprocess.Popen(
|
|
cmd,
|
|
shell=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
)
|
|
else:
|
|
p = subprocess.Popen(
|
|
cmd,
|
|
shell=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
close_fds=True
|
|
)
|
|
stdout, stderr = p.communicate()
|
|
if p.returncode != 0:
|
|
print(stdout, file=sys.stderr)
|
|
# Stop the doctest
|
|
raise KeyboardInterrupt(stderr)
|
|
return stdout
|
|
|
|
def cut(y, column_nums, sort=False):
|
|
"""
|
|
returns a list of lines reduced to the chosen column_nums
|
|
"""
|
|
assert y and len(y) > 0, "Empty string passed to cut"
|
|
#
|
|
if hasattr(y,'encode'):
|
|
s = y.encode('utf-8')
|
|
else:
|
|
s = y
|
|
|
|
lines = s.splitlines()
|
|
line_lists = [l.split() for l in lines if l]
|
|
rez = ["\t".join([col[col_num]
|
|
for col_num in column_nums if col_num < len(col)])
|
|
for col in line_lists]
|
|
if sort:
|
|
return sorted(rez)
|
|
else:
|
|
return rez
|
|
|
|
|
|
def joined_cut(s, column_nums, sort=False):
|
|
return "\n".join(cut(s, column_nums, sort))
|
|
|
|
|
|
for box in lBoxes:
|
|
globs = {
|
|
'ssh_run': ssh_run,
|
|
'run': run,
|
|
'cut': cut,
|
|
'joined_cut': joined_cut,
|
|
'skip_provisioning': args.no_provision,
|
|
'no_provisioning': args.no_provision,
|
|
'forcing': args.force,
|
|
'box': box,
|
|
}
|
|
|
|
if args.fail_fast:
|
|
OPTIONS = doctest.REPORT_ONLY_FIRST_FAILURE | OPTIONS
|
|
if box and not args.force:
|
|
output = subprocess.check_output("%s status %s" % (EXE, box,), shell=True)
|
|
if re.search(r"%s\s+not created" % box, output) is None:
|
|
print( "Vagrant box already exists. Destroy it or use '-f' to skip this test.", file=sys.stderr)
|
|
print ("Use '-f' in combination with '-n' to skip provisioning.", file=sys.stderr)
|
|
exit(1)
|
|
|
|
if args.file is None:
|
|
files = glob.glob('tests/*.txt')
|
|
else:
|
|
files = [args.file]
|
|
|
|
for fn in files:
|
|
print ( "%s / %s" % (box, fn) , file=sys.stderr)
|
|
|
|
print( '*' * 50 )
|
|
print (box)
|
|
print( '*' * 50 )
|
|
print (fn)
|
|
print( '*' * 50 )
|
|
try:
|
|
failure_count, test_count = doctest.testfile(fn,
|
|
module_relative=False,
|
|
optionflags=OPTIONS,
|
|
globs=globs)
|
|
except Exception as e:
|
|
sys.stderr.write('\n'.join(sys.path) +'\n')
|
|
raise
|
|
if args.haltonfail and failure_count > 0:
|
|
print ("Test failures occurred. Stopping tests and leaving vagrant box %s running." % box , file=sys.stderr)
|
|
exit(1)
|
|
|
|
# Clean up our vagrant box.
|
|
|
|
if box and not args.force:
|
|
print ( "Destroying %s" % box , file=sys.stderr)
|
|
run("%s destroy %s -f" % (EXE, box,))
|
|
elif box:
|
|
print ( "Vagrant box %s left running." % box, file=sys.stderr)
|
|
|