├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── bashtest.py ├── circle.yml ├── setup.py ├── test_ls.bashtest ├── test_ls_extra.bashtest ├── test_margin.bashtest ├── test_nginx_logs_counting_unique_ip.bashtest ├── test_statutil.bashtest ├── test_wc.bashtest ├── testsuit ├── list-directory │ ├── file1 │ ├── file2.txt │ ├── file3.py │ └── file4.sh ├── nginx.log └── statutil │ ├── some.py │ ├── stat.sh │ ├── tests.bashtest │ └── testsuit │ ├── foo.py │ └── main.py └── update.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | local_settings.py 54 | 55 | # Flask stuff: 56 | instance/ 57 | .webassets-cache 58 | 59 | # Scrapy stuff: 60 | .scrapy 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | # PyBuilder 66 | target/ 67 | 68 | # IPython Notebook 69 | .ipynb_checkpoints 70 | 71 | # pyenv 72 | .python-version 73 | 74 | # celery beat schedule file 75 | celerybeat-schedule 76 | 77 | # dotenv 78 | .env 79 | 80 | # virtualenv 81 | venv/ 82 | ENV/ 83 | 84 | # Spyder project settings 85 | .spyderproject 86 | 87 | # Rope project settings 88 | .ropeproject 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Pahaz Blinov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Include the data files recursive-include data * 2 | 3 | # If using Python 2.6 or less, then have to include package data, even though 4 | # it's already declared in setup.py 5 | # include sample/*.dat 6 | 7 | include LICENSE 8 | include *.rst 9 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | |CircleCI| |version| 2 | 3 | |pyversions| |license| 4 | 5 | **Author**: `Pahaz Blinov`_ 6 | 7 | **Repo**: https://github.com/pahaz/bashtest/ 8 | 9 | BashTest is a UNIX command-line tool for the testing bash/shell utilites. 10 | 11 | This is a simplest way to write simple bash tests. 12 | 13 | **requirements**: Python2 or Python3 14 | 15 | **keywords**: bash unittest, unittesting, bash tesing, sh unit testing 16 | 17 | Installation 18 | ============ 19 | 20 | `bashtest`_ is on PyPI, so simply run: 21 | 22 | :: 23 | 24 | pip install bashtest 25 | 26 | or :: 27 | 28 | easy_install bashtest 29 | 30 | to have it installed in your environment. 31 | 32 | For installing from source, clone the 33 | `repo `_ and run:: 34 | 35 | python setup.py install 36 | 37 | If you don`t have **pip** you can `install it `_ 38 | 39 | Typical use case 40 | ================ 41 | 42 | You have a command line util or bash/sh script and you want to test it. 43 | 44 | For example, we want to test **ls** util. 45 | 46 | All of we need is to create text file with execution log. 47 | 48 | **./test_ls.bashtest** :: 49 | 50 | $ ls ./testsuit/list-directory 51 | file1 52 | file2.txt 53 | file3.py 54 | file4.sh 55 | 56 | Run this tests :: 57 | 58 | $ bashtest test_ls.bashtest 59 | 1 items passed all tests: 60 | 1 tests in test_ls.bashtest 61 | 1 tests in 1 items. 62 | 1 passed and 0 failed. 63 | Test passed. 64 | 65 | Test README examples 66 | ==================== 67 | 68 | You have a some open source project like this. And of course, as in any good 69 | open source project, you have examples. You can automatically check this 70 | examples. Just add **bashtest README.rst** in your CI tests. 71 | 72 | More examples 73 | ============= 74 | 75 | You can finde some examples in this project. Please check **test_*.bashtest** 76 | files 77 | 78 | changelog 79 | ========= 80 | 81 | ** 0.0.8 ** 82 | - add ``#skipbashtest`` support 83 | 84 | ** 0.0.7 ** 85 | - fix! remove ugly and useless options 86 | - feat! Test README.rst `#4 `_ 87 | - feat! find \*.bashtest by default `#3 `_ 88 | 89 | ** 0.0.6 ** 90 | - fix! bad command escaping bug `#5 `_ 91 | 92 | HELP 93 | ---- 94 | 95 | :: 96 | 97 | $ bashtest --help 98 | usage: bashtest [-h] [--exitcode] [-v] [-q] [--debug] [--version] 99 | [file [file ...]] 100 | 101 | BashTest is a UNIX command-line tool for running text-based bash tests. 102 | 103 | positional arguments: 104 | file Input file (by default uses *.bashtest) 105 | 106 | optional arguments: 107 | -h, --help show this help message and exit 108 | --exitcode Print exitcode after command end of output 109 | -v, --verbose Verbose output mode 110 | -q, --quiet Silent output mode 111 | --debug Print the debug information 112 | --version Print the version string 113 | 114 | 115 | .. _Pahaz Blinov: https://github.com/pahaz/ 116 | .. _bashtest: https://pypi.python.org/pypi/bashtest 117 | .. |CircleCI| image:: https://circleci.com/gh/pahaz/bashtest.svg?style=svg 118 | :target: https://circleci.com/gh/pahaz/bashtest 119 | .. |DwnMonth| image:: https://img.shields.io/pypi/dm/bashtest.svg 120 | .. |DwnWeek| image:: https://img.shields.io/pypi/dw/bashtest.svg 121 | .. |DwnDay| image:: https://img.shields.io/pypi/dd/bashtest.svg 122 | .. |pyversions| image:: https://img.shields.io/pypi/pyversions/bashtest.svg 123 | .. |version| image:: https://img.shields.io/pypi/v/bashtest.svg 124 | :target: `bashtest`_ 125 | .. |license| image:: https://img.shields.io/pypi/l/bashtest.svg 126 | :target: https://github.com/pahaz/bashtest/blob/master/LICENSE 127 | -------------------------------------------------------------------------------- /bashtest.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import doctest 3 | import re 4 | import sys 5 | import types 6 | import subprocess 7 | import glob 8 | 9 | __version__ = '0.0.8' 10 | NAME = 'bashtest' 11 | CHECK_EXITCODE = False 12 | 13 | _find_unsafe = re.compile(r'[^\w@%+=:,./-]').search 14 | 15 | 16 | def quote(s): 17 | """Return a shell-escaped version of the string *s*.""" 18 | if not s: 19 | return "''" 20 | if _find_unsafe(s) is None: 21 | return "'" + s + "'" 22 | 23 | # use single quotes, and put single quotes into double quotes 24 | # the string $'b is then quoted as '$'"'"'b' 25 | return "'" + s.replace("'", "'\"'\"'") + "'" 26 | 27 | 28 | def _fake_module_run(command): 29 | command = 'bash -c %s' % (quote(command)) 30 | kwargs = dict(stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) 31 | process = subprocess.Popen(command, **kwargs) 32 | stdout, _ = process.communicate() 33 | exitcode = process.poll() 34 | 35 | print(stdout.decode()) 36 | if CHECK_EXITCODE: 37 | print(''.format(exitcode)) 38 | 39 | 40 | def parseargs(): 41 | description = ( 42 | "BashTest is a UNIX command-line tool for running text-based bash " 43 | "tests. " 44 | ) 45 | 46 | parser = argparse.ArgumentParser(description=description) 47 | parser.add_argument('files', metavar='file', nargs='*', 48 | help='Input file (by default uses *.bashtest)') 49 | parser.add_argument('--exitcode', 50 | action='store_true', 51 | help='Print exitcode after command end of output') 52 | 53 | parser.add_argument('-v', '--verbose', 54 | dest='verbose', 55 | action='store_true', 56 | help='Verbose output mode') 57 | parser.add_argument('-q', '--quiet', 58 | dest='quiet', 59 | action='store_true', 60 | help='Silent output mode') 61 | parser.add_argument('--debug', 62 | dest='debug', 63 | action='store_true', 64 | help='Print the debug information') 65 | 66 | parser.add_argument('--version', 67 | dest='version', 68 | action='store_true', 69 | help='Print the version string') 70 | 71 | return parser.parse_args() 72 | 73 | 74 | def __re_repl(match): 75 | g1 = match.group(1) 76 | g2 = match.group(2) 77 | g2 = quote(g2.replace('\\', '\\\\')) 78 | if re.search(r'#skipbashtest', g2): 79 | return 'skip! %r %r' % (g1, g2) 80 | return '%s>>> run(%s)' % (g1, g2) 81 | 82 | 83 | def main(): 84 | global CHECK_EXITCODE 85 | 86 | args = parseargs() 87 | optionflags = doctest.NORMALIZE_WHITESPACE 88 | 89 | if not args.files: 90 | args.files = glob.glob('*.bashtest') 91 | 92 | if args.version: 93 | print(__version__) 94 | return 0 95 | 96 | if args.quiet: 97 | args.verbose = False 98 | args.debug = False 99 | 100 | if args.exitcode: 101 | CHECK_EXITCODE = True 102 | 103 | finder = doctest.DocTestFinder() 104 | runner = doctest.DocTestRunner( 105 | verbose=args.verbose, optionflags=optionflags) 106 | 107 | ret = 0 108 | rgx = re.compile('^(\s*)\$ (.+)$') 109 | margin = '' 110 | in_block = False 111 | for file in args.files: 112 | with open(file) as f: 113 | res = [] 114 | for line in f: 115 | line = line.rstrip('\r\n') 116 | match = rgx.match(line) 117 | if match: 118 | if in_block: 119 | res.append('') 120 | margin = match.group(1) 121 | in_block = True 122 | line, _ = rgx.subn(__re_repl, line) 123 | else: 124 | if not line and in_block: 125 | line = margin + '' 126 | if line and in_block: 127 | if not line.startswith(margin): 128 | in_block = False 129 | res.append('') 130 | res.append(line) 131 | 132 | res = '\n'.join(res) 133 | 134 | if args.debug: 135 | print('**** %s ****\n%s\n**** /%s ****\n\n' % (file, res, file)) 136 | 137 | fake_module = types.ModuleType(file, res) 138 | fake_module.run = _fake_module_run 139 | for test in finder.find(fake_module, file): 140 | runner.run(test) 141 | 142 | runner.summarize(verbose=not args.quiet) 143 | 144 | if runner.failures: 145 | ret = 1 146 | 147 | sys.exit(ret) 148 | 149 | if __name__ == '__main__': 150 | main() 151 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | override: 3 | - pip -V 4 | - pip install -U pip 5 | - pip install -U ipdb 6 | - pip install coveralls 7 | - pip install docutils 8 | - pip install check-manifest 9 | - pip install Pygments 10 | 11 | test: 12 | override: 13 | - python setup.py check -m -r -s 14 | - python setup.py build 15 | - python -m bashtest 16 | - pip install bashtest 17 | - bashtest README.rst 18 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """A setuptools based setup module. 2 | 3 | See: 4 | https://packaging.python.org/en/latest/distributing.html 5 | https://github.com/pypa/sampleproject 6 | """ 7 | 8 | import re 9 | from os import path 10 | from codecs import open # To use a consistent encoding 11 | 12 | from setuptools import setup # Always prefer setuptools over distutils 13 | 14 | here = path.abspath(path.dirname(__file__)) 15 | name = 'bashtest' 16 | description = 'UNIX command-line tool for bash/shell utils unit testing' 17 | url = 'https://github.com/pahaz/bashtest' 18 | ppa = 'https://pypi.python.org/packages/source/s/{0}/{0}-'.format(name) 19 | 20 | # Get the long description from the README file 21 | with open(path.join(here, 'README.rst'), encoding='utf-8') as f: 22 | long_description = f.read() 23 | 24 | with open(path.join(here, name + '.py'), encoding='utf-8') as f: 25 | data = f.read() 26 | version = eval(re.search("__version__[ ]*=[ ]*([^\r\n]+)", data).group(1)) 27 | 28 | setup( 29 | name=name, 30 | 31 | # Versions should comply with PEP440. For a discussion on single-sourcing 32 | # the version across setup.py and the project code, see 33 | # https://packaging.python.org/en/latest/single_source_version.html 34 | version=version, 35 | 36 | description=description, 37 | long_description=long_description, 38 | 39 | # The project's main homepage. 40 | url=url, 41 | download_url=ppa + version + '.zip', # noqa 42 | 43 | # Author details 44 | author='Pahaz Blinov', 45 | author_email='pahaz.blinov@gmail.com', 46 | 47 | # Choose your license 48 | license='MIT', 49 | 50 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 51 | classifiers=[ 52 | # How mature is this project? Common values are 53 | # 3 - Alpha 54 | # 4 - Beta 55 | # 5 - Production/Stable 56 | 'Development Status :: 3 - Alpha', 57 | 58 | # Indicate who your project is intended for 59 | 'Intended Audience :: Developers', 60 | 'Topic :: Utilities', 61 | 62 | # Pick your license as you wish (should match "license" above) 63 | 'License :: OSI Approved :: MIT License', 64 | 65 | # Specify the Python versions you support here. In particular, ensure 66 | # that you indicate whether you support Python 2, Python 3 or both. 67 | 'Programming Language :: Python :: 2', 68 | 'Programming Language :: Python :: 2.7', 69 | 'Programming Language :: Python :: 3', 70 | 'Programming Language :: Python :: 3.4', 71 | 'Programming Language :: Python :: 3.5', 72 | ], 73 | 74 | platforms=['unix', 'macos', 'windows'], 75 | 76 | # What does your project relate to? 77 | keywords='google spreadsheet api util helper', 78 | 79 | # You can just specify the packages manually here if your project is 80 | # simple. Or you can use find_packages(). 81 | # packages=find_packages(exclude=['contrib', 'docs', 'tests']), 82 | 83 | # Alternatively, if you want to distribute just a my_module.py, uncomment 84 | # this: 85 | py_modules=["%s" % name], 86 | 87 | # List run-time dependencies here. These will be installed by pip when 88 | # your project is installed. For an analysis of "install_requires" vs pip's 89 | # requirements files see: 90 | # https://packaging.python.org/en/latest/requirements.html 91 | install_requires=[ 92 | ], 93 | 94 | # List additional groups of dependencies here (e.g. development 95 | # dependencies). You can install these using the following syntax, 96 | # for example: 97 | # $ pip install -e .[dev,test] 98 | extras_require={ 99 | 'dev': ['check-manifest', 'docutils', 'Pygments'], 100 | # 'test': [ 101 | # 'tox>=1.8.1', 102 | # ], 103 | # 'build_sphinx': [ 104 | # 'sphinx', 105 | # 'sphinxcontrib-napoleon', 106 | # ], 107 | }, 108 | 109 | # If there are data files included in your packages that need to be 110 | # installed, specify them here. If using Python 2.6 or less, then these 111 | # have to be included in MANIFEST.in as well. 112 | # package_data={ 113 | # 'tests': ['testrsa.key'], 114 | # }, 115 | 116 | # To provide executable scripts, use entry points in preference to the 117 | # "scripts" keyword. Entry points provide cross-platform support and allow 118 | # pip to create the appropriate form of executable for the target platform. 119 | entry_points={ 120 | 'console_scripts': [ 121 | 'bashtest=bashtest:main', 122 | ] 123 | }, 124 | 125 | # Integrate tox with setuptools 126 | # cmdclass={'test': Tox}, 127 | ) 128 | -------------------------------------------------------------------------------- /test_ls.bashtest: -------------------------------------------------------------------------------- 1 | $ ls ./testsuit/list-directory 2 | file1 3 | file2.txt 4 | file3.py 5 | file4.sh 6 | 7 | -------------------------------------------------------------------------------- /test_ls_extra.bashtest: -------------------------------------------------------------------------------- 1 | $ chmod -R 0777 ./testsuit/list-directory 2 | $ ls -lAh ./testsuit/list-directory | awk {'print $1" "$9'} 3 | total 4 | -rwxrwxrwx file1 5 | -rwxrwxrwx file2.txt 6 | -rwxrwxrwx file3.py 7 | -rwxrwxrwx file4.sh 8 | 9 | -------------------------------------------------------------------------------- /test_margin.bashtest: -------------------------------------------------------------------------------- 1 | This is an example with the margin! and additional comments! 2 | 3 | $ cd ./testsuit/statutil && python ../../bashtest.py -v *.bashtest 4 | Trying: 5 | run('ls testsuit/') 6 | Expecting: 7 | foo.py main.py 8 | 9 | ok 10 | Trying: 11 | run('./stat.sh testsuit/') 12 | Expecting: 13 | Evaluate *.py statistics 14 | PYTHON FILES: 2 15 | PYTHON LINES: 4 16 | 17 | ok 18 | Trying: 19 | run('./stat.sh') 20 | Expecting: 21 | Evaluate *.py statistics 22 | PYTHON FILES: 3 23 | PYTHON LINES: 8 24 | 25 | ok 26 | 1 items passed all tests: 27 | 3 tests in tests.bashtest 28 | 3 tests in 1 items. 29 | 3 passed and 0 failed. 30 | Test passed. 31 | 32 | This is just text. It will be ignored! 33 | 34 | $ cat ./testsuit/nginx.log | cut -f3 -d ' ' | sort | uniq -c | sort -g -r 35 | 5 92.248.130.149 36 | 3 50.112.95.211 37 | 3 109.173.101.89 38 | 2 54.251.34.67 39 | 2 54.248.250.232 40 | 2 184.73.237.85 41 | 1 54.247.188.179 42 | 43 | Another example! 44 | 45 | The best test is: 46 | 47 | $ echo -e "\n\n\n\n1\n\n2\n\n\n3" 48 | 49 | 50 | 51 | 52 | 1 53 | 54 | 2 55 | 56 | 57 | 3 58 | 59 | 60 | eof! -------------------------------------------------------------------------------- /test_nginx_logs_counting_unique_ip.bashtest: -------------------------------------------------------------------------------- 1 | $ cat ./testsuit/nginx.log | cut -f3 -d ' ' | sort | uniq -c | sort -g -r 2 | 5 92.248.130.149 3 | 3 50.112.95.211 4 | 3 109.173.101.89 5 | 2 54.251.34.67 6 | 2 54.248.250.232 7 | 2 184.73.237.85 8 | 1 54.247.188.179 9 | -------------------------------------------------------------------------------- /test_statutil.bashtest: -------------------------------------------------------------------------------- 1 | $ cd ./testsuit/statutil && python ../../bashtest.py -v *.bashtest 2 | Trying: 3 | run('ls testsuit/') 4 | Expecting: 5 | foo.py main.py 6 | 7 | ok 8 | Trying: 9 | run('./stat.sh testsuit/') 10 | Expecting: 11 | Evaluate *.py statistics 12 | PYTHON FILES: 2 13 | PYTHON LINES: 4 14 | 15 | ok 16 | Trying: 17 | run('./stat.sh') 18 | Expecting: 19 | Evaluate *.py statistics 20 | PYTHON FILES: 3 21 | PYTHON LINES: 8 22 | 23 | ok 24 | 1 items passed all tests: 25 | 3 tests in tests.bashtest 26 | 3 tests in 1 items. 27 | 3 passed and 0 failed. 28 | Test passed. 29 | 30 | -------------------------------------------------------------------------------- /test_wc.bashtest: -------------------------------------------------------------------------------- 1 | $ cat ./testsuit/list-directory/file1 2 | line1 3 | line2 4 | 5 | $ cat ./testsuit/list-directory/file1 | wc -l 6 | 2 7 | 8 | -------------------------------------------------------------------------------- /testsuit/list-directory/file1: -------------------------------------------------------------------------------- 1 | line1 2 | line2 3 | -------------------------------------------------------------------------------- /testsuit/list-directory/file2.txt: -------------------------------------------------------------------------------- 1 | This utility was born from the fact that I keep forgetting how to use "sed", 2 | and I suck at Perl. It brings ad-hoc command-line piping sensibilities 3 | to the Python interpeter. 4 | 5 | (Version 1.2 does better outputting of list-like results, 6 | thanks to Mark Eichin.) 7 | -------------------------------------------------------------------------------- /testsuit/list-directory/file3.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.exit(22) 3 | -------------------------------------------------------------------------------- /testsuit/list-directory/file4.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | exit 33 3 | -------------------------------------------------------------------------------- /testsuit/nginx.log: -------------------------------------------------------------------------------- 1 | [19/Jul/2016:21:50:12 +0300] 184.73.237.85 [1/18089] uget=- uset=uuid=0C00A8C064768E57F54178C002700503 - rt="0.005" uht="0.005" urt="0.005" - status=302 - "HEAD / HTTP/1.0" len=214 sent=581/0 - "-" "NewRelicPinger/1.0 (1336397)" set_cookie="uuid=wKgADFeOdmTAeEH1AwVwAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/" 2 | [19/Jul/2016:21:50:21 +0300] 50.112.95.211 [1/18091] uget=- uset=uuid=0C00A8C06D768E57F54178C002710503 - rt="0.005" uht="0.005" urt="0.005" - status=302 - "HEAD / HTTP/1.0" len=214 sent=581/0 - "-" "NewRelicPinger/1.0 (1336397)" set_cookie="uuid=wKgADFeOdm3AeEH1AwVxAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/" 3 | [19/Jul/2016:21:52:09 +0300] 54.251.34.67 [1/18093] uget=- uset=uuid=0C00A8C0D9768E57F54178C002720503 - rt="0.007" uht="0.007" urt="0.007" - status=302 - "HEAD / HTTP/1.0" len=214 sent=581/0 - "-" "NewRelicPinger/1.0 (1336397)" set_cookie="uuid=wKgADFeOdtnAeEH1AwVyAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/" 4 | [19/Jul/2016:21:52:35 +0300] 54.248.250.232 [1/18095] uget=- uset=uuid=0C00A8C0F3768E57F54178C002730503 - rt="0.006" uht="0.006" urt="0.006" - status=302 - "HEAD / HTTP/1.0" len=214 sent=581/0 - "-" "NewRelicPinger/1.0 (1336397)" set_cookie="uuid=wKgADFeOdvPAeEH1AwVzAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/" 5 | [19/Jul/2016:21:30:21 +0300] 50.112.95.211 [1/17958] uget=- uset=uuid=0C00A8C0BD718E57F3414CC0029F0503 - rt="0.004" uht="0.004" urt="0.004" - status=302 - "HEAD / HTTP/1.0" len=214 sent=581/0 - "-" "NewRelicPinger/1.0 (1336397)" set_cookie="uuid=wKgADFeOcb3ATEHzAwWfAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/" 6 | [19/Jul/2016:21:30:22 +0300] 92.248.130.149 [5/17954] uget=uuid=0C00A8C0BF940F57526A38BB02200303 uset=- - rt="0.424" uht="0.396" urt="0.410" - status=200 - "GET /admin/moktoring/session/add/ HTTP/1.1" len=672 sent=42330/41540 - "https://o7x.examus.net/admin/moktoring/session/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" set_cookie="csrftoken=06uZBa0KnNSSsTg0q0G4ifvzZwQSBJ7n; expires=Tue, 18-Jul-2017 18:30:22 GMT; Max-Age=31449600; Path=/" 7 | [19/Jul/2016:21:30:23 +0300] 92.248.130.149 [6/17954] uget=uuid=0C00A8C0BF940F57526A38BB02200303 uset=- - rt="0.029" uht="0.026" urt="0.029" - status=200 - "GET /admin/jsi18n/ HTTP/1.1" len=559 sent=15167/14680 - "https://o7x.examus.net/admin/moktoring/session/add/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" set_cookie="-" 8 | [19/Jul/2016:21:30:23 +0300] 92.248.130.149 [3/17950] uget=uuid=0C00A8C0BF940F57526A38BB02200303 uset=- - rt="0.000" uht="-" urt="-" - status=200 - "GET /static/admin/img/icon-calendar.svg HTTP/1.1" len=604 sent=1612/1086 - "https://o7x.examus.net/static/admin/css/widgets.css" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" set_cookie="-" 9 | [19/Jul/2016:21:30:23 +0300] 92.248.130.149 [7/17954] uget=uuid=0C00A8C0BF940F57526A38BB02200303 uset=- - rt="0.000" uht="-" urt="-" - status=200 - "GET /static/admin/img/icon-clock.svg HTTP/1.1" len=601 sent=1202/677 - "https://o7x.examus.net/static/admin/css/widgets.css" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" set_cookie="-" 10 | [19/Jul/2016:21:32:00 +0300] 54.251.34.67 [1/17963] uget=- uset=uuid=0C00A8C020728E57F3414CC002A00503 - rt="0.008" uht="0.008" urt="0.008" - status=302 - "HEAD / HTTP/1.0" len=214 sent=581/0 - "-" "NewRelicPinger/1.0 (1336397)" set_cookie="uuid=wKgADFeOciDATEHzAwWgAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/" 11 | [19/Jul/2016:21:32:35 +0300] 54.248.250.232 [1/17965] uget=- uset=uuid=0C00A8C043728E57F3414CC002A10503 - rt="0.007" uht="0.007" urt="0.007" - status=302 - "HEAD / HTTP/1.0" len=214 sent=581/0 - "-" "NewRelicPinger/1.0 (1336397)" set_cookie="uuid=wKgADFeOckPATEHzAwWhAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/" 12 | [19/Jul/2016:21:32:38 +0300] 184.73.237.85 [1/17967] uget=- uset=uuid=0C00A8C046728E57F3414CC002A20503 - rt="0.005" uht="0.005" urt="0.005" - status=302 - "HEAD / HTTP/1.0" len=214 sent=581/0 - "-" "NewRelicPinger/1.0 (1336397)" set_cookie="uuid=wKgADFeOckbATEHzAwWiAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/" 13 | [19/Jul/2016:21:32:40 +0300] 54.247.188.179 [1/17969] uget=- uset=uuid=0C00A8C048728E57F3414CC002A30503 - rt="0.007" uht="0.007" urt="0.007" - status=302 - "HEAD / HTTP/1.0" len=214 sent=581/0 - "-" "NewRelicPinger/1.0 (1336397)" set_cookie="uuid=wKgADFeOckjATEHzAwWjAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/" 14 | [19/Jul/2016:21:32:51 +0300] 50.112.95.211 [1/17971] uget=- uset=uuid=0C00A8C053728E57F3414CC002A40503 - rt="0.005" uht="0.005" urt="0.005" - status=302 - "HEAD / HTTP/1.0" len=214 sent=581/0 - "-" "NewRelicPinger/1.0 (1336397)" set_cookie="uuid=wKgADFeOclPATEHzAwWkAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/" 15 | [19/Jul/2016:21:33:42 +0300] 109.173.101.89 [1/17973] uget=- uset=uuid=0C00A8C086728E57F3414CC002A50503 - rt="0.009" uht="0.005" urt="0.009" - status=302 - "GET /logout/?reason=startApplication HTTP/1.1" len=433 sent=606/5 - "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" set_cookie="uuid=wKgADFeOcobATEHzAwWlAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/" 16 | [19/Jul/2016:21:33:42 +0300] 109.173.101.89 [2/17973] uget=uuid=0C00A8C086728E57F3414CC002A50503 uset=- - rt="0.010" uht="0.006" urt="0.010" - status=302 - "GET / HTTP/1.1" len=442 sent=530/5 - "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" set_cookie="-" 17 | [19/Jul/2016:21:33:42 +0300] 109.173.101.89 [3/17973] uget=uuid=0C00A8C086728E57F3414CC002A50503 uset=- - rt="0.047" uht="0.039" urt="0.047" - status=200 - "GET /login/?next=/ HTTP/1.1" len=455 sent=8370/7580 - "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" set_cookie="csrftoken=xZxfdkNjadcYIQXcKg8Wv0x8nQq9NwL0; expires=Tue, 18-Jul-2017 18:33:42 GMT; Max-Age=31449600; Path=/" 18 | [19/Jul/2016:20:09:50 +0300] 92.248.130.149 [22/17536] uget=uuid=0C00A8C0BF940F57526A38BB02200303 uset=- - rt="0.078" uht="0.072" urt="0.078" - status=302 - "POST /admin/customauth/user/?q=%D0%9F%D0%B0%D1%81%D0%B5%D1%87%D0%BD%D0%B8%D0%BA HTTP/1.1" len=1189 sent=1063/5 - "https://o7x.examus.net/admin/customauth/user/?q=%D0%9F%D0%B0%D1%81%D0%B5%D1%87%D0%BD%D0%B8%D0%BA" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" set_cookie="messages=\x2292939612ff14300f94f8722c99618e22187e3503$[[\x5C\x22__json_message\x5C\x22\x5C0540\x5C05425\x5C054\x5C\x221 \x5C\x5Cu041f\x5C\x5Cu043e\x5C\x5Cu043b\x5C\x5Cu044c\x5C\x5Cu0437\x5C\x5Cu043e\x5C\x5Cu0432\x5C\x5Cu0430\x5C\x5Cu0442\x5C\x5Cu0435\x5C\x5Cu043b\x5C\x5Cu044c \x5C\x5Cu0431\x5C\x5Cu044b\x5C\x5Cu043b \x5C\x5Cu0443\x5C\x5Cu0441\x5C\x5Cu043f\x5C\x5Cu0435\x5C\x5Cu0448\x5C\x5Cu043d\x5C\x5Cu043e \x5C\x5Cu0438\x5C\x5Cu0437\x5C\x5Cu043c\x5C\x5Cu0435\x5C\x5Cu043d\x5C\x5Cu0435\x5C\x5Cu043d.\x5C\x22]]\x22; Path=/" 19 | -------------------------------------------------------------------------------- /testsuit/statutil/some.py: -------------------------------------------------------------------------------- 1 | qwe 2 | qwe 3 | qwe 4 | qwe 5 | -------------------------------------------------------------------------------- /testsuit/statutil/stat.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -z "$1" ] 4 | then 5 | DIR=./ 6 | else 7 | DIR=$1 8 | fi 9 | 10 | echo "Evaluate *.py statistics" 11 | FILES=$(find $DIR -name '*.py' | wc -l) 12 | LINES=$((find $DIR -name '*.py' -print0 | xargs -0 cat) | wc -l) 13 | echo "PYTHON FILES: $FILES" 14 | echo "PYTHON LINES: $LINES" 15 | -------------------------------------------------------------------------------- /testsuit/statutil/tests.bashtest: -------------------------------------------------------------------------------- 1 | $ ls testsuit/ 2 | foo.py main.py 3 | 4 | $ ./stat.sh testsuit/ 5 | Evaluate *.py statistics 6 | PYTHON FILES: 2 7 | PYTHON LINES: 4 8 | 9 | $ ./stat.sh 10 | Evaluate *.py statistics 11 | PYTHON FILES: 3 12 | PYTHON LINES: 8 13 | 14 | -------------------------------------------------------------------------------- /testsuit/statutil/testsuit/foo.py: -------------------------------------------------------------------------------- 1 | A=1 2 | b=A+1 3 | -------------------------------------------------------------------------------- /testsuit/statutil/testsuit/main.py: -------------------------------------------------------------------------------- 1 | import qwe 2 | print(qwe) 3 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -x 4 | 5 | let num_git_changes="$(git status --porcelain | wc -l)" 6 | let num_git_changes="$(printf '%s\n' "$num_git_changes")" 7 | if [ "$num_git_changes" != "0" ]; then 8 | echo "please commit or stash changes before" 9 | exit 1 10 | fi 11 | 12 | python setup.py build sdist upload 13 | if [ "$?" != "0" ]; then 14 | echo "build and upload problem" 15 | exit 2 16 | fi 17 | 18 | VERSION=$(python setup.py --version) 19 | NAME=$(python setup.py --name) 20 | NEXTVERSION=`echo $VERSION | python3 -c "v = input().strip().split('.'); v[-1] = str(int(v[-1]) + 1); print('.'.join(v))"` 21 | 22 | git tag -a v$VERSION -m "version $VERSION" 23 | sed -e "s|$VERSION|$NEXTVERSION|g" -i.back $NAME.py $NAME/* 24 | rm *.back 25 | --------------------------------------------------------------------------------